This commit was manufactured by cvs2svn to create tag
[linux-2.6.git] / fs / partitions / efi.c
index 0f5b017..4cb2960 100644 (file)
@@ -3,7 +3,7 @@
  * Per Intel EFI Specification v1.02
  * http://developer.intel.com/technology/efi/efi.htm
  * efi.[ch] by Matt Domsch <Matt_Domsch@dell.com>
- *   Copyright 2000,2001,2002,2004 Dell Inc.
+ *   Copyright 2000,2001,2002 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
  * TODO:
  *
  * Changelog:
- * Mon Nov 09 2004 Matt Domsch <Matt_Domsch@dell.com>
- * - 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 <Matt_Domsch@dell.com>
  * - Ported to 2.5.7-pre1 and 2.5.7-dj2
  * - Applied patch to avoid fault in alternate header handling
@@ -135,6 +130,32 @@ 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
@@ -147,40 +168,7 @@ efi_crc32(const void *buf, unsigned long len)
 static u64
 last_lba(struct block_device *bdev)
 {
-       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;
+       return (bdev->bd_inode->i_size >> 9) - 1;
 }
 
 /**
@@ -198,8 +186,8 @@ read_lba(struct block_device *bdev, u64 lba, u8 * buffer, size_t count)
 {
        size_t totalreadcount = 0;
 
-       if (!bdev || !buffer || lba > last_lba(bdev))
-                return 0;
+       if (!bdev || !buffer)
+               return 0;
 
        while (count) {
                int copied = 512;
@@ -218,6 +206,7 @@ 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
@@ -300,7 +289,6 @@ 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;
@@ -313,7 +301,9 @@ 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);
-               goto fail;
+               kfree(*gpt);
+               *gpt = NULL;
+               return 0;
        }
 
        /* Check the GUID Partition Table CRC */
@@ -325,7 +315,9 @@ is_gpt_valid(struct block_device *bdev, u64 lba,
                Dprintk
                    ("GUID Partition Table Header CRC is wrong: %x != %x\n",
                     crc, origcrc);
-               goto fail;
+               kfree(*gpt);
+               *gpt = NULL;
+               return 0;
        }
        (*gpt)->header_crc32 = cpu_to_le32(origcrc);
 
@@ -335,29 +327,17 @@ 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);
-               goto fail;
+               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))) {
+               kfree(*gpt);
+               *gpt = NULL;
+               return 0;
        }
 
-       if (!(*ptes = alloc_read_gpt_entries(bdev, *gpt)))
-               goto fail;
-
        /* Check the GUID Partition Entry Array CRC */
        crc = efi_crc32((const unsigned char *) (*ptes),
                        le32_to_cpu((*gpt)->num_partition_entries) *
@@ -365,36 +345,15 @@ 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");
-               goto fail_ptes;
+               kfree(*gpt);
+               *gpt = NULL;
+               kfree(*ptes);
+               *ptes = NULL;
+               return 0;
        }
 
        /* 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;
 }
 
 /**
@@ -505,13 +464,8 @@ 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 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.
+ * Validity depends on finding either the Primary GPT header and PTEs valid,
+ * or the Alternate GPT header and PTEs valid, and the PMBR valid.
  */
 static int
 find_valid_gpt(struct block_device *bdev, gpt_header **gpt, gpt_entry **ptes)
@@ -525,43 +479,70 @@ 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 && force_gpt)
+                if (!good_agpt) {
+                        good_agpt = is_gpt_valid(bdev, lastlba,
+                                                 &agpt, &aptes);
+                }
+        }
+        else {
                 good_agpt = is_gpt_valid(bdev, lastlba,
                                          &agpt, &aptes);
+        }
 
         /* The obviously unsuccessful case */
-        if (!good_pgpt && !good_agpt)
+        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");
                 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) {
+        if (good_pgpt && (good_pmbr || force_gpt)) {
                 *gpt  = pgpt;
                 *ptes = pptes;
-                kfree(agpt);
-                kfree(aptes);
+                if (agpt)  { kfree(agpt);   agpt = NULL; }
+                if (aptes) { kfree(aptes); aptes = NULL; }
                 if (!good_agpt) {
                         printk(KERN_WARNING 
                               "Alternate GPT is invalid, "
@@ -569,21 +550,21 @@ find_valid_gpt(struct block_device *bdev, gpt_header **gpt, gpt_entry **ptes)
                 }
                 return 1;
         }
-        else if (good_agpt) {
+        else if (good_agpt && (good_pmbr || force_gpt)) {
                 *gpt  = agpt;
                 *ptes = aptes;
-                kfree(pgpt);
-                kfree(pptes);
+                if (pgpt)  { kfree(pgpt);   pgpt = NULL; }
+                if (pptes) { kfree(pptes); pptes = NULL; }
                 printk(KERN_WARNING 
                        "Primary GPT is invalid, using alternate GPT.\n");
                 return 1;
         }
 
  fail:
-        kfree(pgpt);
-        kfree(agpt);
-        kfree(pptes);
-        kfree(aptes);
+        if (pgpt)  { kfree(pgpt);   pgpt=NULL; }
+        if (agpt)  { kfree(agpt);   agpt=NULL; }
+        if (pptes) { kfree(pptes); pptes=NULL; }
+        if (aptes) { kfree(aptes); aptes=NULL; }
         *gpt = NULL;
         *ptes = NULL;
         return 0;
@@ -625,15 +606,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 (!is_pte_valid(&ptes[i], last_lba(bdev)))
+               if (!efi_guidcmp(ptes[i].partition_type_guid, NULL_GUID))
                        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) +
-                                 1ULL));
+                                 1));
 
-               /* If this is a RAID volume, tell md */
+               /* If there's 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;
@@ -643,3 +624,22 @@ 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:
+ */