/* * MS-DOS partition parsing code * * Copyright (C) 2009 Karel Zak * * This file may be redistributed under the terms of the * GNU Lesser General Public License. * * Inspired by fdisk, partx, Linux kernel and libparted. */ #include #include #include #include #include "partitions.h" #include "dos.h" #include "aix.h" /* see superblocks/vfat.c */ extern int blkid_probe_is_vfat(blkid_probe pr); static const struct dos_subtypes { unsigned char type; const struct blkid_idinfo *id; } dos_nested[] = { { BLKID_FREEBSD_PARTITION, &bsd_pt_idinfo }, { BLKID_NETBSD_PARTITION, &bsd_pt_idinfo }, { BLKID_OPENBSD_PARTITION, &bsd_pt_idinfo }, { BLKID_UNIXWARE_PARTITION, &unixware_pt_idinfo }, { BLKID_SOLARIS_X86_PARTITION, &solaris_x86_pt_idinfo }, { BLKID_MINIX_PARTITION, &minix_pt_idinfo } }; static inline int is_extended(struct dos_partition *p) { return (p->sys_type == BLKID_DOS_EXTENDED_PARTITION || p->sys_type == BLKID_W95_EXTENDED_PARTITION || p->sys_type == BLKID_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; while (1) { struct dos_partition *p, *p0; uint32_t start, size; if (++ct_nodata > 100) return 0; data = blkid_probe_get_sector(pr, cur_start); if (!data) goto leave; /* malformed partition? */ if (!is_valid_mbr_signature(data)) goto leave; p0 = (struct dos_partition *) (data + BLKID_MSDOS_PT_OFFSET); /* 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_start(p) * ssf; size = dos_partition_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; } par = blkid_partlist_add_partition(ls, tab, abs_start, size); if (!par) goto err; blkid_partition_set_type(par, p->sys_type); blkid_partition_set_flags(par, p->boot_ind); 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_start(p) * ssf; size = dos_partition_size(p) * ssf; if (size && is_extended(p)) break; } if (i == 4) goto leave; cur_start = ex_start + start; cur_size = size; } leave: return 0; err: return -1; } 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; data = blkid_probe_get_sector(pr, 0); if (!data) 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; /* * 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)) { DBG(DEBUG_LOWPROBE, printf("probably FAT -- ignore\n")); goto nothing; } p0 = (struct dos_partition *) (data + BLKID_MSDOS_PT_OFFSET); /* * 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(DEBUG_LOWPROBE, printf("missing boot indicator -- ignore\n")); goto nothing; } /* * GPT uses valid MBR */ for (p = p0, i = 0; i < 4; i++, p++) { if (p->sys_type == BLKID_GPT_PARTITION) { DBG(DEBUG_LOWPROBE, printf("probably GPT -- ignore\n")); goto nothing; } } blkid_probe_use_wiper(pr, BLKID_MSDOS_PT_OFFSET, 512 - BLKID_MSDOS_PT_OFFSET); /* * Well, all checks pass, it's MS-DOS partiton table */ if (blkid_partitions_need_typeonly(pr)) /* caller does not ask for details about partitions */ return 0; ls = blkid_probe_get_partlist(pr); /* 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", BLKID_MSDOS_PT_OFFSET); if (!tab) goto err; id = dos_parttable_id(data); if (id) { char buf[37]; snprintf(buf, sizeof(buf), "0x%08x", id); blkid_parttable_set_id(tab, (unsigned char *) buf); } /* Parse primary partitions */ for (p = p0, i = 0; i < 4; i++, p++) { blkid_partition par; start = dos_partition_start(p) * ssf; size = dos_partition_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) goto err; blkid_partition_set_type(par, p->sys_type); blkid_partition_set_flags(par, p->boot_ind); } /* 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_start(p) * ssf; size = dos_partition_size(p) * ssf; if (!size) continue; if (is_extended(p) && parse_dos_extended(pr, tab, start, size, ssf) == -1) goto err; } /* Parse subtypes (nested partitions) on large disks */ if (!blkid_probe_is_tiny(pr)) { for (p = p0, i = 0; i < 4; i++, p++) { size_t n; if (!dos_partition_size(p) || is_extended(p)) continue; for (n = 0; n < ARRAY_SIZE(dos_nested); n++) { if (dos_nested[n].type != p->sys_type) continue; if (blkid_partitions_do_subprobe(pr, blkid_partlist_get_partition(ls, i), dos_nested[n].id) == -1) goto err; break; } } } return 0; nothing: return 1; err: return -1; } 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 } } };