partx: complete rewrite

Co-Author: Karel Zak <kzak@redhat.com>
Signed-off-by: Davidlohr Bueso <dave@gnu.org>
Signed-off-by: Karel Zak <kzak@redhat.com>
This commit is contained in:
Davidlohr Bueso 2010-12-09 21:54:17 +01:00 committed by Karel Zak
parent 4bfa39d5d5
commit c4ecaf21d5
19 changed files with 970 additions and 2145 deletions

View file

@ -26,5 +26,6 @@ static inline void xstrncpy(char *dest, const char *src, size_t n)
}
extern void strmode(mode_t mode, char *str);
extern char *size_to_human_string(uint64_t bytes);
#endif

View file

@ -3,12 +3,15 @@
* Copyright (C) 2010 Davidlohr Bueso <dave@gnu.org>
*/
#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
#include <ctype.h>
#include <errno.h>
#include <err.h>
#include <sys/stat.h>
#include <locale.h>
#include <string.h>
static int do_scale_by_power (uintmax_t *x, int base, int power)
{
@ -224,3 +227,49 @@ void strmode(mode_t mode, char *str)
: (mode & S_IXOTH ? 'x' : '-'));
str[10] = '\0';
}
/*
* returns exponent (2^x=n) in range KiB..PiB
*/
static int get_exp(uint64_t n)
{
int shft;
for (shft = 10; shft <= 60; shft += 10) {
if (n < (1ULL << shft))
break;
}
return shft - 10;
}
char *size_to_human_string(uint64_t bytes)
{
char buf[32];
int dec, frac, exp;
const char *letters = "BKMGTP";
char c;
exp = get_exp(bytes);
c = *(letters + (exp ? exp / 10 : 0));
dec = exp ? bytes / (1ULL << exp) : bytes;
frac = exp ? bytes % (1ULL << exp) : 0;
if (frac) {
/* round */
frac = (frac / (1ULL << (exp - 10)) + 50) / 100;
if (frac == 10)
dec++, frac = 0;
}
if (frac) {
struct lconv const *l = localeconv();
char *dp = l ? l->decimal_point : NULL;
if (!dp || !*dp)
dp = ".";
snprintf(buf, sizeof(buf), "%d%s%d%c", dec, dp, frac, c);
} else
snprintf(buf, sizeof(buf), "%d%c", dec, c);
return strdup(buf);
}

View file

