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