diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-14 19:10:49 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-14 19:10:49 +0000 |
commit | cfe5e3905201349e9cf3f95d52ff4bd100bde37d (patch) | |
tree | d0baf160cbee3195249d095f85e52d20c21acf02 /libblkid/src/partitions | |
parent | Initial commit. (diff) | |
download | util-linux-cfe5e3905201349e9cf3f95d52ff4bd100bde37d.tar.xz util-linux-cfe5e3905201349e9cf3f95d52ff4bd100bde37d.zip |
Adding upstream version 2.39.3.upstream/2.39.3
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'libblkid/src/partitions')
-rw-r--r-- | libblkid/src/partitions/aix.c | 57 | ||||
-rw-r--r-- | libblkid/src/partitions/aix.h | 7 | ||||
-rw-r--r-- | libblkid/src/partitions/atari.c | 312 | ||||
-rw-r--r-- | libblkid/src/partitions/bsd.c | 205 | ||||
-rw-r--r-- | libblkid/src/partitions/dos.c | 375 | ||||
-rw-r--r-- | libblkid/src/partitions/gpt.c | 473 | ||||
-rw-r--r-- | libblkid/src/partitions/mac.c | 200 | ||||
-rw-r--r-- | libblkid/src/partitions/minix.c | 102 | ||||
-rw-r--r-- | libblkid/src/partitions/partitions.c | 1532 | ||||
-rw-r--r-- | libblkid/src/partitions/partitions.h | 74 | ||||
-rw-r--r-- | libblkid/src/partitions/sgi.c | 87 | ||||
-rw-r--r-- | libblkid/src/partitions/solaris_x86.c | 154 | ||||
-rw-r--r-- | libblkid/src/partitions/sun.c | 125 | ||||
-rw-r--r-- | libblkid/src/partitions/ultrix.c | 99 | ||||
-rw-r--r-- | libblkid/src/partitions/unixware.c | 197 |
15 files changed, 3999 insertions, 0 deletions
diff --git a/libblkid/src/partitions/aix.c b/libblkid/src/partitions/aix.c new file mode 100644 index 0000000..03a311a --- /dev/null +++ b/libblkid/src/partitions/aix.c @@ -0,0 +1,57 @@ +/* + * aix partitions + * + * Copyright (C) 2009 Karel Zak <kzak@redhat.com> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + */ +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <stdint.h> + +#include "partitions.h" +#include "aix.h" + +static int probe_aix_pt(blkid_probe pr, + const struct blkid_idmag *mag __attribute__((__unused__))) +{ + blkid_partlist ls; + blkid_parttable tab; + + if (blkid_partitions_need_typeonly(pr)) + /* caller does not ask for details about partitions */ + return BLKID_PROBE_OK; + + ls = blkid_probe_get_partlist(pr); + if (!ls) + return BLKID_PROBE_NONE; + + tab = blkid_partlist_new_parttable(ls, "aix", 0); + if (!tab) + return -ENOMEM; + + return BLKID_PROBE_OK; +} + +/* + * We know nothing about AIX on-disk structures. Everything what we know is the + * magic number at begin of the disk. + * + * Note, Linux kernel is trying to be smart and AIX signature is ignored when + * there is a valid DOS partitions table. We don't support such behavior. All + * fdisk-like programs has to properly wipe the fist sector. Everything other + * is a bug. + */ +const struct blkid_idinfo aix_pt_idinfo = +{ + .name = "aix", + .probefunc = probe_aix_pt, + .magics = + { + { .magic = BLKID_AIX_MAGIC_STRING, .len = BLKID_AIX_MAGIC_STRLEN }, + { NULL } + } +}; + diff --git a/libblkid/src/partitions/aix.h b/libblkid/src/partitions/aix.h new file mode 100644 index 0000000..f767c5a --- /dev/null +++ b/libblkid/src/partitions/aix.h @@ -0,0 +1,7 @@ +#ifndef BLKID_PARTITIONS_AIX_H +#define BLKID_PARTITIONS_AIX_H + +#define BLKID_AIX_MAGIC_STRING "\xC9\xC2\xD4\xC1" +#define BLKID_AIX_MAGIC_STRLEN (sizeof(BLKID_AIX_MAGIC_STRING) - 1) + +#endif diff --git a/libblkid/src/partitions/atari.c b/libblkid/src/partitions/atari.c new file mode 100644 index 0000000..314f047 --- /dev/null +++ b/libblkid/src/partitions/atari.c @@ -0,0 +1,312 @@ +/* + * atari partitions parsing code + * + * Copyright (C) 2018 Vaclav Dolezal <vdolezal@redhat.com> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + * + * Based on Linux kernel implementation and atari-fdisk + */ + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <stdint.h> + +#include "partitions.h" + +struct atari_part_def { + /* + * flags: + * 0 (LSB): active + * 1-6: (reserved) + * 7 (MSB): bootable + */ + unsigned char flags; + char id[3]; + uint32_t start; + uint32_t size; +} __attribute__((packed)); + +struct atari_rootsector { + char unused0[0x156]; /* boot code */ + struct atari_part_def icd_part[8]; /* ICD partition entries */ + char unused1[0xc]; + uint32_t hd_size; + struct atari_part_def part[4]; /* primary partition entries */ + uint32_t bsl_start; /* bad sector list start */ + uint32_t bsl_len; /* bad sector list length */ + uint16_t checksum; +} __attribute__((packed)); + + +/* + * Generated using linux kernel ctype.{c,h} + * + * Since kernel uses isalnum() to detect whether it is Atari PT, we need same + * definition of alnum character to be consistent with kernel. + */ +static const unsigned char _linux_isalnum[] = { +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0, +0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0, +0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1 +}; + +static int linux_isalnum(unsigned char c) { + return _linux_isalnum[c]; +} + +#define isalnum linux_isalnum + +#define IS_ACTIVE(partdef) ((partdef).flags & 1) + +static int is_valid_dimension(uint32_t start, uint32_t size, uint32_t maxoff) +{ + uint64_t end = start + size; + + return end >= start + && 0 < start && start <= maxoff + && 0 < size && size <= maxoff + && 0 < end && end <= maxoff; +} + +static int is_valid_partition(struct atari_part_def *part, uint32_t maxoff) +{ + uint32_t start = be32_to_cpu(part->start), + size = be32_to_cpu(part->size); + + return (part->flags & 1) + && isalnum(part->id[0]) + && isalnum(part->id[1]) + && isalnum(part->id[2]) + && is_valid_dimension(start, size, maxoff); +} + +static int is_id_common(char *id) +{ + const char *ids[] = {"GEM", "BGM", "LNX", "SWP", "RAW", }; + unsigned i; + + for (i = 0; i < ARRAY_SIZE(ids); i++) { + if (!memcmp(ids[i], id, 3)) + return 1; + } + return 0; +} + +static int parse_partition(blkid_partlist ls, blkid_parttable tab, + struct atari_part_def *part, uint32_t offset) +{ + blkid_partition par; + uint32_t start; + uint32_t size; + + start = be32_to_cpu(part->start) + offset; + size = be32_to_cpu(part->size); + + if (blkid_partlist_get_partition_by_start(ls, start)) { + /* Don't increment partno for extended parts */ + if (!offset) + blkid_partlist_increment_partno(ls); + return 0; + } + + par = blkid_partlist_add_partition(ls, tab, start, size); + if (!par) + return -ENOMEM; + + blkid_partition_set_type_string(par, (unsigned char *) part->id, + sizeof(part->id)); + return 1; +} + +/* + * \return 1: OK, 0: bad format or -errno + */ +static int parse_extended(blkid_probe pr, blkid_partlist ls, + blkid_parttable tab, struct atari_part_def *part) +{ + uint32_t x0start, xstart; + unsigned ct = 0, i = 0; + int rc; + + x0start = xstart = be32_to_cpu(part->start); + while (1) { + struct atari_rootsector *xrs; + + if (++ct > 100) + break; + + xrs = (struct atari_rootsector *) blkid_probe_get_sector(pr, xstart); + if (!xrs) { + if (errno) + return -errno; + return 0; + } + + /* + * There must be data partition followed by reference to next + * XGM or inactive entry. + */ + for (i=0; ; i++) { + if (i >= ARRAY_SIZE(xrs->part) - 1) + return 0; + if (IS_ACTIVE(xrs->part[i])) + break; + } + + if (!memcmp(xrs->part[i].id, "XGM", 3)) + return 0; + + rc = parse_partition(ls, tab, &xrs->part[i], xstart); + if (rc <= 0) + return rc; + + if (!IS_ACTIVE(xrs->part[i+1])) + break; + + if (memcmp(xrs->part[i+1].id, "XGM", 3) != 0) + return 0; + + xstart = x0start + be32_to_cpu(xrs->part[i+1].start); + } + + return 1; +} + +static int probe_atari_pt(blkid_probe pr, + const struct blkid_idmag *mag __attribute__((__unused__))) +{ + struct atari_rootsector *rs; + + blkid_parttable tab = NULL; + blkid_partlist ls; + + unsigned i; + int has_xgm = 0; + int rc = 0; + uint32_t rssize; /* size in sectors from root sector */ + uint64_t size; /* size in sectors from system */ + + /* Atari partition is not defined for other sector sizes */ + if (blkid_probe_get_sectorsize(pr) != 512) + goto nothing; + + size = blkid_probe_get_size(pr) / 512; + + /* Atari is not well defined to support large disks */ + if (size > INT32_MAX) + goto nothing; + + /* read root sector */ + rs = (struct atari_rootsector *) blkid_probe_get_sector(pr, 0); + if (!rs) { + if (errno) + return -errno; + goto nothing; + } + + rssize = be32_to_cpu(rs->hd_size); + + /* check number of sectors stored in the root sector */ + if (rssize < 2 || rssize > size) + goto nothing; + + /* check list of bad blocks */ + if ((rs->bsl_start || rs->bsl_len) + && !is_valid_dimension(be32_to_cpu(rs->bsl_start), + be32_to_cpu(rs->bsl_len), + rssize)) + goto nothing; + + /* + * At least one valid partition required + */ + for (i = 0; i < 4; i++) { + if (is_valid_partition(&rs->part[i], rssize)) { + if (blkid_probe_set_magic(pr, + offsetof(struct atari_rootsector, part[i]), + sizeof(rs->part[i].flags) + sizeof(rs->part[i].id), + (unsigned char *) &rs->part[i])) + goto err; + break; + } + } + + if (i == 4) + goto nothing; + + if (blkid_partitions_need_typeonly(pr)) + /* caller does not ask for details about partitions */ + return BLKID_PROBE_OK; + + ls = blkid_probe_get_partlist(pr); + if (!ls) + goto nothing; + + tab = blkid_partlist_new_parttable(ls, "atari", 0); + if (!tab) + goto err; + + for (i = 0; i < ARRAY_SIZE(rs->part); i++) { + struct atari_part_def *p = &rs->part[i]; + + if (!IS_ACTIVE(*p)) { + blkid_partlist_increment_partno(ls); + continue; + } + if (!memcmp(p->id, "XGM", 3)) { + has_xgm = 1; + rc = parse_extended(pr, ls, tab, p); + } else { + rc = parse_partition(ls, tab, p, 0); + } + if (rc < 0) + return rc; + } + + /* if there are no XGM partitions, we can try ICD format */ + /* if first ICD partition ID is not valid, assume no ICD format */ + if (!has_xgm && is_id_common(rs->icd_part[0].id)) { + for (i = 0; i < ARRAY_SIZE(rs->icd_part); i++) { + struct atari_part_def *p = &rs->icd_part[i]; + + if (!IS_ACTIVE(*p) || !is_id_common(p->id)) { + blkid_partlist_increment_partno(ls); + continue; + } + + rc = parse_partition(ls, tab, p, 0); + if (rc < 0) + return rc; + } + } + + return BLKID_PROBE_OK; + +nothing: + return BLKID_PROBE_NONE; +err: + return -ENOMEM; +} + +const struct blkid_idinfo atari_pt_idinfo = +{ + .name = "atari", + .probefunc = probe_atari_pt, + .magics = BLKID_NONE_MAGIC +}; diff --git a/libblkid/src/partitions/bsd.c b/libblkid/src/partitions/bsd.c new file mode 100644 index 0000000..ba12019 --- /dev/null +++ b/libblkid/src/partitions/bsd.c @@ -0,0 +1,205 @@ +/* + * BSD/OSF partition parsing code + * + * Copyright (C) 2009 Karel Zak <kzak@redhat.com> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + * + * Inspired by fdisk, partx, Linux kernel, libparted and openbsd header files. + */ +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <stdint.h> + +#include "partitions.h" +#include "pt-bsd.h" + +/* Returns 'blkid_idmag' in 512-sectors */ +#define BLKID_MAG_SECTOR(_mag) (((_mag)->kboff / 2) + ((_mag)->sboff >> 9)) + +/* Returns 'blkid_idmag' in bytes */ +#define BLKID_MAG_OFFSET(_mag) ((_mag)->kboff << 10) + ((_mag)->sboff) + +/* Returns 'blkid_idmag' offset in bytes within the last sector */ +#define BLKID_MAG_LASTOFFSET(_mag) \ + (BLKID_MAG_OFFSET(_mag) - (BLKID_MAG_SECTOR(_mag) << 9)) + +static uint16_t bsd_checksum(const struct bsd_disklabel *l) +{ + uint16_t v, csum = 0; + const char *end = (const char *) (l + 1); + + for (const char *c = (const char *) l; c < end; c += sizeof(uint16_t)) { + memcpy(&v, c, sizeof(v)); + csum ^= v; + } + return csum ^ le16_to_cpu(l->d_checksum); +} + +static int probe_bsd_pt(blkid_probe pr, const struct blkid_idmag *mag) +{ + struct bsd_disklabel *l; + struct bsd_partition *p; + const char *name = "bsd" ; + blkid_parttable tab = NULL; + blkid_partition parent; + blkid_partlist ls; + int i, nparts = BSD_MAXPARTITIONS; + unsigned char *data; + int rc = BLKID_PROBE_NONE; + uint32_t abs_offset = 0; + + if (blkid_partitions_need_typeonly(pr)) + /* caller does not ask for details about partitions */ + return rc; + + data = blkid_probe_get_sector(pr, BLKID_MAG_SECTOR(mag)); + if (!data) { + if (errno) + rc = -errno; + goto nothing; + } + + l = (struct bsd_disklabel *) (data + BLKID_MAG_LASTOFFSET(mag)); + + if (!blkid_probe_verify_csum(pr, bsd_checksum(l), le16_to_cpu(l->d_checksum))) { + rc = BLKID_PROBE_NONE; + goto nothing; + } + + ls = blkid_probe_get_partlist(pr); + if (!ls) + goto nothing; + + /* try to determine the real type of BSD system according to + * (parental) primary partition */ + parent = blkid_partlist_get_parent(ls); + if (parent) { + switch(blkid_partition_get_type(parent)) { + case MBR_FREEBSD_PARTITION: + name = "freebsd"; + abs_offset = blkid_partition_get_start(parent); + break; + case MBR_NETBSD_PARTITION: + name = "netbsd"; + break; + case MBR_OPENBSD_PARTITION: + name = "openbsd"; + break; + default: + DBG(LOWPROBE, ul_debug( + "WARNING: BSD label detected on unknown (0x%x) " + "primary partition", + blkid_partition_get_type(parent))); + break; + } + } + + tab = blkid_partlist_new_parttable(ls, name, BLKID_MAG_OFFSET(mag)); + if (!tab) { + rc = -ENOMEM; + goto nothing; + } + + if (le16_to_cpu(l->d_npartitions) < BSD_MAXPARTITIONS) + nparts = le16_to_cpu(l->d_npartitions); + + else if (le16_to_cpu(l->d_npartitions) > BSD_MAXPARTITIONS) + DBG(LOWPROBE, ul_debug( + "WARNING: ignore %d more BSD partitions", + le16_to_cpu(l->d_npartitions) - BSD_MAXPARTITIONS)); + + for (i = 0, p = l->d_partitions; i < nparts; i++, p++) { + blkid_partition par; + uint32_t start, size; + + if (p->p_fstype == BSD_FS_UNUSED) + continue; + + start = le32_to_cpu(p->p_offset); + size = le32_to_cpu(p->p_size); + + /* FreeBSD since version 10 uses relative offsets. We can use + * 3rd partition (special wholedisk partition) to detect this + * situation. + */ + if (abs_offset && nparts >= 3 + && le32_to_cpu(l->d_partitions[2].p_offset) == 0) + start += abs_offset; + + if (parent && blkid_partition_get_start(parent) == start + && blkid_partition_get_size(parent) == size) { + DBG(LOWPROBE, ul_debug( + "WARNING: BSD partition (%d) same like parent, " + "ignore", i)); + continue; + } + if (parent && !blkid_is_nested_dimension(parent, start, size)) { + DBG(LOWPROBE, ul_debug( + "WARNING: BSD partition (%d) overflow " + "detected, ignore", i)); + continue; + } + + par = blkid_partlist_add_partition(ls, tab, start, size); + if (!par) { + rc = -ENOMEM; + goto nothing; + } + + blkid_partition_set_type(par, p->p_fstype); + } + + return BLKID_PROBE_OK; + +nothing: + return rc; +} + + +/* + * All BSD variants use the same magic string (little-endian), + * and the same disklabel. + * + * The difference between {Free,Open,...}BSD is in the (parental) + * primary partition type. + * + * See also: http://en.wikipedia.org/wiki/BSD_disklabel + * + * The location of BSD disk label is architecture specific and in defined by + * LABELSECTOR and LABELOFFSET macros in the disklabel.h file. The location + * also depends on BSD variant, FreeBSD uses only one location, NetBSD and + * OpenBSD are more creative... + * + * The basic overview: + * + * arch | LABELSECTOR | LABELOFFSET + * ------------------------+-------------+------------ + * amd64 arm hppa hppa64 | | + * i386, macppc, mvmeppc | 1 | 0 + * sgi, aviion, sh, socppc | | + * ------------------------+-------------+------------ + * alpha luna88k mac68k | 0 | 64 + * sparc(OpenBSD) vax | | + * ------------------------+-------------+------------ + * sparc64 sparc(NetBSD) | 0 | 128 + * ------------------------+-------------+------------ + * + * ...and more (see http://fxr.watson.org/fxr/ident?v=NETBSD;i=LABELSECTOR) + * + */ +const struct blkid_idinfo bsd_pt_idinfo = +{ + .name = "bsd", + .probefunc = probe_bsd_pt, + .magics = + { + { .magic = "\x57\x45\x56\x82", .len = 4, .sboff = 512 }, + { .magic = "\x57\x45\x56\x82", .len = 4, .sboff = 64 }, + { .magic = "\x57\x45\x56\x82", .len = 4, .sboff = 128 }, + { NULL } + } +}; + diff --git a/libblkid/src/partitions/dos.c b/libblkid/src/partitions/dos.c new file mode 100644 index 0000000..5c7718c --- /dev/null +++ b/libblkid/src/partitions/dos.c @@ -0,0 +1,375 @@ +/* + * MS-DOS partition parsing code + * + * Copyright (C) 2009 Karel Zak <kzak@redhat.com> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + * + * Inspired by fdisk, partx, Linux kernel and libparted. + */ +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <stdint.h> + +#include "partitions.h" +#include "superblocks/superblocks.h" +#include "aix.h" + +/* see superblocks/vfat.c */ +extern int blkid_probe_is_vfat(blkid_probe pr); +/* see superblocks/exfat.c */ +extern int blkid_probe_is_exfat(blkid_probe pr); + +static const struct dos_subtypes { + unsigned char type; + const struct blkid_idinfo *id; +} dos_nested[] = { + { MBR_FREEBSD_PARTITION, &bsd_pt_idinfo }, + { MBR_NETBSD_PARTITION, &bsd_pt_idinfo }, + { MBR_OPENBSD_PARTITION, &bsd_pt_idinfo }, + { MBR_UNIXWARE_PARTITION, &unixware_pt_idinfo }, + { MBR_SOLARIS_X86_PARTITION, &solaris_x86_pt_idinfo }, + { MBR_MINIX_PARTITION, &minix_pt_idinfo } +}; + +static inline int is_extended(struct dos_partition *p) +{ + return (p->sys_ind == MBR_DOS_EXTENDED_PARTITION || + p->sys_ind == MBR_W95_EXTENDED_PARTITION || + p->sys_ind == MBR_LINUX_EXTENDED_PARTITION); +} + +static int parse_dos_extended(blkid_probe pr, blkid_parttable tab, + uint32_t ex_start, uint32_t ex_size, int ssf) +{ + blkid_partlist ls = blkid_probe_get_partlist(pr); + uint32_t cur_start = ex_start, cur_size = ex_size; + unsigned char *data; + int ct_nodata = 0; /* count ext.partitions without data partitions */ + int i; + + DBG(LOWPROBE, ul_debug("parse EBR [start=%d, size=%d]", ex_start/ssf, ex_size/ssf)); + if (ex_start == 0) { + DBG(LOWPROBE, ul_debug("Bad offset in primary extended partition -- ignore")); + return 0; + } + + while (1) { + struct dos_partition *p, *p0; + uint32_t start = 0, size; + + if (++ct_nodata > 100) + return BLKID_PROBE_OK; + data = blkid_probe_get_sector(pr, cur_start); + if (!data) { + if (errno) + return -errno; + goto leave; /* malformed partition? */ + } + + if (!mbr_is_valid_magic(data)) + goto leave; + + p0 = mbr_get_partition(data, 0); + + /* Usually, the first entry is the real data partition, + * the 2nd entry is the next extended partition, or empty, + * and the 3rd and 4th entries are unused. + * However, DRDOS sometimes has the extended partition as + * the first entry (when the data partition is empty), + * and OS/2 seems to use all four entries. + * -- Linux kernel fs/partitions/dos.c + * + * See also http://en.wikipedia.org/wiki/Extended_boot_record + */ + + /* Parse data partition */ + for (p = p0, i = 0; i < 4; i++, p++) { + uint32_t abs_start; + blkid_partition par; + + /* the start is relative to the parental ext.partition */ + start = dos_partition_get_start(p) * ssf; + size = dos_partition_get_size(p) * ssf; + abs_start = cur_start + start; /* absolute start */ + + if (!size || is_extended(p)) + continue; + if (i >= 2) { + /* extra checks to detect real data on + * 3rd and 4th entries */ + if (start + size > cur_size) + continue; + if (abs_start < ex_start) + continue; + if (abs_start + size > ex_start + ex_size) + continue; + } + + /* Avoid recursive non-empty links, see ct_nodata counter */ + if (blkid_partlist_get_partition_by_start(ls, abs_start)) { + DBG(LOWPROBE, ul_debug("#%d: EBR duplicate data partition [abs start=%u] -- ignore", + i + 1, abs_start)); + continue; + } + + par = blkid_partlist_add_partition(ls, tab, abs_start, size); + if (!par) + return -ENOMEM; + + blkid_partition_set_type(par, p->sys_ind); + blkid_partition_set_flags(par, p->boot_ind); + blkid_partition_gen_uuid(par); + ct_nodata = 0; + } + /* The first nested ext.partition should be a link to the next + * logical partition. Everything other (recursive ext.partitions) + * is junk. + */ + for (p = p0, i = 0; i < 4; i++, p++) { + start = dos_partition_get_start(p) * ssf; + size = dos_partition_get_size(p) * ssf; + + if (size && is_extended(p)) { + if (start == 0) + DBG(LOWPROBE, ul_debug("#%d: EBR link offset is zero -- ignore", i + 1)); + else + break; + } + } + if (i == 4) + goto leave; + + cur_start = ex_start + start; + cur_size = size; + } +leave: + return BLKID_PROBE_OK; +} + +static inline int is_lvm(blkid_probe pr) +{ + struct blkid_prval *v = __blkid_probe_lookup_value(pr, "TYPE"); + + return (v && v->data && strcmp((char *) v->data, "LVM2_member") == 0); +} + +static inline int is_empty_mbr(unsigned char *mbr) +{ + struct dos_partition *p = mbr_get_partition(mbr, 0); + int i, nparts = 0; + + for (i = 0; i < 4; i++) { + if (dos_partition_get_size(p) > 0) + nparts++; + p++; + } + + return nparts == 0; +} + +static int probe_dos_pt(blkid_probe pr, + const struct blkid_idmag *mag __attribute__((__unused__))) +{ + int i; + int ssf; + blkid_parttable tab = NULL; + blkid_partlist ls; + struct dos_partition *p0, *p; + unsigned char *data; + uint32_t start, size, id; + char idstr[UUID_STR_LEN]; + + + data = blkid_probe_get_sector(pr, 0); + if (!data) { + if (errno) + return -errno; + goto nothing; + } + + /* ignore disks with AIX magic number -- for more details see aix.c */ + if (memcmp(data, BLKID_AIX_MAGIC_STRING, BLKID_AIX_MAGIC_STRLEN) == 0) + goto nothing; + + p0 = mbr_get_partition(data, 0); + + /* + * Reject PT where boot indicator is not 0 or 0x80. + */ + for (p = p0, i = 0; i < 4; i++, p++) + if (p->boot_ind != 0 && p->boot_ind != 0x80) { + DBG(LOWPROBE, ul_debug("missing boot indicator -- ignore")); + goto nothing; + } + + /* + * GPT uses valid MBR + */ + for (p = p0, i = 0; i < 4; i++, p++) { + if (p->sys_ind == MBR_GPT_PARTITION) { + DBG(LOWPROBE, ul_debug("probably GPT -- ignore")); + goto nothing; + } + } + + /* + * Now that the 55aa signature is present, this is probably + * either the boot sector of a FAT filesystem or a DOS-type + * partition table. + */ + if (blkid_probe_is_vfat(pr) == 1 || blkid_probe_is_exfat(pr) == 1) { + DBG(LOWPROBE, ul_debug("probably FAT -- ignore")); + goto nothing; + } + + /* Another false positive is NTFS */ + if (blkid_probe_is_ntfs(pr) == 1) { + DBG(LOWPROBE, ul_debug("probably NTFS -- ignore")); + goto nothing; + } + + /* + * Ugly exception, if the device contains a valid LVM physical volume + * and empty MBR (=no partition defined) then it's LVM and MBR should + * be ignored. Crazy people use it to boot from LVM devices. + */ + if (is_lvm(pr) && is_empty_mbr(data)) { + DBG(LOWPROBE, ul_debug("empty MBR on LVM device -- ignore")); + goto nothing; + } + + blkid_probe_use_wiper(pr, MBR_PT_OFFSET, 512 - MBR_PT_OFFSET); + + id = mbr_get_id(data); + if (id) + snprintf(idstr, sizeof(idstr), "%08x", id); + + /* + * Well, all checks pass, it's MS-DOS partition table + */ + if (blkid_partitions_need_typeonly(pr)) { + /* Non-binary interface -- caller does not ask for details + * about partitions, just set generic variables only. */ + if (id) + blkid_partitions_strcpy_ptuuid(pr, idstr); + return 0; + } + + ls = blkid_probe_get_partlist(pr); + if (!ls) + goto nothing; + + /* sector size factor (the start and size are in the real sectors, but + * we need to convert all sizes to 512 logical sectors + */ + ssf = blkid_probe_get_sectorsize(pr) / 512; + + /* allocate a new partition table */ + tab = blkid_partlist_new_parttable(ls, "dos", MBR_PT_OFFSET); + if (!tab) + return -ENOMEM; + + if (id) + blkid_parttable_set_id(tab, (unsigned char *) idstr); + + /* Parse primary partitions */ + for (p = p0, i = 0; i < 4; i++, p++) { + blkid_partition par; + + start = dos_partition_get_start(p) * ssf; + size = dos_partition_get_size(p) * ssf; + + if (!size) { + /* Linux kernel ignores empty partitions, but partno for + * the empty primary partitions is not reused */ + blkid_partlist_increment_partno(ls); + continue; + } + par = blkid_partlist_add_partition(ls, tab, start, size); + if (!par) + return -ENOMEM; + + blkid_partition_set_type(par, p->sys_ind); + blkid_partition_set_flags(par, p->boot_ind); + blkid_partition_gen_uuid(par); + } + + /* Linux uses partition numbers greater than 4 + * for all logical partition and all nested partition tables (bsd, ..) + */ + blkid_partlist_set_partno(ls, 5); + + /* Parse logical partitions */ + for (p = p0, i = 0; i < 4; i++, p++) { + start = dos_partition_get_start(p) * ssf; + size = dos_partition_get_size(p) * ssf; + + if (!size) + continue; + if (is_extended(p) && + parse_dos_extended(pr, tab, start, size, ssf) == -1) + goto nothing; + } + + /* Parse subtypes (nested partitions) on large disks */ + if (!blkid_probe_is_tiny(pr)) { + int nparts = blkid_partlist_numof_partitions(ls); + + DBG(LOWPROBE, ul_debug("checking for subtypes")); + + for (i = 0; i < nparts; i++) { + size_t n; + int type; + blkid_partition pa = blkid_partlist_get_partition(ls, i); + + if (pa == NULL + || blkid_partition_get_size(pa) == 0 + || blkid_partition_is_extended(pa) + || blkid_partition_is_logical(pa)) + continue; + + type = blkid_partition_get_type(pa); + + for (n = 0; n < ARRAY_SIZE(dos_nested); n++) { + int rc; + + if (dos_nested[n].type != type) + continue; + + rc = blkid_partitions_do_subprobe(pr, pa, + dos_nested[n].id); + if (rc < 0) + return rc; + break; + } + } + } + return BLKID_PROBE_OK; + +nothing: + return BLKID_PROBE_NONE; +} + + +const struct blkid_idinfo dos_pt_idinfo = +{ + .name = "dos", + .probefunc = probe_dos_pt, + .magics = + { + /* DOS master boot sector: + * + * 0 | Code Area + * 440 | Optional Disk signature + * 446 | Partition table + * 510 | 0x55 + * 511 | 0xAA + */ + { .magic = "\x55\xAA", .len = 2, .sboff = 510 }, + { NULL } + } +}; + diff --git a/libblkid/src/partitions/gpt.c b/libblkid/src/partitions/gpt.c new file mode 100644 index 0000000..89e7bb6 --- /dev/null +++ b/libblkid/src/partitions/gpt.c @@ -0,0 +1,473 @@ +/* + * EFI GPT partition parsing code + * + * Copyright (C) 2009 Karel Zak <kzak@redhat.com> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + * + * This code is not copy & past from any other implementation. + * + * For more information about GPT start your study at: + * http://en.wikipedia.org/wiki/GUID_Partition_Table + * http://technet.microsoft.com/en-us/library/cc739412(WS.10).aspx + */ +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <stdint.h> +#include <stddef.h> +#include <limits.h> +#include <inttypes.h> + +#include "partitions.h" +#include "crc32.h" + +#define GPT_PRIMARY_LBA 1 + +/* Signature - “EFI PART” */ +#define GPT_HEADER_SIGNATURE 0x5452415020494645ULL +#define GPT_HEADER_SIGNATURE_STR "EFI PART" + +/* basic types */ +typedef uint16_t efi_char16_t; + +/* UUID */ +typedef struct { + uint32_t time_low; + uint16_t time_mid; + uint16_t time_hi_and_version; + uint8_t clock_seq_hi; + uint8_t clock_seq_low; + uint8_t node[6]; +} efi_guid_t; + + +#define GPT_UNUSED_ENTRY_GUID \ + ((efi_guid_t) { 0x00000000, 0x0000, 0x0000, 0x00, 0x00, \ + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }}) +struct gpt_header { + uint64_t signature; /* "EFI PART" */ + uint32_t revision; + uint32_t header_size; /* usually 92 bytes */ + uint32_t header_crc32; /* checksum of header with this + * field zeroed during calculation */ + uint32_t reserved1; + + uint64_t my_lba; /* location of this header copy */ + uint64_t alternate_lba; /* location of the other header copy */ + uint64_t first_usable_lba; /* first usable LBA for partitions */ + uint64_t last_usable_lba; /* last usable LBA for partitions */ + + efi_guid_t disk_guid; /* disk UUID */ + + uint64_t partition_entries_lba; /* always 2 in primary header copy */ + uint32_t num_partition_entries; + uint32_t sizeof_partition_entry; + uint32_t partition_entry_array_crc32; + + /* + * The rest of the block is reserved by UEFI and must be zero. EFI + * standard handles this by: + * + * uint8_t reserved2[ BLKSSZGET - 92 ]; + * + * This definition is useless in practice. It is necessary to read + * whole block from the device rather than sizeof(struct gpt_header) + * only. + */ +} __attribute__ ((packed)); + +/*** not used +struct gpt_entry_attributes { + uint64_t required_to_function:1; + uint64_t reserved:47; + uint64_t type_guid_specific:16; +} __attribute__ ((packed)); +***/ + +struct gpt_entry { + efi_guid_t partition_type_guid; /* type UUID */ + efi_guid_t unique_partition_guid; /* partition UUID */ + uint64_t starting_lba; + uint64_t ending_lba; + + /*struct gpt_entry_attributes attributes;*/ + + uint64_t attributes; + + efi_char16_t partition_name[72 / sizeof(efi_char16_t)]; /* UTF-16LE string*/ +} __attribute__ ((packed)); + + +/* + * EFI uses crc32 with ~0 seed and xor's with ~0 at the end. + */ +static inline uint32_t count_crc32(const unsigned char *buf, size_t len, + size_t exclude_off, size_t exclude_len) +{ + return (ul_crc32_exclude_offset(~0L, buf, len, exclude_off, exclude_len) ^ ~0L); +} + +static inline unsigned char *get_lba_buffer(blkid_probe pr, + uint64_t lba, size_t bytes) +{ + return blkid_probe_get_buffer(pr, + blkid_probe_get_sectorsize(pr) * lba, bytes); +} + +static inline int guidcmp(efi_guid_t left, efi_guid_t right) +{ + return memcmp(&left, &right, sizeof (efi_guid_t)); +} + +/* + * UUID is traditionally 16 byte big-endian array, except Intel EFI + * specification where the UUID is a structure of little-endian fields. + */ +static void swap_efi_guid(efi_guid_t *uid) +{ + uid->time_low = swab32(uid->time_low); + uid->time_mid = swab16(uid->time_mid); + uid->time_hi_and_version = swab16(uid->time_hi_and_version); +} + +static int last_lba(blkid_probe pr, uint64_t *lba) +{ + uint64_t sz = blkid_probe_get_size(pr); + unsigned int ssz = blkid_probe_get_sectorsize(pr); + + if (sz < ssz) + return -1; + + *lba = (sz / ssz) - 1ULL; + return 0; +} + +/* + * Protective (legacy) MBR. + * + * This MBR contains standard DOS partition table with a single partition, type + * of 0xEE. The partition usually encompassing the entire GPT drive - or 2TiB + * for large disks. + * + * Note that Apple uses GPT/MBR hybrid disks, where the DOS partition table is + * synchronized with GPT. This synchronization has many restriction of course + * (due DOS PT limitations). + * + * Note that the PMBR detection is optional (enabled by default) and could be + * disabled by BLKID_PARTS_FOPCE_GPT flag (see also blkid_partitions_set_flags()). + */ +static int is_pmbr_valid(blkid_probe pr, int *has) +{ + int flags = blkid_partitions_get_flags(pr); + unsigned char *data; + struct dos_partition *p; + int i; + + if (has) + *has = 0; + else if (flags & BLKID_PARTS_FORCE_GPT) + return 1; /* skip PMBR check */ + + data = blkid_probe_get_sector(pr, 0); + if (!data) { + if (errno) + return -errno; + goto failed; + } + + if (!mbr_is_valid_magic(data)) + goto failed; + + for (i = 0, p = mbr_get_partition(data, 0); i < 4; i++, p++) { + if (p->sys_ind == MBR_GPT_PARTITION) { + DBG(LOWPROBE, ul_debug(" #%d valid PMBR partition", i + 1)); + goto ok; + } + } +failed: + return 0; +ok: + if (has) + *has = 1; + return 1; +} + +/* + * Reads GPT header to @hdr and returns a pointer to @hdr or NULL in case of + * error. The function also returns GPT entries in @ents. + * + * Note, this function does not allocate any memory. The GPT header has fixed + * size so we use stack, and @ents returns memory from libblkid buffer (so the + * next blkid_probe_get_buffer() will overwrite this buffer). + * + * This function checks validity of header and entries array. A corrupted + * header is not returned. + */ +static struct gpt_header *get_gpt_header( + blkid_probe pr, struct gpt_header *hdr, + struct gpt_entry **ents, uint64_t lba, + uint64_t lastlba) +{ + struct gpt_header *h; + uint32_t crc; + uint64_t lu, fu; + uint64_t esz; + uint32_t hsz, ssz; + + ssz = blkid_probe_get_sectorsize(pr); + + DBG(LOWPROBE, ul_debug(" checking for GPT header at %"PRIu64, lba)); + + /* whole sector is allocated for GPT header */ + h = (struct gpt_header *) get_lba_buffer(pr, lba, ssz); + if (!h) + return NULL; + + if (le64_to_cpu(h->signature) != GPT_HEADER_SIGNATURE) + return NULL; + + hsz = le32_to_cpu(h->header_size); + + /* EFI: The HeaderSize must be greater than 92 and must be less + * than or equal to the logical block size. + */ + if (hsz > ssz || hsz < sizeof(*h)) + return NULL; + + /* Header has to be verified when header_crc32 is zero */ + crc = count_crc32((unsigned char *) h, hsz, + offsetof(struct gpt_header, header_crc32), + sizeof(h->header_crc32)); + + if (!blkid_probe_verify_csum(pr, crc, le32_to_cpu(h->header_crc32))) { + DBG(LOWPROBE, ul_debug("GPT header corrupted")); + return NULL; + } + + /* Valid header has to be at MyLBA */ + if (le64_to_cpu(h->my_lba) != lba) { + DBG(LOWPROBE, ul_debug( + "GPT->MyLBA mismatch with real position")); + return NULL; + } + + fu = le64_to_cpu(h->first_usable_lba); + lu = le64_to_cpu(h->last_usable_lba); + + /* Check if First and Last usable LBA makes sense */ + if (lu < fu || fu > lastlba || lu > lastlba) { + DBG(LOWPROBE, ul_debug( + "GPT->{First,Last}UsableLBA out of range")); + return NULL; + } + + /* The header has to be outside usable range */ + if (fu < lba && lba < lu) { + DBG(LOWPROBE, ul_debug("GPT header is inside usable area")); + return NULL; + } + + /* Size of blocks with GPT entries */ + esz = (uint64_t)le32_to_cpu(h->num_partition_entries) * + le32_to_cpu(h->sizeof_partition_entry); + + if (esz == 0 || esz >= UINT32_MAX || + le32_to_cpu(h->sizeof_partition_entry) != sizeof(struct gpt_entry)) { + DBG(LOWPROBE, ul_debug("GPT entries undefined")); + return NULL; + } + + /* The header seems valid, save it + * (we don't care about zeros in hdr->reserved2 area) */ + memcpy(hdr, h, sizeof(*h)); + h = hdr; + + /* Read GPT entries */ + *ents = (struct gpt_entry *) get_lba_buffer(pr, + le64_to_cpu(h->partition_entries_lba), esz); + if (!*ents) { + DBG(LOWPROBE, ul_debug("GPT entries unreadable")); + return NULL; + } + + /* Validate entries */ + crc = count_crc32((unsigned char *) *ents, esz, 0, 0); + if (crc != le32_to_cpu(h->partition_entry_array_crc32)) { + DBG(LOWPROBE, ul_debug("GPT entries corrupted")); + return NULL; + } + + return h; +} + +static int probe_gpt_pt(blkid_probe pr, + const struct blkid_idmag *mag __attribute__((__unused__))) +{ + uint64_t lastlba = 0, lba; + struct gpt_header hdr, *h; + struct gpt_entry *e; + blkid_parttable tab = NULL; + blkid_partlist ls; + uint64_t fu, lu; + uint32_t ssf, i; + efi_guid_t guid; + int ret; + + if (last_lba(pr, &lastlba)) + goto nothing; + + ret = is_pmbr_valid(pr, NULL); + if (ret < 0) + return ret; + if (ret == 0) + goto nothing; + + errno = 0; + h = get_gpt_header(pr, &hdr, &e, (lba = GPT_PRIMARY_LBA), lastlba); + if (!h && !errno) + h = get_gpt_header(pr, &hdr, &e, (lba = lastlba), lastlba); + + if (!h) { + if (errno) + return -errno; + goto nothing; + } + + blkid_probe_use_wiper(pr, lba * blkid_probe_get_size(pr), 8); + + if (blkid_probe_set_magic(pr, blkid_probe_get_sectorsize(pr) * lba, + sizeof(GPT_HEADER_SIGNATURE_STR) - 1, + (unsigned char *) GPT_HEADER_SIGNATURE_STR)) + goto err; + + guid = h->disk_guid; + swap_efi_guid(&guid); + + if (blkid_partitions_need_typeonly(pr)) { + /* Non-binary interface -- caller does not ask for details + * about partitions, just set generic variables only. */ + blkid_partitions_set_ptuuid(pr, (unsigned char *) &guid); + return BLKID_PROBE_OK; + } + + ls = blkid_probe_get_partlist(pr); + if (!ls) + goto nothing; + + tab = blkid_partlist_new_parttable(ls, "gpt", + blkid_probe_get_sectorsize(pr) * lba); + if (!tab) + goto err; + + blkid_parttable_set_uuid(tab, (const unsigned char *) &guid); + + ssf = blkid_probe_get_sectorsize(pr) / 512; + + fu = le64_to_cpu(h->first_usable_lba); + lu = le64_to_cpu(h->last_usable_lba); + + for (i = 0; i < le32_to_cpu(h->num_partition_entries); i++, e++) { + + blkid_partition par; + uint64_t start = le64_to_cpu(e->starting_lba); + uint64_t size = le64_to_cpu(e->ending_lba) - + le64_to_cpu(e->starting_lba) + 1ULL; + + /* 00000000-0000-0000-0000-000000000000 entry */ + if (!guidcmp(e->partition_type_guid, GPT_UNUSED_ENTRY_GUID)) { + blkid_partlist_increment_partno(ls); + continue; + } + /* the partition has to inside usable range */ + if (start < fu || start + size - 1 > lu) { + DBG(LOWPROBE, ul_debug( + "GPT entry[%d] overflows usable area - ignore", + i)); + blkid_partlist_increment_partno(ls); + continue; + } + + par = blkid_partlist_add_partition(ls, tab, + start * ssf, size * ssf); + if (!par) + goto err; + + blkid_partition_set_utf8name(par, + (unsigned char *) e->partition_name, + sizeof(e->partition_name), UL_ENCODE_UTF16LE); + + guid = e->unique_partition_guid; + swap_efi_guid(&guid); + blkid_partition_set_uuid(par, (const unsigned char *) &guid); + + guid = e->partition_type_guid; + swap_efi_guid(&guid); + blkid_partition_set_type_uuid(par, (const unsigned char *) &guid); + + blkid_partition_set_flags(par, le64_to_cpu(e->attributes)); + } + + return BLKID_PROBE_OK; + +nothing: + return BLKID_PROBE_NONE; + +err: + return -ENOMEM; +} + + +const struct blkid_idinfo gpt_pt_idinfo = +{ + .name = "gpt", + .probefunc = probe_gpt_pt, + + /* + * It would be possible to check for DOS signature (0xAA55), but + * unfortunately almost all EFI GPT implementations allow to optionally + * skip the legacy MBR. We follows this behavior and MBR is optional. + * See is_valid_pmbr(). + * + * It means we have to always call probe_gpt_pt(). + */ + .magics = BLKID_NONE_MAGIC +}; + + + +/* probe for *alone* protective MBR */ +static int probe_pmbr_pt(blkid_probe pr, + const struct blkid_idmag *mag __attribute__((__unused__))) +{ + int has = 0; + struct gpt_entry *e; + uint64_t lastlba = 0; + struct gpt_header hdr; + + if (last_lba(pr, &lastlba)) + goto nothing; + + is_pmbr_valid(pr, &has); + if (!has) + goto nothing; + + if (!get_gpt_header(pr, &hdr, &e, GPT_PRIMARY_LBA, lastlba) && + !get_gpt_header(pr, &hdr, &e, lastlba, lastlba)) + return 0; +nothing: + return 1; +} + +const struct blkid_idinfo pmbr_pt_idinfo = +{ + .name = "PMBR", + .probefunc = probe_pmbr_pt, + .magics = + { + { .magic = "\x55\xAA", .len = 2, .sboff = 510 }, + { NULL } + } +}; + diff --git a/libblkid/src/partitions/mac.c b/libblkid/src/partitions/mac.c new file mode 100644 index 0000000..75a558b --- /dev/null +++ b/libblkid/src/partitions/mac.c @@ -0,0 +1,200 @@ +/* + * mac partitions parsing code + * + * Copyright (C) 2009 Karel Zak <kzak@redhat.com> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + * + */ +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <stdint.h> + +#include "partitions.h" + +#define MAC_PARTITION_MAGIC 0x504d +#define MAC_PARTITION_MAGIC_OLD 0x5453 + +/* + * Mac partition entry + * http://developer.apple.com/legacy/mac/library/documentation/mac/Devices/Devices-126.html + */ +struct mac_partition { + uint16_t signature; /* expected to be MAC_PARTITION_MAGIC */ + uint16_t reserved; /* reserved */ + 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 */ + char name[32]; /* partition name */ + char type[32]; /* string type description */ + uint32_t data_start; /* rel block # of first data block */ + uint32_t data_count; /* number of data blocks */ + uint32_t status; /* partition status bits */ + uint32_t boot_start; /* first logical block of boot code */ + uint32_t boot_size; /* size of boot code, in bytes */ + uint32_t boot_load; /* boot code load address */ + uint32_t boot_load2; /* reserved */ + uint32_t boot_entry; /* boot code entry point */ + uint32_t boot_entry2; /* reserved */ + uint32_t boot_cksum; /* boot code checksum */ + char processor[16]; /* identifies ISA of boot */ + + /* there is more stuff after this that we don't need */ +} __attribute__((packed)); + +/* + * Driver descriptor structure, in block 0 + * http://developer.apple.com/legacy/mac/library/documentation/mac/Devices/Devices-121.html + */ +struct mac_driver_desc { + uint16_t signature; /* expected to be MAC_DRIVER_MAGIC */ + uint16_t block_size; /* block size of the device */ + uint32_t block_count; /* number of blocks on the device */ + + /* there is more stuff after this that we don't need */ +} __attribute__((packed)); + +static inline unsigned char *get_mac_block( + blkid_probe pr, + uint16_t block_size, + uint32_t num) +{ + return blkid_probe_get_buffer(pr, (uint64_t) num * block_size, block_size); +} + +static inline int has_part_signature(struct mac_partition *p) +{ + return be16_to_cpu(p->signature) == MAC_PARTITION_MAGIC || + be16_to_cpu(p->signature) == MAC_PARTITION_MAGIC_OLD; +} + +static int probe_mac_pt(blkid_probe pr, + const struct blkid_idmag *mag __attribute__((__unused__))) +{ + struct mac_driver_desc *md; + struct mac_partition *p; + blkid_parttable tab = NULL; + blkid_partlist ls; + uint16_t block_size; + uint16_t ssf; /* sector size fragment */ + uint32_t nblks, nprts, i; + + + /* The driver descriptor record is always located at physical block 0, + * the first block on the disk. + */ + md = (struct mac_driver_desc *) blkid_probe_get_sector(pr, 0); + if (!md) { + if (errno) + return -errno; + goto nothing; + } + + block_size = be16_to_cpu(md->block_size); + if (block_size < sizeof(struct mac_partition)) + goto nothing; + + /* The partition map always begins at physical block 1, + * the second block on the disk. + */ + p = (struct mac_partition *) get_mac_block(pr, block_size, 1); + if (!p) { + if (errno) + return -errno; + goto nothing; + } + + /* check the first partition signature */ + if (!has_part_signature(p)) + goto nothing; + + if (blkid_partitions_need_typeonly(pr)) + /* caller does not ask for details about partitions */ + return 0; + + ls = blkid_probe_get_partlist(pr); + if (!ls) + goto nothing; + + tab = blkid_partlist_new_parttable(ls, "mac", 0); + if (!tab) + goto err; + + ssf = block_size / 512; + nblks = be32_to_cpu(p->map_count); + if (nblks > 256) { + nprts = 256; + DBG(LOWPROBE, ul_debug( + "mac: map_count too large, entry[0]: %u, " + "enforcing limit of %u", nblks, nprts)); + } else + nprts = nblks; + + for (i = 0; i < nprts; ++i) { + blkid_partition par; + uint32_t start; + uint32_t size; + + p = (struct mac_partition *) get_mac_block(pr, block_size, i + 1); + if (!p) { + if (errno) + return -errno; + goto nothing; + } + if (!has_part_signature(p)) + goto nothing; + + if (be32_to_cpu(p->map_count) != nblks) { + DBG(LOWPROBE, ul_debug( + "mac: inconsistent map_count in partition map, " + "entry[0]: %u, entry[%u]: %u", + nblks, i, + be32_to_cpu(p->map_count))); + } + + /* + * note that libparted ignores some mac partitions according to + * the partition name (e.g. "Apple_Free" or "Apple_Void"). We + * follows Linux kernel and all partitions are visible + */ + + start = be32_to_cpu(p->start_block) * ssf; + size = be32_to_cpu(p->block_count) * ssf; + + par = blkid_partlist_add_partition(ls, tab, start, size); + if (!par) + goto err; + + blkid_partition_set_name(par, (unsigned char *) p->name, + sizeof(p->name)); + + blkid_partition_set_type_string(par, (unsigned char *) p->type, + sizeof(p->type)); + } + + return BLKID_PROBE_OK; + +nothing: + return BLKID_PROBE_NONE; +err: + return -ENOMEM; +} + +/* + * Mac disk always begin with "Driver Descriptor Record" + * (struct mac_driver_desc) and magic 0x4552. + */ +const struct blkid_idinfo mac_pt_idinfo = +{ + .name = "mac", + .probefunc = probe_mac_pt, + .magics = + { + /* big-endian magic string */ + { .magic = "\x45\x52", .len = 2 }, + { NULL } + } +}; + diff --git a/libblkid/src/partitions/minix.c b/libblkid/src/partitions/minix.c new file mode 100644 index 0000000..43c9d9a --- /dev/null +++ b/libblkid/src/partitions/minix.c @@ -0,0 +1,102 @@ +/* + * Minix partition parsing code + * + * Copyright (C) 2009 Karel Zak <kzak@redhat.com> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + */ +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <stdint.h> + +#include "partitions.h" +#include "minix.h" + +static int probe_minix_pt(blkid_probe pr, + const struct blkid_idmag *mag __attribute__((__unused__))) +{ + struct dos_partition *p; + blkid_parttable tab = NULL; + blkid_partition parent; + blkid_partlist ls; + unsigned char *data; + int i; + + data = blkid_probe_get_sector(pr, 0); + if (!data) { + if (errno) + return -errno; + goto nothing; + } + + ls = blkid_probe_get_partlist(pr); + if (!ls) + goto nothing; + + /* Parent is required, because Minix uses the same PT as DOS and + * difference is only in primary partition (parent) type. + */ + parent = blkid_partlist_get_parent(ls); + if (!parent) + goto nothing; + + if (blkid_partition_get_type(parent) != MBR_MINIX_PARTITION) + goto nothing; + + if (blkid_partitions_need_typeonly(pr)) + /* caller does not ask for details about partitions */ + return BLKID_PROBE_OK; + + tab = blkid_partlist_new_parttable(ls, "minix", MBR_PT_OFFSET); + if (!tab) + goto err; + + for (i = 0, p = mbr_get_partition(data, 0); + i < MINIX_MAXPARTITIONS; i++, p++) { + + uint32_t start, size; + blkid_partition par; + + if (p->sys_ind != MBR_MINIX_PARTITION) + continue; + + start = dos_partition_get_start(p); + size = dos_partition_get_size(p); + + if (parent && !blkid_is_nested_dimension(parent, start, size)) { + DBG(LOWPROBE, ul_debug( + "WARNING: minix partition (%d) overflow " + "detected, ignore", i)); + continue; + } + + par = blkid_partlist_add_partition(ls, tab, start, size); + if (!par) + goto err; + + blkid_partition_set_type(par, p->sys_ind); + blkid_partition_set_flags(par, p->boot_ind); + } + + return BLKID_PROBE_OK; + +nothing: + return BLKID_PROBE_NONE; +err: + return -ENOMEM; +} + +/* same as DOS */ +const struct blkid_idinfo minix_pt_idinfo = +{ + .name = "minix", + .probefunc = probe_minix_pt, + .magics = + { + { .magic = "\x55\xAA", .len = 2, .sboff = 510 }, + { NULL } + } +}; + diff --git a/libblkid/src/partitions/partitions.c b/libblkid/src/partitions/partitions.c new file mode 100644 index 0000000..8ebf480 --- /dev/null +++ b/libblkid/src/partitions/partitions.c @@ -0,0 +1,1532 @@ +/* + * partitions - partition tables parsing + * + * Copyright (C) 2008-2009 Karel Zak <kzak@redhat.com> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + * + */ +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <ctype.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <errno.h> +#include <stdint.h> +#include <inttypes.h> +#include <stdarg.h> + +#include "partitions.h" +#include "sysfs.h" +#include "strutils.h" + +/** + * SECTION: partitions + * @title: Partitions probing + * @short_description: partitions tables detection and parsing + * + * This chain supports binary and NAME=value interfaces, but complete PT + * description is provided by binary interface only. The libblkid prober is + * compatible with kernel partition tables parser. The parser does not return + * empty (size=0) partitions or special hidden partitions. + * + * NAME=value interface, supported tags: + * + * @PTTYPE: partition table type (dos, gpt, etc.). + * + * @PTUUID: partition table id (uuid for gpt, hex for dos). + + * @PART_ENTRY_SCHEME: partition table type + * + * @PART_ENTRY_NAME: partition name (gpt and mac only) + * + * @PART_ENTRY_UUID: partition UUID (gpt, or pseudo IDs for MBR) + * + * @PART_ENTRY_TYPE: partition type, 0xNN (e.g. 0x82) or type UUID (gpt only) or type string (mac) + * + * @PART_ENTRY_FLAGS: partition flags (e.g. boot_ind) or attributes (e.g. gpt attributes) + * + * @PART_ENTRY_NUMBER: partition number + * + * @PART_ENTRY_OFFSET: the begin of the partition + * + * @PART_ENTRY_SIZE: size of the partition + * + * @PART_ENTRY_DISK: whole-disk maj:min + * + * Example: + * + * <informalexample> + * <programlisting> + * blkid_probe pr; + * const char *ptname; + * + * pr = blkid_new_probe_from_filename(devname); + * if (!pr) + * err("%s: failed to open device", devname); + * + * blkid_probe_enable_partitions(pr, TRUE); + * blkid_do_fullprobe(pr); + * + * blkid_probe_lookup_value(pr, "PTTYPE", &ptname, NULL); + * printf("%s partition type detected\n", pttype); + * + * blkid_free_probe(pr); + * + * // don't forget to check return codes in your code! + * </programlisting> + * </informalexample> + * + * Binary interface: + * + * <informalexample> + * <programlisting> + * blkid_probe pr; + * blkid_partlist ls; + * int nparts, i; + * + * pr = blkid_new_probe_from_filename(devname); + * if (!pr) + * err("%s: failed to open device", devname); + * + * ls = blkid_probe_get_partitions(pr); + * nparts = blkid_partlist_numof_partitions(ls); + * + * for (i = 0; i < nparts; i++) { + * blkid_partition par = blkid_partlist_get_partition(ls, i); + * printf("#%d: %llu %llu 0x%x", + * blkid_partition_get_partno(par), + * blkid_partition_get_start(par), + * blkid_partition_get_size(par), + * blkid_partition_get_type(par)); + * } + * + * blkid_free_probe(pr); + * + * // don't forget to check return codes in your code! + * </programlisting> + * </informalexample> + */ + +/* + * Chain driver function + */ +static int partitions_probe(blkid_probe pr, struct blkid_chain *chn); +static void partitions_free_data(blkid_probe pr, void *data); + +/* + * Partitions chain probing functions + */ +static const struct blkid_idinfo *idinfos[] = +{ + &aix_pt_idinfo, + &sgi_pt_idinfo, + &sun_pt_idinfo, + &dos_pt_idinfo, + &gpt_pt_idinfo, + &pmbr_pt_idinfo, /* always after GPT */ + &mac_pt_idinfo, + &ultrix_pt_idinfo, + &bsd_pt_idinfo, + &unixware_pt_idinfo, + &solaris_x86_pt_idinfo, + &minix_pt_idinfo, + &atari_pt_idinfo +}; + +/* + * Driver definition + */ +const struct blkid_chaindrv partitions_drv = { + .id = BLKID_CHAIN_PARTS, + .name = "partitions", + .dflt_enabled = FALSE, + .idinfos = idinfos, + .nidinfos = ARRAY_SIZE(idinfos), + .has_fltr = TRUE, + .probe = partitions_probe, + .safeprobe = partitions_probe, + .free_data = partitions_free_data +}; + + +/* + * For compatibility with the rest of libblkid API (with the old high-level + * API) we use completely opaque typedefs for all structs. Don't forget that + * the final blkid_* types are pointers! See blkid.h. + * + * [Just for the record, I hate typedef for pointers --kzak] + */ + +/* exported as opaque type "blkid_parttable" */ +struct blkid_struct_parttable { + const char *type; /* partition table type */ + uint64_t offset; /* begin of the partition table (in bytes) */ + int nparts; /* number of partitions */ + blkid_partition parent; /* parent of nested partition table */ + char id[UUID_STR_LEN]; /* PT identifier (e.g. UUID for GPT) */ + + struct list_head t_tabs; /* all tables */ +}; + +/* exported as opaque type "blkid_partition" */ +struct blkid_struct_partition { + uint64_t start; /* begin of the partition (512-bytes sectors) */ + uint64_t size; /* size of the partitions (512-bytes sectors) */ + + int type; /* partition type */ + char typestr[UUID_STR_LEN]; /* partition type string (GPT and Mac) */ + + unsigned long long flags; /* partition flags / attributes */ + + int partno; /* partition number */ + char uuid[UUID_STR_LEN]; /* UUID (when supported by PT), e.g. GPT */ + unsigned char name[128]; /* Partition in UTF8 name (when supported by PT), e.g. Mac */ + + blkid_parttable tab; /* partition table */ +}; + +/* exported as opaque type "blkid_partlist" */ +struct blkid_struct_partlist { + int next_partno; /* next partition number */ + blkid_partition next_parent; /* next parent if parsing nested PT */ + + int nparts; /* number of partitions */ + int nparts_max; /* max.number of partitions */ + blkid_partition parts; /* array of partitions */ + + struct list_head l_tabs; /* list of partition tables */ +}; + +static int blkid_partitions_probe_partition(blkid_probe pr); + +/** + * blkid_probe_enable_partitions: + * @pr: probe + * @enable: TRUE/FALSE + * + * Enables/disables the partitions probing for non-binary interface. + * + * Returns: 0 on success, or -1 in case of error. + */ +int blkid_probe_enable_partitions(blkid_probe pr, int enable) +{ + pr->chains[BLKID_CHAIN_PARTS].enabled = enable; + return 0; +} + +/** + * blkid_probe_set_partitions_flags: + * @pr: prober + * @flags: BLKID_PARTS_* flags + * + * Sets probing flags to the partitions prober. This function is optional. + * + * Returns: 0 on success, or -1 in case of error. + */ +int blkid_probe_set_partitions_flags(blkid_probe pr, int flags) +{ + pr->chains[BLKID_CHAIN_PARTS].flags = flags; + return 0; +} + +int blkid_probe_get_partitions_flags(blkid_probe pr) +{ + return pr->chains[BLKID_CHAIN_PARTS].flags; +} + +/** + * blkid_probe_reset_partitions_filter: + * @pr: prober + * + * Resets partitions probing filter + * + * Returns: 0 on success, or -1 in case of error. + */ +int blkid_probe_reset_partitions_filter(blkid_probe pr) +{ + return __blkid_probe_reset_filter(pr, BLKID_CHAIN_PARTS); +} + +/** + * blkid_probe_invert_partitions_filter: + * @pr: prober + * + * Inverts partitions probing filter + * + * Returns: 0 on success, or -1 in case of error. + */ +int blkid_probe_invert_partitions_filter(blkid_probe pr) +{ + return __blkid_probe_invert_filter(pr, BLKID_CHAIN_PARTS); +} + +/** + * blkid_probe_filter_partitions_type: + * @pr: prober + * @flag: filter BLKID_FLTR_{NOTIN,ONLYIN} flag + * @names: NULL terminated array of probing function names (e.g. "vfat"). + * + * %BLKID_FLTR_NOTIN - probe for all items which are NOT IN @names + * + * %BLKID_FLTR_ONLYIN - probe for items which are IN @names + * + * Returns: 0 on success, or -1 in case of error. + */ +int blkid_probe_filter_partitions_type(blkid_probe pr, int flag, char *names[]) +{ + return __blkid_probe_filter_types(pr, BLKID_CHAIN_PARTS, flag, names); +} + +/** + * blkid_probe_get_partitions: + * @pr: probe + * + * This is a binary interface for partitions. See also blkid_partlist_* + * functions. + * + * This function is independent on blkid_do_[safe,full]probe() and + * blkid_probe_enable_partitions() calls. + * + * WARNING: the returned object will be overwritten by the next + * blkid_probe_get_partitions() call for the same @pr. If you want to + * use more blkid_partlist objects in the same time you have to create + * more blkid_probe handlers (see blkid_new_probe()). + * + * Returns: list of partitions, or NULL in case of error. + */ +blkid_partlist blkid_probe_get_partitions(blkid_probe pr) +{ + return (blkid_partlist) blkid_probe_get_binary_data(pr, + &pr->chains[BLKID_CHAIN_PARTS]); +} + +/* for internal usage only */ +blkid_partlist blkid_probe_get_partlist(blkid_probe pr) +{ + return (blkid_partlist) pr->chains[BLKID_CHAIN_PARTS].data; +} + +static void blkid_probe_set_partlist(blkid_probe pr, blkid_partlist ls) +{ + pr->chains[BLKID_CHAIN_PARTS].data = ls; +} + +static void ref_parttable(blkid_parttable tab) +{ + if (tab) + tab->nparts++; +} + +static void unref_parttable(blkid_parttable tab) +{ + if (!tab) + return; + + tab->nparts--; + if (tab->nparts <= 0) { + list_del(&tab->t_tabs); + free(tab); + } +} + +/* free all allocated parttables */ +static void free_parttables(blkid_partlist ls) +{ + if (!ls || !ls->l_tabs.next) + return; + + /* remove unassigned partition tables */ + while (!list_empty(&ls->l_tabs)) { + blkid_parttable tab = list_entry(ls->l_tabs.next, + struct blkid_struct_parttable, t_tabs); + unref_parttable(tab); + } +} + +static void reset_partlist(blkid_partlist ls) +{ + if (!ls) + return; + + free_parttables(ls); + + if (ls->next_partno) { + /* already initialized - reset */ + int tmp_nparts = ls->nparts_max; + blkid_partition tmp_parts = ls->parts; + + memset(ls, 0, sizeof(struct blkid_struct_partlist)); + + ls->nparts_max = tmp_nparts; + ls->parts = tmp_parts; + } + + ls->nparts = 0; + ls->next_partno = 1; + INIT_LIST_HEAD(&ls->l_tabs); + + DBG(LOWPROBE, ul_debug("partlist reset")); +} + +static blkid_partlist partitions_init_data(struct blkid_chain *chn) +{ + blkid_partlist ls; + + if (chn->data) + ls = (blkid_partlist) chn->data; + else { + /* allocate the new list of partitions */ + ls = calloc(1, sizeof(struct blkid_struct_partlist)); + if (!ls) + return NULL; + chn->data = (void *) ls; + } + + reset_partlist(ls); + + DBG(LOWPROBE, ul_debug("parts: initialized partitions list (size=%d)", ls->nparts_max)); + return ls; +} + +static void partitions_free_data(blkid_probe pr __attribute__((__unused__)), + void *data) +{ + blkid_partlist ls = (blkid_partlist) data; + + if (!ls) + return; + + free_parttables(ls); + + /* deallocate partitions and partlist */ + free(ls->parts); + free(ls); +} + +blkid_parttable blkid_partlist_new_parttable(blkid_partlist ls, + const char *type, uint64_t offset) +{ + blkid_parttable tab; + + tab = calloc(1, sizeof(struct blkid_struct_parttable)); + if (!tab) + return NULL; + tab->type = type; + tab->offset = offset; + tab->parent = ls->next_parent; + + INIT_LIST_HEAD(&tab->t_tabs); + list_add_tail(&tab->t_tabs, &ls->l_tabs); + + DBG(LOWPROBE, ul_debug("parts: create a new partition table " + "(type=%s, offset=%"PRId64")", type, offset)); + return tab; +} + +static blkid_partition new_partition(blkid_partlist ls, blkid_parttable tab) +{ + blkid_partition par; + + if (ls->nparts + 1 > ls->nparts_max) { + /* Linux kernel has DISK_MAX_PARTS=256, but it's too much for + * generic Linux machine -- let start with 32 partitions. + */ + void *tmp = realloc(ls->parts, (ls->nparts_max + 32) * + sizeof(struct blkid_struct_partition)); + if (!tmp) + return NULL; + ls->parts = tmp; + ls->nparts_max += 32; + } + + par = &ls->parts[ls->nparts++]; + memset(par, 0, sizeof(struct blkid_struct_partition)); + + ref_parttable(tab); + par->tab = tab; + par->partno = blkid_partlist_increment_partno(ls); + + return par; +} + +blkid_partition blkid_partlist_add_partition(blkid_partlist ls, + blkid_parttable tab, uint64_t start, uint64_t size) +{ + blkid_partition par = new_partition(ls, tab); + + if (!par) + return NULL; + + par->start = start; + par->size = size; + + DBG(LOWPROBE, ul_debug("parts: add partition (start=%" + PRIu64 ", size=%" PRIu64 ")", + par->start, par->size)); + return par; +} + +/* can be used to modify used partitions numbers (for example for logical partitions) */ +int blkid_partlist_set_partno(blkid_partlist ls, int partno) +{ + if (!ls) + return -1; + ls->next_partno = partno; + return 0; +} + +int blkid_partlist_increment_partno(blkid_partlist ls) +{ + return ls ? ls->next_partno++ : -1; +} + +/* can be used to set "parent" for the next nested partition */ +static int blkid_partlist_set_parent(blkid_partlist ls, blkid_partition par) +{ + if (!ls) + return -1; + ls->next_parent = par; + return 0; +} + +blkid_partition blkid_partlist_get_parent(blkid_partlist ls) +{ + if (!ls) + return NULL; + return ls->next_parent; +} + +int blkid_partitions_need_typeonly(blkid_probe pr) +{ + struct blkid_chain *chn = blkid_probe_get_chain(pr); + + return chn && chn->data && chn->binary ? FALSE : TRUE; +} + +/* get private chain flags */ +int blkid_partitions_get_flags(blkid_probe pr) +{ + struct blkid_chain *chn = blkid_probe_get_chain(pr); + + return chn ? chn->flags : 0; +} + +/* check if @start and @size are within @par partition */ +int blkid_is_nested_dimension(blkid_partition par, + uint64_t start, uint64_t size) +{ + uint64_t pstart; + uint64_t psize; + + if (!par) + return 0; + + pstart = blkid_partition_get_start(par); + psize = blkid_partition_get_size(par); + + if (start < pstart || start + size > pstart + psize) + return 0; + + return 1; +} + +static int idinfo_probe(blkid_probe pr, const struct blkid_idinfo *id, + struct blkid_chain *chn) +{ + const struct blkid_idmag *mag = NULL; + uint64_t off; + int rc = BLKID_PROBE_NONE; /* default is nothing */ + + if (pr->size <= 0 || (id->minsz && (unsigned)id->minsz > pr->size)) + goto nothing; /* the device is too small */ + if (pr->flags & BLKID_FL_NOSCAN_DEV) + goto nothing; + + rc = blkid_probe_get_idmag(pr, id, &off, &mag); + if (rc != BLKID_PROBE_OK) + goto nothing; + + /* final check by probing function */ + if (id->probefunc) { + DBG(LOWPROBE, ul_debug( + "%s: ---> call probefunc()", id->name)); + errno = 0; + rc = id->probefunc(pr, mag); + if (rc < 0) { + /* reset after error */ + reset_partlist(blkid_probe_get_partlist(pr)); + if (chn && !chn->binary) + blkid_probe_chain_reset_values(pr, chn); + DBG(LOWPROBE, ul_debug("%s probefunc failed, rc %d", + id->name, rc)); + } + if (rc == BLKID_PROBE_OK && mag && chn && !chn->binary) + rc = blkid_probe_set_magic(pr, off, mag->len, + (const unsigned char *) mag->magic); + + DBG(LOWPROBE, ul_debug("%s: <--- (rc = %d)", id->name, rc)); + } + + return rc; + +nothing: + return BLKID_PROBE_NONE; +} + +/* + * The blkid_do_probe() backend. + */ +static int partitions_probe(blkid_probe pr, struct blkid_chain *chn) +{ + int rc = BLKID_PROBE_NONE; + size_t i; + + if (!pr || chn->idx < -1) + return -EINVAL; + + blkid_probe_chain_reset_values(pr, chn); + + if (pr->flags & BLKID_FL_NOSCAN_DEV) + return BLKID_PROBE_NONE; + + if (chn->binary) + partitions_init_data(chn); + + if (!pr->wipe_size && (pr->prob_flags & BLKID_PROBE_FL_IGNORE_PT)) + goto details_only; + + DBG(LOWPROBE, ul_debug("--> starting probing loop [PARTS idx=%d]", + chn->idx)); + + i = chn->idx < 0 ? 0 : chn->idx + 1U; + + for ( ; i < ARRAY_SIZE(idinfos); i++) { + const char *name; + + chn->idx = i; + + /* apply filter */ + if (chn->fltr && blkid_bmp_get_item(chn->fltr, i)) + continue; + + /* apply checks from idinfo */ + rc = idinfo_probe(pr, idinfos[i], chn); + if (rc < 0) + break; + if (rc != BLKID_PROBE_OK) + continue; + + name = idinfos[i]->name; + + if (!chn->binary) + /* + * Non-binary interface, set generic variables. Note + * that the another variables could be set in prober + * functions. + */ + blkid_probe_set_value(pr, "PTTYPE", + (const unsigned char *) name, + strlen(name) + 1); + + DBG(LOWPROBE, ul_debug("<-- leaving probing loop (type=%s) [PARTS idx=%d]", + name, chn->idx)); + rc = BLKID_PROBE_OK; + break; + } + + if (rc != BLKID_PROBE_OK) { + DBG(LOWPROBE, ul_debug("<-- leaving probing loop (failed=%d) [PARTS idx=%d]", + rc, chn->idx)); + } + +details_only: + /* + * Gather PART_ENTRY_* values if the current device is a partition. + */ + if ((rc == BLKID_PROBE_OK || rc == BLKID_PROBE_NONE) && !chn->binary && + (blkid_partitions_get_flags(pr) & BLKID_PARTS_ENTRY_DETAILS)) { + + int xrc = blkid_partitions_probe_partition(pr); + + /* partition entry probing is optional, and "not-found" from + * this sub-probing must not to overwrite previous success. */ + if (xrc < 0) + rc = xrc; /* always propagate errors */ + else if (rc == BLKID_PROBE_NONE) + rc = xrc; + } + + DBG(LOWPROBE, ul_debug("partitions probe done [rc=%d]", rc)); + return rc; +} + +/* Probe for nested partition table within the parental partition */ +int blkid_partitions_do_subprobe(blkid_probe pr, blkid_partition parent, + const struct blkid_idinfo *id) +{ + blkid_probe prc; + int rc; + blkid_partlist ls; + uint64_t sz, off; + + DBG(LOWPROBE, ul_debug( + "parts: ----> %s subprobe requested)", + id->name)); + + if (!pr || !parent || !parent->size) + return -EINVAL; + if (pr->flags & BLKID_FL_NOSCAN_DEV) + return BLKID_PROBE_NONE; + + /* range defined by parent */ + sz = parent->size << 9; + off = parent->start << 9; + + if (off < pr->off || pr->off + pr->size < off + sz) { + DBG(LOWPROBE, ul_debug( + "ERROR: parts: <---- '%s' subprobe: overflow detected.", + id->name)); + return -ENOSPC; + } + + /* create private prober */ + prc = blkid_clone_probe(pr); + if (!prc) + return -ENOMEM; + + blkid_probe_set_dimension(prc, off, sz); + + /* clone is always with reset chain, fix it */ + prc->cur_chain = blkid_probe_get_chain(pr); + + /* + * Set 'parent' to the current list of the partitions and use the list + * in cloned prober (so the cloned prober will extend the current list + * of partitions rather than create a new). + */ + ls = blkid_probe_get_partlist(pr); + blkid_partlist_set_parent(ls, parent); + + blkid_probe_set_partlist(prc, ls); + + rc = idinfo_probe(prc, id, blkid_probe_get_chain(pr)); + + blkid_probe_set_partlist(prc, NULL); + blkid_partlist_set_parent(ls, NULL); + + blkid_free_probe(prc); /* free cloned prober */ + + DBG(LOWPROBE, ul_debug( + "parts: <---- %s subprobe done (rc=%d)", + id->name, rc)); + + return rc; +} + +static int blkid_partitions_probe_partition(blkid_probe pr) +{ + blkid_probe disk_pr = NULL; + blkid_partlist ls; + blkid_partition par; + dev_t devno; + + DBG(LOWPROBE, ul_debug("parts: start probing for partition entry")); + + if (pr->flags & BLKID_FL_NOSCAN_DEV) + goto nothing; + + devno = blkid_probe_get_devno(pr); + if (!devno) + goto nothing; + + disk_pr = blkid_probe_get_wholedisk_probe(pr); + if (!disk_pr) + goto nothing; + + /* parse PT */ + ls = blkid_probe_get_partitions(disk_pr); + if (!ls) + goto nothing; + + par = blkid_partlist_devno_to_partition(ls, devno); + if (!par) + goto nothing; + else { + const char *v; + blkid_parttable tab = blkid_partition_get_table(par); + dev_t disk = blkid_probe_get_devno(disk_pr); + + if (tab) { + v = blkid_parttable_get_type(tab); + if (v) + blkid_probe_set_value(pr, "PART_ENTRY_SCHEME", + (const unsigned char *) v, strlen(v) + 1); + } + + v = blkid_partition_get_name(par); + if (v) + blkid_probe_set_value(pr, "PART_ENTRY_NAME", + (const unsigned char *) v, strlen(v) + 1); + + v = blkid_partition_get_uuid(par); + if (v) + blkid_probe_set_value(pr, "PART_ENTRY_UUID", + (const unsigned char *) v, strlen(v) + 1); + + /* type */ + v = blkid_partition_get_type_string(par); + if (v) + blkid_probe_set_value(pr, "PART_ENTRY_TYPE", + (const unsigned char *) v, strlen(v) + 1); + else + blkid_probe_sprintf_value(pr, "PART_ENTRY_TYPE", + "0x%x", blkid_partition_get_type(par)); + + if (blkid_partition_get_flags(par)) + blkid_probe_sprintf_value(pr, "PART_ENTRY_FLAGS", + "0x%llx", blkid_partition_get_flags(par)); + + blkid_probe_sprintf_value(pr, "PART_ENTRY_NUMBER", + "%d", blkid_partition_get_partno(par)); + + blkid_probe_sprintf_value(pr, "PART_ENTRY_OFFSET", "%jd", + (intmax_t)blkid_partition_get_start(par)); + blkid_probe_sprintf_value(pr, "PART_ENTRY_SIZE", "%jd", + (intmax_t)blkid_partition_get_size(par)); + + blkid_probe_sprintf_value(pr, "PART_ENTRY_DISK", "%u:%u", + major(disk), minor(disk)); + } + + DBG(LOWPROBE, ul_debug("parts: end probing for partition entry [success]")); + return BLKID_PROBE_OK; + +nothing: + DBG(LOWPROBE, ul_debug("parts: end probing for partition entry [nothing]")); + return BLKID_PROBE_NONE; + + +} + +/* + * Returns 1 if the device is whole-disk and the area specified by @offset and + * @size is covered by any partition. + */ +int blkid_probe_is_covered_by_pt(blkid_probe pr, + uint64_t offset, uint64_t size) +{ + blkid_probe prc = NULL; + blkid_partlist ls = NULL; + uint64_t start, end; + int nparts, i, rc = 0; + + DBG(LOWPROBE, ul_debug( + "=> checking if off=%"PRIu64" size=%"PRIu64" covered by PT", + offset, size)); + + if (pr->flags & BLKID_FL_NOSCAN_DEV) + goto done; + + prc = blkid_clone_probe(pr); + if (!prc) + goto done; + + ls = blkid_probe_get_partitions(prc); + if (!ls) + goto done; + + nparts = blkid_partlist_numof_partitions(ls); + if (!nparts) + goto done; + + end = (offset + size) >> 9; + start = offset >> 9; + + /* check if the partition table fits into the device */ + for (i = 0; i < nparts; i++) { + blkid_partition par = &ls->parts[i]; + + if (par->start + par->size > (pr->size >> 9)) { + DBG(LOWPROBE, ul_debug("partition #%d overflows " + "device (off=%" PRId64 " size=%" PRId64 ")", + par->partno, par->start, par->size)); + goto done; + } + } + + /* check if the requested area is covered by PT */ + for (i = 0; i < nparts; i++) { + blkid_partition par = &ls->parts[i]; + + if (start >= par->start && end <= par->start + par->size) { + rc = 1; + break; + } + } +done: + blkid_free_probe(prc); + + DBG(LOWPROBE, ul_debug("<= %s covered by PT", rc ? "IS" : "NOT")); + return rc; +} + +/** + * blkid_known_pttype: + * @pttype: partition name + * + * Returns: 1 for known or 0 for unknown partition type. + */ +int blkid_known_pttype(const char *pttype) +{ + size_t i; + + if (!pttype) + return 0; + + for (i = 0; i < ARRAY_SIZE(idinfos); i++) { + const struct blkid_idinfo *id = idinfos[i]; + if (strcmp(id->name, pttype) == 0) + return 1; + } + return 0; +} + +/** + * blkid_partitions_get_name: + * @idx: number >= 0 + * @name: returns name of a supported partition + * + * Since: 2.30 + * + * Returns: -1 if @idx is out of range, or 0 on success. + */ +int blkid_partitions_get_name(const size_t idx, const char **name) +{ + if (idx < ARRAY_SIZE(idinfos)) { + *name = idinfos[idx]->name; + return 0; + } + return -1; +} + +/** + * blkid_partlist_numof_partitions: + * @ls: partitions list + * + * Returns: number of partitions in the list or -1 in case of error. + */ +int blkid_partlist_numof_partitions(blkid_partlist ls) +{ + return ls->nparts; +} + +/** + * blkid_partlist_get_table: + * @ls: partitions list + * + * Returns: top-level partition table or NULL if there is not a partition table + * on the device. + */ +blkid_parttable blkid_partlist_get_table(blkid_partlist ls) +{ + if (list_empty(&ls->l_tabs)) + return NULL; + + return list_entry(ls->l_tabs.next, + struct blkid_struct_parttable, t_tabs); +} + + +/** + * blkid_partlist_get_partition: + * @ls: partitions list + * @n: partition number in range 0..N, where 'N' is blkid_partlist_numof_partitions(). + * + * It's possible that the list of partitions is *empty*, but there is a valid + * partition table on the disk. This happen when on-disk details about + * partitions are unknown or the partition table is empty. + * + * See also blkid_partlist_get_table(). + * + * Returns: partition object or NULL in case or error. + */ +blkid_partition blkid_partlist_get_partition(blkid_partlist ls, int n) +{ + if (n < 0 || n >= ls->nparts) + return NULL; + + return &ls->parts[n]; +} + +blkid_partition blkid_partlist_get_partition_by_start(blkid_partlist ls, uint64_t start) +{ + int i, nparts; + blkid_partition par; + + nparts = blkid_partlist_numof_partitions(ls); + for (i = 0; i < nparts; i++) { + par = blkid_partlist_get_partition(ls, i); + if ((uint64_t) blkid_partition_get_start(par) == start) + return par; + } + return NULL; +} + +/** + * blkid_partlist_get_partition_by_partno + * @ls: partitions list + * @n: the partition number (e.g. 'N' from sda'N') + * + * This does not assume any order of the input blkid_partlist. And correctly + * handles "out of order" partition tables. partition N is located after + * partition N+1 on the disk. + * + * Returns: partition object or NULL in case or error. + */ +blkid_partition blkid_partlist_get_partition_by_partno(blkid_partlist ls, int n) +{ + int i, nparts; + blkid_partition par; + + nparts = blkid_partlist_numof_partitions(ls); + for (i = 0; i < nparts; i++) { + par = blkid_partlist_get_partition(ls, i); + if (n == blkid_partition_get_partno(par)) + return par; + } + return NULL; +} + + +/** + * blkid_partlist_devno_to_partition: + * @ls: partitions list + * @devno: requested partition + * + * This function tries to get start and size for @devno from sysfs and + * returns a partition from @ls which matches with the values from sysfs. + * + * This function is necessary when you want to make a relation between an entry + * in the partition table (@ls) and block devices in your system. + * + * Returns: partition object or NULL in case or error. + */ +blkid_partition blkid_partlist_devno_to_partition(blkid_partlist ls, dev_t devno) +{ + struct path_cxt *pc; + uint64_t start = 0, size; + int i, rc, partno = 0; + + DBG(LOWPROBE, ul_debug("trying to convert devno 0x%llx to partition", + (long long) devno)); + + + pc = ul_new_sysfs_path(devno, NULL, NULL); + if (!pc) { + DBG(LOWPROBE, ul_debug("failed t init sysfs context")); + return NULL; + } + rc = ul_path_read_u64(pc, &size, "size"); + if (!rc) { + rc = ul_path_read_u64(pc, &start, "start"); + if (rc) { + /* try to get partition number from DM uuid. + */ + char *uuid = NULL, *tmp, *prefix; + + ul_path_read_string(pc, &uuid, "dm/uuid"); + tmp = uuid; + prefix = uuid ? strsep(&tmp, "-") : NULL; + + if (prefix && strncasecmp(prefix, "part", 4) == 0) { + char *end = NULL; + + errno = 0; + partno = strtol(prefix + 4, &end, 10); + if (errno || prefix == end || (end && *end)) + partno = 0; + else + rc = 0; /* success */ + } + free(uuid); + } + } + + ul_unref_path(pc); + + if (rc) + return NULL; + + if (partno) { + DBG(LOWPROBE, ul_debug("mapped by DM, using partno %d", partno)); + + /* + * Partition mapped by kpartx does not provide "start" offset + * in /sys, but if we know partno and size of the partition + * that we can probably make the relation between the device + * and an entry in partition table. + */ + for (i = 0; i < ls->nparts; i++) { + blkid_partition par = &ls->parts[i]; + + if (partno != blkid_partition_get_partno(par)) + continue; + + if (size == (uint64_t)blkid_partition_get_size(par) || + (blkid_partition_is_extended(par) && size <= 1024ULL)) + return par; + + } + return NULL; + } + + DBG(LOWPROBE, ul_debug("searching by offset/size")); + + for (i = 0; i < ls->nparts; i++) { + blkid_partition par = &ls->parts[i]; + + if ((uint64_t)blkid_partition_get_start(par) == start && + (uint64_t)blkid_partition_get_size(par) == size) + return par; + + /* exception for extended dos partitions */ + if ((uint64_t)blkid_partition_get_start(par) == start && + blkid_partition_is_extended(par) && size <= 1024ULL) + return par; + + } + + DBG(LOWPROBE, ul_debug("not found partition for device")); + return NULL; +} + + +int blkid_parttable_set_uuid(blkid_parttable tab, const unsigned char *id) +{ + if (!tab) + return -1; + + blkid_unparse_uuid(id, tab->id, sizeof(tab->id)); + return 0; +} + +int blkid_parttable_set_id(blkid_parttable tab, const unsigned char *id) +{ + if (!tab) + return -1; + + xstrncpy(tab->id, (const char *) id, sizeof(tab->id)); + return 0; +} + +/* set PTUUID variable for non-binary API */ +int blkid_partitions_set_ptuuid(blkid_probe pr, unsigned char *uuid) +{ + struct blkid_chain *chn = blkid_probe_get_chain(pr); + struct blkid_prval *v; + + if (chn->binary || blkid_uuid_is_empty(uuid, 16)) + return 0; + + v = blkid_probe_assign_value(pr, "PTUUID"); + if (!v) + return -ENOMEM; + + v->len = UUID_STR_LEN; + v->data = calloc(1, v->len); + if (v->data) { + blkid_unparse_uuid(uuid, (char *) v->data, v->len); + return 0; + } + + blkid_probe_free_value(v); + return -ENOMEM; +} + +/* set PTUUID variable for non-binary API for tables where + * the ID is just a string */ +int blkid_partitions_strcpy_ptuuid(blkid_probe pr, const char *str) +{ + struct blkid_chain *chn = blkid_probe_get_chain(pr); + + if (chn->binary || !str || !*str) + return 0; + + if (!blkid_probe_set_value(pr, "PTUUID", (unsigned char *) str, strlen(str) + 1)) + return -ENOMEM; + + return 0; +} + +/** + * blkid_parttable_get_id: + * @tab: partition table + * + * The ID is GPT disk UUID or DOS disk ID (in hex format). + * + * Returns: partition table ID (for example GPT disk UUID) or NULL + */ +const char *blkid_parttable_get_id(blkid_parttable tab) +{ + return *tab->id ? tab->id : NULL; +} + + +int blkid_partition_set_type(blkid_partition par, int type) +{ + par->type = type; + return 0; +} + +/** + * blkid_parttable_get_type: + * @tab: partition table + * + * Returns: partition table type (type name, e.g. "dos", "gpt", ...) + */ +const char *blkid_parttable_get_type(blkid_parttable tab) +{ + return tab->type; +} + +/** + * blkid_parttable_get_parent: + * @tab: partition table + * + * Returns: parent for nested partition tables or NULL. + */ +blkid_partition blkid_parttable_get_parent(blkid_parttable tab) +{ + return tab->parent; +} + +/** + * blkid_parttable_get_offset: + * @tab: partition table + * + * Note the position is relative to begin of the device as defined by + * blkid_probe_set_device() for primary partition table, and relative + * to parental partition for nested partition tables. + * + * <informalexample> + * <programlisting> + * off_t offset; + * blkid_partition parent = blkid_parttable_get_parent(tab); + * + * offset = blkid_parttable_get_offset(tab); + * + * if (parent) + * / * 'tab' is nested partition table * / + * offset += blkid_partition_get_start(parent); + * </programlisting> + * </informalexample> + + * Returns: position (in bytes) of the partition table or -1 in case of error. + * + */ +blkid_loff_t blkid_parttable_get_offset(blkid_parttable tab) +{ + return (blkid_loff_t)tab->offset; +} + +/** + * blkid_partition_get_table: + * @par: partition + * + * The "parttable" describes partition table. The table is usually the same for + * all partitions -- except nested partition tables. + * + * For example bsd, solaris, etc. use a nested partition table within + * standard primary dos partition: + * + * <informalexample> + * <programlisting> + * + * -- dos partition table + * 0: sda1 dos primary partition + * 1: sda2 dos primary partition + * -- bsd partition table (with in sda2) + * 2: sda5 bds partition + * 3: sda6 bds partition + * + * </programlisting> + * </informalexample> + * + * The library does not to use a separate partition table object for dos logical + * partitions (partitions within extended partition). It's possible to + * differentiate between logical, extended and primary partitions by + * + * blkid_partition_is_{extended,primary,logical}(). + * + * Returns: partition table object or NULL in case of error. + */ +blkid_parttable blkid_partition_get_table(blkid_partition par) +{ + return par->tab; +} + +static int partition_get_logical_type(blkid_partition par) +{ + blkid_parttable tab; + + if (!par) + return -1; + + tab = blkid_partition_get_table(par); + if (!tab || !tab->type) + return -1; + + if (tab->parent) + return 'L'; /* report nested partitions as logical */ + + if (!strcmp(tab->type, "dos")) { + if (par->partno > 4) + return 'L'; /* logical */ + + if(par->type == MBR_DOS_EXTENDED_PARTITION || + par->type == MBR_W95_EXTENDED_PARTITION || + par->type == MBR_LINUX_EXTENDED_PARTITION) + return 'E'; + } + return 'P'; +} + +/** + * blkid_partition_is_primary: + * @par: partition + * + * Note, this function returns FALSE for DOS extended partitions and + * all partitions in nested partition tables. + * + * Returns: 1 if the partitions is primary partition or 0 if not. + */ +int blkid_partition_is_primary(blkid_partition par) +{ + return partition_get_logical_type(par) == 'P' ? TRUE : FALSE; +} + +/** + * blkid_partition_is_extended: + * @par: partition + * + * Returns: 1 if the partitions is extended (dos, windows or linux) + * partition or 0 if not. + */ +int blkid_partition_is_extended(blkid_partition par) +{ + return partition_get_logical_type(par) == 'E' ? TRUE : FALSE; +} + +/** + * blkid_partition_is_logical: + * @par: partition + * + * Note that this function returns TRUE for all partitions in all + * nested partition tables (e.g. BSD labels). + * + * Returns: 1 if the partitions is logical partition or 0 if not. + */ +int blkid_partition_is_logical(blkid_partition par) +{ + return partition_get_logical_type(par) == 'L' ? TRUE : FALSE; +} + +static void set_string(unsigned char *item, size_t max, + const unsigned char *data, size_t len) +{ + if (len >= max) + len = max - 1; + + memcpy(item, data, len); + item[len] = '\0'; + + blkid_rtrim_whitespace(item); +} + +int blkid_partition_set_name(blkid_partition par, + const unsigned char *name, size_t len) +{ + if (!par) + return -1; + + set_string(par->name, sizeof(par->name), name, len); + return 0; +} + +int blkid_partition_set_utf8name(blkid_partition par, const unsigned char *name, + size_t len, int enc) +{ + if (!par) + return -1; + + ul_encode_to_utf8(enc, par->name, sizeof(par->name), name, len); + blkid_rtrim_whitespace(par->name); + return 0; +} + +int blkid_partition_set_uuid(blkid_partition par, const unsigned char *uuid) +{ + if (!par) + return -1; + + blkid_unparse_uuid(uuid, par->uuid, sizeof(par->uuid)); + return 0; +} + +int blkid_partition_gen_uuid(blkid_partition par) +{ + if (!par || !par->tab || !*par->tab->id) + return -1; + + snprintf(par->uuid, sizeof(par->uuid), "%.33s-%02x", + par->tab->id, par->partno); + return 0; +} + +/** + * blkid_partition_get_name: + * @par: partition + * + * Returns: partition name string if supported by PT (e.g. Mac) or NULL. + */ +const char *blkid_partition_get_name(blkid_partition par) +{ + return *par->name ? (char *) par->name : NULL; +} + +/** + * blkid_partition_get_uuid: + * @par: partition + * + * Returns: partition UUID string if supported by PT (e.g. GPT) or NULL. + */ +const char *blkid_partition_get_uuid(blkid_partition par) +{ + return *par->uuid ? par->uuid : NULL; +} + +/** + * blkid_partition_get_partno: + * @par: partition + * + * Returns: proposed partition number (e.g. 'N' from sda'N') or -1 in case of + * error. Note that the number is generated by library independently of your OS. + */ +int blkid_partition_get_partno(blkid_partition par) +{ + return par->partno; +} + +/** + * blkid_partition_get_start: + * @par: partition + * + * Be careful if you _not_ probe whole disk: + * + * 1) the offset is usually relative to begin of the disk -- but if you probe a + * fragment of the disk only -- then the offset could be still relative to + * the begin of the disk rather that relative to the fragment. + * + * 2) the offset for nested partitions could be relative to parent (e.g. Solaris) + * _or_ relative to the begin of the whole disk (e.g. bsd). + * + * You don't have to care about such details if you probe whole disk. In such + * a case libblkid always returns the offset relative to the begin of the disk. + * + * Returns: start of the partition (in 512-sectors). + */ +blkid_loff_t blkid_partition_get_start(blkid_partition par) +{ + return (blkid_loff_t)par->start; +} + +/** + * blkid_partition_get_size: + * @par: partition + * + * WARNING: be very careful when you work with MS-DOS extended partitions. The + * library always returns full size of the partition. If you want to + * add the partition to the Linux system (BLKPG_ADD_PARTITION ioctl) + * you need to reduce the size of the partition to 1 or 2 blocks. The + * rest of the partition has to be inaccessible for mkfs or mkswap + * programs, we need a small space for boot loaders only. + * + * For some unknown reason this (safe) practice is not to used for + * nested BSD, Solaris, ..., partition tables in Linux kernel. + * + * Returns: size of the partition (in 512-sectors). + */ +blkid_loff_t blkid_partition_get_size(blkid_partition par) +{ + return (blkid_loff_t)par->size; +} + +/** + * blkid_partition_get_type: + * @par: partition + * + * Returns: partition type. + */ +int blkid_partition_get_type(blkid_partition par) +{ + return par->type; +} + +/* Sets partition 'type' for PT where the type is defined by string rather + * than by number + */ +int blkid_partition_set_type_string(blkid_partition par, + const unsigned char *type, size_t len) +{ + set_string((unsigned char *) par->typestr, + sizeof(par->typestr), type, len); + return 0; +} + +/* Sets partition 'type' for PT where the type is defined by UUID rather + * than by number + */ +int blkid_partition_set_type_uuid(blkid_partition par, const unsigned char *uuid) +{ + blkid_unparse_uuid(uuid, par->typestr, sizeof(par->typestr)); + return 0; +} + +/** + * blkid_partition_get_type_string: + * @par: partition + * + * The type string is supported by a small subset of partition tables (e.g. Mac + * and EFI GPT). Note that GPT uses type UUID and this function returns this + * UUID as string. + * + * Returns: partition type string or NULL. + */ +const char *blkid_partition_get_type_string(blkid_partition par) +{ + return *par->typestr ? par->typestr : NULL; +} + + +int blkid_partition_set_flags(blkid_partition par, unsigned long long flags) +{ + par->flags = flags; + return 0; +} + +/** + * blkid_partition_get_flags + * @par: partition + * + * Returns: partition flags (or attributes for gpt). + */ +unsigned long long blkid_partition_get_flags(blkid_partition par) +{ + return par->flags; +} + diff --git a/libblkid/src/partitions/partitions.h b/libblkid/src/partitions/partitions.h new file mode 100644 index 0000000..784e0c0 --- /dev/null +++ b/libblkid/src/partitions/partitions.h @@ -0,0 +1,74 @@ +#ifndef BLKID_PARTITIONS_H +#define BLKID_PARTITIONS_H + +#include "blkidP.h" +#include "pt-mbr.h" + +extern int blkid_partitions_get_flags(blkid_probe pr); + +extern blkid_parttable blkid_partlist_new_parttable(blkid_partlist ls, + const char *type, uint64_t offset); + +extern int blkid_parttable_set_uuid(blkid_parttable tab, const unsigned char *id); +extern int blkid_parttable_set_id(blkid_parttable tab, const unsigned char *id); + +extern blkid_partition blkid_partlist_add_partition(blkid_partlist ls, + blkid_parttable tab, + uint64_t start, uint64_t size); + +extern int blkid_partlist_set_partno(blkid_partlist ls, int partno); +extern int blkid_partlist_increment_partno(blkid_partlist ls); + +extern blkid_partition blkid_partlist_get_parent(blkid_partlist ls); + +extern blkid_partition blkid_partlist_get_partition_by_start(blkid_partlist ls, uint64_t start); + +extern int blkid_partitions_do_subprobe(blkid_probe pr, + blkid_partition parent, const struct blkid_idinfo *id); + +extern int blkid_partitions_need_typeonly(blkid_probe pr); +extern int blkid_partitions_set_ptuuid(blkid_probe pr, unsigned char *uuid); +extern int blkid_partitions_strcpy_ptuuid(blkid_probe pr, const char *str); + + +extern int blkid_is_nested_dimension(blkid_partition par, + uint64_t start, uint64_t size); + +extern int blkid_partition_set_name(blkid_partition par, + const unsigned char *name, size_t len); + +extern int blkid_partition_set_utf8name(blkid_partition par, + const unsigned char *name, size_t len, int enc); + +extern int blkid_partition_set_uuid(blkid_partition par, + const unsigned char *uuid); +extern int blkid_partition_gen_uuid(blkid_partition par); + +extern int blkid_partition_set_type(blkid_partition par, int type); + +extern int blkid_partition_set_type_string(blkid_partition par, + const unsigned char *type, size_t len); + +extern int blkid_partition_set_type_uuid(blkid_partition par, + const unsigned char *uuid); + +extern int blkid_partition_set_flags(blkid_partition par, unsigned long long flags); + +/* + * partition probers + */ +extern const struct blkid_idinfo aix_pt_idinfo; +extern const struct blkid_idinfo bsd_pt_idinfo; +extern const struct blkid_idinfo unixware_pt_idinfo; +extern const struct blkid_idinfo solaris_x86_pt_idinfo; +extern const struct blkid_idinfo sun_pt_idinfo; +extern const struct blkid_idinfo sgi_pt_idinfo; +extern const struct blkid_idinfo mac_pt_idinfo; +extern const struct blkid_idinfo dos_pt_idinfo; +extern const struct blkid_idinfo minix_pt_idinfo; +extern const struct blkid_idinfo gpt_pt_idinfo; +extern const struct blkid_idinfo pmbr_pt_idinfo; +extern const struct blkid_idinfo ultrix_pt_idinfo; +extern const struct blkid_idinfo atari_pt_idinfo; + +#endif /* BLKID_PARTITIONS_H */ diff --git a/libblkid/src/partitions/sgi.c b/libblkid/src/partitions/sgi.c new file mode 100644 index 0000000..d4f81bb --- /dev/null +++ b/libblkid/src/partitions/sgi.c @@ -0,0 +1,87 @@ +/* + * sgi partition parsing code + * + * Copyright (C) 2009 Karel Zak <kzak@redhat.com> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + * + */ +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <stdint.h> + +#include "partitions.h" +#include "pt-sgi.h" + +static int probe_sgi_pt(blkid_probe pr, + const struct blkid_idmag *mag __attribute__((__unused__))) +{ + struct sgi_disklabel *l; + struct sgi_partition *p; + blkid_parttable tab = NULL; + blkid_partlist ls; + int i; + + l = (struct sgi_disklabel *) blkid_probe_get_sector(pr, 0); + if (!l) { + if (errno) + return -errno; + goto nothing; + } + + if (!blkid_probe_verify_csum(pr, sgi_pt_checksum(l), 0)) { + DBG(LOWPROBE, ul_debug( + "detected corrupted sgi disk label -- ignore")); + goto nothing; + } + + if (blkid_partitions_need_typeonly(pr)) + /* caller does not ask for details about partitions */ + return BLKID_PROBE_OK; + + ls = blkid_probe_get_partlist(pr); + if (!ls) + goto nothing; + + tab = blkid_partlist_new_parttable(ls, "sgi", 0); + if (!tab) + goto err; + + for(i = 0, p = &l->partitions[0]; i < SGI_MAXPARTITIONS; i++, p++) { + uint32_t size = be32_to_cpu(p->num_blocks); + uint32_t start = be32_to_cpu(p->first_block); + uint32_t type = be32_to_cpu(p->type); + blkid_partition par; + + if (!size) { + blkid_partlist_increment_partno(ls); + continue; + } + par = blkid_partlist_add_partition(ls, tab, start, size); + if (!par) + goto err; + + blkid_partition_set_type(par, type); + } + + return BLKID_PROBE_OK; + +nothing: + return BLKID_PROBE_NONE; +err: + return -ENOMEM; +} + +const struct blkid_idinfo sgi_pt_idinfo = +{ + .name = "sgi", + .probefunc = probe_sgi_pt, + .magics = + { + { .magic = "\x0B\xE5\xA9\x41", .len = 4 }, + { NULL } + } +}; + diff --git a/libblkid/src/partitions/solaris_x86.c b/libblkid/src/partitions/solaris_x86.c new file mode 100644 index 0000000..df1def5 --- /dev/null +++ b/libblkid/src/partitions/solaris_x86.c @@ -0,0 +1,154 @@ +/* + * Solaris x86 partition parsing code + * + * Copyright (C) 2009 Karel Zak <kzak@redhat.com> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + */ +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <stdint.h> + +#include "partitions.h" + +/* + * Solaris-x86 is always within primary dos partition (nested PT table). The + * solaris-x86 vtoc can be used to split the entire partition to "slices". The + * offset (start) of the slice is always relatively to the primary dos + * partition. + * + * Note that Solaris-SPARC uses entire disk with a different partitioning + * scheme. + */ + +/* some other implementation than Linux kernel assume 8 partitions only */ +#define SOLARIS_MAXPARTITIONS 16 + +/* disklabel (vtoc) location */ +#define SOLARIS_SECTOR 1 /* in 512-sectors */ +#define SOLARIS_OFFSET (SOLARIS_SECTOR << 9) /* in bytes */ +#define SOLARIS_MAGICOFFSET (SOLARIS_OFFSET + 12) /* v_sanity offset in bytes */ + +/* slice tags */ +#define SOLARIS_TAG_WHOLEDISK 5 + +struct solaris_slice { + uint16_t s_tag; /* ID tag of partition */ + uint16_t s_flag; /* permission flags */ + uint32_t s_start; /* start sector no of partition */ + uint32_t s_size; /* # of blocks in partition */ +} __attribute__((packed)); + +struct solaris_vtoc { + unsigned int v_bootinfo[3]; /* info needed by mboot (unsupported) */ + + uint32_t v_sanity; /* to verify vtoc sanity */ + uint32_t v_version; /* layout version */ + char v_volume[8]; /* volume name */ + uint16_t v_sectorsz; /* sector size in bytes */ + uint16_t v_nparts; /* number of partitions */ + unsigned int v_reserved[10]; /* free space */ + + struct solaris_slice v_slice[SOLARIS_MAXPARTITIONS]; /* slices */ + + unsigned int timestamp[SOLARIS_MAXPARTITIONS]; /* timestamp (unsupported) */ + char v_asciilabel[128]; /* for compatibility */ +} __attribute__((packed)); + +static int probe_solaris_pt(blkid_probe pr, + const struct blkid_idmag *mag __attribute__((__unused__))) +{ + struct solaris_vtoc *l; /* disk label */ + struct solaris_slice *p; /* partition */ + blkid_parttable tab = NULL; + blkid_partition parent; + blkid_partlist ls; + int i; + uint16_t nparts; + + l = (struct solaris_vtoc *) blkid_probe_get_sector(pr, SOLARIS_SECTOR); + if (!l) { + if (errno) + return -errno; + goto nothing; + } + + if (le32_to_cpu(l->v_version) != 1) { + DBG(LOWPROBE, ul_debug( + "WARNING: unsupported solaris x86 version %d, ignore", + le32_to_cpu(l->v_version))); + goto nothing; + } + + if (blkid_partitions_need_typeonly(pr)) + /* caller does not ask for details about partitions */ + return BLKID_PROBE_OK; + + ls = blkid_probe_get_partlist(pr); + if (!ls) + goto nothing; + + parent = blkid_partlist_get_parent(ls); + + tab = blkid_partlist_new_parttable(ls, "solaris", SOLARIS_OFFSET); + if (!tab) + goto err; + + nparts = le16_to_cpu(l->v_nparts); + if (nparts > SOLARIS_MAXPARTITIONS) + nparts = SOLARIS_MAXPARTITIONS; + + for (i = 1, p = &l->v_slice[0]; i < nparts; i++, p++) { + + uint32_t start = le32_to_cpu(p->s_start); + uint32_t size = le32_to_cpu(p->s_size); + blkid_partition par; + + if (size == 0 || le16_to_cpu(p->s_tag) == SOLARIS_TAG_WHOLEDISK) + continue; + + if (parent) + /* Solaris slices are relative to the parent (primary + * DOS partition) */ + start += blkid_partition_get_start(parent); + + if (parent && !blkid_is_nested_dimension(parent, start, size)) { + DBG(LOWPROBE, ul_debug( + "WARNING: solaris partition (%d) overflow " + "detected, ignore", i)); + continue; + } + + par = blkid_partlist_add_partition(ls, tab, start, size); + if (!par) + goto err; + + blkid_partition_set_type(par, le16_to_cpu(p->s_tag)); + blkid_partition_set_flags(par, le16_to_cpu(p->s_flag)); + } + + return BLKID_PROBE_OK; + +nothing: + return BLKID_PROBE_NONE; +err: + return -ENOMEM; +} + +const struct blkid_idinfo solaris_x86_pt_idinfo = +{ + .name = "solaris", + .probefunc = probe_solaris_pt, + .magics = + { + { + .magic = "\xEE\xDE\x0D\x60", /* little-endian magic string */ + .len = 4, /* v_sanity size in bytes */ + .sboff = SOLARIS_MAGICOFFSET /* offset of v_sanity */ + }, + { NULL } + } +}; + diff --git a/libblkid/src/partitions/sun.c b/libblkid/src/partitions/sun.c new file mode 100644 index 0000000..ce24364 --- /dev/null +++ b/libblkid/src/partitions/sun.c @@ -0,0 +1,125 @@ +/* + * sun (solaris-sparc) partition parsing code + * + * Copyright (C) 2009 Karel Zak <kzak@redhat.com> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + */ +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <stdint.h> +#include <stddef.h> + +#include "pt-sun.h" +#include "partitions.h" + +static int probe_sun_pt(blkid_probe pr, + const struct blkid_idmag *mag __attribute__((__unused__))) +{ + struct sun_disklabel *l; + struct sun_partition *p; + blkid_parttable tab = NULL; + blkid_partlist ls; + uint16_t nparts; + uint64_t spc; + int i, use_vtoc; + + l = (struct sun_disklabel *) blkid_probe_get_sector(pr, 0); + if (!l) { + if (errno) + return -errno; + goto nothing; + } + + if (!blkid_probe_verify_csum(pr, sun_pt_checksum(l), 0)) { + DBG(LOWPROBE, ul_debug( + "detected corrupted sun disk label -- ignore")); + goto nothing; + } + + if (blkid_partitions_need_typeonly(pr)) + /* caller does not ask for details about partitions */ + return BLKID_PROBE_OK; + + ls = blkid_probe_get_partlist(pr); + if (!ls) + goto nothing; + + tab = blkid_partlist_new_parttable(ls, "sun", 0); + if (!tab) + goto err; + + /* sectors per cylinder (partition offset is in cylinders...) */ + spc = (uint64_t) be16_to_cpu(l->nhead) * be16_to_cpu(l->nsect); + + DBG(LOWPROBE, ul_debug("Sun VTOC sanity=%u version=%u nparts=%u", + be32_to_cpu(l->vtoc.sanity), + be32_to_cpu(l->vtoc.version), + be16_to_cpu(l->vtoc.nparts))); + + /* Check to see if we can use the VTOC table */ + use_vtoc = ((be32_to_cpu(l->vtoc.sanity) == SUN_VTOC_SANITY) && + (be32_to_cpu(l->vtoc.version) == SUN_VTOC_VERSION) && + (be16_to_cpu(l->vtoc.nparts) <= SUN_MAXPARTITIONS)); + + /* Use 8 partition entries if not specified in validated VTOC */ + nparts = use_vtoc ? be16_to_cpu(l->vtoc.nparts) : SUN_MAXPARTITIONS; + + /* + * So that old Linux-Sun partitions continue to work, + * allow the VTOC to be used under the additional condition ... + */ + use_vtoc = use_vtoc || !(l->vtoc.sanity || l->vtoc.version || l->vtoc.nparts); + + for (i = 0, p = l->partitions; i < nparts; i++, p++) { + + uint64_t start, size; + uint16_t type = 0, flags = 0; + blkid_partition par; + + start = be32_to_cpu(p->start_cylinder) * spc; + size = be32_to_cpu(p->num_sectors); + if (use_vtoc) { + type = be16_to_cpu(l->vtoc.infos[i].id); + flags = be16_to_cpu(l->vtoc.infos[i].flags); + } + + if (type == SUN_TAG_WHOLEDISK || !size) { + blkid_partlist_increment_partno(ls); + continue; + } + par = blkid_partlist_add_partition(ls, tab, start, size); + if (!par) + goto err; + + if (type) + blkid_partition_set_type(par, type); + if (flags) + blkid_partition_set_flags(par, flags); + } + return BLKID_PROBE_OK; + +nothing: + return BLKID_PROBE_NONE; +err: + return -ENOMEM; +} + + +const struct blkid_idinfo sun_pt_idinfo = +{ + .name = "sun", + .probefunc = probe_sun_pt, + .magics = + { + { + .magic = "\xDA\xBE", /* big-endian magic string */ + .len = 2, + .sboff = offsetof(struct sun_disklabel, magic) + }, + { NULL } + } +}; + diff --git a/libblkid/src/partitions/ultrix.c b/libblkid/src/partitions/ultrix.c new file mode 100644 index 0000000..9c060be --- /dev/null +++ b/libblkid/src/partitions/ultrix.c @@ -0,0 +1,99 @@ +/* + * uktrix partition parsing code + * + * Copyright (C) 2010 Karel Zak <kzak@redhat.com> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + * + */ +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <stdint.h> + +#include "partitions.h" + +#define ULTRIX_MAXPARTITIONS 8 + +#define ULTRIX_MAGIC 0x032957 +#define ULTRIX_MAGIC_STR "\x02\x29\x57" + +/* sector with partition table */ +#define ULTRIX_SECTOR ((16384 - sizeof(struct ultrix_disklabel)) >> 9) +/* position of partition table within ULTRIX_SECTOR */ +#define ULTRIX_OFFSET (512 - sizeof(struct ultrix_disklabel)) + +struct ultrix_disklabel { + int32_t pt_magic; /* magic no. indicating part. info exits */ + int32_t pt_valid; /* set by driver if pt is current */ + struct pt_info { + int32_t pi_nblocks; /* no. of sectors */ + uint32_t pi_blkoff; /* block offset for start */ + } pt_part[ULTRIX_MAXPARTITIONS]; +} __attribute__((packed)); + + +static int probe_ultrix_pt(blkid_probe pr, + const struct blkid_idmag *mag __attribute__((__unused__))) +{ + unsigned char *data; + struct ultrix_disklabel *l; + blkid_parttable tab = NULL; + blkid_partlist ls; + int i; + + data = blkid_probe_get_sector(pr, ULTRIX_SECTOR); + if (!data) { + if (errno) + return -errno; + goto nothing; + } + + l = (struct ultrix_disklabel *) (data + ULTRIX_OFFSET); + + if (l->pt_magic != ULTRIX_MAGIC || l->pt_valid != 1) + goto nothing; + + if (blkid_probe_set_magic(pr, (ULTRIX_SECTOR << 9) + ULTRIX_OFFSET, + sizeof(ULTRIX_MAGIC_STR) - 1, + (unsigned char *) ULTRIX_MAGIC_STR)) + goto err; + + if (blkid_partitions_need_typeonly(pr)) + /* caller does not ask for details about partitions */ + return BLKID_PROBE_OK; + + ls = blkid_probe_get_partlist(pr); + if (!ls) + goto nothing; + + tab = blkid_partlist_new_parttable(ls, "ultrix", 0); + if (!tab) + goto err; + + for (i = 0; i < ULTRIX_MAXPARTITIONS; i++) { + if (!l->pt_part[i].pi_nblocks) + blkid_partlist_increment_partno(ls); + else { + if (!blkid_partlist_add_partition(ls, tab, + l->pt_part[i].pi_blkoff, + l->pt_part[i].pi_nblocks)) + goto err; + } + } + + return BLKID_PROBE_OK; +nothing: + return BLKID_PROBE_NONE; +err: + return -ENOMEM; +} + +const struct blkid_idinfo ultrix_pt_idinfo = +{ + .name = "ultrix", + .probefunc = probe_ultrix_pt, + .magics = BLKID_NONE_MAGIC +}; + diff --git a/libblkid/src/partitions/unixware.c b/libblkid/src/partitions/unixware.c new file mode 100644 index 0000000..845924e --- /dev/null +++ b/libblkid/src/partitions/unixware.c @@ -0,0 +1,197 @@ +/* + * unixware partition parsing code + * + * Copyright (C) 2009 Karel Zak <kzak@redhat.com> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + * + * + * The interesting information about unixware PT: + * - Linux kernel / partx + * - vtoc(7) SCO UNIX command man page + * - evms source code (http://evms.sourceforge.net/) + * - vxtools source code (http://martin.hinner.info/fs/vxfs/) + */ +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <stdint.h> + +#include "partitions.h" + +/* disklabel location */ +#define UNIXWARE_SECTOR 29 +#define UNIXWARE_OFFSET (UNIXWARE_SECTOR << 9) /* offset in bytes */ +#define UNIXWARE_KBOFFSET (UNIXWARE_OFFSET >> 10) /* offset in 1024-blocks */ + +/* disklabel->d_magic offset within the last 1024 block */ +#define UNIXWARE_MAGICOFFSET (UNIXWARE_OFFSET - UNIXWARE_KBOFFSET + 4) + +#define UNIXWARE_VTOCMAGIC 0x600DDEEEUL +#define UNIXWARE_MAXPARTITIONS 16 + +/* unixware_partition->s_label flags */ +#define UNIXWARE_TAG_UNUSED 0x0000 /* unused partition */ +#define UNIXWARE_TAG_BOOT 0x0001 /* boot fs */ +#define UNIXWARE_TAG_ROOT 0x0002 /* root fs */ +#define UNIXWARE_TAG_SWAP 0x0003 /* swap fs */ +#define UNIXWARE_TAG_USER 0x0004 /* user fs */ +#define UNIXWARE_TAG_ENTIRE_DISK 0x0005 /* whole disk */ +#define UNIXWARE_TAG_ALT_S 0x0006 /* alternate sector space */ +#define UNIXWARE_TAG_OTHER 0x0007 /* non unix */ +#define UNIXWARE_TAG_ALT_T 0x0008 /* alternate track space */ +#define UNIXWARE_TAG_STAND 0x0009 /* stand partition */ +#define UNIXWARE_TAG_VAR 0x000a /* var partition */ +#define UNIXWARE_TAG_HOME 0x000b /* home partition */ +#define UNIXWARE_TAG_DUMP 0x000c /* dump partition */ +#define UNIXWARE_TAG_ALT_ST 0x000d /* alternate sector track */ +#define UNIXWARE_TAG_VM_PUBLIC 0x000e /* volume mgt public partition */ +#define UNIXWARE_TAG_VM_PRIVATE 0x000f /* volume mgt private partition */ + + +/* unixware_partition->s_flags flags */ +#define UNIXWARE_FLAG_VALID 0x0200 + +struct unixware_partition { + uint16_t s_label; /* partition label (tag) */ + uint16_t s_flags; /* permission flags */ + uint32_t start_sect; /* starting sector */ + uint32_t nr_sects; /* number of sectors */ +} __attribute__((packed)); + +struct unixware_disklabel { + uint32_t d_type; /* drive type */ + uint32_t d_magic; /* the magic number */ + uint32_t d_version; /* version number */ + char d_serial[12]; /* serial number of the device */ + uint32_t d_ncylinders; /* # of data cylinders per device */ + uint32_t d_ntracks; /* # of tracks per cylinder */ + uint32_t d_nsectors; /* # of data sectors per track */ + uint32_t d_secsize; /* # of bytes per sector */ + uint32_t d_part_start; /* # of first sector of this partition */ + uint32_t d_unknown1[12]; /* ? */ + uint32_t d_alt_tbl; /* byte offset of alternate table */ + uint32_t d_alt_len; /* byte length of alternate table */ + uint32_t d_phys_cyl; /* # of physical cylinders per device */ + uint32_t d_phys_trk; /* # of physical tracks per cylinder */ + uint32_t d_phys_sec; /* # of physical sectors per track */ + uint32_t d_phys_bytes; /* # of physical bytes per sector */ + uint32_t d_unknown2; /* ? */ + uint32_t d_unknown3; /* ? */ + uint32_t d_pad[8]; /* pad */ + + struct unixware_vtoc { + uint32_t v_magic; /* the magic number */ + uint32_t v_version; /* version number */ + char v_name[8]; /* volume name */ + uint16_t v_nslices; /* # of partitions */ + uint16_t v_unknown1; /* ? */ + uint32_t v_reserved[10]; /* reserved */ + + struct unixware_partition + v_slice[UNIXWARE_MAXPARTITIONS]; /* partition */ + } __attribute__((packed)) vtoc; +}; + +static int probe_unixware_pt(blkid_probe pr, + const struct blkid_idmag *mag __attribute__((__unused__))) +{ + struct unixware_disklabel *l; + struct unixware_partition *p; + blkid_parttable tab = NULL; + blkid_partition parent; + blkid_partlist ls; + int i; + + l = (struct unixware_disklabel *) + blkid_probe_get_sector(pr, UNIXWARE_SECTOR); + if (!l) { + if (errno) + return -errno; + goto nothing; + } + + if (le32_to_cpu(l->vtoc.v_magic) != UNIXWARE_VTOCMAGIC) + goto nothing; + + if (blkid_partitions_need_typeonly(pr)) + /* caller does not ask for details about partitions */ + return BLKID_PROBE_OK; + + ls = blkid_probe_get_partlist(pr); + if (!ls) + goto nothing; + + parent = blkid_partlist_get_parent(ls); + + tab = blkid_partlist_new_parttable(ls, "unixware", UNIXWARE_OFFSET); + if (!tab) + goto err; + + /* Skip the first partition that describe whole disk + */ + for (i = 1, p = &l->vtoc.v_slice[1]; + i < UNIXWARE_MAXPARTITIONS; i++, p++) { + + uint32_t start, size; + uint16_t tag, flg; + blkid_partition par; + + tag = le16_to_cpu(p->s_label); + flg = le16_to_cpu(p->s_flags); + + if (tag == UNIXWARE_TAG_UNUSED || + tag == UNIXWARE_TAG_ENTIRE_DISK || + flg != UNIXWARE_FLAG_VALID) + continue; + + start = le32_to_cpu(p->start_sect); + size = le32_to_cpu(p->nr_sects); + + if (parent && !blkid_is_nested_dimension(parent, start, size)) { + DBG(LOWPROBE, ul_debug( + "WARNING: unixware partition (%d) overflow " + "detected, ignore", i)); + continue; + } + + par = blkid_partlist_add_partition(ls, tab, start, size); + if (!par) + goto err; + + blkid_partition_set_type(par, tag); + blkid_partition_set_flags(par, flg); + } + + return BLKID_PROBE_OK; + +nothing: + return BLKID_PROBE_NONE; +err: + return -ENOMEM; +} + + +/* + * The unixware partition table is within primary DOS partition. The PT is + * located on 29 sector, PT magic string is d_magic member of 'struct + * unixware_disklabel'. + */ +const struct blkid_idinfo unixware_pt_idinfo = +{ + .name = "unixware", + .probefunc = probe_unixware_pt, + .minsz = 1024 * 1440 + 1, /* ignore floppies */ + .magics = + { + { + .magic = "\x0D\x60\xE5\xCA", /* little-endian magic string */ + .len = 4, /* d_magic size in bytes */ + .kboff = UNIXWARE_KBOFFSET, + .sboff = UNIXWARE_MAGICOFFSET + }, + { NULL } + } +}; + |