@ -235,52 +235,6 @@ static struct dirent *xreaddir(DIR *dp)
return d;
}
/*
* returns exponent (2^x=n) in range KiB..PiB
*/
static int get_exp(uint64_t n)
{
int shft;
for (shft = 10; shft <= 60; shft += 10) {
if (n < (1ULL << shft))
break;
}
return shft - 10;
}
static char *size_to_human_string(uint64_t bytes)
{
char buf[32];
int dec, frac, exp;
const char *letters = "BKMGTP";
char c;
exp = get_exp(bytes);
c = *(letters + (exp ? exp / 10 : 0));
dec = exp ? bytes / (1ULL << exp) : bytes;
frac = exp ? bytes % (1ULL << exp) : 0;
if (frac) {
/* round */
frac = (frac / (1ULL << (exp - 10)) + 50) / 100;
if (frac == 10)
dec++, frac = 0;
}
if (frac) {
struct lconv const *l = localeconv();
char *dp = l ? l->decimal_point : NULL;
if (!dp || !*dp)
dp = ".";
snprintf(buf, sizeof(buf), "%d%s%d%c", dec, dp, frac, c);
} else
snprintf(buf, sizeof(buf), "%d%c", dec, c);
return xstrdup(buf);
}
static int is_partition_dirent(DIR *dir, struct dirent *d, const char *parent_name)
{

View file

@ -1,15 +1,23 @@
include $(top_srcdir)/config/include-Makefile.am
if BUILD_PARTX
usrsbin_exec_PROGRAMS = addpart delpart
dist_man_MANS = addpart.8 delpart.8
usrsbin_exec_PROGRAMS = addpart delpart partx
partx_SOURCES = bsd.c dos.c partx.c solaris.c unixware.c sun.c mac.c gpt.c crc32.c \
efi.h gpt.h crc32.h partx.h dos.h $(top_srcdir)/lib/blkdev.c
if BUILD_PARTX
if BUILD_LIBBLKID
usrsbin_exec_PROGRAMS += partx
partx_SOURCES = partx.c partx.h \
$(top_srcdir)/lib/blkdev.c \
$(top_srcdir)/lib/tt.c \
$(top_srcdir)/lib/strutils.c
if LINUX
partx_SOURCES += $(top_srcdir)/lib/linux_version.c
endif
dist_man_MANS = addpart.8 delpart.8 partx.8
partx_CFLAGS = -I$(ul_libblkid_incdir)
partx_LDADD = $(ul_libblkid_la)
dist_man_MANS += partx.8
endif
endif

View file

@ -1,83 +0,0 @@
#include <stdio.h>
#include "partx.h"
#define BSD_DISKMAGIC (0x82564557UL) /* The disk magic number */
#define XBSD_MAXPARTITIONS 16
#define BSD_FS_UNUSED 0
struct bsd_disklabel {
unsigned int d_magic; /* the magic number */
short int d_type; /* drive type */
short int d_subtype; /* controller/d_type specific */
char d_typename[16]; /* type name, e.g. "eagle" */
char d_packname[16]; /* pack identifier */
unsigned int d_secsize; /* # of bytes per sector */
unsigned int d_nsectors; /* # of data sectors per track */
unsigned int d_ntracks; /* # of tracks per cylinder */
unsigned int d_ncylinders; /* # of data cylinders per unit */
unsigned int d_secpercyl; /* # of data sectors per cylinder */
unsigned int d_secperunit; /* # of data sectors per unit */
unsigned short d_sparespertrack;/* # of spare sectors per track */
unsigned short d_sparespercyl; /* # of spare sectors per cylinder */
unsigned int d_acylinders; /* # of alt. cylinders per unit */
unsigned short d_rpm; /* rotational speed */
unsigned short d_interleave; /* hardware sector interleave */
unsigned short d_trackskew; /* sector 0 skew, per track */
unsigned short d_cylskew; /* sector 0 skew, per cylinder */
unsigned int d_headswitch; /* head switch time, usec */
unsigned int d_trkseek; /* track-to-track seek, usec */
unsigned int d_flags; /* generic flags */
unsigned int d_drivedata[5]; /* drive-type specific information */
unsigned int d_spare[5]; /* reserved for future use */
unsigned int d_magic2; /* the magic number (again) */
unsigned short d_checksum; /* xor of data incl. partitions */
/* filesystem and partition information: */
unsigned short d_npartitions; /* number of partitions in following */
unsigned int d_bbsize; /* size of boot area at sn0, bytes */
unsigned int d_sbsize; /* max size of fs superblock, bytes */
struct bsd_partition { /* the partition table */
unsigned int p_size; /* number of sectors in partition */
unsigned int p_offset; /* starting sector */
unsigned int p_fsize; /* filesystem basic fragment size */
unsigned char p_fstype; /* filesystem type, see below */
unsigned char p_frag; /* filesystem fragments per block */
unsigned short p_cpg; /* filesystem cylinders per group */
} d_partitions[XBSD_MAXPARTITIONS];/* actually may be more */
};
int
read_bsd_pt(int fd, struct slice all, struct slice *sp, int ns) {
struct bsd_disklabel *l;
struct bsd_partition *p;
unsigned int offset = all.start;
int max_partitions;
unsigned char *bp;
int n = 0;
bp = getblock(fd, offset+1); /* 1 sector suffices */
if (bp == NULL)
return -1;
l = (struct bsd_disklabel *) bp;
if (l->d_magic != BSD_DISKMAGIC)
return -1;
max_partitions = 16;
if (l->d_npartitions < max_partitions)
max_partitions = l->d_npartitions;
for (p = l->d_partitions; p - l->d_partitions < max_partitions; p++) {
if (p->p_fstype == BSD_FS_UNUSED)
/* nothing */;
else if (n < ns) {
sp[n].start = p->p_offset;
sp[n].size = p->p_size;
n++;
} else {
fprintf(stderr,
"bsd_partition: too many slices\n");
break;
}
}
return n;
}

View file

@ -1,393 +0,0 @@
/*
* crc32.c
* This code is in the public domain; copyright abandoned.
* Liability for non-performance of this code is limited to the amount
* you paid for it. Since it is distributed for free, your refund will
* be very very small. If it breaks, you get to keep both pieces.
*/
#include "crc32.h"
#if __GNUC__ >= 3 /* 2.x has "attribute", but only 3.0 has "pure */
#define attribute(x) __attribute__(x)
#else
#define attribute(x)
#endif
/*
* There are multiple 16-bit CRC polynomials in common use, but this is
* *the* standard CRC-32 polynomial, first popularized by Ethernet.
* x^32+x^26+x^23+x^22+x^16+x^12+x^11+x^10+x^8+x^7+x^5+x^4+x^2+x^1+x^0
*/
#define CRCPOLY_LE 0xedb88320
#define CRCPOLY_BE 0x04c11db7
/* How many bits at a time to use. Requires a table of 4<<CRC_xx_BITS bytes. */
/* For less performance-sensitive, use 4 */
#define CRC_LE_BITS 8
#define CRC_BE_BITS 8
/*
* Little-endian CRC computation. Used with serial bit streams sent
* lsbit-first. Be sure to use cpu_to_le32() to append the computed CRC.
*/
#if CRC_LE_BITS > 8 || CRC_LE_BITS < 1 || CRC_LE_BITS & CRC_LE_BITS-1
# error CRC_LE_BITS must be a power of 2 between 1 and 8
#endif
#if CRC_LE_BITS == 1
/*
* In fact, the table-based code will work in this case, but it can be
* simplified by inlining the table in ?: form.
*/
#define crc32init_le()
#define crc32cleanup_le()
/**
* crc32_le() - Calculate bitwise little-endian Ethernet AUTODIN II CRC32
* @crc - seed value for computation. ~0 for Ethernet, sometimes 0 for
* other uses, or the previous crc32 value if computing incrementally.
* @p - pointer to buffer over which CRC is run
* @len - length of buffer @p
*
*/
uint32_t attribute((pure)) crc32_le(uint32_t crc, unsigned char const *p, size_t len)
{
int i;
while (len--) {
crc ^= *p++;
for (i = 0; i < 8; i++)
crc = (crc >> 1) ^ ((crc & 1) ? CRCPOLY_LE : 0);
}
return crc;
}
#else /* Table-based approach */
static uint32_t *crc32table_le;
/**
* crc32init_le() - allocate and initialize LE table data
*
* crc is the crc of the byte i; other entries are filled in based on the
* fact that crctable[i^j] = crctable[i] ^ crctable[j].
*
*/
static int
crc32init_le(void)
{
unsigned i, j;
uint32_t crc = 1;
crc32table_le =
malloc((1 << CRC_LE_BITS) * sizeof(uint32_t));
if (!crc32table_le)
return 1;
crc32table_le[0] = 0;
for (i = 1 << (CRC_LE_BITS - 1); i; i >>= 1) {
crc = (crc >> 1) ^ ((crc & 1) ? CRCPOLY_LE : 0);
for (j = 0; j < 1 << CRC_LE_BITS; j += 2 * i)
crc32table_le[i + j] = crc ^ crc32table_le[j];
}
return 0;
}
/**
* crc32cleanup_le(): free LE table data
*/
static void
crc32cleanup_le(void)
{
free(crc32table_le);
crc32table_le = NULL;
}
/**
* crc32_le() - Calculate bitwise little-endian Ethernet AUTODIN II CRC32
* @crc - seed value for computation. ~0 for Ethernet, sometimes 0 for
* other uses, or the previous crc32 value if computing incrementally.
* @p - pointer to buffer over which CRC is run
* @len - length of buffer @p
*
*/
uint32_t attribute((pure)) crc32_le(uint32_t crc, unsigned char const *p, size_t len)
{
while (len--) {
# if CRC_LE_BITS == 8
crc = (crc >> 8) ^ crc32table_le[(crc ^ *p++) & 255];
# elif CRC_LE_BITS == 4
crc ^= *p++;
crc = (crc >> 4) ^ crc32table_le[crc & 15];
crc = (crc >> 4) ^ crc32table_le[crc & 15];
# elif CRC_LE_BITS == 2
crc ^= *p++;
crc = (crc >> 2) ^ crc32table_le[crc & 3];
crc = (crc >> 2) ^ crc32table_le[crc & 3];
crc = (crc >> 2) ^ crc32table_le[crc & 3];
crc = (crc >> 2) ^ crc32table_le[crc & 3];
# endif
}
return crc;
}
#endif
/*
* Big-endian CRC computation. Used with serial bit streams sent
* msbit-first. Be sure to use cpu_to_be32() to append the computed CRC.
*/
#if CRC_BE_BITS > 8 || CRC_BE_BITS < 1 || CRC_BE_BITS & CRC_BE_BITS-1
# error CRC_BE_BITS must be a power of 2 between 1 and 8
#endif
#if CRC_BE_BITS == 1
/*
* In fact, the table-based code will work in this case, but it can be
* simplified by inlining the table in ?: form.
*/
#define crc32init_be()
#define crc32cleanup_be()
/**
* crc32_be() - Calculate bitwise big-endian Ethernet AUTODIN II CRC32
* @crc - seed value for computation. ~0 for Ethernet, sometimes 0 for
* other uses, or the previous crc32 value if computing incrementally.
* @p - pointer to buffer over which CRC is run
* @len - length of buffer @p
*
*/
uint32_t attribute((pure)) crc32_be(uint32_t crc, unsigned char const *p, size_t len)
{
int i;
while (len--) {
crc ^= *p++ << 24;
for (i = 0; i < 8; i++)
crc =
(crc << 1) ^ ((crc & 0x80000000) ? CRCPOLY_BE :
0);
}
return crc;
}
#else /* Table-based approach */
static uint32_t *crc32table_be;
/**
* crc32init_be() - allocate and initialize BE table data
*/
static int
crc32init_be(void)
{
unsigned i, j;
uint32_t crc = 0x80000000;
crc32table_be =
malloc((1 << CRC_BE_BITS) * sizeof(uint32_t));
if (!crc32table_be)
return 1;
crc32table_be[0] = 0;
for (i = 1; i < 1 << CRC_BE_BITS; i <<= 1) {
crc = (crc << 1) ^ ((crc & 0x80000000) ? CRCPOLY_BE : 0);
for (j = 0; j < i; j++)
crc32table_be[i + j] = crc ^ crc32table_be[j];
}
return 0;
}
/**
* crc32cleanup_be(): free BE table data
*/
static void
crc32cleanup_be(void)
{
free(crc32table_be);
crc32table_be = NULL;
}
/**
* crc32_be() - Calculate bitwise big-endian Ethernet AUTODIN II CRC32
* @crc - seed value for computation. ~0 for Ethernet, sometimes 0 for
* other uses, or the previous crc32 value if computing incrementally.
* @p - pointer to buffer over which CRC is run
* @len - length of buffer @p
*
*/
uint32_t attribute((pure)) crc32_be(uint32_t crc, unsigned char const *p, size_t len)
{
while (len--) {
# if CRC_BE_BITS == 8
crc = (crc << 8) ^ crc32table_be[(crc >> 24) ^ *p++];
# elif CRC_BE_BITS == 4
crc ^= *p++ << 24;
crc = (crc << 4) ^ crc32table_be[crc >> 28];
crc = (crc << 4) ^ crc32table_be[crc >> 28];
# elif CRC_BE_BITS == 2
crc ^= *p++ << 24;
crc = (crc << 2) ^ crc32table_be[crc >> 30];
crc = (crc << 2) ^ crc32table_be[crc >> 30];
crc = (crc << 2) ^ crc32table_be[crc >> 30];
crc = (crc << 2) ^ crc32table_be[crc >> 30];
# endif
}
return crc;
}
#endif
/*
* A brief CRC tutorial.
*
* A CRC is a long-division remainder. You add the CRC to the message,
* and the whole thing (message+CRC) is a multiple of the given
* CRC polynomial. To check the CRC, you can either check that the
* CRC matches the recomputed value, *or* you can check that the
* remainder computed on the message+CRC is 0. This latter approach
* is used by a lot of hardware implementations, and is why so many
* protocols put the end-of-frame flag after the CRC.
*
* It's actually the same long division you learned in school, except that
* - We're working in binary, so the digits are only 0 and 1, and
* - When dividing polynomials, there are no carries. Rather than add and
* subtract, we just xor. Thus, we tend to get a bit sloppy about
* the difference between adding and subtracting.
*
* A 32-bit CRC polynomial is actually 33 bits long. But since it's
* 33 bits long, bit 32 is always going to be set, so usually the CRC
* is written in hex with the most significant bit omitted. (If you're
* familiar with the IEEE 754 floating-point format, it's the same idea.)
*
* Note that a CRC is computed over a string of *bits*, so you have
* to decide on the endianness of the bits within each byte. To get
* the best error-detecting properties, this should correspond to the
* order they're actually sent. For example, standard RS-232 serial is
* little-endian; the most significant bit (sometimes used for parity)
* is sent last. And when appending a CRC word to a message, you should
* do it in the right order, matching the endianness.
*
* Just like with ordinary division, the remainder is always smaller than
* the divisor (the CRC polynomial) you're dividing by. Each step of the
* division, you take one more digit (bit) of the dividend and append it
* to the current remainder. Then you figure out the appropriate multiple
* of the divisor to subtract to being the remainder back into range.
* In binary, it's easy - it has to be either 0 or 1, and to make the
* XOR cancel, it's just a copy of bit 32 of the remainder.
*
* When computing a CRC, we don't care about the quotient, so we can
* throw the quotient bit away, but subtract the appropriate multiple of
* the polynomial from the remainder and we're back to where we started,
* ready to process the next bit.
*
* A big-endian CRC written this way would be coded like:
* for (i = 0; i < input_bits; i++) {
* multiple = remainder & 0x80000000 ? CRCPOLY : 0;
* remainder = (remainder << 1 | next_input_bit()) ^ multiple;
* }
* Notice how, to get at bit 32 of the shifted remainder, we look
* at bit 31 of the remainder *before* shifting it.
*
* But also notice how the next_input_bit() bits we're shifting into
* the remainder don't actually affect any decision-making until
* 32 bits later. Thus, the first 32 cycles of this are pretty boring.
* Also, to add the CRC to a message, we need a 32-bit-long hole for it at
* the end, so we have to add 32 extra cycles shifting in zeros at the
* end of every message,
*
* So the standard trick is to rearrage merging in the next_input_bit()
* until the moment it's needed. Then the first 32 cycles can be precomputed,
* and merging in the final 32 zero bits to make room for the CRC can be
* skipped entirely.
* This changes the code to:
* for (i = 0; i < input_bits; i++) {
* remainder ^= next_input_bit() << 31;
* multiple = (remainder & 0x80000000) ? CRCPOLY : 0;
* remainder = (remainder << 1) ^ multiple;
* }
* With this optimization, the little-endian code is simpler:
* for (i = 0; i < input_bits; i++) {
* remainder ^= next_input_bit();
* multiple = (remainder & 1) ? CRCPOLY : 0;
* remainder = (remainder >> 1) ^ multiple;
* }
*
* Note that the other details of endianness have been hidden in CRCPOLY
* (which must be bit-reversed) and next_input_bit().
*
* However, as long as next_input_bit is returning the bits in a sensible
* order, we can actually do the merging 8 or more bits at a time rather
* than one bit at a time:
* for (i = 0; i < input_bytes; i++) {
* remainder ^= next_input_byte() << 24;
* for (j = 0; j < 8; j++) {
* multiple = (remainder & 0x80000000) ? CRCPOLY : 0;
* remainder = (remainder << 1) ^ multiple;
* }
* }
* Or in little-endian:
* for (i = 0; i < input_bytes; i++) {
* remainder ^= next_input_byte();
* for (j = 0; j < 8; j++) {
* multiple = (remainder & 1) ? CRCPOLY : 0;
* remainder = (remainder << 1) ^ multiple;
* }
* }
* If the input is a multiple of 32 bits, you can even XOR in a 32-bit
* word at a time and increase the inner loop count to 32.
*
* You can also mix and match the two loop styles, for example doing the
* bulk of a message byte-at-a-time and adding bit-at-a-time processing
* for any fractional bytes at the end.
*
* The only remaining optimization is to the byte-at-a-time table method.
* Here, rather than just shifting one bit of the remainder to decide
* in the correct multiple to subtract, we can shift a byte at a time.
* This produces a 40-bit (rather than a 33-bit) intermediate remainder,
* but again the multiple of the polynomial to subtract depends only on
* the high bits, the high 8 bits in this case.
*
* The multile we need in that case is the low 32 bits of a 40-bit
* value whose high 8 bits are given, and which is a multiple of the
* generator polynomial. This is simply the CRC-32 of the given
* one-byte message.
*
* Two more details: normally, appending zero bits to a message which
* is already a multiple of a polynomial produces a larger multiple of that
* polynomial. To enable a CRC to detect this condition, it's common to
* invert the CRC before appending it. This makes the remainder of the
* message+crc come out not as zero, but some fixed non-zero value.
*
* The same problem applies to zero bits prepended to the message, and
* a similar solution is used. Instead of starting with a remainder of
* 0, an initial remainder of all ones is used. As long as you start
* the same way on decoding, it doesn't make a difference.
*/
/**
* init_crc32(): generates CRC32 tables
*
* On successful initialization, use count is increased.
* This guarantees that the library functions will stay resident
* in memory, and prevents someone from 'rmmod crc32' while
* a driver that needs it is still loaded.
* This also greatly simplifies drivers, as there's no need
* to call an initialization/cleanup function from each driver.
* Since crc32.o is a library module, there's no requirement
* that the user can unload it.
*/
int
init_crc32(void)
{
int rc1, rc2, rc;
rc1 = crc32init_le();
rc2 = crc32init_be();
rc = rc1 || rc2;
return rc;
}
/**
* cleanup_crc32(): frees crc32 data when no longer needed
*/
void
cleanup_crc32(void)
{
crc32cleanup_le();
crc32cleanup_be();
}

View file

@ -1,19 +0,0 @@
/*
* crc32.h
*/
#ifndef _CRC32_H
#define _CRC32_H
#include <inttypes.h>
#include <stdlib.h>
extern int init_crc32(void);
extern void cleanup_crc32(void);
extern uint32_t crc32_le(uint32_t crc, unsigned char const *p, size_t len);
extern uint32_t crc32_be(uint32_t crc, unsigned char const *p, size_t len);
#define crc32(seed, data, length) crc32_le(seed, (unsigned char const *)data, length)
#define ether_crc_le(length, data) crc32_le(~0, data, length)
#define ether_crc(length, data) crc32_be(~0, data, length)
#endif /* _CRC32_H */

View file

@ -1,138 +0,0 @@
#include <stdio.h>
#include "blkdev.h"
#include "partx.h"
#include "dos.h"
static int
is_extended(int type) {
return (type == 5 || type == 0xf || type == 0x85);
}
/* assemble badly aligned little endian integer */
static inline unsigned int
assemble4le(unsigned char *p) {
return p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24);
}
static inline unsigned int
partition_start(struct partition *p) {
return assemble4le(&(p->start_sect[0]));
}
static inline unsigned int
partition_size(struct partition *p) {
return assemble4le(&(p->nr_sects[0]));
}
static int
read_extended_partition(int fd, struct partition *ep,
struct slice *sp, int ns, int ssf)
{
struct partition *p;
unsigned long start, here;
unsigned char *bp;
int loopct = 0;
int moretodo = 1;
int i, n=0;
here = start = partition_start(ep);;
while (moretodo) {
moretodo = 0;
if (++loopct > 100)
return n;
bp = getblock(fd, here * ssf); /* in 512 blocks */
if (bp == NULL)
return n;
if (bp[510] != 0x55 || bp[511] != 0xaa)
return n;
p = (struct partition *) (bp + 0x1be);
for (i=0; i<2; i++, p++) {
if (partition_size(p) == 0 || is_extended(p->sys_type))
continue;
if (n < ns) {
sp[n].start = (here + partition_start(p)) * ssf;
sp[n].size = partition_size(p) * ssf;
n++;
} else {
fprintf(stderr,
"dos_extd_partition: too many slices\n");
return n;
}
loopct = 0;
}
p -= 2;
for (i=0; i<2; i++, p++) {
if (partition_size(p) != 0 &&
is_extended(p->sys_type)) {
here = start + partition_start(p);
moretodo = 1;
break;
}
}
}
return n;
}
static int
is_gpt(int type) {
return (type == 0xEE);
}
int
read_dos_pt(int fd, struct slice all, struct slice *sp, int ns) {
struct partition *p;
unsigned long offset = all.start;
int i, n=0;
unsigned char *bp;
int ssf;
bp = getblock(fd, offset);
if (bp == NULL)
return -1;
if (bp[510] != 0x55 || bp[511] != 0xaa)
return -1;
/* msdos PT depends sector size... */
if (blkdev_get_sector_size(fd, &ssf) != 0)
ssf = DEFAULT_SECTOR_SIZE;
/* ... but partx counts everything in 512-byte sectors */
ssf /= 512;
p = (struct partition *) (bp + 0x1be);
for (i=0; i<4; i++) {
if (is_gpt(p->sys_type))
return 0;
p++;
}
p = (struct partition *) (bp + 0x1be);
for (i=0; i<4; i++) {
/* always add, even if zero length */
if (n < ns) {
sp[n].start = partition_start(p) * ssf;
sp[n].size = partition_size(p) * ssf;
n++;
} else {
fprintf(stderr,
"dos_partition: too many slices\n");
break;
}
p++;
}
p = (struct partition *) (bp + 0x1be);
for (i=0; i<4; i++) {
if (is_extended(p->sys_type))
n += read_extended_partition(fd, p, sp+n, ns-n, ssf);
p++;
}
return n;
}

