/src/util-linux/libblkid/src/partitions/dos.c
Line | Count | Source |
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 "superblocks/superblocks.h" |
18 | | #include "aix.h" |
19 | | |
20 | | /* see superblocks/vfat.c */ |
21 | | extern int blkid_probe_is_vfat(blkid_probe pr); |
22 | | /* see superblocks/exfat.c */ |
23 | | extern int blkid_probe_is_exfat(blkid_probe pr); |
24 | | |
25 | | static const struct dos_subtypes { |
26 | | unsigned char type; |
27 | | const struct blkid_idinfo *id; |
28 | | } dos_nested[] = { |
29 | | { MBR_FREEBSD_PARTITION, &bsd_pt_idinfo }, |
30 | | { MBR_NETBSD_PARTITION, &bsd_pt_idinfo }, |
31 | | { MBR_OPENBSD_PARTITION, &bsd_pt_idinfo }, |
32 | | { MBR_UNIXWARE_PARTITION, &unixware_pt_idinfo }, |
33 | | { MBR_SOLARIS_X86_PARTITION, &solaris_x86_pt_idinfo }, |
34 | | { MBR_MINIX_PARTITION, &minix_pt_idinfo } |
35 | | }; |
36 | | |
37 | | static inline int is_extended(const struct dos_partition *p) |
38 | 0 | { |
39 | 0 | return (p->sys_ind == MBR_DOS_EXTENDED_PARTITION || |
40 | 0 | p->sys_ind == MBR_W95_EXTENDED_PARTITION || |
41 | 0 | p->sys_ind == MBR_LINUX_EXTENDED_PARTITION); |
42 | 0 | } |
43 | | |
44 | | static int parse_dos_extended(blkid_probe pr, blkid_parttable tab, |
45 | | uint32_t ex_start, uint32_t ex_size, int ssf) |
46 | 0 | { |
47 | 0 | blkid_partlist ls = blkid_probe_get_partlist(pr); |
48 | 0 | uint32_t cur_start = ex_start, cur_size = ex_size; |
49 | 0 | const unsigned char *data; |
50 | 0 | int ct_nodata = 0; /* count ext.partitions without data partitions */ |
51 | 0 | int i; |
52 | |
|
53 | 0 | DBG(LOWPROBE, ul_debug("parse EBR [start=%d, size=%d]", ex_start/ssf, ex_size/ssf)); |
54 | 0 | if (ex_start == 0) { |
55 | 0 | DBG(LOWPROBE, ul_debug("Bad offset in primary extended partition -- ignore")); |
56 | 0 | return 0; |
57 | 0 | } |
58 | | |
59 | 0 | while (1) { |
60 | 0 | const struct dos_partition *p, *p0; |
61 | 0 | uint32_t start = 0, size; |
62 | |
|
63 | 0 | if (++ct_nodata > 100) |
64 | 0 | return BLKID_PROBE_OK; |
65 | 0 | data = blkid_probe_get_sector(pr, cur_start); |
66 | 0 | if (!data) { |
67 | 0 | if (errno) |
68 | 0 | return -errno; |
69 | 0 | goto leave; /* malformed partition? */ |
70 | 0 | } |
71 | | |
72 | 0 | if (!mbr_is_valid_magic(data)) |
73 | 0 | goto leave; |
74 | | |
75 | 0 | p0 = mbr_get_partition(data, 0); |
76 | | |
77 | | /* Usually, the first entry is the real data partition, |
78 | | * the 2nd entry is the next extended partition, or empty, |
79 | | * and the 3rd and 4th entries are unused. |
80 | | * However, DRDOS sometimes has the extended partition as |
81 | | * the first entry (when the data partition is empty), |
82 | | * and OS/2 seems to use all four entries. |
83 | | * -- Linux kernel fs/partitions/dos.c |
84 | | * |
85 | | * See also http://en.wikipedia.org/wiki/Extended_boot_record |
86 | | */ |
87 | | |
88 | | /* Parse data partition */ |
89 | 0 | for (p = p0, i = 0; i < 4; i++, p++) { |
90 | 0 | uint32_t abs_start; |
91 | 0 | blkid_partition par; |
92 | | |
93 | | /* the start is relative to the parental ext.partition */ |
94 | 0 | start = dos_partition_get_start(p) * ssf; |
95 | 0 | size = dos_partition_get_size(p) * ssf; |
96 | 0 | abs_start = cur_start + start; /* absolute start */ |
97 | |
|
98 | 0 | if (!size || is_extended(p)) |
99 | 0 | continue; |
100 | 0 | if (i >= 2) { |
101 | | /* extra checks to detect real data on |
102 | | * 3rd and 4th entries */ |
103 | 0 | if (start + size > cur_size) |
104 | 0 | continue; |
105 | 0 | if (abs_start < ex_start) |
106 | 0 | continue; |
107 | 0 | if (abs_start + size > ex_start + ex_size) |
108 | 0 | continue; |
109 | 0 | } |
110 | | |
111 | | /* Avoid recursive non-empty links, see ct_nodata counter */ |
112 | 0 | if (blkid_partlist_get_partition_by_start(ls, abs_start)) { |
113 | 0 | DBG(LOWPROBE, ul_debug("#%d: EBR duplicate data partition [abs start=%u] -- ignore", |
114 | 0 | i + 1, abs_start)); |
115 | 0 | continue; |
116 | 0 | } |
117 | | |
118 | 0 | par = blkid_partlist_add_partition(ls, tab, abs_start, size); |
119 | 0 | if (!par) |
120 | 0 | return -ENOMEM; |
121 | | |
122 | 0 | blkid_partition_set_type(par, p->sys_ind); |
123 | 0 | blkid_partition_set_flags(par, p->boot_ind); |
124 | 0 | blkid_partition_gen_uuid(par); |
125 | 0 | ct_nodata = 0; |
126 | 0 | } |
127 | | /* The first nested ext.partition should be a link to the next |
128 | | * logical partition. Everything other (recursive ext.partitions) |
129 | | * is junk. |
130 | | */ |
131 | 0 | for (p = p0, i = 0; i < 4; i++, p++) { |
132 | 0 | start = dos_partition_get_start(p) * ssf; |
133 | 0 | size = dos_partition_get_size(p) * ssf; |
134 | |
|
135 | 0 | if (size && is_extended(p)) { |
136 | 0 | if (start == 0) |
137 | 0 | DBG(LOWPROBE, ul_debug("#%d: EBR link offset is zero -- ignore", i + 1)); |
138 | 0 | else |
139 | 0 | break; |
140 | 0 | } |
141 | 0 | } |
142 | 0 | if (i == 4) |
143 | 0 | goto leave; |
144 | | |
145 | 0 | cur_start = ex_start + start; |
146 | 0 | cur_size = size; |
147 | 0 | } |
148 | 0 | leave: |
149 | 0 | return BLKID_PROBE_OK; |
150 | 0 | } |
151 | | |
152 | | static inline int is_lvm(blkid_probe pr) |
153 | 78 | { |
154 | 78 | struct blkid_prval *v = __blkid_probe_lookup_value(pr, "TYPE"); |
155 | | |
156 | 78 | return (v && v->data && strcmp((char *) v->data, "LVM2_member") == 0); |
157 | 78 | } |
158 | | |
159 | | static inline int is_empty_mbr(const unsigned char *mbr) |
160 | 0 | { |
161 | 0 | const struct dos_partition *p = mbr_get_partition(mbr, 0); |
162 | 0 | int i, nparts = 0; |
163 | |
|
164 | 0 | for (i = 0; i < 4; i++) { |
165 | 0 | if (dos_partition_get_size(p) > 0) |
166 | 0 | nparts++; |
167 | 0 | p++; |
168 | 0 | } |
169 | |
|
170 | 0 | return nparts == 0; |
171 | 0 | } |
172 | | |
173 | | static int probe_dos_pt(blkid_probe pr, |
174 | | const struct blkid_idmag *mag __attribute__((__unused__))) |
175 | 1.27k | { |
176 | 1.27k | int i; |
177 | 1.27k | int ssf; |
178 | 1.27k | blkid_parttable tab = NULL; |
179 | 1.27k | blkid_partlist ls; |
180 | 1.27k | const struct dos_partition *p0, *p; |
181 | 1.27k | const unsigned char *data; |
182 | 1.27k | uint32_t start, size, id; |
183 | 1.27k | char idstr[UUID_STR_LEN]; |
184 | | |
185 | | |
186 | 1.27k | data = blkid_probe_get_sector(pr, 0); |
187 | 1.27k | if (!data) { |
188 | 0 | if (errno) |
189 | 0 | return -errno; |
190 | 0 | goto nothing; |
191 | 0 | } |
192 | | |
193 | | /* ignore disks with AIX magic number -- for more details see aix.c */ |
194 | 1.27k | if (memcmp(data, BLKID_AIX_MAGIC_STRING, BLKID_AIX_MAGIC_STRLEN) == 0) |
195 | 0 | goto nothing; |
196 | | |
197 | 1.27k | p0 = mbr_get_partition(data, 0); |
198 | | |
199 | | /* |
200 | | * Reject PT where boot indicator is not 0 or 0x80. |
201 | | */ |
202 | 2.18k | for (p = p0, i = 0; i < 4; i++, p++) |
203 | 2.02k | if (p->boot_ind != 0 && p->boot_ind != 0x80) { |
204 | 1.11k | DBG(LOWPROBE, ul_debug("missing boot indicator -- ignore")); |
205 | 1.11k | goto nothing; |
206 | 1.11k | } |
207 | | |
208 | | /* |
209 | | * GPT uses valid MBR |
210 | | */ |
211 | 795 | for (p = p0, i = 0; i < 4; i++, p++) { |
212 | 636 | if (p->sys_ind == MBR_GPT_PARTITION) { |
213 | 0 | DBG(LOWPROBE, ul_debug("probably GPT -- ignore")); |
214 | 0 | goto nothing; |
215 | 0 | } |
216 | 636 | } |
217 | | |
218 | | /* |
219 | | * Now that the 55aa signature is present, this is probably |
220 | | * either the boot sector of a FAT filesystem or a DOS-type |
221 | | * partition table. |
222 | | */ |
223 | 159 | if (blkid_probe_is_vfat(pr) == 1 || blkid_probe_is_exfat(pr) == 1) { |
224 | 70 | DBG(LOWPROBE, ul_debug("probably FAT -- ignore")); |
225 | 70 | goto nothing; |
226 | 70 | } |
227 | | |
228 | | /* Another false positive is NTFS */ |
229 | 89 | if (blkid_probe_is_ntfs(pr) == 1) { |
230 | 11 | DBG(LOWPROBE, ul_debug("probably NTFS -- ignore")); |
231 | 11 | goto nothing; |
232 | 11 | } |
233 | | |
234 | | /* |
235 | | * Ugly exception, if the device contains a valid LVM physical volume |
236 | | * and empty MBR (=no partition defined) then it's LVM and MBR should |
237 | | * be ignored. Crazy people use it to boot from LVM devices. |
238 | | */ |
239 | 78 | if (is_lvm(pr) && is_empty_mbr(data)) { |
240 | 0 | DBG(LOWPROBE, ul_debug("empty MBR on LVM device -- ignore")); |
241 | 0 | goto nothing; |
242 | 0 | } |
243 | | |
244 | 78 | blkid_probe_use_wiper(pr, MBR_PT_OFFSET, 512 - MBR_PT_OFFSET); |
245 | | |
246 | 78 | id = mbr_get_id(data); |
247 | 78 | if (id) |
248 | 50 | snprintf(idstr, sizeof(idstr), "%08x", id); |
249 | | |
250 | | /* |
251 | | * Well, all checks pass, it's MS-DOS partition table |
252 | | */ |
253 | 78 | if (blkid_partitions_need_typeonly(pr)) { |
254 | | /* Non-binary interface -- caller does not ask for details |
255 | | * about partitions, just set generic variables only. */ |
256 | 78 | if (id) |
257 | 50 | blkid_partitions_strcpy_ptuuid(pr, idstr); |
258 | 78 | return 0; |
259 | 78 | } |
260 | | |
261 | 0 | ls = blkid_probe_get_partlist(pr); |
262 | 0 | if (!ls) |
263 | 0 | goto nothing; |
264 | | |
265 | | /* sector size factor (the start and size are in the real sectors, but |
266 | | * we need to convert all sizes to 512 logical sectors |
267 | | */ |
268 | 0 | ssf = blkid_probe_get_sectorsize(pr) / 512; |
269 | | |
270 | | /* allocate a new partition table */ |
271 | 0 | tab = blkid_partlist_new_parttable(ls, "dos", MBR_PT_OFFSET); |
272 | 0 | if (!tab) |
273 | 0 | return -ENOMEM; |
274 | | |
275 | 0 | if (id) |
276 | 0 | blkid_parttable_set_id(tab, (unsigned char *) idstr); |
277 | | |
278 | | /* Parse primary partitions */ |
279 | 0 | for (p = p0, i = 0; i < 4; i++, p++) { |
280 | 0 | blkid_partition par; |
281 | |
|
282 | 0 | start = dos_partition_get_start(p) * ssf; |
283 | 0 | size = dos_partition_get_size(p) * ssf; |
284 | |
|
285 | 0 | if (!size) { |
286 | | /* Linux kernel ignores empty partitions, but partno for |
287 | | * the empty primary partitions is not reused */ |
288 | 0 | blkid_partlist_increment_partno(ls); |
289 | 0 | continue; |
290 | 0 | } |
291 | 0 | par = blkid_partlist_add_partition(ls, tab, start, size); |
292 | 0 | if (!par) |
293 | 0 | return -ENOMEM; |
294 | | |
295 | 0 | blkid_partition_set_type(par, p->sys_ind); |
296 | 0 | blkid_partition_set_flags(par, p->boot_ind); |
297 | 0 | blkid_partition_gen_uuid(par); |
298 | 0 | } |
299 | | |
300 | | /* Linux uses partition numbers greater than 4 |
301 | | * for all logical partition and all nested partition tables (bsd, ..) |
302 | | */ |
303 | 0 | blkid_partlist_set_partno(ls, 5); |
304 | | |
305 | | /* Parse logical partitions */ |
306 | 0 | for (p = p0, i = 0; i < 4; i++, p++) { |
307 | 0 | start = dos_partition_get_start(p) * ssf; |
308 | 0 | size = dos_partition_get_size(p) * ssf; |
309 | |
|
310 | 0 | if (!size) |
311 | 0 | continue; |
312 | 0 | if (is_extended(p) && |
313 | 0 | parse_dos_extended(pr, tab, start, size, ssf) == -1) |
314 | 0 | goto nothing; |
315 | 0 | } |
316 | | |
317 | | /* Parse subtypes (nested partitions) on large disks */ |
318 | 0 | if (!blkid_probe_is_tiny(pr)) { |
319 | 0 | int nparts = blkid_partlist_numof_partitions(ls); |
320 | |
|
321 | 0 | DBG(LOWPROBE, ul_debug("checking for subtypes")); |
322 | |
|
323 | 0 | for (i = 0; i < nparts; i++) { |
324 | 0 | size_t n; |
325 | 0 | int type; |
326 | 0 | blkid_partition pa = blkid_partlist_get_partition(ls, i); |
327 | |
|
328 | 0 | if (pa == NULL |
329 | 0 | || blkid_partition_get_size(pa) == 0 |
330 | 0 | || blkid_partition_is_extended(pa) |
331 | 0 | || blkid_partition_is_logical(pa)) |
332 | 0 | continue; |
333 | | |
334 | 0 | type = blkid_partition_get_type(pa); |
335 | |
|
336 | 0 | for (n = 0; n < ARRAY_SIZE(dos_nested); n++) { |
337 | 0 | int rc; |
338 | |
|
339 | 0 | if (dos_nested[n].type != type) |
340 | 0 | continue; |
341 | | |
342 | 0 | rc = blkid_partitions_do_subprobe(pr, pa, |
343 | 0 | dos_nested[n].id); |
344 | 0 | if (rc < 0) |
345 | 0 | return rc; |
346 | 0 | break; |
347 | 0 | } |
348 | 0 | } |
349 | 0 | } |
350 | 0 | return BLKID_PROBE_OK; |
351 | | |
352 | 1.19k | nothing: |
353 | 1.19k | return BLKID_PROBE_NONE; |
354 | 0 | } |
355 | | |
356 | | |
357 | | const struct blkid_idinfo dos_pt_idinfo = |
358 | | { |
359 | | .name = "dos", |
360 | | .probefunc = probe_dos_pt, |
361 | | .magics = |
362 | | { |
363 | | /* DOS master boot sector: |
364 | | * |
365 | | * 0 | Code Area |
366 | | * 440 | Optional Disk signature |
367 | | * 446 | Partition table |
368 | | * 510 | 0x55 |
369 | | * 511 | 0xAA |
370 | | */ |
371 | | { .magic = "\x55\xAA", .len = 2, .sboff = 510 }, |
372 | | { NULL } |
373 | | } |
374 | | }; |
375 | | |