X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=fs%2Fpartitions%2Fefi.c;h=0f5b017aebad2ad1adb6657d819c52fa4b936153;hb=6a77f38946aaee1cd85eeec6cf4229b204c15071;hp=4cb29608753d78678d995376ff5649a50ef94122;hpb=87fc8d1bb10cd459024a742c6a10961fefcef18f;p=linux-2.6.git diff --git a/fs/partitions/efi.c b/fs/partitions/efi.c index 4cb296087..0f5b017ae 100644 --- a/fs/partitions/efi.c +++ b/fs/partitions/efi.c @@ -3,7 +3,7 @@ * Per Intel EFI Specification v1.02 * http://developer.intel.com/technology/efi/efi.htm * efi.[ch] by Matt Domsch - * Copyright 2000,2001,2002 Dell Inc. + * Copyright 2000,2001,2002,2004 Dell Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -23,6 +23,11 @@ * TODO: * * Changelog: + * Mon Nov 09 2004 Matt Domsch + * - test for valid PMBR and valid PGPT before ever reading + * AGPT, allow override with 'gpt' kernel command line option. + * - check for first/last_usable_lba outside of size of disk + * * Tue Mar 26 2002 Matt Domsch * - Ported to 2.5.7-pre1 and 2.5.7-dj2 * - Applied patch to avoid fault in alternate header handling @@ -130,32 +135,6 @@ efi_crc32(const void *buf, unsigned long len) return (crc32(~0L, buf, len) ^ ~0L); } -/** - * is_pmbr_valid(): test Protective MBR for validity - * @mbr: pointer to a legacy mbr structure - * - * Description: Returns 1 if PMBR is valid, 0 otherwise. - * Validity depends on two things: - * 1) MSDOS signature is in the last two bytes of the MBR - * 2) One partition of type 0xEE is found - */ -static int -is_pmbr_valid(legacy_mbr *mbr) -{ - int i, found = 0, signature = 0; - if (!mbr) - return 0; - signature = (le16_to_cpu(mbr->signature) == MSDOS_MBR_SIGNATURE); - for (i = 0; signature && i < 4; i++) { - if (mbr->partition_record[i].sys_ind == - EFI_PMBR_OSTYPE_EFI_GPT) { - found = 1; - break; - } - } - return (signature && found); -} - /** * last_lba(): return number of last logical block of device * @bdev: block device @@ -168,7 +147,40 @@ is_pmbr_valid(legacy_mbr *mbr) static u64 last_lba(struct block_device *bdev) { - return (bdev->bd_inode->i_size >> 9) - 1; + if (!bdev || !bdev->bd_inode) + return 0; + return (bdev->bd_inode->i_size >> 9) - 1ULL; +} + +static inline int +pmbr_part_valid(struct partition *part, u64 lastlba) +{ + if (part->sys_ind == EFI_PMBR_OSTYPE_EFI_GPT && + le32_to_cpu(part->start_sect) == 1UL) + return 1; + return 0; +} + +/** + * is_pmbr_valid(): test Protective MBR for validity + * @mbr: pointer to a legacy mbr structure + * @lastlba: last_lba for the whole device + * + * Description: Returns 1 if PMBR is valid, 0 otherwise. + * Validity depends on two things: + * 1) MSDOS signature is in the last two bytes of the MBR + * 2) One partition of type 0xEE is found + */ +static int +is_pmbr_valid(legacy_mbr *mbr, u64 lastlba) +{ + int i; + if (!mbr || le16_to_cpu(mbr->signature) != MSDOS_MBR_SIGNATURE) + return 0; + for (i = 0; i < 4; i++) + if (pmbr_part_valid(&mbr->partition_record[i], lastlba)) + return 1; + return 0; } /** @@ -186,8 +198,8 @@ read_lba(struct block_device *bdev, u64 lba, u8 * buffer, size_t count) { size_t totalreadcount = 0; - if (!bdev || !buffer) - return 0; + if (!bdev || !buffer || lba > last_lba(bdev)) + return 0; while (count) { int copied = 512; @@ -206,7 +218,6 @@ read_lba(struct block_device *bdev, u64 lba, u8 * buffer, size_t count) return totalreadcount; } - /** * alloc_read_gpt_entries(): reads partition entries from disk * @bdev @@ -289,6 +300,7 @@ is_gpt_valid(struct block_device *bdev, u64 lba, gpt_header **gpt, gpt_entry **ptes) { u32 crc, origcrc; + u64 lastlba; if (!bdev || !gpt || !ptes) return 0; @@ -301,9 +313,7 @@ is_gpt_valid(struct block_device *bdev, u64 lba, "%lld != %lld\n", (unsigned long long)le64_to_cpu((*gpt)->signature), (unsigned long long)GPT_HEADER_SIGNATURE); - kfree(*gpt); - *gpt = NULL; - return 0; + goto fail; } /* Check the GUID Partition Table CRC */ @@ -315,9 +325,7 @@ is_gpt_valid(struct block_device *bdev, u64 lba, Dprintk ("GUID Partition Table Header CRC is wrong: %x != %x\n", crc, origcrc); - kfree(*gpt); - *gpt = NULL; - return 0; + goto fail; } (*gpt)->header_crc32 = cpu_to_le32(origcrc); @@ -327,16 +335,28 @@ is_gpt_valid(struct block_device *bdev, u64 lba, Dprintk("GPT my_lba incorrect: %lld != %lld\n", (unsigned long long)le64_to_cpu((*gpt)->my_lba), (unsigned long long)lba); - kfree(*gpt); - *gpt = NULL; - return 0; + goto fail; } - if (!(*ptes = alloc_read_gpt_entries(bdev, *gpt))) { - kfree(*gpt); - *gpt = NULL; - return 0; + /* Check the first_usable_lba and last_usable_lba are + * within the disk. + */ + lastlba = last_lba(bdev); + if (le64_to_cpu((*gpt)->first_usable_lba) > lastlba) { + Dprintk("GPT: first_usable_lba incorrect: %lld > %lld\n", + (unsigned long long)le64_to_cpu((*gpt)->first_usable_lba), + (unsigned long long)lastlba); + goto fail; } + if (le64_to_cpu((*gpt)->last_usable_lba) > lastlba) { + Dprintk("GPT: last_usable_lba incorrect: %lld > %lld\n", + (unsigned long long)le64_to_cpu((*gpt)->last_usable_lba), + (unsigned long long)lastlba); + goto fail; + } + + if (!(*ptes = alloc_read_gpt_entries(bdev, *gpt))) + goto fail; /* Check the GUID Partition Entry Array CRC */ crc = efi_crc32((const unsigned char *) (*ptes), @@ -345,15 +365,36 @@ is_gpt_valid(struct block_device *bdev, u64 lba, if (crc != le32_to_cpu((*gpt)->partition_entry_array_crc32)) { Dprintk("GUID Partitition Entry Array CRC check failed.\n"); - kfree(*gpt); - *gpt = NULL; - kfree(*ptes); - *ptes = NULL; - return 0; + goto fail_ptes; } /* We're done, all's well */ return 1; + + fail_ptes: + kfree(*ptes); + *ptes = NULL; + fail: + kfree(*gpt); + *gpt = NULL; + return 0; +} + +/** + * is_pte_valid() - tests one PTE for validity + * @pte is the pte to check + * @lastlba is last lba of the disk + * + * Description: returns 1 if valid, 0 on error. + */ +static inline int +is_pte_valid(const gpt_entry *pte, const u64 lastlba) +{ + if ((!efi_guidcmp(pte->partition_type_guid, NULL_GUID)) || + le64_to_cpu(pte->starting_lba) > lastlba || + le64_to_cpu(pte->ending_lba) > lastlba) + return 0; + return 1; } /** @@ -464,8 +505,13 @@ compare_gpts(gpt_header *pgpt, gpt_header *agpt, u64 lastlba) * @ptes is a PTEs ptr, filled on return. * Description: Returns 1 if valid, 0 on error. * If valid, returns pointers to newly allocated GPT header and PTEs. - * Validity depends on finding either the Primary GPT header and PTEs valid, - * or the Alternate GPT header and PTEs valid, and the PMBR valid. + * Validity depends on PMBR being valid (or being overridden by the + * 'gpt' kernel command line option) and finding either the Primary + * GPT header and PTEs valid, or the Alternate GPT header and PTEs + * valid. If the Primary GPT header is not valid, the Alternate GPT header + * is not checked unless the 'gpt' kernel command line option is passed. + * This protects against devices which misreport their size, and forces + * the user to decide to use the Alternate GPT. */ static int find_valid_gpt(struct block_device *bdev, gpt_header **gpt, gpt_entry **ptes) @@ -479,70 +525,43 @@ find_valid_gpt(struct block_device *bdev, gpt_header **gpt, gpt_entry **ptes) return 0; lastlba = last_lba(bdev); + if (!force_gpt) { + /* This will be added to the EFI Spec. per Intel after v1.02. */ + legacymbr = kmalloc(sizeof (*legacymbr), GFP_KERNEL); + if (legacymbr) { + memset(legacymbr, 0, sizeof (*legacymbr)); + read_lba(bdev, 0, (u8 *) legacymbr, + sizeof (*legacymbr)); + good_pmbr = is_pmbr_valid(legacymbr, lastlba); + kfree(legacymbr); + legacymbr=NULL; + } + if (!good_pmbr) + goto fail; + } + good_pgpt = is_gpt_valid(bdev, GPT_PRIMARY_PARTITION_TABLE_LBA, &pgpt, &pptes); - if (good_pgpt) { + if (good_pgpt) good_agpt = is_gpt_valid(bdev, - le64_to_cpu(pgpt->alternate_lba), + le64_to_cpu(pgpt->alternate_lba), &agpt, &aptes); - if (!good_agpt) { - good_agpt = is_gpt_valid(bdev, lastlba, - &agpt, &aptes); - } - } - else { + if (!good_agpt && force_gpt) good_agpt = is_gpt_valid(bdev, lastlba, &agpt, &aptes); - } /* The obviously unsuccessful case */ - if (!good_pgpt && !good_agpt) { - goto fail; - } - - /* This will be added to the EFI Spec. per Intel after v1.02. */ - legacymbr = kmalloc(sizeof (*legacymbr), GFP_KERNEL); - if (legacymbr) { - memset(legacymbr, 0, sizeof (*legacymbr)); - read_lba(bdev, 0, (u8 *) legacymbr, - sizeof (*legacymbr)); - good_pmbr = is_pmbr_valid(legacymbr); - kfree(legacymbr); - legacymbr=NULL; - } - - /* Failure due to bad PMBR */ - if ((good_pgpt || good_agpt) && !good_pmbr && !force_gpt) { - printk(KERN_WARNING - " Warning: Disk has a valid GPT signature " - "but invalid PMBR.\n"); - printk(KERN_WARNING - " Assuming this disk is *not* a GPT disk anymore.\n"); - printk(KERN_WARNING - " Use gpt kernel option to override. " - "Use GNU Parted to correct disk.\n"); + if (!good_pgpt && !good_agpt) goto fail; - } - - /* Would fail due to bad PMBR, but force GPT anyhow */ - if ((good_pgpt || good_agpt) && !good_pmbr && force_gpt) { - printk(KERN_WARNING - " Warning: Disk has a valid GPT signature but " - "invalid PMBR.\n"); - printk(KERN_WARNING - " Use GNU Parted to correct disk.\n"); - printk(KERN_WARNING - " gpt option taken, disk treated as GPT.\n"); - } compare_gpts(pgpt, agpt, lastlba); /* The good cases */ - if (good_pgpt && (good_pmbr || force_gpt)) { + if (good_pgpt) { *gpt = pgpt; *ptes = pptes; - if (agpt) { kfree(agpt); agpt = NULL; } - if (aptes) { kfree(aptes); aptes = NULL; } + kfree(agpt); + kfree(aptes); if (!good_agpt) { printk(KERN_WARNING "Alternate GPT is invalid, " @@ -550,21 +569,21 @@ find_valid_gpt(struct block_device *bdev, gpt_header **gpt, gpt_entry **ptes) } return 1; } - else if (good_agpt && (good_pmbr || force_gpt)) { + else if (good_agpt) { *gpt = agpt; *ptes = aptes; - if (pgpt) { kfree(pgpt); pgpt = NULL; } - if (pptes) { kfree(pptes); pptes = NULL; } + kfree(pgpt); + kfree(pptes); printk(KERN_WARNING "Primary GPT is invalid, using alternate GPT.\n"); return 1; } fail: - if (pgpt) { kfree(pgpt); pgpt=NULL; } - if (agpt) { kfree(agpt); agpt=NULL; } - if (pptes) { kfree(pptes); pptes=NULL; } - if (aptes) { kfree(aptes); aptes=NULL; } + kfree(pgpt); + kfree(agpt); + kfree(pptes); + kfree(aptes); *gpt = NULL; *ptes = NULL; return 0; @@ -606,15 +625,15 @@ efi_partition(struct parsed_partitions *state, struct block_device *bdev) Dprintk("GUID Partition Table is valid! Yea!\n"); for (i = 0; i < le32_to_cpu(gpt->num_partition_entries) && i < state->limit-1; i++) { - if (!efi_guidcmp(ptes[i].partition_type_guid, NULL_GUID)) + if (!is_pte_valid(&ptes[i], last_lba(bdev))) continue; put_partition(state, i+1, le64_to_cpu(ptes[i].starting_lba), (le64_to_cpu(ptes[i].ending_lba) - le64_to_cpu(ptes[i].starting_lba) + - 1)); + 1ULL)); - /* If there's this is a RAID volume, tell md */ + /* If this is a RAID volume, tell md */ if (!efi_guidcmp(ptes[i].partition_type_guid, PARTITION_LINUX_RAID_GUID)) state->parts[i+1].flags = 1; @@ -624,22 +643,3 @@ efi_partition(struct parsed_partitions *state, struct block_device *bdev) printk("\n"); return 1; } - -/* - * Overrides for Emacs so that we follow Linus's tabbing style. - * Emacs will notice this stuff at the end of the file and automatically - * adjust the settings for this buffer only. This must remain at the end - * of the file. - * --------------------------------------------------------------------------- - * Local variables: - * c-indent-level: 4 - * c-brace-imaginary-offset: 0 - * c-brace-offset: -4 - * c-argdecl-indent: 4 - * c-label-offset: -4 - * c-continued-statement-offset: 4 - * c-continued-brace-offset: 0 - * indent-tabs-mode: nil - * tab-width: 8 - * End: - */