View file

@ -1,13 +0,0 @@
#ifndef DOS_H_INCLUDED
#define DOS_H_INCLUDED
struct partition {
unsigned char boot_ind; /* 0x80 - active */
unsigned char bh, bs, bc;
unsigned char sys_type;
unsigned char eh, es, ec;
unsigned char start_sect[4];
unsigned char nr_sects[4];
};
#endif /* DOS_H_INCLUDED */

View file

@ -1,57 +0,0 @@
/*
efi.[ch] - Manipulates EFI variables as exported in /proc/efi/vars
Copyright (C) 2001 Dell Computer Corporation <Matt_Domsch@dell.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef EFI_H
#define EFI_H
/*
* Extensible Firmware Interface
* Based on 'Extensible Firmware Interface Specification'
* version 1.02, 12 December, 2000
*/
#include <stdint.h>
typedef struct {
uint8_t b[16];
} efi_guid_t;
#define EFI_GUID(a,b,c,d0,d1,d2,d3,d4,d5,d6,d7) \
((efi_guid_t) \
{{ (a) & 0xff, ((a) >> 8) & 0xff, ((a) >> 16) & 0xff, ((a) >> 24) & 0xff, \
(b) & 0xff, ((b) >> 8) & 0xff, \
(c) & 0xff, ((c) >> 8) & 0xff, \
(d0), (d1), (d2), (d3), (d4), (d5), (d6), (d7) }})
/******************************************************
* GUIDs
******************************************************/
#define NULL_GUID \
EFI_GUID( 0x00000000, 0x0000, 0x0000, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00)
static inline int
efi_guidcmp(efi_guid_t left, efi_guid_t right)
{
return memcmp(&left, &right, sizeof (efi_guid_t));
}
typedef uint16_t efi_char16_t; /* UNICODE character */
#endif /* EFI_H */

