/* pi_gpt_ctor() - GPT iterator specific initialization */ static int 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 ) { uint64_t siz; if (pi_ctor(iter, di, flags)) return -1; siz = (uint64_t)gpth->part_count * gpth->part_size; if (!(iter->data = malloc((size_t)siz))) { critm(); goto bail; } memcpy(iter->data, gptl, (size_t)siz); iter->gpt.pe_count = (int)gpth->part_count; iter->gpt.pe_size = (int)gpth->part_size; iter->gpt.ufirst = gpth->lba_first_usable; iter->gpt.ulast = gpth->lba_last_usable; memcpy(&iter->gpt.disk_guid, &gpth->disk_guid, sizeof gpth->disk_guid); memcpy(&iter->gpt.part_guid, &gpth->disk_guid, sizeof gpth->disk_guid); iter->type = typegpt; return 0; bail: pi_dtor_(iter); return -1; }
/* pi_dos_ctor() - MBR/EBR iterator specific initialization */ static int pi_dos_ctor(struct part_iter *iter, const struct disk_info *di, int flags, const struct disk_dos_mbr *mbr ) { if (pi_ctor(iter, di, flags)) return -1; if (!(iter->data = malloc(sizeof *mbr))) { critm(); goto bail; } memcpy(iter->data, mbr, sizeof *mbr); iter->dos.bebr_index0 = -1; iter->dos.disk_sig = mbr->disk_sig; iter->type = typedos; return 0; bail: pi_dtor_(iter); return -1; }
/* pi_begin() - validate and and get proper iterator for a disk described by di */ struct part_iter *pi_begin(const struct disk_info *di, int flags) { int gptprot, ret = -1; struct part_iter *iter; struct disk_dos_mbr *mbr = NULL; struct disk_gpt_header *gpth = NULL; struct disk_gpt_part_entry *gptl = NULL; /* Preallocate iterator */ if (!(iter = pi_alloc())) goto bail; /* Read MBR */ if (!(mbr = disk_read_sectors(di, 0, 1))) { error("Couldn't read the first disk sector."); goto bail; } /* Check for MBR magic */ if (mbr->sig != disk_mbr_sig_magic) { warn("No MBR magic, treating disk as raw."); /* looks like RAW */ ret = pi_ctor(iter, di, flags); goto bail; } /* Check for GPT protective MBR */ gptprot = 0; for (size_t i = 0; i < 4; i++) gptprot |= (mbr->table[i].ostype == 0xEE); if (gptprot && !(flags & PIF_PREFMBR)) { if (!(gpth = disk_read_sectors(di, 1, 1))) { error("Couldn't read potential GPT header."); goto bail; } } if (gpth && gpth->rev.uint32 == 0x00010000 && !memcmp(gpth->sig, disk_gpt_sig_magic, sizeof gpth->sig)) { /* looks like GPT v1.0 */ uint64_t gpt_loff; /* offset to GPT partition list in sectors */ uint64_t gpt_lsiz; /* size of GPT partition list in bytes */ uint64_t gpt_lcnt; /* size of GPT partition in sectors */ #ifdef DEBUG dprintf("Looks like a GPT v1.0 disk.\n"); disk_gpt_header_dump(gpth); #endif /* Verify checksum, fallback to backup, then bail if invalid */ if (gpt_check_hdr_crc(di, &gpth)) goto bail; gpt_loff = gpth->lba_table; gpt_lsiz = (uint64_t)gpth->part_size * gpth->part_count; gpt_lcnt = (gpt_lsiz + di->bps - 1) / di->bps; /* * disk_read_sectors allows reading of max 255 sectors, so we use * it as a sanity check base. EFI doesn't specify max (AFAIK). * Apart from that, some extensive sanity checks. */ if (!(flags & PIF_RELAX) && ( !gpt_loff || !gpt_lsiz || gpt_lcnt > 255u || gpth->lba_first_usable > gpth->lba_last_usable || !sane(gpt_loff, gpt_lcnt) || gpt_loff + gpt_lcnt > gpth->lba_first_usable || !sane(gpth->lba_last_usable, gpt_lcnt) || gpth->lba_last_usable + gpt_lcnt >= gpth->lba_alt || gpth->lba_alt >= di->lbacnt || gpth->part_size < sizeof *gptl)) { error("Invalid GPT header's values."); goto bail; } if (!(gptl = disk_read_sectors(di, gpt_loff, gpt_lcnt))) { error("Couldn't read GPT partition list."); goto bail; } /* Check array checksum(s). */ if (!valid_crc(gpth->table_chksum, (const uint8_t *)gptl, (unsigned int)gpt_lsiz)) { warn("Checksum of the main GPT partition list is invalid, trying backup."); free(gptl); /* secondary array directly precedes secondary header */ if (!(gptl = disk_read_sectors(di, gpth->lba_alt - gpt_lcnt, gpt_lcnt))) { error("Couldn't read backup GPT partition list."); goto bail; } if (!valid_crc(gpth->table_chksum, (const uint8_t *)gptl, gpt_lsiz)) { error("Checksum of the backup GPT partition list is invalid, giving up."); goto bail; } } /* looks like GPT */ ret = pi_gpt_ctor(iter, di, flags, gpth, gptl); } else { /* looks like MBR */ ret = pi_dos_ctor(iter, di, flags, mbr); } bail: if (ret < 0) free(iter); free(mbr); free(gpth); free(gptl); return iter; }
/* pi_begin() - validate and and get proper iterator for a disk described by di */ struct part_iter *pi_begin(const struct disk_info *di, int flags) { int isgpt = 0, ret = -1; struct part_iter *iter; struct disk_dos_mbr *mbr = NULL; struct disk_gpt_header *gpth = NULL; struct disk_gpt_part_entry *gptl = NULL; /* Preallocate iterator */ if (!(iter = pi_alloc())) goto out; /* Read MBR */ if (!(mbr = disk_read_sectors(di, 0, 1))) { error("Unable to read the first disk sector."); goto out; } /* Check for MBR magic */ if (mbr->sig != disk_mbr_sig_magic) { warn("No MBR magic, treating disk as raw."); /* looks like RAW */ ret = pi_ctor(iter, di, flags); goto out; } /* Check for GPT protective MBR */ for (size_t i = 0; i < 4; i++) isgpt |= (mbr->table[i].ostype == 0xEE); isgpt = isgpt && !(flags & PIF_PREFMBR); /* Try to read GPT header */ if (isgpt) { gpth = try_gpt_hdr(di, 0); if (!gpth) /* * this read might fail if bios reports different disk size (different vm/pc) * not much we can do here to avoid it */ gpth = try_gpt_hdr(di, 1); if (!gpth) goto out; } if (gpth && gpth->rev.uint32 == 0x00010000 && !memcmp(gpth->sig, disk_gpt_sig_magic, sizeof gpth->sig)) { /* looks like GPT v1.0 */ #ifdef DEBUG dprintf("Looks like a GPT v1.0 disk.\n"); disk_gpt_header_dump(gpth); #endif if (notsane_gpt_hdr(di, gpth, flags)) { error("GPT header values are corrupted."); goto out; } gptl = try_gpt_list(di, gpth, 0); if (!gptl) gptl = try_gpt_list(di, gpth, 1); if (!gptl) goto out; /* looks like GPT */ ret = pi_gpt_ctor(iter, di, flags, gpth, gptl); } else { /* looks like MBR */ ret = pi_dos_ctor(iter, di, flags, mbr); } out: if (ret < 0) { free(iter); iter = NULL; } free(mbr); free(gpth); free(gptl); return iter; }