bigbiff bigbiff | e60683a | 2013-02-22 20:55:50 -0500 | [diff] [blame] | 1 | /* |
| 2 | * MS-DOS partition parsing code |
| 3 | * |
| 4 | * Copyright (C) 2009 Karel Zak <kzak@redhat.com> |
| 5 | * |
| 6 | * This file may be redistributed under the terms of the |
| 7 | * GNU Lesser General Public License. |
| 8 | * |
| 9 | * Inspired by fdisk, partx, Linux kernel and libparted. |
| 10 | */ |
| 11 | #include <stdio.h> |
| 12 | #include <string.h> |
| 13 | #include <stdlib.h> |
| 14 | #include <stdint.h> |
| 15 | |
| 16 | #include "partitions.h" |
| 17 | #include "dos.h" |
| 18 | #include "aix.h" |
| 19 | |
| 20 | /* see superblocks/vfat.c */ |
| 21 | extern int blkid_probe_is_vfat(blkid_probe pr); |
| 22 | |
| 23 | static const struct dos_subtypes { |
| 24 | unsigned char type; |
| 25 | const struct blkid_idinfo *id; |
| 26 | } dos_nested[] = { |
| 27 | { BLKID_FREEBSD_PARTITION, &bsd_pt_idinfo }, |
| 28 | { BLKID_NETBSD_PARTITION, &bsd_pt_idinfo }, |
| 29 | { BLKID_OPENBSD_PARTITION, &bsd_pt_idinfo }, |
| 30 | { BLKID_UNIXWARE_PARTITION, &unixware_pt_idinfo }, |
| 31 | { BLKID_SOLARIS_X86_PARTITION, &solaris_x86_pt_idinfo }, |
| 32 | { BLKID_MINIX_PARTITION, &minix_pt_idinfo } |
| 33 | }; |
| 34 | |
| 35 | static inline int is_extended(struct dos_partition *p) |
| 36 | { |
| 37 | return (p->sys_type == BLKID_DOS_EXTENDED_PARTITION || |
| 38 | p->sys_type == BLKID_W95_EXTENDED_PARTITION || |
| 39 | p->sys_type == BLKID_LINUX_EXTENDED_PARTITION); |
| 40 | } |
| 41 | |
| 42 | static int parse_dos_extended(blkid_probe pr, blkid_parttable tab, |
| 43 | uint32_t ex_start, uint32_t ex_size, int ssf) |
| 44 | { |
| 45 | blkid_partlist ls = blkid_probe_get_partlist(pr); |
| 46 | uint32_t cur_start = ex_start, cur_size = ex_size; |
| 47 | unsigned char *data; |
| 48 | int ct_nodata = 0; /* count ext.partitions without data partitions */ |
| 49 | int i; |
| 50 | |
| 51 | while (1) { |
| 52 | struct dos_partition *p, *p0; |
| 53 | uint32_t start, size; |
| 54 | |
| 55 | if (++ct_nodata > 100) |
| 56 | return 0; |
| 57 | data = blkid_probe_get_sector(pr, cur_start); |
| 58 | if (!data) |
| 59 | goto leave; /* malformed partition? */ |
| 60 | |
| 61 | if (!is_valid_mbr_signature(data)) |
| 62 | goto leave; |
| 63 | |
| 64 | p0 = (struct dos_partition *) (data + BLKID_MSDOS_PT_OFFSET); |
| 65 | |
| 66 | /* Usually, the first entry is the real data partition, |
| 67 | * the 2nd entry is the next extended partition, or empty, |
| 68 | * and the 3rd and 4th entries are unused. |
| 69 | * However, DRDOS sometimes has the extended partition as |
| 70 | * the first entry (when the data partition is empty), |
| 71 | * and OS/2 seems to use all four entries. |
| 72 | * -- Linux kernel fs/partitions/dos.c |
| 73 | * |
| 74 | * See also http://en.wikipedia.org/wiki/Extended_boot_record |
| 75 | */ |
| 76 | |
| 77 | /* Parse data partition */ |
| 78 | for (p = p0, i = 0; i < 4; i++, p++) { |
| 79 | uint32_t abs_start; |
| 80 | blkid_partition par; |
| 81 | |
| 82 | /* the start is relative to the parental ext.partition */ |
| 83 | start = dos_partition_start(p) * ssf; |
| 84 | size = dos_partition_size(p) * ssf; |
| 85 | abs_start = cur_start + start; /* absolute start */ |
| 86 | |
| 87 | if (!size || is_extended(p)) |
| 88 | continue; |
| 89 | if (i >= 2) { |
| 90 | /* extra checks to detect real data on |
| 91 | * 3rd and 4th entries */ |
| 92 | if (start + size > cur_size) |
| 93 | continue; |
| 94 | if (abs_start < ex_start) |
| 95 | continue; |
| 96 | if (abs_start + size > ex_start + ex_size) |
| 97 | continue; |
| 98 | } |
| 99 | |
| 100 | par = blkid_partlist_add_partition(ls, tab, abs_start, size); |
| 101 | if (!par) |
| 102 | goto err; |
| 103 | |
| 104 | blkid_partition_set_type(par, p->sys_type); |
| 105 | blkid_partition_set_flags(par, p->boot_ind); |
| 106 | ct_nodata = 0; |
| 107 | } |
| 108 | /* The first nested ext.partition should be a link to the next |
| 109 | * logical partition. Everything other (recursive ext.partitions) |
| 110 | * is junk. |
| 111 | */ |
| 112 | for (p = p0, i = 0; i < 4; i++, p++) { |
| 113 | start = dos_partition_start(p) * ssf; |
| 114 | size = dos_partition_size(p) * ssf; |
| 115 | |
| 116 | if (size && is_extended(p)) |
| 117 | break; |
| 118 | } |
| 119 | if (i == 4) |
| 120 | goto leave; |
| 121 | |
| 122 | cur_start = ex_start + start; |
| 123 | cur_size = size; |
| 124 | } |
| 125 | leave: |
| 126 | return 0; |
| 127 | err: |
| 128 | return -1; |
| 129 | } |
| 130 | |
| 131 | static int probe_dos_pt(blkid_probe pr, |
| 132 | const struct blkid_idmag *mag __attribute__((__unused__))) |
| 133 | { |
| 134 | int i; |
| 135 | int ssf; |
| 136 | blkid_parttable tab = NULL; |
| 137 | blkid_partlist ls; |
| 138 | struct dos_partition *p0, *p; |
| 139 | unsigned char *data; |
| 140 | uint32_t start, size, id; |
| 141 | |
| 142 | data = blkid_probe_get_sector(pr, 0); |
| 143 | if (!data) |
| 144 | goto nothing; |
| 145 | |
| 146 | /* ignore disks with AIX magic number -- for more details see aix.c */ |
| 147 | if (memcmp(data, BLKID_AIX_MAGIC_STRING, BLKID_AIX_MAGIC_STRLEN) == 0) |
| 148 | goto nothing; |
| 149 | |
| 150 | /* |
| 151 | * Now that the 55aa signature is present, this is probably |
| 152 | * either the boot sector of a FAT filesystem or a DOS-type |
| 153 | * partition table. |
| 154 | */ |
| 155 | if (blkid_probe_is_vfat(pr)) { |
| 156 | DBG(DEBUG_LOWPROBE, printf("probably FAT -- ignore\n")); |
| 157 | goto nothing; |
| 158 | } |
| 159 | |
| 160 | p0 = (struct dos_partition *) (data + BLKID_MSDOS_PT_OFFSET); |
| 161 | |
| 162 | /* |
| 163 | * Reject PT where boot indicator is not 0 or 0x80. |
| 164 | */ |
| 165 | for (p = p0, i = 0; i < 4; i++, p++) |
| 166 | if (p->boot_ind != 0 && p->boot_ind != 0x80) { |
| 167 | DBG(DEBUG_LOWPROBE, printf("missing boot indicator -- ignore\n")); |
| 168 | goto nothing; |
| 169 | } |
| 170 | |
| 171 | /* |
| 172 | * GPT uses valid MBR |
| 173 | */ |
| 174 | for (p = p0, i = 0; i < 4; i++, p++) { |
| 175 | if (p->sys_type == BLKID_GPT_PARTITION) { |
| 176 | DBG(DEBUG_LOWPROBE, printf("probably GPT -- ignore\n")); |
| 177 | goto nothing; |
| 178 | } |
| 179 | } |
| 180 | |
| 181 | blkid_probe_use_wiper(pr, BLKID_MSDOS_PT_OFFSET, |
| 182 | 512 - BLKID_MSDOS_PT_OFFSET); |
| 183 | |
| 184 | /* |
| 185 | * Well, all checks pass, it's MS-DOS partiton table |
| 186 | */ |
| 187 | if (blkid_partitions_need_typeonly(pr)) |
| 188 | /* caller does not ask for details about partitions */ |
| 189 | return 0; |
| 190 | |
| 191 | ls = blkid_probe_get_partlist(pr); |
| 192 | |
| 193 | /* sector size factor (the start and size are in the real sectors, but |
| 194 | * we need to convert all sizes to 512 logical sectors |
| 195 | */ |
| 196 | ssf = blkid_probe_get_sectorsize(pr) / 512; |
| 197 | |
| 198 | /* allocate a new partition table */ |
| 199 | tab = blkid_partlist_new_parttable(ls, "dos", BLKID_MSDOS_PT_OFFSET); |
| 200 | if (!tab) |
| 201 | goto err; |
| 202 | |
| 203 | id = dos_parttable_id(data); |
| 204 | if (id) { |
| 205 | char buf[37]; |
| 206 | |
| 207 | snprintf(buf, sizeof(buf), "0x%08x", id); |
| 208 | blkid_parttable_set_id(tab, (unsigned char *) buf); |
| 209 | } |
| 210 | |
| 211 | |
| 212 | /* Parse primary partitions */ |
| 213 | for (p = p0, i = 0; i < 4; i++, p++) { |
| 214 | blkid_partition par; |
| 215 | |
| 216 | start = dos_partition_start(p) * ssf; |
| 217 | size = dos_partition_size(p) * ssf; |
| 218 | |
| 219 | if (!size) { |
| 220 | /* Linux kernel ignores empty partitions, but partno for |
| 221 | * the empty primary partitions is not reused */ |
| 222 | blkid_partlist_increment_partno(ls); |
| 223 | continue; |
| 224 | } |
| 225 | par = blkid_partlist_add_partition(ls, tab, start, size); |
| 226 | if (!par) |
| 227 | goto err; |
| 228 | |
| 229 | blkid_partition_set_type(par, p->sys_type); |
| 230 | blkid_partition_set_flags(par, p->boot_ind); |
| 231 | } |
| 232 | |
| 233 | /* Linux uses partition numbers greater than 4 |
| 234 | * for all logical partition and all nested partition tables (bsd, ..) |
| 235 | */ |
| 236 | blkid_partlist_set_partno(ls, 5); |
| 237 | |
| 238 | /* Parse logical partitions */ |
| 239 | for (p = p0, i = 0; i < 4; i++, p++) { |
| 240 | start = dos_partition_start(p) * ssf; |
| 241 | size = dos_partition_size(p) * ssf; |
| 242 | |
| 243 | if (!size) |
| 244 | continue; |
| 245 | if (is_extended(p) && |
| 246 | parse_dos_extended(pr, tab, start, size, ssf) == -1) |
| 247 | goto err; |
| 248 | } |
| 249 | |
| 250 | /* Parse subtypes (nested partitions) on large disks */ |
| 251 | if (!blkid_probe_is_tiny(pr)) { |
| 252 | for (p = p0, i = 0; i < 4; i++, p++) { |
| 253 | size_t n; |
| 254 | |
| 255 | if (!dos_partition_size(p) || is_extended(p)) |
| 256 | continue; |
| 257 | |
| 258 | for (n = 0; n < ARRAY_SIZE(dos_nested); n++) { |
| 259 | if (dos_nested[n].type != p->sys_type) |
| 260 | continue; |
| 261 | |
| 262 | if (blkid_partitions_do_subprobe(pr, |
| 263 | blkid_partlist_get_partition(ls, i), |
| 264 | dos_nested[n].id) == -1) |
| 265 | goto err; |
| 266 | break; |
| 267 | } |
| 268 | } |
| 269 | } |
| 270 | return 0; |
| 271 | |
| 272 | nothing: |
| 273 | return 1; |
| 274 | err: |
| 275 | return -1; |
| 276 | } |
| 277 | |
| 278 | |
| 279 | const struct blkid_idinfo dos_pt_idinfo = |
| 280 | { |
| 281 | .name = "dos", |
| 282 | .probefunc = probe_dos_pt, |
| 283 | .magics = |
| 284 | { |
| 285 | /* DOS master boot sector: |
| 286 | * |
| 287 | * 0 | Code Area |
| 288 | * 440 | Optional Disk signature |
| 289 | * 446 | Partition table |
| 290 | * 510 | 0x55 |
| 291 | * 511 | 0xAA |
| 292 | */ |
| 293 | { .magic = "\x55\xAA", .len = 2, .sboff = 510 }, |
| 294 | { NULL } |
| 295 | } |
| 296 | }; |
| 297 | |