View file

@ -1,510 +0,0 @@
/*
gpt.[ch]
Copyright (C) 2000-2001 Dell Computer Corporation <Matt_Domsch@dell.com>
EFI GUID Partition Table handling
Per Intel EFI Specification v1.02
http://developer.intel.com/technology/efi/efi.htm
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <inttypes.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include "blkdev.h"
#include "crc32.h"
#include "gpt.h"
#include "partx.h"
#include "bitops.h"
static inline uint32_t
efi_crc32(const void *buf, unsigned long len)
{
return (crc32(~0L, buf, len) ^ ~0L);
}
/**
* is_pmbr_valid(): test Protective MBR for validity
* @mbr: pointer to a legacy mbr structure
*
* Description: Returns 1 if PMBR is valid, 0 otherwise.
* Validity depends on two things:
* 1) MSDOS signature is in the last two bytes of the MBR
* 2) One partition of type 0xEE is found
*/
static int
is_pmbr_valid(legacy_mbr *mbr)
{
int i, found = 0, signature = 0;
if (!mbr)
return 0;
signature = (le16_to_cpu(mbr->signature) == MSDOS_MBR_SIGNATURE);
for (i = 0; signature && i < 4; i++) {
if (mbr->partition[i].sys_type ==
EFI_PMBR_OSTYPE_EFI_GPT) {
found = 1;
break;
}
}
return (signature && found);
}
static int
get_sector_size (int fd)
{
int sector_size;
if (blkdev_get_sector_size(fd, &sector_size) == -1)
return DEFAULT_SECTOR_SIZE;
return sector_size;
}
static uint64_t
get_num_sectors(int fd)
{
unsigned long long bytes=0;
if (blkdev_get_size(fd, &bytes) == -1)
return 0;
return bytes / get_sector_size(fd);
}
static uint64_t
last_lba(int filedes)
{
int rc;
uint64_t sectors = 0;
struct stat s;
memset(&s, 0, sizeof (s));
rc = fstat(filedes, &s);
if (rc == -1) {
fprintf(stderr, "last_lba() could not stat: %s\n",
strerror(errno));
return 0;
}
if (S_ISBLK(s.st_mode)) {
sectors = get_num_sectors(filedes);
} else {
fprintf(stderr,
"last_lba(): I don't know how to handle files with mode %x\n",
s.st_mode);
sectors = 1;
}
return sectors - 1;
}
static ssize_t
read_lba(int fd, uint64_t lba, void *buffer, size_t bytes)
{
int sector_size = get_sector_size(fd);
off_t offset = lba * sector_size;
lseek(fd, offset, SEEK_SET);
return read(fd, buffer, bytes);
}
/**
* alloc_read_gpt_entries(): reads partition entries from disk
* @fd is an open file descriptor to the whole disk
* @gpt is a buffer into which the GPT will be put
* Description: Returns ptes on success, NULL on error.
* Allocates space for PTEs based on information found in @gpt.
* Notes: remember to free pte when you're done!
*/
static gpt_entry *
alloc_read_gpt_entries(int fd, gpt_header * gpt)
{
gpt_entry *pte;
size_t count = le32_to_cpu(gpt->num_partition_entries) *
le32_to_cpu(gpt->sizeof_partition_entry);
if (!count) return NULL;
pte = (gpt_entry *)malloc(count);
if (!pte)
return NULL;
memset(pte, 0, count);
if (!read_lba(fd, le64_to_cpu(gpt->partition_entry_lba), pte,
count)) {
free(pte);
return NULL;
}
return pte;
}
/**
* alloc_read_gpt_header(): Allocates GPT header, reads into it from disk
* @fd is an open file descriptor to the whole disk
* @lba is the Logical Block Address of the partition table
*
* Description: returns GPT header on success, NULL on error. Allocates
* and fills a GPT header starting at @ from @bdev.
* Note: remember to free gpt when finished with it.
*/
static gpt_header *
alloc_read_gpt_header(int fd, uint64_t lba)
{
gpt_header *gpt;
gpt = (gpt_header *)
malloc(sizeof (gpt_header));
if (!gpt)
return NULL;
memset(gpt, 0, sizeof (*gpt));
if (!read_lba(fd, lba, gpt, sizeof (gpt_header))) {
free(gpt);
return NULL;
}
return gpt;
}
/**
* is_gpt_valid() - tests one GPT header and PTEs for validity
* @fd is an open file descriptor to the whole disk
* @lba is the logical block address of the GPT header to test
* @gpt is a GPT header ptr, filled on return.
* @ptes is a PTEs ptr, filled on return.
*
* Description: returns 1 if valid, 0 on error.
* If valid, returns pointers to newly allocated GPT header and PTEs.
*/
static int
is_gpt_valid(int fd, uint64_t lba,
gpt_header ** gpt, gpt_entry ** ptes)
{
int rc = 0; /* default to not valid */
uint32_t crc, origcrc;
if (!gpt || !ptes)
return 0;
if (!(*gpt = alloc_read_gpt_header(fd, lba)))
return 0;
/* Check the GUID Partition Table signature */
if (le64_to_cpu((*gpt)->signature) != GPT_HEADER_SIGNATURE) {
/*
printf("GUID Partition Table Header signature is wrong: %" PRIx64" != %" PRIx64 "\n",
le64_to_cpu((*gpt)->signature), GUID_PT_HEADER_SIGNATURE);
*/
free(*gpt);
*gpt = NULL;
return rc;
}
/* Check the GUID Partition Table Header CRC */
origcrc = le32_to_cpu((*gpt)->header_crc32);
(*gpt)->header_crc32 = 0;
crc = efi_crc32(*gpt, le32_to_cpu((*gpt)->header_size));
if (crc != origcrc) {
/* printf( "GPTH CRC check failed, %x != %x.\n", origcrc, crc); */
(*gpt)->header_crc32 = cpu_to_le32(origcrc);
free(*gpt);
*gpt = NULL;
return 0;
}
(*gpt)->header_crc32 = cpu_to_le32(origcrc);
/* Check that the my_lba entry points to the LBA
* that contains the GPT we read */
if (le64_to_cpu((*gpt)->my_lba) != lba) {
/* printf( "my_lba % PRIx64 "x != lba %"PRIx64 "x.\n", le64_to_cpu((*gpt)->my_lba), lba); */
free(*gpt);
*gpt = NULL;
return 0;
}
if (!(*ptes = alloc_read_gpt_entries(fd, *gpt))) {
free(*gpt);
*gpt = NULL;
return 0;
}
/* Check the GUID Partition Entry Array CRC */
crc = efi_crc32(*ptes,
le32_to_cpu((*gpt)->num_partition_entries) *
le32_to_cpu((*gpt)->sizeof_partition_entry));
if (crc != le32_to_cpu((*gpt)->partition_entry_array_crc32)) {
/* printf("GUID Partitition Entry Array CRC check failed.\n"); */
free(*gpt);
*gpt = NULL;
free(*ptes);
*ptes = NULL;
return 0;
}
/* We're done, all's well */
return 1;
}
/**
* compare_gpts() - Search disk for valid GPT headers and PTEs
* @pgpt is the primary GPT header
* @agpt is the alternate GPT header
* @lastlba is the last LBA number
* Description: Returns nothing. Sanity checks pgpt and agpt fields
* and prints warnings on discrepancies.
*
*/
static void
compare_gpts(gpt_header *pgpt, gpt_header *agpt, uint64_t lastlba)
{
int error_found = 0;
if (!pgpt || !agpt)
return;
if (le64_to_cpu(pgpt->my_lba) != le64_to_cpu(agpt->alternate_lba)) {
fprintf(stderr,
"GPT:Primary header LBA != Alt. header alternate_lba\n");
fprintf(stderr, "GPT:%" PRIx64 "x != %" PRIx64 "x\n",
le64_to_cpu(pgpt->my_lba),
le64_to_cpu(agpt->alternate_lba));
error_found++;
}
if (le64_to_cpu(pgpt->alternate_lba) != le64_to_cpu(agpt->my_lba)) {
fprintf(stderr,
"GPT:Primary header alternate_lba != Alt. header my_lba\n");
fprintf(stderr, "GPT:%" PRIx64 " != %" PRIx64 "\n",
le64_to_cpu(pgpt->alternate_lba),
le64_to_cpu(agpt->my_lba));
error_found++;
}
if (le64_to_cpu(pgpt->first_usable_lba) !=
le64_to_cpu(agpt->first_usable_lba)) {
fprintf(stderr, "GPT:first_usable_lbas don't match.\n");
fprintf(stderr, "GPT:%" PRIx64 " != %" PRIx64 "\n",
le64_to_cpu(pgpt->first_usable_lba),
le64_to_cpu(agpt->first_usable_lba));
error_found++;
}
if (le64_to_cpu(pgpt->last_usable_lba) !=
le64_to_cpu(agpt->last_usable_lba)) {
fprintf(stderr, "GPT:last_usable_lbas don't match.\n");
fprintf(stderr, "GPT:%" PRIx64 " != %" PRIx64 "\n",
le64_to_cpu(pgpt->last_usable_lba),
le64_to_cpu(agpt->last_usable_lba));
error_found++;
}
if (efi_guidcmp(pgpt->disk_guid, agpt->disk_guid)) {
fprintf(stderr, "GPT:disk_guids don't match.\n");
error_found++;
}
if (le32_to_cpu(pgpt->num_partition_entries) !=
le32_to_cpu(agpt->num_partition_entries)) {
fprintf(stderr, "GPT:num_partition_entries don't match: "
"0x%x != 0x%x\n",
le32_to_cpu(pgpt->num_partition_entries),
le32_to_cpu(agpt->num_partition_entries));
error_found++;
}
if (le32_to_cpu(pgpt->sizeof_partition_entry) !=
le32_to_cpu(agpt->sizeof_partition_entry)) {
fprintf(stderr,
"GPT:sizeof_partition_entry values don't match: "
"0x%x != 0x%x\n",
le32_to_cpu(pgpt->sizeof_partition_entry),
le32_to_cpu(agpt->sizeof_partition_entry));
error_found++;
}
if (le32_to_cpu(pgpt->partition_entry_array_crc32) !=
le32_to_cpu(agpt->partition_entry_array_crc32)) {
fprintf(stderr,
"GPT:partition_entry_array_crc32 values don't match: "
"0x%x != 0x%x\n",
le32_to_cpu(pgpt->partition_entry_array_crc32),
le32_to_cpu(agpt->partition_entry_array_crc32));
error_found++;
}
if (le64_to_cpu(pgpt->alternate_lba) != lastlba) {
fprintf(stderr,
"GPT:Primary header thinks Alt. header is not at the end of the disk.\n");
fprintf(stderr, "GPT:%" PRIx64 " != %" PRIx64 "\n",
le64_to_cpu(pgpt->alternate_lba), lastlba);
error_found++;
}
if (le64_to_cpu(agpt->my_lba) != lastlba) {
fprintf(stderr,
"GPT:Alternate GPT header not at the end of the disk.\n");
fprintf(stderr, "GPT:%" PRIx64 " != %" PRIx64 "\n",
le64_to_cpu(agpt->my_lba), lastlba);
error_found++;
}
if (error_found)
fprintf(stderr,
"GPT: Use GNU Parted to correct GPT errors.\n");
return;
}
/**
* find_valid_gpt() - Search disk for valid GPT headers and PTEs
* @fd is an open file descriptor to the whole disk
* @gpt is a GPT header ptr, filled on return.
* @ptes is a PTEs ptr, filled on return.
* Description: Returns 1 if valid, 0 on error.
* If valid, returns pointers to newly allocated GPT header and PTEs.
* Validity depends on finding either the Primary GPT header and PTEs valid,
* or the Alternate GPT header and PTEs valid, and the PMBR valid.
*/
static int
find_valid_gpt(int fd, gpt_header ** gpt, gpt_entry ** ptes)
{
extern int force_gpt;
int good_pgpt = 0, good_agpt = 0, good_pmbr = 0;
gpt_header *pgpt = NULL, *agpt = NULL;
gpt_entry *pptes = NULL, *aptes = NULL;
legacy_mbr *legacymbr = NULL;
uint64_t lastlba;
if (!gpt || !ptes)
return 0;
lastlba = last_lba(fd);
good_pgpt = is_gpt_valid(fd, GPT_PRIMARY_PARTITION_TABLE_LBA,
&pgpt, &pptes);
if (good_pgpt) {
good_agpt = is_gpt_valid(fd,
le64_to_cpu(pgpt->alternate_lba),
&agpt, &aptes);
if (!good_agpt) {
good_agpt = is_gpt_valid(fd, lastlba,
&agpt, &aptes);
}
}
else {
good_agpt = is_gpt_valid(fd, lastlba,
&agpt, &aptes);
}
/* The obviously unsuccessful case */
if (!good_pgpt && !good_agpt) {
goto fail;
}
/* This will be added to the EFI Spec. per Intel after v1.02. */
legacymbr = malloc(sizeof (*legacymbr));
if (legacymbr) {
memset(legacymbr, 0, sizeof (*legacymbr));
read_lba(fd, 0, (uint8_t *) legacymbr,
sizeof (*legacymbr));
good_pmbr = is_pmbr_valid(legacymbr);
free(legacymbr);
legacymbr=NULL;
}
/* Failure due to bad PMBR */
if ((good_pgpt || good_agpt) && !good_pmbr && !force_gpt) {
fprintf(stderr,
" Warning: Disk has a valid GPT signature "
"but invalid PMBR.\n"
" Assuming this disk is *not* a GPT disk anymore.\n"
" Use gpt kernel option to override. "
"Use GNU Parted to correct disk.\n");
goto fail;
}
/* Would fail due to bad PMBR, but force GPT anyhow */
if ((good_pgpt || good_agpt) && !good_pmbr && force_gpt) {
fprintf(stderr,
" Warning: Disk has a valid GPT signature but "
"invalid PMBR.\n"
" Use GNU Parted to correct disk.\n"
" gpt option taken, disk treated as GPT.\n");
}
compare_gpts(pgpt, agpt, lastlba);
/* The good cases */
if (good_pgpt && (good_pmbr || force_gpt)) {
*gpt = pgpt;
*ptes = pptes;
if (agpt) { free(agpt); agpt = NULL; }
if (aptes) { free(aptes); aptes = NULL; }
if (!good_agpt) {
fprintf(stderr,
"Alternate GPT is invalid, "
"using primary GPT.\n");
}
return 1;
}
else if (good_agpt && (good_pmbr || force_gpt)) {
*gpt = agpt;
*ptes = aptes;
if (pgpt) { free(pgpt); pgpt = NULL; }
if (pptes) { free(pptes); pptes = NULL; }
fprintf(stderr,
"Primary GPT is invalid, using alternate GPT.\n");
return 1;
}
fail:
if (pgpt) { free(pgpt); pgpt=NULL; }
if (agpt) { free(agpt); agpt=NULL; }
if (pptes) { free(pptes); pptes=NULL; }
if (aptes) { free(aptes); aptes=NULL; }
*gpt = NULL;
*ptes = NULL;
return 0;
}
/**
* read_gpt_pt()
* @fd
* @all - slice with start/size of whole disk
*
* 0 if this isn't our partition table
* number of partitions if successful
*
*/
int
read_gpt_pt (int fd, struct slice all, struct slice *sp, int ns)
{
gpt_header *gpt = NULL;
gpt_entry *ptes = NULL;
uint32_t i;
int n = 0;
int last_used_index=-1;
if (!find_valid_gpt (fd, &gpt, &ptes) || !gpt || !ptes) {
free (gpt);
free (ptes);
return 0;
}
for (i = 0; i < le32_to_cpu(gpt->num_partition_entries) && i < ns; i++) {
if (!efi_guidcmp (NULL_GUID, ptes[i].partition_type_guid)) {
sp[n].start = 0;
sp[n].size = 0;
n++;
} else {
sp[n].start = le64_to_cpu(ptes[i].starting_lba);
sp[n].size = le64_to_cpu(ptes[i].ending_lba) -
le64_to_cpu(ptes[i].starting_lba) + 1;
last_used_index=n;
n++;
}
}
free (ptes);
free (gpt);
return last_used_index+1;
}

