]> git.proxmox.com Git - mirror_zfs.git/commitdiff
Make libefi-created GPT compatible with gptfdisk
authorZachary Bedell <zac@thebedells.org>
Mon, 19 Sep 2011 01:35:42 +0000 (21:35 -0400)
committerBrian Behlendorf <behlendorf1@llnl.gov>
Mon, 26 Sep 2011 16:44:43 +0000 (09:44 -0700)
GPT's created by libefi set the HeaderSize attribute in the GPT
header to 512 -- size of the GPT header INCLUDING the 420 padding
bytes at the end.  Most other tools set the size to 92 -- size of
the actual header itself excluding the padding.  Most tools check
the recorded HeaderSize when verifying CRC, but gptfdisk hardcodes
92 and thus reports CRC verification problems for full-disk vdevs
created IE with `zpool create pool sdc`.

This commit changes libefi's behavior for GPT creation and also
fixes several edge cases where libefi's behavior was similar
(though in an incompatible manner) to gptfdisk.  Libefi assumed
HeaderSize was always 512 even if the GPT recorded a different
value.  Sanity checks of the GPT headersize read from disk were
added before applying checksum calculation -- this will prevent
segfault in cases of bogus on-disk values.

Zpools created with the resuling libefi are verified as correct
both by parted and gptfdisk.  Also pool have been tested to
import correctly on ZFS on Linux as well as Solaris Express 11
livecd.

Signed-off-by: Zachary Bedell <zac@thebedells.org>
Signed-off-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #344

include/sys/efi_partition.h
lib/libefi/rdwr_efi.c

index ff38df5e79ebfd9784fc1b26423b5db65e098131..e75e45a6bc0d2f6484485cba5429064091e0b1c5 100644 (file)
@@ -35,6 +35,7 @@ extern "C" {
  * GUID Partition Table Header
  */
 
+#define        EFI_MIN_LABEL_SIZE 92
 #define        EFI_LABEL_SIZE  512
 #define        LEN_EFI_PAD     (EFI_LABEL_SIZE - \
                            ((5 * sizeof (diskaddr_t)) + \
index 2af0c4e2e472f0122532bfecf5f18e8ba9242598..43a5310a318a016aa03c4f1858d1c7d464a31abf 100644 (file)
@@ -541,16 +541,22 @@ check_label(int fd, dk_efi_t *dk_ioc)
         */
        crc = efi->efi_gpt_HeaderCRC32;
        efi->efi_gpt_HeaderCRC32 = 0;
+       len_t headerSize = (len_t)LE_32(efi->efi_gpt_HeaderSize);
 
-       if (((len_t)LE_32(efi->efi_gpt_HeaderSize) > dk_ioc->dki_length) ||
-           crc != LE_32(efi_crc32((unsigned char *)efi,
-           LE_32(efi->efi_gpt_HeaderSize)))) {
+       if(headerSize < EFI_MIN_LABEL_SIZE || headerSize > EFI_LABEL_SIZE) {
+               if (efi_debug)
+                       (void) fprintf(stderr,
+                               "Invalid EFI HeaderSize %llu.  Assuming %d.\n",
+                               headerSize, EFI_MIN_LABEL_SIZE);
+       }
+
+       if ((headerSize > dk_ioc->dki_length) ||
+           crc != LE_32(efi_crc32((unsigned char *)efi, headerSize))) {
                if (efi_debug)
                        (void) fprintf(stderr,
                            "Bad EFI CRC: 0x%x != 0x%x\n",
-                           crc,
-                           LE_32(efi_crc32((unsigned char *)efi,
-                           sizeof (struct efi_gpt))));
+                           crc, LE_32(efi_crc32((unsigned char *)efi,
+                           headerSize)));
                return (VT_EINVAL);
        }
 
@@ -1152,7 +1158,7 @@ efi_write(int fd, struct dk_gpt *vtoc)
        /* stuff user's input into EFI struct */
        efi->efi_gpt_Signature = LE_64(EFI_SIGNATURE);
        efi->efi_gpt_Revision = LE_32(vtoc->efi_version); /* 0x02000100 */
-       efi->efi_gpt_HeaderSize = LE_32(sizeof (struct efi_gpt));
+       efi->efi_gpt_HeaderSize = LE_32(sizeof (struct efi_gpt) - LEN_EFI_PAD);
        efi->efi_gpt_Reserved1 = 0;
        efi->efi_gpt_MyLBA = LE_64(1ULL);
        efi->efi_gpt_AlternateLBA = LE_64(lba_backup_gpt_hdr);
@@ -1221,7 +1227,8 @@ efi_write(int fd, struct dk_gpt *vtoc)
            LE_32(efi_crc32((unsigned char *)efi_parts,
            vtoc->efi_nparts * (int)sizeof (struct efi_gpe)));
        efi->efi_gpt_HeaderCRC32 =
-           LE_32(efi_crc32((unsigned char *)efi, sizeof (struct efi_gpt)));
+           LE_32(efi_crc32((unsigned char *)efi,
+           LE_32(efi->efi_gpt_HeaderSize)));
 
        if (efi_ioctl(fd, DKIOCSETEFI, &dk_ioc) == -1) {
                free(dk_ioc.dki_data);
@@ -1274,7 +1281,7 @@ efi_write(int fd, struct dk_gpt *vtoc)
        efi->efi_gpt_HeaderCRC32 = 0;
        efi->efi_gpt_HeaderCRC32 =
            LE_32(efi_crc32((unsigned char *)dk_ioc.dki_data,
-           sizeof (struct efi_gpt)));
+           LE_32(efi->efi_gpt_HeaderSize)));
 
        if (efi_ioctl(fd, DKIOCSETEFI, &dk_ioc) == -1) {
                if (efi_debug) {