• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* ----------------------------------------------------------------------- *
2  *
3  *   Copyright 2003-2009 H. Peter Anvin - All Rights Reserved
4  *   Copyright 2009-2010 Intel Corporation; author: H. Peter Anvin
5  *   Copyright 2010 Shao Miller
6  *   Copyright 2010-2012 Michal Soltys
7  *
8  *   Permission is hereby granted, free of charge, to any person
9  *   obtaining a copy of this software and associated documentation
10  *   files (the "Software"), to deal in the Software without
11  *   restriction, including without limitation the rights to use,
12  *   copy, modify, merge, publish, distribute, sublicense, and/or
13  *   sell copies of the Software, and to permit persons to whom
14  *   the Software is furnished to do so, subject to the following
15  *   conditions:
16  *
17  *   The above copyright notice and this permission notice shall
18  *   be included in all copies or substantial portions of the Software.
19  *
20  *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21  *   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
22  *   OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23  *   NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
24  *   HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
25  *   WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26  *   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
27  *   OTHER DEALINGS IN THE SOFTWARE.
28  *
29  * ----------------------------------------------------------------------- */
30 
31 /*
32  * partiter.c
33  *
34  * Provides disk / partition iteration.
35  */
36 
37 #include <stdlib.h>
38 #include <stdio.h>
39 #include <string.h>
40 #include <stdarg.h>
41 #include <zlib.h>
42 #include <syslinux/disk.h>
43 #include "partiter.h"
44 #include "utility.h"
45 
46 #define ost_is_ext(type) ((type) == 0x05 || (type) == 0x0F || (type) == 0x85)
47 #define ost_is_nondata(type) (ost_is_ext(type) || (type) == 0x00)
48 #define sane(s,l) ((s)+(l) > (s))
49 
50 /* virtual forwards */
51 
52 static void pi_dtor_(struct part_iter *);
53 static int  pi_next_(struct part_iter *);
54 static int  pi_dos_next(struct part_iter *);
55 static int  pi_gpt_next(struct part_iter *);
56 
57 /* vtab and types */
58 
59 static struct itertype types[] = {
60    [0] = {
61 	.dtor = &pi_dtor_,
62 	.next = &pi_dos_next,
63 }, [1] = {
64 	.dtor = &pi_dtor_,
65 	.next = &pi_gpt_next,
66 }, [2] = {
67 	.dtor = &pi_dtor_,
68 	.next = &pi_next_,
69 }};
70 
71 const struct itertype * const typedos = types;
72 const struct itertype * const typegpt = types+1;
73 const struct itertype * const typeraw = types+2;
74 
75 /* pi_dtor_() - common/raw iterator cleanup */
pi_dtor_(struct part_iter * iter)76 static void pi_dtor_(struct part_iter *iter)
77 {
78     /* syslinux's free is null resilient */
79     free(iter->data);
80 }
81 
82 /* pi_ctor() - common/raw iterator initialization */
pi_ctor(struct part_iter * iter,const struct disk_info * di,int flags)83 static int pi_ctor(struct part_iter *iter,
84 	const struct disk_info *di, int flags
85 )
86 {
87     memcpy(&iter->di, di, sizeof *di);
88     iter->flags = flags;
89     iter->index0 = -1;
90     iter->length = di->lbacnt;
91 
92     iter->type = typeraw;
93     return 0;
94 }
95 
96 /* pi_dos_ctor() - MBR/EBR iterator specific initialization */
pi_dos_ctor(struct part_iter * iter,const struct disk_info * di,int flags,const struct disk_dos_mbr * mbr)97 static int pi_dos_ctor(struct part_iter *iter,
98 	const struct disk_info *di, int flags,
99 	const struct disk_dos_mbr *mbr
100 )
101 {
102     if (pi_ctor(iter, di, flags))
103 	return -1;
104 
105     if (!(iter->data = malloc(sizeof *mbr))) {
106 	critm();
107 	goto bail;
108     }
109 
110     memcpy(iter->data, mbr, sizeof *mbr);
111 
112     iter->dos.bebr_index0 = -1;
113     iter->dos.disk_sig = mbr->disk_sig;
114 
115     iter->type = typedos;
116     return 0;
117 bail:
118     pi_dtor_(iter);
119     return -1;
120 }
121 
122 /* pi_gpt_ctor() - GPT iterator specific initialization */
pi_gpt_ctor(struct part_iter * iter,const struct disk_info * di,int flags,const struct disk_gpt_header * gpth,const struct disk_gpt_part_entry * gptl)123 static int pi_gpt_ctor(struct part_iter *iter,
124 	const struct disk_info *di, int flags,
125 	const struct disk_gpt_header *gpth, const struct disk_gpt_part_entry *gptl
126 )
127 {
128     uint64_t siz;
129 
130     if (pi_ctor(iter, di, flags))
131 	return -1;
132 
133     siz = (uint64_t)gpth->part_count * gpth->part_size;
134 
135     if (!(iter->data = malloc((size_t)siz))) {
136 	critm();
137 	goto bail;
138     }
139 
140     memcpy(iter->data, gptl, (size_t)siz);
141 
142     iter->gpt.pe_count = (int)gpth->part_count;
143     iter->gpt.pe_size = (int)gpth->part_size;
144     iter->gpt.ufirst = gpth->lba_first_usable;
145     iter->gpt.ulast = gpth->lba_last_usable;
146 
147     memcpy(&iter->gpt.disk_guid, &gpth->disk_guid, sizeof gpth->disk_guid);
148     memcpy(&iter->gpt.part_guid, &gpth->disk_guid, sizeof gpth->disk_guid);
149 
150     iter->type = typegpt;
151     return 0;
152 bail:
153     pi_dtor_(iter);
154     return -1;
155 }
156 
157 /* Logical partition must be sane, meaning:
158  * - must be data or empty
159  * - must have non-0 start and length
160  * - values must not wrap around 32bit
161  * - must be inside current EBR frame
162  */
163 
notsane_logical(const struct part_iter * iter)164 static int notsane_logical(const struct part_iter *iter)
165 {
166     const struct disk_dos_part_entry *dp;
167     uint32_t end_log;
168 
169     dp = ((struct disk_dos_mbr *)iter->data)->table;
170 
171     if (!dp[0].ostype)
172 	return 0;
173 
174     if (ost_is_ext(dp[0].ostype)) {
175 	error("The 1st EBR entry must be data or empty.");
176 	return -1;
177     }
178 
179     if (!(iter->flags & PIF_STRICT))
180 	return 0;
181 
182     end_log = dp[0].start_lba + dp[0].length;
183 
184     if (!dp[0].start_lba ||
185 	!dp[0].length ||
186 	!sane(dp[0].start_lba, dp[0].length) ||
187 	end_log > iter->dos.nebr_siz) {
188 
189 	error("Logical partition (in EBR) with invalid offset and/or length.");
190 	return -1;
191     }
192 
193     return 0;
194 }
195 
196 /* Extended partition must be sane, meaning:
197  * - must be extended or empty
198  * - must have non-0 start and length
199  * - values must not wrap around 32bit
200  * - must be inside base EBR frame
201  */
202 
notsane_extended(const struct part_iter * iter)203 static int notsane_extended(const struct part_iter *iter)
204 {
205     const struct disk_dos_part_entry *dp;
206     uint32_t end_ebr;
207 
208     dp = ((struct disk_dos_mbr *)iter->data)->table;
209 
210     if (!dp[1].ostype)
211 	return 0;
212 
213     if (!ost_is_nondata(dp[1].ostype)) {
214 	error("The 2nd EBR entry must be extended or empty.");
215 	return -1;
216     }
217 
218     if (!(iter->flags & PIF_STRICT))
219 	return 0;
220 
221     end_ebr = dp[1].start_lba + dp[1].length;
222 
223     if (!dp[1].start_lba ||
224 	!dp[1].length ||
225 	!sane(dp[1].start_lba, dp[1].length) ||
226 	end_ebr > iter->dos.bebr_siz) {
227 
228 	error("Extended partition (EBR) with invalid offset and/or length.");
229 	return -1;
230     }
231 
232     return 0;
233 }
234 
235 /* Primary partition must be sane, meaning:
236  * - must have non-0 start and length
237  * - values must not wrap around 32bit
238  */
239 
notsane_primary(const struct part_iter * iter)240 static int notsane_primary(const struct part_iter *iter)
241 {
242     const struct disk_dos_part_entry *dp;
243     dp = ((struct disk_dos_mbr *)iter->data)->table + iter->index0;
244 
245     if (!dp->ostype)
246 	return 0;
247 
248     if (!(iter->flags & PIF_STRICT))
249 	return 0;
250 
251     if (!dp->start_lba ||
252 	!dp->length ||
253 	!sane(dp->start_lba, dp->length) ||
254 	((iter->flags & PIF_STRICTER) && (dp->start_lba + dp->length > iter->di.lbacnt))) {
255 	error("Primary partition (in MBR) with invalid offset and/or length.");
256 	return -1;
257     }
258 
259     return 0;
260 }
261 
notsane_gpt(const struct part_iter * iter)262 static int notsane_gpt(const struct part_iter *iter)
263 {
264     const struct disk_gpt_part_entry *gp;
265     gp = (const struct disk_gpt_part_entry *)
266 	(iter->data + iter->index0 * iter->gpt.pe_size);
267 
268     if (guid_is0(&gp->type))
269 	return 0;
270 
271     if (!(iter->flags & PIF_STRICT))
272 	return 0;
273 
274     if (gp->lba_first < iter->gpt.ufirst ||
275 	gp->lba_last > iter->gpt.ulast) {
276 	error("LBA sectors of GPT partition are beyond the range allowed in GPT header.");
277 	return -1;
278     }
279 
280     return 0;
281 }
282 
dos_next_mbr(struct part_iter * iter,uint32_t * lba,struct disk_dos_part_entry ** _dp)283 static int dos_next_mbr(struct part_iter *iter, uint32_t *lba,
284 			    struct disk_dos_part_entry **_dp)
285 {
286     struct disk_dos_part_entry *dp;
287 
288     while (++iter->index0 < 4) {
289 	dp = ((struct disk_dos_mbr *)iter->data)->table + iter->index0;
290 
291 	if (notsane_primary(iter)) {
292 	    iter->status = PI_INSANE;
293 	    return -1;
294 	}
295 
296 	if (ost_is_ext(dp->ostype)) {
297 	    if (iter->dos.bebr_index0 >= 0) {
298 		error("More than 1 extended partition.");
299 		iter->status = PI_INSANE;
300 		return -1;
301 	    }
302 	    /* record base EBR index */
303 	    iter->dos.bebr_index0 = iter->index0;
304 	}
305 	if (!ost_is_nondata(dp->ostype) || (iter->flags & PIF_STEPALL)) {
306 	    *lba = dp->start_lba;
307 	    *_dp = dp;
308 	    break;
309 	}
310     }
311 
312     return 0;
313 }
314 
prep_base_ebr(struct part_iter * iter)315 static int prep_base_ebr(struct part_iter *iter)
316 {
317     struct disk_dos_part_entry *dp;
318 
319     if (iter->dos.bebr_index0 < 0)	/* if we don't have base extended partition at all */
320 	return -1;
321     else if (!iter->dos.bebr_lba) { /* if not initialized yet */
322 	dp = ((struct disk_dos_mbr *)iter->data)->table + iter->dos.bebr_index0;
323 
324 	iter->dos.bebr_lba = dp->start_lba;
325 	iter->dos.bebr_siz = dp->length;
326 
327 	iter->dos.nebr_lba = dp->start_lba;
328 	iter->dos.nebr_siz = dp->length;
329 
330 	iter->index0--;
331     }
332     return 0;
333 }
334 
dos_next_ebr(struct part_iter * iter,uint32_t * lba,struct disk_dos_part_entry ** _dp)335 static int dos_next_ebr(struct part_iter *iter, uint32_t *lba,
336 			    struct disk_dos_part_entry **_dp)
337 {
338     struct disk_dos_part_entry *dp;
339 
340     if (prep_base_ebr(iter) < 0) {
341 	iter->status = PI_DONE;
342 	return -1;
343     }
344 
345     while (++iter->index0 < 1024 && iter->dos.nebr_lba) {
346 	free(iter->data);
347 	if (!(iter->data =
348 		    disk_read_sectors(&iter->di, iter->dos.nebr_lba, 1))) {
349 	    error("Couldn't load EBR.");
350 	    iter->status = PI_ERRLOAD;
351 	    return -1;
352 	}
353 
354 	/* check sanity of loaded data */
355 	if (notsane_logical(iter) || notsane_extended(iter)) {
356 	    iter->status = PI_INSANE;
357 	    return -1;
358 	}
359 
360 	dp = ((struct disk_dos_mbr *)iter->data)->table;
361 
362 	iter->dos.cebr_lba = iter->dos.nebr_lba;
363 	iter->dos.cebr_siz = iter->dos.nebr_siz;
364 
365 	/* setup next frame values */
366 	if (dp[1].ostype) {
367 	    iter->dos.nebr_lba = iter->dos.bebr_lba + dp[1].start_lba;
368 	    iter->dos.nebr_siz = dp[1].length;
369 	} else {
370 	    iter->dos.nebr_lba = 0;
371 	    iter->dos.nebr_siz = 0;
372 	}
373 
374 	if (!dp[0].ostype)
375 	    iter->dos.logskipcnt++;
376 
377 	if (dp[0].ostype || (iter->flags & PIF_STEPALL)) {
378 	    *lba = dp[0].start_lba ? iter->dos.cebr_lba + dp[0].start_lba : 0;
379 	    *_dp = dp;
380 	    return 0;
381 	}
382 	/*
383 	 * This way it's possible to continue, if some crazy soft left a "hole"
384 	 * - EBR with a valid extended partition without a logical one. In
385 	 * such case, linux will not reserve a number for such hole - so we
386 	 * don't increase index0. If PIF_STEPALL flag is set, we will never
387 	 * reach this place.
388 	 */
389     }
390     iter->status = PI_DONE;
391     return -1;
392 }
393 
gpt_conv_label(struct part_iter * iter)394 static void gpt_conv_label(struct part_iter *iter)
395 {
396     const struct disk_gpt_part_entry *gp;
397     const int16_t *orig_lab;
398 
399     gp = (const struct disk_gpt_part_entry *)
400 	(iter->data + iter->index0 * iter->gpt.pe_size);
401     orig_lab = (const int16_t *)gp->name;
402 
403     /* caveat: this is very crude conversion */
404     for (int i = 0; i < PI_GPTLABSIZE/2; i++) {
405 	iter->gpt.part_label[i] = (char)orig_lab[i];
406     }
407     iter->gpt.part_label[PI_GPTLABSIZE/2] = 0;
408 }
409 
valid_crc(uint32_t crc,const uint8_t * buf,unsigned int siz)410 static inline int valid_crc(uint32_t crc, const uint8_t *buf, unsigned int siz)
411 {
412     return crc == crc32(crc32(0, NULL, 0), buf, siz);
413 }
414 
valid_crc_hdr(void * buf)415 static int valid_crc_hdr(void *buf)
416 {
417     struct disk_gpt_header *gh = buf;
418     uint32_t crc = gh->chksum;
419     int valid;
420 
421     gh->chksum = 0;
422     valid = crc == crc32(crc32(0, NULL, 0), buf, gh->hdr_size);
423     gh->chksum = crc;
424     return valid;
425 }
426 
pi_next_(struct part_iter * iter)427 static int pi_next_(struct part_iter *iter)
428 {
429     iter->status = PI_DONE;
430     return iter->status;
431 }
432 
pi_dos_next(struct part_iter * iter)433 static int pi_dos_next(struct part_iter *iter)
434 {
435     uint32_t abs_lba = 0;
436     struct disk_dos_part_entry *dos_part = NULL;
437 
438     if (iter->status)
439 	return iter->status;
440 
441     /* look for primary partitions */
442     if (iter->index0 < 4 &&
443 	    dos_next_mbr(iter, &abs_lba, &dos_part) < 0)
444 	return iter->status;
445 
446     /* look for logical partitions */
447     if (iter->index0 >= 4 &&
448 	    dos_next_ebr(iter, &abs_lba, &dos_part) < 0)
449 	return iter->status;
450 
451     /*
452      * note special index handling:
453      * in case PIF_STEPALL is set - this makes the index consistent with
454      * non-PIF_STEPALL iterators
455      */
456 
457     if (!dos_part->ostype)
458 	iter->index = -1;
459     else
460 	iter->index = iter->index0 + 1 - iter->dos.logskipcnt;
461     iter->abs_lba = abs_lba;
462     iter->length = dos_part->length;
463     iter->record = (char *)dos_part;
464 
465 #ifdef DEBUG
466     disk_dos_part_dump(dos_part);
467 #endif
468 
469     return iter->status;
470 }
471 
pi_gpt_next(struct part_iter * iter)472 static int pi_gpt_next(struct part_iter *iter)
473 {
474     const struct disk_gpt_part_entry *gpt_part = NULL;
475 
476     if (iter->status)
477 	return iter->status;
478 
479     while (++iter->index0 < iter->gpt.pe_count) {
480 	gpt_part = (const struct disk_gpt_part_entry *)
481 	    (iter->data + iter->index0 * iter->gpt.pe_size);
482 
483 	if (notsane_gpt(iter)) {
484 	    iter->status = PI_INSANE;
485 	    return iter->status;
486 	}
487 
488 	if (!guid_is0(&gpt_part->type) || (iter->flags & PIF_STEPALL))
489 	    break;
490     }
491     /* no more partitions ? */
492     if (iter->index0 == iter->gpt.pe_count) {
493 	iter->status = PI_DONE;
494 	return iter->status;
495     }
496     /* gpt_part is guaranteed to be valid here */
497     iter->index = iter->index0 + 1;
498     iter->abs_lba = gpt_part->lba_first;
499     iter->length = gpt_part->lba_last - gpt_part->lba_first + 1;
500     iter->record = (char *)gpt_part;
501     memcpy(&iter->gpt.part_guid, &gpt_part->uid, sizeof(struct guid));
502     gpt_conv_label(iter);
503 
504 #ifdef DEBUG
505     disk_gpt_part_dump(gpt_part);
506 #endif
507 
508     return iter->status;
509 }
510 
pi_alloc(void)511 static struct part_iter *pi_alloc(void)
512 {
513     struct part_iter *iter;
514     if (!(iter = malloc(sizeof *iter)))
515 	critm();
516     else
517 	memset(iter, 0, sizeof *iter);
518     return iter;
519 }
520 
521 /* pi_del() - delete iterator */
pi_del(struct part_iter ** _iter)522 void pi_del(struct part_iter **_iter)
523 {
524     if(!_iter || !*_iter)
525 	return;
526     pi_dtor(*_iter);
527     free(*_iter);
528     *_iter = NULL;
529 }
530 
try_gpt_we(const char * str,int sec)531 static void try_gpt_we(const char *str, int sec)
532 {
533     if (sec)
534 	error(str);
535     else
536 	warn(str);
537 }
538 
try_gpt_hdr(const struct disk_info * di,int sec)539 static struct disk_gpt_header *try_gpt_hdr(const struct disk_info *di, int sec)
540 {
541     const char *desc = sec ? "backup" : "primary";
542     uint64_t gpt_cur = sec ? di->lbacnt - 1 : 1;
543     struct disk_gpt_header *gpth;
544     char errbuf[64];
545 
546     gpth = disk_read_sectors(di, gpt_cur, 1);
547     if (!gpth) {
548 	sprintf(errbuf, "Unable to read %s GPT header.", desc);
549 	try_gpt_we(errbuf, sec);
550 	return NULL;
551     }
552     if(!valid_crc_hdr(gpth)) {
553 	sprintf(errbuf, "Invalid checksum of %s GPT header.", desc);
554 	try_gpt_we(errbuf, sec);
555 	free(gpth);
556 	return NULL;
557     }
558     return gpth;
559 }
560 
try_gpt_list(const struct disk_info * di,const struct disk_gpt_header * gpth,int alt)561 static struct disk_gpt_part_entry *try_gpt_list(const struct disk_info *di, const struct disk_gpt_header *gpth, int alt)
562 {
563     int pri = gpth->lba_cur < gpth->lba_alt;
564     const char *desc = alt ? "alternative" : "main";
565     struct disk_gpt_part_entry *gptl;
566     char errbuf[64];
567     uint64_t gpt_lsiz;	    /* size of GPT partition list in bytes */
568     uint64_t gpt_lcnt;	    /* size of GPT partition in sectors */
569     uint64_t gpt_loff;	    /* offset to GPT partition list in sectors */
570 
571     gpt_lsiz = (uint64_t)gpth->part_size * gpth->part_count;
572     gpt_lcnt = (gpt_lsiz + di->bps - 1) / di->bps;
573     if (!alt) {
574 	/* prefer header value for partition table if not asking for alternative */
575 	gpt_loff = gpth->lba_table;
576     } else {
577 	/* try to read alternative, we have to calculate its position */
578 	if (!pri)
579 	    gpt_loff = gpth->lba_alt + 1;
580 	else
581 	    gpt_loff = gpth->lba_alt - gpt_lcnt;
582     }
583 
584     gptl = disk_read_sectors(di, gpt_loff, gpt_lcnt);
585     if (!gptl) {
586 	sprintf(errbuf, "Unable to read %s GPT partition list.", desc);
587 	try_gpt_we(errbuf, alt);
588 	return NULL;
589     }
590     if (!valid_crc(gpth->table_chksum, (const uint8_t *)gptl, gpt_lsiz)) {
591 	sprintf(errbuf, "Invalid checksum of %s GPT partition list.", desc);
592 	try_gpt_we(errbuf, alt);
593 	free(gptl);
594 	return NULL;
595     }
596     return gptl;
597 }
598 
notsane_gpt_hdr(const struct disk_info * di,const struct disk_gpt_header * gpth,int flags)599 static int notsane_gpt_hdr(const struct disk_info *di, const struct disk_gpt_header *gpth, int flags)
600 {
601     uint64_t gpt_loff;	    /* offset to GPT partition list in sectors */
602     uint64_t gpt_lsiz;	    /* size of GPT partition list in bytes */
603     uint64_t gpt_lcnt;	    /* size of GPT partition in sectors */
604     uint64_t gpt_sec;	    /* secondary gpt header */
605 
606     if (!(flags & PIF_STRICT))
607 	return 0;
608 
609     if (gpth->lba_alt < gpth->lba_cur)
610 	gpt_sec = gpth->lba_cur;
611     else
612 	gpt_sec = gpth->lba_alt;
613     gpt_loff = gpth->lba_table;
614     gpt_lsiz = (uint64_t)gpth->part_size * gpth->part_count;
615     gpt_lcnt = (gpt_lsiz + di->bps - 1) / di->bps;
616 
617     /*
618      * disk_read_sectors allows reading of max 255 sectors, so we use
619      * it as a sanity check base. EFI doesn't specify max (AFAIK).
620      */
621     if (gpt_loff < 2 || !gpt_lsiz || gpt_lcnt > 255u ||
622 	    gpth->lba_first_usable > gpth->lba_last_usable ||
623 	    !sane(gpt_loff, gpt_lcnt) ||
624 	    (gpt_loff + gpt_lcnt > gpth->lba_first_usable && gpt_loff <= gpth->lba_last_usable) ||
625 	     gpt_loff + gpt_lcnt > gpt_sec ||
626 	    ((flags & PIF_STRICTER) && (gpt_sec >= di->lbacnt)) ||
627 	    gpth->part_size < sizeof(struct disk_gpt_part_entry))
628 	return -1;
629 
630     return 0;
631 }
632 
633 /* pi_begin() - validate and and get proper iterator for a disk described by di */
pi_begin(const struct disk_info * di,int flags)634 struct part_iter *pi_begin(const struct disk_info *di, int flags)
635 {
636     int isgpt = 0, ret = -1;
637     struct part_iter *iter;
638     struct disk_dos_mbr *mbr = NULL;
639     struct disk_gpt_header *gpth = NULL;
640     struct disk_gpt_part_entry *gptl = NULL;
641 
642     /* Preallocate iterator */
643     if (!(iter = pi_alloc()))
644 	goto out;
645 
646     /* Read MBR */
647     if (!(mbr = disk_read_sectors(di, 0, 1))) {
648 	error("Unable to read the first disk sector.");
649 	goto out;
650     }
651 
652     /* Check for MBR magic */
653     if (mbr->sig != disk_mbr_sig_magic) {
654 	warn("No MBR magic, treating disk as raw.");
655 	/* looks like RAW */
656 	ret = pi_ctor(iter, di, flags);
657 	goto out;
658     }
659 
660     /* Check for GPT protective MBR */
661     for (size_t i = 0; i < 4; i++)
662 	isgpt |= (mbr->table[i].ostype == 0xEE);
663     isgpt = isgpt && !(flags & PIF_PREFMBR);
664 
665     /* Try to read GPT header */
666     if (isgpt) {
667 	gpth = try_gpt_hdr(di, 0);
668 	if (!gpth)
669 	    /*
670 	     * this read might fail if bios reports different disk size (different vm/pc)
671 	     * not much we can do here to avoid it
672 	     */
673 	    gpth = try_gpt_hdr(di, 1);
674 	if (!gpth)
675 	    goto out;
676     }
677 
678     if (gpth && gpth->rev.uint32 == 0x00010000 &&
679 	    !memcmp(gpth->sig, disk_gpt_sig_magic, sizeof gpth->sig)) {
680 	/* looks like GPT v1.0 */
681 #ifdef DEBUG
682 	dprintf("Looks like a GPT v1.0 disk.\n");
683 	disk_gpt_header_dump(gpth);
684 #endif
685 	if (notsane_gpt_hdr(di, gpth, flags)) {
686 	    error("GPT header values are corrupted.");
687 	    goto out;
688 	}
689 
690 	gptl = try_gpt_list(di, gpth, 0);
691 	if (!gptl)
692 	    gptl = try_gpt_list(di, gpth, 1);
693 	if (!gptl)
694 	    goto out;
695 
696 	/* looks like GPT */
697 	ret = pi_gpt_ctor(iter, di, flags, gpth, gptl);
698     } else {
699 	/* looks like MBR */
700 	ret = pi_dos_ctor(iter, di, flags, mbr);
701     }
702 out:
703     if (ret < 0) {
704 	free(iter);
705 	iter = NULL;
706     }
707     free(mbr);
708     free(gpth);
709     free(gptl);
710 
711     return iter;
712 }
713 
714 /* vim: set ts=8 sts=4 sw=4 noet: */
715