View file

@ -1,131 +0,0 @@
/*
gpt.[ch]
Copyright (C) 2000-2001 Dell Computer Corporation <Matt_Domsch@dell.com>
EFI GUID Partition Table handling
Per Intel EFI Specification v1.02
http://developer.intel.com/technology/efi/efi.htm
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _GPT_H
#define _GPT_H
#include <inttypes.h>
#include "partx.h"
#include "dos.h"
#include "efi.h"
#define EFI_PMBR_OSTYPE_EFI 0xEF
#define EFI_PMBR_OSTYPE_EFI_GPT 0xEE
#define MSDOS_MBR_SIGNATURE 0xaa55
#define GPT_BLOCK_SIZE 512
#define GPT_HEADER_SIGNATURE 0x5452415020494645ULL
#define GPT_HEADER_REVISION_V1_02 0x00010200
#define GPT_HEADER_REVISION_V1_00 0x00010000
#define GPT_HEADER_REVISION_V0_99 0x00009900
#define GPT_PRIMARY_PARTITION_TABLE_LBA 1
typedef struct _gpt_header {
uint64_t signature;
uint32_t revision;
uint32_t header_size;
uint32_t header_crc32;
uint32_t reserved1;
uint64_t my_lba;
uint64_t alternate_lba;
uint64_t first_usable_lba;
uint64_t last_usable_lba;
efi_guid_t disk_guid;
uint64_t partition_entry_lba;
uint32_t num_partition_entries;
uint32_t sizeof_partition_entry;
uint32_t partition_entry_array_crc32;
uint8_t reserved2[GPT_BLOCK_SIZE - 92];
} __attribute__ ((packed)) gpt_header;
typedef struct _gpt_entry_attributes {
uint64_t required_to_function:1;
uint64_t reserved:47;
uint64_t type_guid_specific:16;
} __attribute__ ((packed)) gpt_entry_attributes;
typedef struct _gpt_entry {
efi_guid_t partition_type_guid;
efi_guid_t unique_partition_guid;
uint64_t starting_lba;
uint64_t ending_lba;
gpt_entry_attributes attributes;
efi_char16_t partition_name[72 / sizeof(efi_char16_t)];
} __attribute__ ((packed)) gpt_entry;
/*
These values are only defaults. The actual on-disk structures
may define different sizes, so use those unless creating a new GPT disk!
*/
#define GPT_DEFAULT_RESERVED_PARTITION_ENTRY_ARRAY_SIZE 16384
/*
Number of actual partition entries should be calculated
as:
*/
#define GPT_DEFAULT_RESERVED_PARTITION_ENTRIES \
(GPT_DEFAULT_RESERVED_PARTITION_ENTRY_ARRAY_SIZE / \
sizeof(gpt_entry))
/* Protected Master Boot Record & Legacy MBR share same structure */
/* Needs to be packed because the u16s force misalignment. */
typedef struct _legacy_mbr {
uint8_t bootcode[440];
uint32_t unique_mbr_signature;
uint16_t unknown;
struct partition partition[4];
uint16_t signature;
} __attribute__ ((packed)) legacy_mbr;
#define EFI_GPT_PRIMARY_PARTITION_TABLE_LBA 1
/* Functions */
int read_gpt_pt (int fd, struct slice all, struct slice *sp, int ns);
#endif
/*
* Overrides for Emacs so that we follow Linus's tabbing style.
* Emacs will notice this stuff at the end of the file and automatically
* adjust the settings for this buffer only. This must remain at the end
* of the file.
* ---------------------------------------------------------------------------
* Local variables:
* c-indent-level: 4
* c-brace-imaginary-offset: 0
* c-brace-offset: -4
* c-argdecl-indent: 4
* c-label-offset: -4
* c-continued-statement-offset: 4
* c-continued-brace-offset: 0
* indent-tabs-mode: nil
* tab-width: 8
* End:
*/

View file

@ -1,74 +0,0 @@
/*
* Lifted from kpartx's mac.c
*
* Integrated to partx
* Davidlohr Bueso <dave@gnu.org>
*/
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include "bitops.h"
#include "partx.h"
#define MAC_PARTITION_MAGIC 0x504d
#define MAC_DRIVER_MAGIC 0x4552
struct mac_partition {
uint16_t signature; /* expected to be MAC_PARTITION_MAGIC */
uint16_t res1;
uint32_t map_count; /* # blocks in partition map */
uint32_t start_block; /* absolute starting block # of partition */
uint32_t block_count; /* number of blocks in partition */
/* there is more stuff after this that we don't need */
};
/* Driver descriptor structure, in block 0 */
struct mac_driver_desc {
uint16_t signature; /* expected to be MAC_DRIVER_MAGIC */
uint16_t block_size;
uint32_t block_count;
};
int
read_mac_pt(int fd, struct slice all, struct slice *sp, int ns) {
struct mac_driver_desc *md;
struct mac_partition *part;
unsigned secsize;
unsigned char *data;
int blk, blocks_in_map;
int n = 0;
md = (struct mac_driver_desc *) getblock(fd, 0);
if (md == NULL)
return -1;
if (be16_to_cpu(md->signature) != MAC_DRIVER_MAGIC)
return -1;
secsize = be16_to_cpu(md->block_size);
data = getblock(fd, secsize/512);
if (!data)
return -1;
part = (struct mac_partition *) (data + secsize%512);
if (be16_to_cpu(part->signature) != MAC_PARTITION_MAGIC)
return -1;
blocks_in_map = be32_to_cpu(part->map_count);
for (blk = 1; blk <= blocks_in_map && blk <= ns; ++blk, ++n) {
int pos = blk * secsize;
data = getblock(fd, pos/512);
if (!data)
return -1;
part = (struct mac_partition *) (data + pos%512);
if (be16_to_cpu(part->signature) != MAC_PARTITION_MAGIC)
break;
sp[n].start = be32_to_cpu(part->start_block) * (secsize/512);
sp[n].size = be32_to_cpu(part->block_count) * (secsize/512);
}
return n;
}

View file

@ -1,48 +1,112 @@
.\" partx.8 --
.\" Copyright 2007 Karel Zak <kzak@redhat.com>
.\" Copyright 2007 Red Hat, Inc.
.\" Copyright 2010 Davidlohr Bueso <dave@gnu.org>
.\" May be distributed under the GNU General Public License
.TH PARTX 8 "11 Jan 2007"
.TH PARTX 8 "28 Oct 2010"
.SH NAME
partx \-
telling the kernel about presence and numbering of on-disk partitions.
tell the linux kernel about presence and numbering of on-disk partitions.
.SH SYNOPSIS
.B partx
.RB [ \-a | \-d | \-l ]
.RB [ \-\-type
.RB [ \-a | \-d | \-s ]
.RB [ \-t
.IR TYPE ]
.RB [ \-\-nr
.IR M-N ]
.RI [ partition ]
.RB [ \-n
.IR M:N ]
.RI [ \- ]
.I disk
.B partx
.RB [ \-a | \-d | \-s ]
.RB [ \-t
.IR TYPE ]
.I partition
.RI [ disk ]
.SH DESCRIPTION
Given a block device (
.B disk
) and a partition table
.B type
, try to parse the partition table, and list the
Given a device or disk-image, try to parse the partition table, and list the
contents. Optionally add or remove partitions.
This is not an fdisk - adding and removing partitions
The
.I disk
argument is optional when a
.I partition
argument is provided. It's possible to force scannig on partition (for example
to list nested subpartitions), then the string "-" has to be used, for example:
.RS
.br
.B partx \-\-show \- /dev/sda3
.RE
This will use sda3 as a whole-disk rather than a partition.
.B This is not an fdisk
\- adding and removing partitions
is not a change of the disk, but just telling the kernel
about presence and numbering of on-disk partitions.
.SH OPTIONS
.TP
.B \-a
.IP "\fB\-a, \-\-add\fP"
add specified partitions or read disk and add all partitions
.TP
.B \-d
.IP "\fB\-b, \-\-bytes\fP"
Print the SIZE column in bytes rather than in human readable format.
.IP "\fB\-d, \-\-delete\fP"
delete specified or all partitions
.IP "\fB\-l, \-\-list\fP"
list partitions. Note that the all numbers are in 512-byte sectors. This output
format is DEPRECATED in favour of \fB\-\-show\fP. Don't use it in newly written
scripts.
.IP "\fB\-g, \-\-noheadings\fP"
Do not print a header line.
.IP "\fB\-o, \-\-output \fIlist\fP"
Define output columns for \fB\-\-show\fP and \fB\-\-raw\fP output. If output format is
not specified then \fB\-\-show\fP is enable by default. Use \fB\-\-help\fP to
get list of all supported columns.
.IP "\fB\-r, \-\-raw\fP"
Use raw output format.
.IP "\fB\-s, \-\-show\fP"
list partitions. All numbers (except SIZE) are in 512-byte sectors. The output
columns could be (re)defined by \fB\-\-output\fP option.
.IP "\fB\-t, \-\-type \fItype\fP"
Specify the partition table type -- dos, bsd, solaris, unixware or gpt.
.IP "\fB\-n, \-\-nr \fIM:N\fP"
Specify the range of partitions. For backward compatibility is also supported
range definition in <M-N> format. The range could be specified by negative
numbers, for example "--nr :-1" means last partition, and "--nr -2:-1" means
last two partition. Supported range specifications:
.RS
.TP
.B \-l
list partitions. Note that the all numbers are in 512-byte sectors.
.B <M>
Specify only one partition (e.g. --nr 3).
.TP
.BI --type " TYPE"
Specify the partition type -- dos, bsd, solaris, unixware or gpt.
.B <M:>
Specify lower limit only (e.g. --nr 2:).
.TP
.BI --nr " M-N"
Specify the range of partitions (e.g --nr 2-4).
.B <:N>
Specify upper limit only (e.g. --nr :4).
.TP
.B <M:N>
or
.B <M-N>
Specify lower and upper limits (e.g. --nr 2:4).
.RE
.SH EXAMPLES
.IP "\fBpartx \-\-show /dev/sdb3\fP"
.IP "\fBpartx \-\-show --nr 3 /dev/sdb\fP"
.IP "\fBpartx \-\-show /dev/sdb3 /dev/sdb\fP"
List partition 3 of /dev/sdb.
.IP "\fBpartx \-\-show \- /dev/sdb3\fP"
List all subpartitions on /dev/sdb3 (the device is used as whole-disk).
.IP "\fBpartx \-o START -g --nr 3 /dev/sdb\fP"
Print the start sector of partition 5 on /dev/sda without header.
.IP "\fBpartx \-o SECTORS,SIZE /dev/sda5 /dev/sda\fP"
List the length in sectors and human readable size of partition 5 on /dev/sda.
.IP "\fBpartx \-\-add --nr 3:5 /dev/sdd\fP"
Add all available partitions from 3 to 5 (inclusive) on /dev/sdd.
.IP "\fBpartx \-d --nr :-1 /dev/sdd\fP"
Removes last partition on /dev/sdd.
.SH SEE ALSO
.BR addpart (8),
@ -51,6 +115,13 @@ Specify the range of partitions (e.g --nr 2-4).
.BR parted (8),
.BR partprobe (8)
.SH SEE ALSO
.nf
Davidlohr Bueso <dave@gnu.org>
Karel Zak <kzak@redhat.com>
.fi
The original version was written by Andries E. Brouwer <aeb@cwi.nl>.
.SH AVAILABILITY
The partx command is part of the util-linux package and is available from
ftp://ftp.kernel.org/pub/linux/utils/util-linux/.

File diff suppressed because it is too large Load diff

View file

@ -1,32 +1,44 @@
#ifndef PARTX_H_INCLUDED
#define PARTX_H_INCLUDED
#ifndef UTIL_LINUX_PARTX_H
#define UTIL_LINUX_PARTX_H
/*
* For each partition type there is a routine that takes
* a block device and a range, and returns the list of
* slices found there in the supplied array SP that can
* hold NS entries. The return value is the number of
* entries stored, or -1 if the appropriate type is not
* present.
*/
#include <sys/ioctl.h>
#include <linux/blkpg.h>
static inline int partx_del_partition(int fd, int partno)
{
struct blkpg_ioctl_arg a;
struct blkpg_partition p;
/* units: 512 byte sectors */
struct slice {
unsigned int start;
unsigned int size;
};
p.pno = partno;
p.start = 0;
p.length = 0;
p.devname[0] = 0;
p.volname[0] = 0;
a.op = BLKPG_DEL_PARTITION;
a.flags = 0;
a.datalen = sizeof(p);
a.data = &p;
typedef int (ptreader)(int fd, struct slice all, struct slice *sp, int ns);
extern ptreader read_dos_pt, read_bsd_pt, read_solaris_pt, read_unixware_pt, read_gpt_pt;
extern ptreader read_sun_pt, read_mac_pt;
unsigned char *getblock(int fd, unsigned int secnr);
static inline int
four2int(unsigned char *p) {
return p[0] + (p[1]<<8) + (p[2]<<16) + (p[3]<<24);
return ioctl(fd, BLKPG, &a);
}
#endif /* PARTX_H_INCLUDED */
static inline int partx_add_partition(int fd, int partno,
unsigned long start, unsigned long size)
{
struct blkpg_ioctl_arg a;
struct blkpg_partition p;
p.pno = partno;
p.start = start << 9;
p.length = size << 9;
p.devname[0] = 0;
p.volname[0] = 0;
a.op = BLKPG_ADD_PARTITION;
a.flags = 0;
a.datalen = sizeof(p);
a.data = &p;
return ioctl(fd, BLKPG, &a);
}
#endif /* UTIL_LINUX_PARTX_H */

View file

@ -1,69 +0,0 @@
#include <stdio.h>
#include <time.h> /* time_t */
#include <sys/types.h>
#include "partx.h"
#define SOLARIS_X86_NUMSLICE 8
#define SOLARIS_X86_VTOC_SANE (0x600DDEEEUL)
struct solaris_x86_slice {
unsigned short s_tag; /* ID tag of partition */
unsigned short s_flag; /* permision flags */
daddr_t s_start; /* start sector no of partition */
long s_size; /* # of blocks in partition */
};
struct solaris_x86_vtoc {
unsigned long v_bootinfo[3]; /* info for mboot */
unsigned long v_sanity; /* to verify vtoc sanity */
unsigned long v_version; /* layout version */
char v_volume[8]; /* volume name */
unsigned short v_sectorsz; /* sector size in bytes */
unsigned short v_nparts; /* number of partitions */
unsigned long v_reserved[10]; /* free space */
struct solaris_x86_slice
v_slice[SOLARIS_X86_NUMSLICE]; /* slice headers */
time_t timestamp[SOLARIS_X86_NUMSLICE]; /* timestamp */
char v_asciilabel[128]; /* for compatibility */
};
int
read_solaris_pt(int fd, struct slice all, struct slice *sp, int ns) {
struct solaris_x86_vtoc *v;
struct solaris_x86_slice *s;
unsigned int offset = all.start;
int i, n;
unsigned char *bp;
bp = getblock(fd, offset+1); /* 1 sector suffices */
if (bp == NULL)
return -1;
v = (struct solaris_x86_vtoc *) bp;
if(v->v_sanity != SOLARIS_X86_VTOC_SANE)
return -1;
if(v->v_version != 1) {
fprintf(stderr, "Cannot handle solaris version %ld vtoc\n",
v->v_version);
return 0;
}
for(i=0, n=0; i<SOLARIS_X86_NUMSLICE; i++) {
s = &v->v_slice[i];
if (s->s_size == 0)
continue;
if (n < ns) {
sp[n].start = offset + s->s_start;
sp[n].size = s->s_size;
n++;
} else {
fprintf(stderr,
"solaris_x86_partition: too many slices\n");
break;
}
}
return n;
}

View file

@ -1,130 +0,0 @@
/*
* Lifted from kpartx's sun.c
*
* Copyrights of the original file apply
* Copyright (c) 2007 Hannes Reinecke
*
* Integrated to partx
* Davidlohr Bueso <dave@gnu.org>
*/
#include <stdio.h>
#include <sys/types.h>
#include "bitops.h"
#include "partx.h"
#define SUN_DISK_MAGIC 0xDABE /* Disk magic number */
#define SUN_DISK_MAXPARTITIONS 8
struct __attribute__ ((packed)) sun_raw_part {
u_int32_t start_cylinder; /* where the part starts... */
u_int32_t num_sectors; /* ...and it's length */
};
struct __attribute__ ((packed)) sun_part_info {
u_int8_t spare1;
u_int8_t id; /* Partition type */
u_int8_t spare2;
u_int8_t flags; /* Partition flags */
};
struct __attribute__ ((packed)) sun_disk_label {
char info[128]; /* Informative text string */
u_int8_t spare0[14];
struct sun_part_info infos[SUN_DISK_MAXPARTITIONS];
u_int8_t spare1[246]; /* Boot information etc. */
u_int16_t rspeed; /* Disk rotational speed */
u_int16_t pcylcount; /* Physical cylinder count */
u_int16_t sparecyl; /* extra sects per cylinder */
u_int8_t spare2[4]; /* More magic... */
u_int16_t ilfact; /* Interleave factor */
u_int16_t ncyl; /* Data cylinder count */
u_int16_t nacyl; /* Alt. cylinder count */
u_int16_t ntrks; /* Tracks per cylinder */
u_int16_t nsect; /* Sectors per track */
u_int8_t spare3[4]; /* Even more magic... */
struct sun_raw_part partitions[SUN_DISK_MAXPARTITIONS];
u_int16_t magic; /* Magic number */
u_int16_t csum; /* Label xor'd checksum */
};
/* Checksum Verification */
static int
sun_verify_checksum (struct sun_disk_label *label)
{
u_int16_t *ush = ((u_int16_t *)(label + 1)) - 1;
u_int16_t csum = 0;
while (ush >= (u_int16_t *)label)
csum ^= *ush--;
return !csum;
}
int
read_sun_pt(int fd, struct slice all, struct slice *sp, int ns) {
struct sun_disk_label *l;
struct sun_raw_part *s;
unsigned int offset = all.start, end;
int i, j, n;
unsigned char *bp;
bp = getblock(fd, offset);
if (bp == NULL)
return -1;
l = (struct sun_disk_label *) bp;
if(be16_to_cpu(l->magic) != SUN_DISK_MAGIC)
return -1;
if (!sun_verify_checksum(l)) {
fprintf(stderr, "Corrupted Sun disk label\n");
return -1;
}
for(i=0, n=0; i<SUN_DISK_MAXPARTITIONS; i++) {
s = &l->partitions[i];
if (s->num_sectors == 0)
continue;
if (n < ns) {
sp[n].start = offset +
be32_to_cpu(s->start_cylinder) * be16_to_cpu(l->nsect) * be16_to_cpu(l->ntrks);
sp[n].size = be32_to_cpu(s->num_sectors);
n++;
} else {
fprintf(stderr,
"sun_disklabel: too many slices\n");
break;
}
}
/*
* Convention has it that the SUN disklabel will always have
* the 'c' partition spanning the entire disk.
* So we have to check for contained slices.
*/
for(i = 0; i < SUN_DISK_MAXPARTITIONS; i++) {
if (sp[i].size == 0)
continue;
end = sp[i].start + sp[i].size;
for(j = 0; j < SUN_DISK_MAXPARTITIONS; j ++) {
if ( i == j )
continue;
if (sp[j].size == 0)
continue;
if (sp[i].start < sp[j].start) {
if (end > sp[j].start &&
end < sp[j].start + sp[j].size) {
/* Invalid slice */
fprintf(stderr,
"sun_disklabel: slice %d overlaps with %d\n", i , j);
sp[i].size = 0;
}
}
}
}
return n;
}

View file

@ -1,83 +0,0 @@
#include <stdio.h>
#include "partx.h"
#define UNIXWARE_FS_UNUSED 0
#define UNIXWARE_NUMSLICE 16
#define UNIXWARE_DISKMAGIC (0xCA5E600D)
#define UNIXWARE_DISKMAGIC2 (0x600DDEEE)
struct unixware_slice {
unsigned short s_label; /* label */
unsigned short s_flags; /* permission flags */
unsigned int start_sect; /* starting sector */
unsigned int nr_sects; /* number of sectors in slice */
};
struct unixware_disklabel {
unsigned int d_type; /* drive type */
unsigned char d_magic[4]; /* the magic number */
unsigned int d_version; /* version number */
char d_serial[12]; /* serial number of the device */
unsigned int d_ncylinders; /* # of data cylinders per device */
unsigned int d_ntracks; /* # of tracks per cylinder */
unsigned int d_nsectors; /* # of data sectors per track */
unsigned int d_secsize; /* # of bytes per sector */
unsigned int d_part_start; /* # of first sector of this partition */
unsigned int d_unknown1[12]; /* ? */
unsigned int d_alt_tbl; /* byte offset of alternate table */
unsigned int d_alt_len; /* byte length of alternate table */
unsigned int d_phys_cyl; /* # of physical cylinders per device */
unsigned int d_phys_trk; /* # of physical tracks per cylinder */
unsigned int d_phys_sec; /* # of physical sectors per track */
unsigned int d_phys_bytes; /* # of physical bytes per sector */
unsigned int d_unknown2; /* ? */
unsigned int d_unknown3; /* ? */
unsigned int d_pad[8]; /* pad */
struct unixware_vtoc {
unsigned char v_magic[4]; /* the magic number */
unsigned int v_version; /* version number */
char v_name[8]; /* volume name */
unsigned short v_nslices; /* # of slices */
unsigned short v_unknown1; /* ? */
unsigned int v_reserved[10]; /* reserved */
struct unixware_slice
v_slice[UNIXWARE_NUMSLICE]; /* slice headers */
} vtoc;
}; /* 408 */
int
read_unixware_pt(int fd, struct slice all, struct slice *sp, int ns) {
struct unixware_disklabel *l;
struct unixware_slice *p;
unsigned int offset = all.start;
unsigned char *bp;
int n = 0;
bp = getblock(fd, offset+29); /* 1 sector suffices */
if (bp == NULL)
return -1;
l = (struct unixware_disklabel *) bp;
if (four2int(l->d_magic) != UNIXWARE_DISKMAGIC ||
four2int(l->vtoc.v_magic) != UNIXWARE_DISKMAGIC2)
return -1;
p = &l->vtoc.v_slice[1]; /* slice 0 is the whole disk. */
while (p - &l->vtoc.v_slice[0] < UNIXWARE_NUMSLICE) {
if (p->s_label == UNIXWARE_FS_UNUSED)
/* nothing */;
else if (n < ns) {
sp[n].start = p->start_sect;
sp[n].size = p->nr_sects;
n++;
} else {
fprintf(stderr,
"unixware_partition: too many slices\n");
break;
}
p++;
}
return n;
}