summaryrefslogtreecommitdiffstats
path: root/libparted/labels/gpt.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--libparted/labels/gpt.c1941
1 files changed, 1941 insertions, 0 deletions
diff --git a/libparted/labels/gpt.c b/libparted/labels/gpt.c
new file mode 100644
index 0000000..780fb70
--- /dev/null
+++ b/libparted/labels/gpt.c
@@ -0,0 +1,1941 @@
+/*
+ libparted - a library for manipulating disk partitions
+
+ original version by Matt Domsch <Matt_Domsch@dell.com>
+ Disclaimed into the Public Domain
+
+ Portions Copyright (C) 2001-2003, 2005-2012 Free Software Foundation, Inc.
+
+ EFI GUID Partition Table handling
+ Per Intel EFI Specification v1.02
+ http://developer.intel.com/technology/efi/efi.htm
+
+ 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
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <config.h>
+
+#include <parted/parted.h>
+#include <parted/debug.h>
+#include <parted/endian.h>
+#include <parted/crc32.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <uuid/uuid.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <iconv.h>
+#include <langinfo.h>
+#include "xalloc.h"
+#include "xalloc-oversized.h"
+#include "verify.h"
+
+#include "pt-tools.h"
+
+#if ENABLE_NLS
+# include <libintl.h>
+# define _(String) gettext (String)
+#else
+# define _(String) (String)
+#endif /* ENABLE_NLS */
+
+#define EFI_PMBR_OSTYPE_EFI 0xEE
+#define MSDOS_MBR_SIGNATURE 0xaa55
+
+#define GPT_HEADER_SIGNATURE 0x5452415020494645LL
+
+/* NOTE: the document that describes revision 1.00 is labelled "version 1.02",
+ * so some implementors got confused...
+ */
+#define GPT_HEADER_REVISION_V1_02 0x00010200
+#define GPT_HEADER_REVISION_V1_00 0x00010000
+#define GPT_HEADER_REVISION_V0_99 0x00009900
+
+typedef uint16_t efi_char16_t; /* UNICODE character */
+typedef struct _GuidPartitionTableHeader_t GuidPartitionTableHeader_t;
+typedef struct _GuidPartitionEntryAttributes_t GuidPartitionEntryAttributes_t;
+typedef struct _GuidPartitionEntry_t GuidPartitionEntry_t;
+typedef struct _PartitionRecord_t PartitionRecord_t;
+typedef struct _LegacyMBR_t LegacyMBR_t;
+typedef struct _GPTDiskData GPTDiskData;
+
+
+typedef struct
+{
+ uint32_t time_low;
+ uint16_t time_mid;
+ uint16_t time_hi_and_version;
+ uint8_t clock_seq_hi_and_reserved;
+ uint8_t clock_seq_low;
+ uint8_t node[6];
+} /* __attribute__ ((packed)) */ efi_guid_t;
+/* commented out "__attribute__ ((packed))" to work around gcc bug (fixed
+ * in gcc3.1): __attribute__ ((packed)) breaks addressing on initialized
+ * data. It turns out we don't need it in this case, so it doesn't break
+ * anything :)
+ */
+
+#define UNUSED_ENTRY_GUID \
+ ((efi_guid_t) { 0x00000000, 0x0000, 0x0000, 0x00, 0x00, \
+ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }})
+#define PARTITION_SYSTEM_GUID \
+ ((efi_guid_t) { PED_CPU_TO_LE32 (0xC12A7328), PED_CPU_TO_LE16 (0xF81F), \
+ PED_CPU_TO_LE16 (0x11d2), 0xBA, 0x4B, \
+ { 0x00, 0xA0, 0xC9, 0x3E, 0xC9, 0x3B }})
+#define PARTITION_BIOS_GRUB_GUID \
+ ((efi_guid_t) { PED_CPU_TO_LE32 (0x21686148), PED_CPU_TO_LE16 (0x6449), \
+ PED_CPU_TO_LE16 (0x6E6f), 0x74, 0x4E, \
+ { 0x65, 0x65, 0x64, 0x45, 0x46, 0x49 }})
+#define LEGACY_MBR_PARTITION_GUID \
+ ((efi_guid_t) { PED_CPU_TO_LE32 (0x024DEE41), PED_CPU_TO_LE16 (0x33E7), \
+ PED_CPU_TO_LE16 (0x11d3, 0x9D, 0x69, \
+ { 0x00, 0x08, 0xC7, 0x81, 0xF3, 0x9F }})
+#define PARTITION_MSFT_RESERVED_GUID \
+ ((efi_guid_t) { PED_CPU_TO_LE32 (0xE3C9E316), PED_CPU_TO_LE16 (0x0B5C), \
+ PED_CPU_TO_LE16 (0x4DB8), 0x81, 0x7D, \
+ { 0xF9, 0x2D, 0xF0, 0x02, 0x15, 0xAE }})
+#define PARTITION_MSFT_RECOVERY \
+ ((efi_guid_t) { PED_CPU_TO_LE32 (0xDE94BBA4), PED_CPU_TO_LE16 (0x06D1), \
+ PED_CPU_TO_LE16 (0x4D40), 0xA1, 0x6A, \
+ { 0xBF, 0xD5, 0x01, 0x79, 0xD6, 0xAC }})
+#define PARTITION_BASIC_DATA_GUID \
+ ((efi_guid_t) { PED_CPU_TO_LE32 (0xEBD0A0A2), PED_CPU_TO_LE16 (0xB9E5), \
+ PED_CPU_TO_LE16 (0x4433), 0x87, 0xC0, \
+ { 0x68, 0xB6, 0xB7, 0x26, 0x99, 0xC7 }})
+#define PARTITION_RAID_GUID \
+ ((efi_guid_t) { PED_CPU_TO_LE32 (0xa19d880f), PED_CPU_TO_LE16 (0x05fc), \
+ PED_CPU_TO_LE16 (0x4d3b), 0xa0, 0x06, \
+ { 0x74, 0x3f, 0x0f, 0x84, 0x91, 0x1e }})
+#define PARTITION_SWAP_GUID \
+ ((efi_guid_t) { PED_CPU_TO_LE32 (0x0657fd6d), PED_CPU_TO_LE16 (0xa4ab), \
+ PED_CPU_TO_LE16 (0x43c4), 0x84, 0xe5, \
+ { 0x09, 0x33, 0xc8, 0x4b, 0x4f, 0x4f }})
+#define PARTITION_LINUX_DATA_GUID \
+ ((efi_guid_t) { PED_CPU_TO_LE32 (0x0FC63DAF), PED_CPU_TO_LE16 (0x8483), \
+ PED_CPU_TO_LE16 (0x4772), 0x8E, 0x79, \
+ { 0x3D, 0x69, 0xD8, 0x47, 0x7D, 0xE4 }})
+#define PARTITION_LVM_GUID \
+ ((efi_guid_t) { PED_CPU_TO_LE32 (0xe6d6d379), PED_CPU_TO_LE16 (0xf507), \
+ PED_CPU_TO_LE16 (0x44c2), 0xa2, 0x3c, \
+ { 0x23, 0x8f, 0x2a, 0x3d, 0xf9, 0x28 }})
+#define PARTITION_RESERVED_GUID \
+ ((efi_guid_t) { PED_CPU_TO_LE32 (0x8da63339), PED_CPU_TO_LE16 (0x0007), \
+ PED_CPU_TO_LE16 (0x60c0), 0xc4, 0x36, \
+ { 0x08, 0x3a, 0xc8, 0x23, 0x09, 0x08 }})
+#define PARTITION_HPSERVICE_GUID \
+ ((efi_guid_t) { PED_CPU_TO_LE32 (0xe2a1e728), PED_CPU_TO_LE16 (0x32e3), \
+ PED_CPU_TO_LE16 (0x11d6), 0xa6, 0x82, \
+ { 0x7b, 0x03, 0xa0, 0x00, 0x00, 0x00 }})
+#define PARTITION_APPLE_HFS_GUID \
+ ((efi_guid_t) { PED_CPU_TO_LE32 (0x48465300), PED_CPU_TO_LE16 (0x0000), \
+ PED_CPU_TO_LE16 (0x11AA), 0xaa, 0x11, \
+ { 0x00, 0x30, 0x65, 0x43, 0xEC, 0xAC }})
+#define PARTITION_APPLE_TV_RECOVERY_GUID \
+ ((efi_guid_t) { PED_CPU_TO_LE32 (0x5265636F), PED_CPU_TO_LE16 (0x7665), \
+ PED_CPU_TO_LE16 (0x11AA), 0xaa, 0x11, \
+ { 0x00, 0x30, 0x65, 0x43, 0xEC, 0xAC }})
+#define PARTITION_PREP_GUID \
+ ((efi_guid_t) { PED_CPU_TO_LE32 (0x9e1a2d38), PED_CPU_TO_LE16 (0xc612), \
+ PED_CPU_TO_LE16 (0x4316), 0xaa, 0x26, \
+ { 0x8b, 0x49, 0x52, 0x1e, 0x5a, 0x8b }})
+#define PARTITION_IRST_GUID \
+ ((efi_guid_t) { PED_CPU_TO_LE32 (0xD3BFE2DE), PED_CPU_TO_LE16 (0x3DAF), \
+ PED_CPU_TO_LE16 (0x11DF), 0xba, 0x40, \
+ { 0xE3, 0xA5, 0x56, 0xD8, 0x95, 0x93 }})
+#define PARTITION_CHROMEOS_KERNEL_GUID \
+ ((efi_guid_t) { PED_CPU_TO_LE32 (0xfe3a2a5d), PED_CPU_TO_LE16 (0x4f32), \
+ PED_CPU_TO_LE16 (0x41a7), 0xb7, 0x25, \
+ { 0xac, 0xcc, 0x32, 0x85, 0xa3, 0x09 }})
+#define PARTITION_BLS_BOOT_GUID \
+ ((efi_guid_t) { PED_CPU_TO_LE32 (0xbc13c2ff), PED_CPU_TO_LE16 (0x59e6), \
+ PED_CPU_TO_LE16 (0x4262), 0xa3, 0x52, \
+ { 0xb2, 0x75, 0xfd, 0x6f, 0x71, 0x72 }})
+#define PARTITION_LINUX_HOME_GUID \
+ ((efi_guid_t) { PED_CPU_TO_LE32 (0x933ac7e1), PED_CPU_TO_LE16 (0x2eb4), \
+ PED_CPU_TO_LE16 (0x4f13), 0xb8, 0x44, \
+ { 0x0e, 0x14, 0xe2, 0xae, 0xf9, 0x15 }})
+
+struct flag_uuid_mapping_t
+{
+ enum _PedPartitionFlag flag;
+ efi_guid_t type_uuid;
+};
+
+static const struct flag_uuid_mapping_t flag_uuid_mapping[] =
+{
+ { PED_PARTITION_APPLE_TV_RECOVERY, PARTITION_APPLE_TV_RECOVERY_GUID },
+ { PED_PARTITION_BIOS_GRUB, PARTITION_BIOS_GRUB_GUID },
+ { PED_PARTITION_BLS_BOOT, PARTITION_BLS_BOOT_GUID },
+ { PED_PARTITION_BOOT, PARTITION_SYSTEM_GUID },
+ { PED_PARTITION_CHROMEOS_KERNEL, PARTITION_CHROMEOS_KERNEL_GUID },
+ { PED_PARTITION_DIAG, PARTITION_MSFT_RECOVERY },
+ { PED_PARTITION_ESP, PARTITION_SYSTEM_GUID },
+ { PED_PARTITION_HPSERVICE, PARTITION_HPSERVICE_GUID },
+ { PED_PARTITION_IRST, PARTITION_IRST_GUID },
+ { PED_PARTITION_LINUX_HOME, PARTITION_LINUX_HOME_GUID },
+ { PED_PARTITION_LVM, PARTITION_LVM_GUID },
+ { PED_PARTITION_MSFT_DATA, PARTITION_BASIC_DATA_GUID },
+ { PED_PARTITION_MSFT_RESERVED, PARTITION_MSFT_RESERVED_GUID },
+ { PED_PARTITION_PREP, PARTITION_PREP_GUID },
+ { PED_PARTITION_RAID, PARTITION_RAID_GUID },
+ { PED_PARTITION_SWAP, PARTITION_SWAP_GUID },
+};
+
+static const efi_guid_t skip_set_system_guids[] =
+{
+ PARTITION_LVM_GUID,
+ PARTITION_SWAP_GUID,
+ PARTITION_RAID_GUID,
+ PARTITION_PREP_GUID,
+ PARTITION_SYSTEM_GUID,
+ PARTITION_BIOS_GRUB_GUID,
+ PARTITION_HPSERVICE_GUID,
+ PARTITION_MSFT_RESERVED_GUID,
+ PARTITION_BASIC_DATA_GUID,
+ PARTITION_MSFT_RECOVERY,
+ PARTITION_APPLE_TV_RECOVERY_GUID,
+ PARTITION_IRST_GUID,
+ PARTITION_CHROMEOS_KERNEL_GUID,
+ PARTITION_BLS_BOOT_GUID,
+};
+
+static const struct flag_uuid_mapping_t* _GL_ATTRIBUTE_CONST
+gpt_find_flag_uuid_mapping (PedPartitionFlag flag)
+{
+ int n = sizeof(flag_uuid_mapping) / sizeof(flag_uuid_mapping[0]);
+
+ for (int i = 0; i < n; ++i)
+ if (flag_uuid_mapping[i].flag == flag)
+ return &flag_uuid_mapping[i];
+
+ return NULL;
+}
+
+struct __attribute__ ((packed)) _GuidPartitionTableHeader_t
+{
+ uint64_t Signature;
+ uint32_t Revision;
+ uint32_t HeaderSize;
+ uint32_t HeaderCRC32;
+ uint32_t Reserved1;
+ uint64_t MyLBA;
+ uint64_t AlternateLBA;
+ uint64_t FirstUsableLBA;
+ uint64_t LastUsableLBA;
+ efi_guid_t DiskGUID;
+ uint64_t PartitionEntryLBA;
+ uint32_t NumberOfPartitionEntries;
+ uint32_t SizeOfPartitionEntry;
+ uint32_t PartitionEntryArrayCRC32;
+ uint8_t *Reserved2;
+};
+
+struct __attribute__ ((packed)) _GuidPartitionEntryAttributes_t
+{
+#ifdef __GNUC__ /* XXX narrow this down to !TinyCC */
+ uint64_t RequiredToFunction:1;
+ uint64_t NoBlockIOProtocol:1;
+ uint64_t LegacyBIOSBootable:1;
+ uint64_t Reserved:45;
+ uint64_t GuidSpecific:15;
+ uint64_t NoAutomount:1;
+#else
+# warning "Using crippled partition entry type"
+ uint32_t RequiredToFunction:1;
+ uint32_t NoBlockIOProtocol:1;
+ uint32_t LegacyBIOSBootable:1;
+ uint32_t Reserved:30;
+ uint32_t LOST:5;
+ uint32_t GuidSpecific:15;
+ uint32_t NoAutomount:1;
+#endif
+};
+
+struct __attribute__ ((packed)) _GuidPartitionEntry_t
+{
+ efi_guid_t PartitionTypeGuid;
+ efi_guid_t UniquePartitionGuid;
+ uint64_t StartingLBA;
+ uint64_t EndingLBA;
+ GuidPartitionEntryAttributes_t Attributes;
+ efi_char16_t PartitionName[36];
+};
+
+#define GPT_PMBR_LBA 0
+#define GPT_PMBR_SECTORS 1
+#define GPT_PRIMARY_HEADER_LBA 1
+#define GPT_HEADER_SECTORS 1
+#define GPT_PRIMARY_PART_TABLE_LBA 2
+
+/*
+ These values are only defaults. The actual on-disk structures
+ may define different sizes, so use those unless creating a new GPT disk!
+*/
+
+#define GPT_DEFAULT_PARTITION_ENTRY_ARRAY_SIZE 16384
+
+/* Number of actual partition entries should be calculated as: */
+#define GPT_DEFAULT_PARTITION_ENTRIES \
+ (GPT_DEFAULT_PARTITION_ENTRY_ARRAY_SIZE / \
+ sizeof(GuidPartitionEntry_t))
+
+struct __attribute__ ((packed)) _PartitionRecord_t
+{
+ /* Not used by EFI firmware. Set to 0x80 to indicate that this
+ is the bootable legacy partition. */
+ uint8_t BootIndicator;
+
+ /* Start of partition in CHS address, not used by EFI firmware. */
+ uint8_t StartHead;
+
+ /* Start of partition in CHS address, not used by EFI firmware. */
+ uint8_t StartSector;
+
+ /* Start of partition in CHS address, not used by EFI firmware. */
+ uint8_t StartTrack;
+
+ /* OS type. A value of 0xEF defines an EFI system partition.
+ Other values are reserved for legacy operating systems, and
+ allocated independently of the EFI specification. */
+ uint8_t OSType;
+
+ /* End of partition in CHS address, not used by EFI firmware. */
+ uint8_t EndHead;
+
+ /* End of partition in CHS address, not used by EFI firmware. */
+ uint8_t EndSector;
+
+ /* End of partition in CHS address, not used by EFI firmware. */
+ uint8_t EndTrack;
+
+ /* Starting LBA address of the partition on the disk. Used by
+ EFI firmware to define the start of the partition. */
+ uint32_t StartingLBA;
+
+ /* Size of partition in LBA. Used by EFI firmware to determine
+ the size of the partition. */
+ uint32_t SizeInLBA;
+};
+
+/* Protected Master Boot Record & Legacy MBR share same structure */
+/* Needs to be packed because the u16s force misalignment. */
+struct __attribute__ ((packed)) _LegacyMBR_t
+{
+ uint8_t BootCode[440];
+ uint32_t UniqueMBRSignature;
+ uint16_t Unknown;
+ PartitionRecord_t PartitionRecord[4];
+ uint16_t Signature;
+};
+
+/* uses libparted's disk_specific field in PedDisk, to store our info */
+struct __attribute__ ((packed, aligned(8))) _GPTDiskData
+{
+ PedGeometry data_area;
+ int entry_count;
+ efi_guid_t uuid;
+ int pmbr_boot;
+ PedSector AlternateLBA;
+};
+
+/* uses libparted's disk_specific field in PedPartition, to store our info */
+typedef struct _GPTPartitionData
+{
+ efi_guid_t type;
+ efi_guid_t uuid;
+ efi_char16_t name[37];
+ char *translated_name;
+ GuidPartitionEntryAttributes_t attributes;
+} GPTPartitionData;
+
+static PedDiskType gpt_disk_type;
+
+static inline uint32_t
+pth_get_size (const PedDevice *dev)
+{
+ return GPT_HEADER_SECTORS * dev->sector_size;
+}
+
+static inline uint32_t
+pth_get_size_static (const PedDevice *dev)
+{
+ return sizeof (GuidPartitionTableHeader_t) - sizeof (uint8_t *);
+}
+
+static inline uint32_t
+pth_get_size_rsv2 (const PedDevice *dev)
+{
+ return pth_get_size (dev) - pth_get_size_static (dev);
+}
+
+static GuidPartitionTableHeader_t *
+pth_new (const PedDevice *dev)
+{
+ GuidPartitionTableHeader_t *pth =
+ ped_malloc (sizeof (GuidPartitionTableHeader_t) + sizeof (uint8_t));
+
+ pth->Reserved2 = ped_malloc (pth_get_size_rsv2 (dev));
+
+ return pth;
+}
+
+static GuidPartitionTableHeader_t *
+pth_new_zeroed (const PedDevice *dev)
+{
+ GuidPartitionTableHeader_t *pth = pth_new (dev);
+
+ memset (pth, 0, pth_get_size_static (dev));
+ memset (pth->Reserved2, 0, pth_get_size_rsv2 (dev));
+
+ return (pth);
+}
+
+static GuidPartitionTableHeader_t *
+pth_new_from_raw (const PedDevice *dev, const uint8_t *pth_raw)
+{
+ GuidPartitionTableHeader_t *pth = pth_new (dev);
+
+ PED_ASSERT (pth_raw != NULL);
+
+ memcpy (pth, pth_raw, pth_get_size_static (dev));
+ memcpy (pth->Reserved2, pth_raw + pth_get_size_static (dev),
+ pth_get_size_rsv2 (dev));
+
+ return pth;
+}
+
+static void
+pth_free (GuidPartitionTableHeader_t *pth)
+{
+ if (pth == NULL)
+ return;
+ PED_ASSERT (pth->Reserved2 != NULL);
+
+ free (pth->Reserved2);
+ free (pth);
+}
+
+static uint8_t *
+pth_get_raw (const PedDevice *dev, const GuidPartitionTableHeader_t *pth)
+{
+ PED_ASSERT (pth != NULL);
+ PED_ASSERT (pth->Reserved2 != NULL);
+
+ int size_static = pth_get_size_static (dev);
+ uint8_t *pth_raw = ped_malloc (pth_get_size (dev));
+ if (pth_raw == NULL)
+ return NULL;
+
+ memcpy (pth_raw, pth, size_static);
+ memcpy (pth_raw + size_static, pth->Reserved2, pth_get_size_rsv2 (dev));
+
+ return pth_raw;
+}
+
+/**
+ * swap_uuid_and_efi_guid() - converts between uuid formats
+ * @uuid - uuid_t in either format (converts it to the other)
+ *
+ * There are two different representations for Globally Unique Identifiers
+ * (GUIDs or UUIDs).
+ *
+ * The RFC specifies a UUID as a string of 16 bytes, essentially
+ * a big-endian array of char.
+ * Intel, in their EFI Specification, references the same RFC, but
+ * then defines a GUID as a structure of little-endian fields.
+ * Coincidentally, both structures have the same format when unparsed.
+ *
+ * When read from disk, EFI GUIDs are in struct of little endian format,
+ * and need to be converted to be treated as uuid_t in memory.
+ *
+ * When writing to disk, uuid_ts need to be converted into EFI GUIDs.
+ *
+ * Blame Intel.
+ */
+static void
+swap_uuid_and_efi_guid (efi_guid_t *guid)
+{
+ PED_ASSERT (guid != NULL);
+ guid->time_low = PED_SWAP32 (guid->time_low);
+ guid->time_mid = PED_SWAP16 (guid->time_mid);
+ guid->time_hi_and_version = PED_SWAP16 (guid->time_hi_and_version);
+}
+
+/* returns the EFI-style CRC32 value for buf
+ * This function uses the crc32 function by Gary S. Brown,
+ * but seeds the function with ~0, and xor's with ~0 at the end.
+ */
+static inline uint32_t
+efi_crc32 (const void *buf, unsigned long len)
+{
+ return (__efi_crc32 (buf, len, ~0L) ^ ~0L);
+}
+
+/* Compute the crc32 checksum of the partition table header
+ and store it in *CRC32. Return 0 upon success. Return 1
+ upon failure to allocate space. */
+static int
+pth_crc32 (const PedDevice *dev, const GuidPartitionTableHeader_t *pth,
+ uint32_t *crc32)
+{
+ PED_ASSERT (dev != NULL);
+ PED_ASSERT (pth != NULL);
+
+ uint8_t *pth_raw = pth_get_raw (dev, pth);
+ if (pth_raw == NULL)
+ return 1;
+
+ *crc32 = efi_crc32 (pth_raw, PED_LE32_TO_CPU (pth->HeaderSize));
+ free (pth_raw);
+
+ return 0;
+}
+
+static inline int
+guid_cmp (efi_guid_t left, efi_guid_t right)
+{
+ return memcmp (&left, &right, sizeof (efi_guid_t));
+}
+
+/* checks if 'mbr' is a protective MBR partition table */
+static inline int _GL_ATTRIBUTE_PURE
+_pmbr_is_valid (const LegacyMBR_t *mbr)
+{
+ int i;
+
+ PED_ASSERT (mbr != NULL);
+
+ if (mbr->Signature != PED_CPU_TO_LE16 (MSDOS_MBR_SIGNATURE))
+ return 0;
+ for (i = 0; i < 4; i++)
+ {
+ if (mbr->PartitionRecord[i].OSType == EFI_PMBR_OSTYPE_EFI)
+ return 1;
+ }
+ return 0;
+}
+
+static int
+gpt_probe (const PedDevice *dev)
+{
+ int gpt_sig_found = 0;
+
+ PED_ASSERT (dev != NULL);
+
+ if (dev->length <= 1)
+ return 0;
+
+ void *label;
+ if (!ptt_read_sector (dev, 0, &label))
+ return 0;
+
+ if (!_pmbr_is_valid (label))
+ {
+ free (label);
+ return 0;
+ }
+ free (label);
+
+ void *pth_raw = ped_malloc (pth_get_size (dev));
+ if (ped_device_read (dev, pth_raw, 1, GPT_HEADER_SECTORS)
+ || ped_device_read (dev, pth_raw, dev->length - 1, GPT_HEADER_SECTORS))
+ {
+ GuidPartitionTableHeader_t *gpt = pth_new_from_raw (dev, pth_raw);
+ if (gpt->Signature == PED_CPU_TO_LE64 (GPT_HEADER_SIGNATURE))
+ gpt_sig_found = 1;
+ pth_free (gpt);
+ }
+ free (pth_raw);
+
+ return gpt_sig_found;
+}
+
+static PedDisk *
+gpt_alloc (const PedDevice *dev)
+{
+ PedDisk *disk;
+ GPTDiskData *gpt_disk_data;
+ PedSector data_start, data_end;
+
+ disk = _ped_disk_alloc ((PedDevice *) dev, &gpt_disk_type);
+ if (!disk)
+ goto error;
+
+ data_start = 2 + GPT_DEFAULT_PARTITION_ENTRY_ARRAY_SIZE / dev->sector_size;
+ data_end = dev->length - 2
+ - GPT_DEFAULT_PARTITION_ENTRY_ARRAY_SIZE / dev->sector_size;
+
+ /* If the device is too small to accommodate GPT headers and one data
+ sector, reject it. */
+ if (data_end < data_start)
+ {
+ ped_exception_throw (PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_OK,
+ _("device is too small for GPT"));
+ goto error_free_disk;
+ }
+
+ disk->disk_specific = gpt_disk_data = ped_malloc (sizeof (GPTDiskData));
+ if (!disk->disk_specific)
+ goto error_free_disk;
+
+ gpt_disk_data->AlternateLBA = dev->length - 1;
+ ped_geometry_init (&gpt_disk_data->data_area, dev, data_start,
+ data_end - data_start + 1);
+ gpt_disk_data->entry_count = GPT_DEFAULT_PARTITION_ENTRIES;
+ uuid_generate ((unsigned char *) &gpt_disk_data->uuid);
+ swap_uuid_and_efi_guid (&gpt_disk_data->uuid);
+ gpt_disk_data->pmbr_boot = 0;
+ return disk;
+
+error_free_disk:
+ free (disk);
+error:
+ return NULL;
+}
+
+static PedDisk *
+gpt_duplicate (const PedDisk *disk)
+{
+ PedDisk *new_disk;
+ GPTDiskData *new_disk_data;
+ GPTDiskData *old_disk_data;
+
+ new_disk = ped_disk_new_fresh (disk->dev, &gpt_disk_type);
+ if (!new_disk)
+ return NULL;
+
+ old_disk_data = disk->disk_specific;
+ new_disk_data = new_disk->disk_specific;
+
+ ped_geometry_init (&new_disk_data->data_area, disk->dev,
+ old_disk_data->data_area.start,
+ old_disk_data->data_area.length);
+ new_disk_data->entry_count = old_disk_data->entry_count;
+ new_disk_data->uuid = old_disk_data->uuid;
+ new_disk_data->pmbr_boot = old_disk_data->pmbr_boot;
+ return new_disk;
+}
+
+static void
+gpt_free (PedDisk *disk)
+{
+ ped_disk_delete_all (disk);
+ free (disk->disk_specific);
+ _ped_disk_free (disk);
+}
+
+/* Given GUID Partition table header, GPT, read its partition array
+ entries from DISK into malloc'd storage. Set *PTES_BYTES to the
+ number of bytes required. Upon success, return a pointer to the
+ resulting buffer. Otherwise, set errno and return NULL. */
+static void *
+gpt_read_PE_array (PedDisk const *disk, GuidPartitionTableHeader_t const *gpt,
+ size_t *ptes_bytes)
+{
+ uint32_t p_ent_size = PED_LE32_TO_CPU (gpt->SizeOfPartitionEntry);
+ *ptes_bytes = p_ent_size * PED_LE32_TO_CPU(gpt->NumberOfPartitionEntries);
+ size_t ptes_sectors = ped_div_round_up (*ptes_bytes,
+ disk->dev->sector_size);
+
+ if (xalloc_oversized (ptes_sectors, disk->dev->sector_size))
+ {
+ errno = ENOMEM;
+ return NULL;
+ }
+ void *ptes = ped_malloc (ptes_sectors * disk->dev->sector_size);
+ if (ptes == NULL)
+ return NULL;
+
+ if (!ped_device_read (disk->dev, ptes,
+ PED_LE64_TO_CPU (gpt->PartitionEntryLBA), ptes_sectors))
+ {
+ int saved_errno = errno;
+ free (ptes);
+ errno = saved_errno;
+ return NULL;
+ }
+
+ return ptes;
+}
+
+static int
+check_PE_array_CRC (PedDisk const *disk,
+ GuidPartitionTableHeader_t const *gpt, bool *valid)
+{
+ size_t ptes_bytes;
+ void *ptes = gpt_read_PE_array (disk, gpt, &ptes_bytes);
+ if (ptes == NULL)
+ return 1;
+
+ uint32_t ptes_crc = efi_crc32 (ptes, ptes_bytes);
+ *valid = (ptes_crc == PED_LE32_TO_CPU (gpt->PartitionEntryArrayCRC32));
+ free (ptes);
+ return 0;
+}
+
+static int
+_header_is_valid (PedDisk const *disk, GuidPartitionTableHeader_t *gpt,
+ PedSector my_lba)
+{
+ uint32_t crc, origcrc;
+ PedDevice const *dev = disk->dev;
+
+ if (PED_LE64_TO_CPU (gpt->Signature) != GPT_HEADER_SIGNATURE)
+ return 0;
+ /*
+ * "While the GUID Partition Table Header's size may increase
+ * in the future it cannot span more than one block on the
+ * device." EFI Specification, version 1.10, 11.2.2.1
+ */
+ if (PED_LE32_TO_CPU (gpt->HeaderSize) < pth_get_size_static (dev)
+ || PED_LE32_TO_CPU (gpt->HeaderSize) > dev->sector_size)
+ return 0;
+
+ /* The SizeOfPartitionEntry must be a multiple of 8 and
+ no smaller than the size of the PartitionEntry structure.
+ We also require that it be no larger than 1/16th of UINT32_MAX,
+ as an additional sanity check. */
+ uint32_t pe_size = PED_LE32_TO_CPU (gpt->SizeOfPartitionEntry);
+ if (pe_size % 8 != 0
+ || ! (sizeof (GuidPartitionEntry_t) <= pe_size
+ && pe_size <= (UINT32_MAX >> 4)))
+ return 0;
+
+ if (PED_LE64_TO_CPU (gpt->MyLBA) != my_lba)
+ return 0;
+
+ PedSector alt_lba = PED_LE64_TO_CPU (gpt->AlternateLBA);
+ /* The backup table's AlternateLBA must be 1. */
+ if (my_lba != 1 && alt_lba != 1)
+ return 0;
+
+ /* The alt_lba must never be the same as my_lba. */
+ if (alt_lba == my_lba)
+ return 0;
+
+ bool crc_match;
+ if (check_PE_array_CRC (disk, gpt, &crc_match) != 0 || !crc_match)
+ return 0;
+
+ PedSector first_usable = PED_LE64_TO_CPU (gpt->FirstUsableLBA);
+ if (first_usable < 3)
+ return 0;
+
+ PedSector last_usable = PED_LE64_TO_CPU (gpt->LastUsableLBA);
+ if (last_usable < first_usable)
+ return 0;
+
+ origcrc = gpt->HeaderCRC32;
+ gpt->HeaderCRC32 = 0;
+ if (pth_crc32 (dev, gpt, &crc) != 0)
+ return 0;
+ gpt->HeaderCRC32 = origcrc;
+
+ return crc == PED_LE32_TO_CPU (origcrc);
+}
+
+/* Return the number of sectors that should be used by the
+ * partition entry table.
+ */
+static PedSector
+_ptes_sectors(PedDisk const *disk, GuidPartitionTableHeader_t const *gpt)
+{
+ size_t ptes_bytes = PED_LE32_TO_CPU (gpt->SizeOfPartitionEntry) *
+ PED_LE32_TO_CPU (gpt->NumberOfPartitionEntries);
+ /* Minimum amount of space reserved is 128 128 byte entries */
+ if (ptes_bytes < 128*128)
+ ptes_bytes = 128*128;
+ return ped_div_round_up (ptes_bytes, disk->dev->sector_size);
+}
+
+/* Return the header's idea of the last sector of the disk
+ * based on LastUsableLBA and the Partition Entry table.
+ */
+static PedSector
+_hdr_disk_end(PedDisk const *disk, GuidPartitionTableHeader_t const *gpt)
+{
+ return PED_LE64_TO_CPU (gpt->LastUsableLBA) + 1 + _ptes_sectors(disk, gpt);
+}
+
+static int
+_parse_header (PedDisk *disk, const GuidPartitionTableHeader_t *gpt,
+ int *update_needed)
+{
+ GPTDiskData *gpt_disk_data = disk->disk_specific;
+ PedSector first_usable;
+ PedSector last_usable;
+ PedSector last_usable_if_grown;
+
+#ifndef DISCOVER_ONLY
+ if (PED_LE32_TO_CPU (gpt->Revision) > GPT_HEADER_REVISION_V1_02)
+ {
+ if (ped_exception_throw
+ (PED_EXCEPTION_WARNING,
+ PED_EXCEPTION_IGNORE_CANCEL,
+ _("The format of the GPT partition table is version "
+ "%x, which is newer than what Parted can "
+ "recognise. Please report this!"),
+ PED_LE32_TO_CPU (gpt->Revision)) != PED_EXCEPTION_IGNORE)
+ return 0;
+ }
+#endif
+
+ first_usable = PED_LE64_TO_CPU (gpt->FirstUsableLBA);
+ last_usable = PED_LE64_TO_CPU (gpt->LastUsableLBA);
+
+ /* Need to check whether the volume has grown, the LastUsableLBA is
+ normally set to disk->dev->length - 2 - ptes_size (at least for parted
+ created volumes), where ptes_size is the number of entries *
+ size of each entry / sector size or 16k / sector size, whatever the greater.
+ If the volume has grown, offer the user the chance to use the new
+ space or continue with the current usable area. Only ask once per
+ parted invocation. */
+
+ last_usable_if_grown = disk->dev->length - 2 - _ptes_sectors(disk, gpt);
+
+ if (last_usable <= first_usable
+ || disk->dev->length < last_usable)
+ return 0;
+
+ if (last_usable_if_grown <= first_usable
+ || disk->dev->length < last_usable_if_grown)
+ return 0;
+
+ if (last_usable < last_usable_if_grown)
+ {
+ PedExceptionOption q;
+
+ q = ped_exception_throw
+ (PED_EXCEPTION_WARNING,
+ PED_EXCEPTION_FIX | PED_EXCEPTION_IGNORE,
+ _("Not all of the space available to %s appears "
+ "to be used, you can fix the GPT to use all of the "
+ "space (an extra %llu blocks) or continue with the "
+ "current setting? "), disk->dev->path,
+ (uint64_t) (last_usable_if_grown - last_usable));
+
+ if (q == PED_EXCEPTION_FIX)
+ {
+ last_usable = last_usable_if_grown;
+ /* clear the old backup gpt header */
+ ptt_clear_sectors (disk->dev,
+ gpt_disk_data->AlternateLBA, 1);
+ gpt_disk_data->AlternateLBA = disk->dev->length - 1;
+ *update_needed = 1;
+ }
+ }
+
+ ped_geometry_init (&gpt_disk_data->data_area, disk->dev,
+ first_usable, last_usable - first_usable + 1);
+
+ gpt_disk_data->entry_count
+ = PED_LE32_TO_CPU (gpt->NumberOfPartitionEntries);
+ PED_ASSERT (gpt_disk_data->entry_count > 0);
+ PED_ASSERT (gpt_disk_data->entry_count <= 8192);
+
+ gpt_disk_data->uuid = gpt->DiskGUID;
+
+ return 1;
+}
+
+static PedPartition *
+_parse_part_entry (PedDisk *disk, GuidPartitionEntry_t *pte)
+{
+ PedPartition *part;
+ GPTPartitionData *gpt_part_data;
+ unsigned int i;
+
+ part = ped_partition_new (disk, PED_PARTITION_NORMAL, NULL,
+ PED_LE64_TO_CPU (pte->StartingLBA),
+ PED_LE64_TO_CPU (pte->EndingLBA));
+ if (!part)
+ return NULL;
+
+ gpt_part_data = part->disk_specific;
+ gpt_part_data->type = pte->PartitionTypeGuid;
+ gpt_part_data->uuid = pte->UniquePartitionGuid;
+ for (i = 0; i < 36; i++)
+ gpt_part_data->name[i] = (efi_char16_t) pte->PartitionName[i];
+ gpt_part_data->name[i] = 0;
+ gpt_part_data->translated_name = 0;
+ gpt_part_data->attributes = pte->Attributes;
+
+ return part;
+}
+
+/* Read the primary GPT at sector 1 of DEV.
+ Verify its CRC and that of its partition entry array.
+ If they are valid, read the backup GPT specified by AlternateLBA.
+ If not, read the backup GPT in the last sector of the disk.
+ Return 1 if any read fails.
+ Upon successful verification of the primary GPT, set *PRIMARY_GPT, else NULL.
+ Upon successful verification of the backup GPT, set *BACKUP_GPT, else NULL.
+ If we've set *BACKUP_GPT to non-NULL, set *BACKUP_SECTOR_NUM_P to the sector
+ number in which it was found. */
+static int
+gpt_read_headers (PedDisk const *disk,
+ GuidPartitionTableHeader_t **primary_gpt,
+ GuidPartitionTableHeader_t **backup_gpt,
+ PedSector *backup_sector_num_p)
+{
+ *primary_gpt = NULL;
+ *backup_gpt = NULL;
+ PedDevice const *dev = disk->dev;
+ GPTDiskData *gpt_disk_data = disk->disk_specific;
+ LegacyMBR_t *mbr;
+
+ if (!ptt_read_sector (dev, 0, (void *)&mbr))
+ return 1;
+
+ if (mbr->PartitionRecord[0].BootIndicator == 0x80)
+ gpt_disk_data->pmbr_boot = 1;
+ free (mbr);
+
+ void *s1;
+ if (!ptt_read_sector (dev, 1, &s1))
+ return 1;
+
+ GuidPartitionTableHeader_t *t = pth_new_from_raw (dev, s1);
+ free (s1);
+ if (t == NULL)
+ return 1;
+ GuidPartitionTableHeader_t *pri = t;
+
+ bool valid_primary = _header_is_valid (disk, pri, 1);
+ if (valid_primary)
+ *primary_gpt = pri;
+ else
+ pth_free (pri);
+
+ gpt_disk_data->AlternateLBA =
+ (valid_primary
+ ? PED_LE64_TO_CPU (pri->AlternateLBA)
+ : dev->length - 1);
+
+ void *s_bak;
+ if (!ptt_read_sector (dev, gpt_disk_data->AlternateLBA ,&s_bak))
+ return 1;
+ t = pth_new_from_raw (dev, s_bak);
+ free (s_bak);
+ if (t == NULL)
+ return 1;
+
+ GuidPartitionTableHeader_t *bak = t;
+ if (_header_is_valid (disk, bak, gpt_disk_data->AlternateLBA))
+ {
+ *backup_gpt = bak;
+ *backup_sector_num_p = gpt_disk_data->AlternateLBA;
+ }
+ else
+ pth_free (bak);
+
+ return 0;
+}
+
+/************************************************************
+ * Intel is changing the EFI Spec. (after v1.02) to say that a
+ * disk is considered to have a GPT label only if the GPT
+ * structures are correct, and the MBR is actually a Protective
+ * MBR (has one 0xEE type partition).
+ * Problem occurs when a GPT-partitioned disk is then
+ * edited with a legacy (non-GPT-aware) application, such as
+ * fdisk (which doesn't generally erase the PGPT or AGPT).
+ * How should such a disk get handled? As a GPT disk (throwing
+ * away the fdisk changes), or as an MSDOS disk (throwing away
+ * the GPT information). Previously, I've taken the GPT-is-right,
+ * MBR is wrong, approach, to stay consistent with the EFI Spec.
+ * Intel disagrees, saying the disk should then be treated
+ * as having a msdos label, not a GPT label. If this is true,
+ * then what's the point of having an AGPT, since if the PGPT
+ * is screwed up, likely the PMBR is too, and the PMBR becomes
+ * a single point of failure.
+ * So, in the Linux kernel, I'm going to test for PMBR, and
+ * warn if it's not there, and treat the disk as MSDOS, with a note
+ * for users to use Parted to "fix up" their disk if they
+ * really want it to be considered GPT.
+ ************************************************************/
+static int
+gpt_read (PedDisk *disk)
+{
+ GPTDiskData *gpt_disk_data = disk->disk_specific;
+ int i;
+#ifndef DISCOVER_ONLY
+ int write_back = 0;
+#endif
+
+ ped_disk_delete_all (disk);
+
+ /* motivation: let the user decide about the pmbr... during
+ ped_disk_probe(), they probably didn't get a choice... */
+ if (!gpt_probe (disk->dev))
+ goto error;
+
+ GuidPartitionTableHeader_t *gpt = NULL;
+ GuidPartitionTableHeader_t *primary_gpt;
+ GuidPartitionTableHeader_t *backup_gpt;
+ PedSector backup_sector_num;
+ int read_failure = gpt_read_headers (disk, &primary_gpt, &backup_gpt,
+ &backup_sector_num);
+ if (read_failure)
+ {
+ /* This includes the case in which there used to be a GPT partition
+ table here, with an alternate LBA that extended beyond the current
+ end-of-device. It's treated as a non-match. */
+
+ /* Another possibility:
+ The primary header is ok, but backup is corrupt.
+ In the UEFI spec, this means the primary GUID table
+ is officially invalid. */
+ pth_free (backup_gpt);
+ pth_free (primary_gpt);
+ return 0;
+ }
+
+ if (primary_gpt && backup_gpt)
+ {
+ /* Both are valid. */
+#ifndef DISCOVER_ONLY
+ /* The backup header must be at the end of the disk, or at what the primary
+ * header thinks is the end of the disk.
+ */
+ gpt_disk_data->AlternateLBA = PED_LE64_TO_CPU (primary_gpt->AlternateLBA);
+ PedSector pri_disk_end = _hdr_disk_end(disk, primary_gpt);
+
+ if (gpt_disk_data->AlternateLBA != disk->dev->length -1 &&
+ gpt_disk_data->AlternateLBA != pri_disk_end)
+ {
+ if (ped_exception_throw
+ (PED_EXCEPTION_ERROR,
+ (PED_EXCEPTION_FIX | PED_EXCEPTION_IGNORE),
+ _("The backup GPT table is not at the end of the disk, as it "
+ "should be. Fix, by moving the backup to the end "
+ "(and removing the old backup)?")) == PED_EXCEPTION_FIX)
+ {
+ ptt_clear_sectors (disk->dev,
+ PED_LE64_TO_CPU (primary_gpt->AlternateLBA), 1);
+ gpt_disk_data->AlternateLBA = disk->dev->length -1;
+ write_back = 1;
+ }
+ }
+#endif /* !DISCOVER_ONLY */
+ pth_free (backup_gpt);
+ gpt = primary_gpt;
+ }
+ else if (!primary_gpt && !backup_gpt)
+ {
+ /* Both are corrupt. */
+ ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
+ _("Both the primary and backup GPT tables "
+ "are corrupt. Try making a fresh table, "
+ "and using Parted's rescue feature to "
+ "recover partitions."));
+ goto error;
+ }
+ else if (primary_gpt && !backup_gpt)
+ {
+ /* The primary header is ok, but backup is corrupt. */
+ if (ped_exception_throw
+ (PED_EXCEPTION_ERROR, PED_EXCEPTION_OK_CANCEL,
+ _("The backup GPT table is corrupt, but the "
+ "primary appears OK, so that will be used."))
+ == PED_EXCEPTION_CANCEL)
+ goto error_free_gpt;
+
+ gpt = primary_gpt;
+ }
+ else /* !primary_gpt && backup_gpt */
+ {
+ /* primary GPT corrupt, backup is ok. */
+ if (ped_exception_throw
+ (PED_EXCEPTION_ERROR, PED_EXCEPTION_OK_CANCEL,
+ _("The primary GPT table is corrupt, but the "
+ "backup appears OK, so that will be used."))
+ == PED_EXCEPTION_CANCEL)
+ goto error_free_gpt;
+
+ gpt = backup_gpt;
+ }
+ backup_gpt = NULL;
+ primary_gpt = NULL;
+
+ if (!_parse_header (disk, gpt, &write_back))
+ goto error_free_gpt;
+
+ size_t ptes_bytes;
+ void *ptes = gpt_read_PE_array (disk, gpt, &ptes_bytes);
+ if (ptes == NULL)
+ goto error_free_gpt;
+
+ uint32_t ptes_crc = efi_crc32 (ptes, ptes_bytes);
+ if (ptes_crc != PED_LE32_TO_CPU (gpt->PartitionEntryArrayCRC32))
+ {
+ ped_exception_throw
+ (PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_CANCEL,
+ _("primary partition table array CRC mismatch"));
+ goto error_free_ptes;
+ }
+
+ uint32_t p_ent_size = PED_LE32_TO_CPU (gpt->SizeOfPartitionEntry);
+ for (i = 0; i < gpt_disk_data->entry_count; i++)
+ {
+ GuidPartitionEntry_t *pte
+ = (GuidPartitionEntry_t *) ((char *) ptes + i * p_ent_size);
+ PedPartition *part;
+
+ if (!guid_cmp (pte->PartitionTypeGuid, UNUSED_ENTRY_GUID))
+ continue;
+
+ part = _parse_part_entry (disk, pte);
+ if (!part)
+ goto error_delete_all;
+
+ part->fs_type = ped_file_system_probe (&part->geom);
+ part->num = i + 1;
+
+ PedConstraint *constraint_exact = ped_constraint_exact (&part->geom);
+ if (!ped_disk_add_partition (disk, part, constraint_exact))
+ {
+ ped_constraint_destroy (constraint_exact);
+ ped_partition_destroy (part);
+ goto error_delete_all;
+ }
+ ped_constraint_destroy (constraint_exact);
+ }
+ free (ptes);
+
+#ifndef DISCOVER_ONLY
+ if (write_back)
+ ped_disk_commit_to_dev (disk);
+#endif
+
+ pth_free (gpt);
+ return 1;
+
+error_delete_all:
+ ped_disk_delete_all (disk);
+error_free_ptes:
+ free (ptes);
+error_free_gpt:
+ pth_free (primary_gpt);
+ pth_free (backup_gpt);
+ pth_free (gpt);
+error:
+ return 0;
+}
+
+#ifndef DISCOVER_ONLY
+/* Write the protective MBR (to keep DOS happy) */
+static int
+_write_pmbr (PedDevice *dev, bool pmbr_boot)
+{
+ /* The UEFI spec is not clear about what to do with the following
+ elements of the Protective MBR (pmbr): BootCode (0-440B),
+ UniqueMBRSignature (440B-444B) and Unknown (444B-446B).
+ With this in mind, we try not to modify these elements. */
+ void *s0;
+ if (!ptt_read_sector (dev, 0, &s0))
+ return 0;
+ LegacyMBR_t *pmbr = s0;
+
+ /* Zero out the legacy partitions. */
+ memset (pmbr->PartitionRecord, 0, sizeof pmbr->PartitionRecord);
+
+ pmbr->Signature = PED_CPU_TO_LE16 (MSDOS_MBR_SIGNATURE);
+ pmbr->PartitionRecord[0].OSType = EFI_PMBR_OSTYPE_EFI;
+ pmbr->PartitionRecord[0].StartSector = 2;
+ pmbr->PartitionRecord[0].EndHead = 0xFF;
+ pmbr->PartitionRecord[0].EndSector = 0xFF;
+ pmbr->PartitionRecord[0].EndTrack = 0xFF;
+ pmbr->PartitionRecord[0].StartingLBA = PED_CPU_TO_LE32 (1);
+ if ((dev->length - 1ULL) > 0xFFFFFFFFULL)
+ pmbr->PartitionRecord[0].SizeInLBA = PED_CPU_TO_LE32 (0xFFFFFFFF);
+ else
+ pmbr->PartitionRecord[0].SizeInLBA = PED_CPU_TO_LE32 (dev->length - 1UL);
+ if (pmbr_boot)
+ pmbr->PartitionRecord[0].BootIndicator = 0x80;
+
+ int write_ok = ped_device_write (dev, pmbr, GPT_PMBR_LBA,
+ GPT_PMBR_SECTORS);
+ free (s0);
+ return write_ok;
+}
+
+static int
+_generate_header (const PedDisk *disk, int alternate, uint32_t ptes_crc,
+ GuidPartitionTableHeader_t **gpt_p)
+{
+ GPTDiskData *gpt_disk_data = disk->disk_specific;
+ GuidPartitionTableHeader_t *gpt;
+
+ *gpt_p = pth_new_zeroed (disk->dev);
+
+ gpt = *gpt_p;
+
+ gpt->Signature = PED_CPU_TO_LE64 (GPT_HEADER_SIGNATURE);
+ gpt->Revision = PED_CPU_TO_LE32 (GPT_HEADER_REVISION_V1_00);
+
+ /* per 1.00 spec */
+ gpt->HeaderSize = PED_CPU_TO_LE32 (pth_get_size_static (disk->dev));
+ gpt->HeaderCRC32 = 0;
+ gpt->Reserved1 = 0;
+
+ if (alternate)
+ {
+ size_t ss = disk->dev->sector_size;
+ PedSector ptes_bytes = (gpt_disk_data->entry_count
+ * sizeof (GuidPartitionEntry_t));
+ PedSector ptes_sectors = (ptes_bytes + ss - 1) / ss;
+
+ gpt->MyLBA = PED_CPU_TO_LE64 (gpt_disk_data->AlternateLBA);
+ gpt->AlternateLBA = PED_CPU_TO_LE64 (1);
+ gpt->PartitionEntryLBA
+ = PED_CPU_TO_LE64 (gpt_disk_data->AlternateLBA - ptes_sectors);
+ }
+ else
+ {
+ gpt->MyLBA = PED_CPU_TO_LE64 (1);
+ gpt->AlternateLBA = PED_CPU_TO_LE64 (gpt_disk_data->AlternateLBA);
+ gpt->PartitionEntryLBA = PED_CPU_TO_LE64 (2);
+ }
+
+ gpt->FirstUsableLBA = PED_CPU_TO_LE64 (gpt_disk_data->data_area.start);
+ gpt->LastUsableLBA = PED_CPU_TO_LE64 (gpt_disk_data->data_area.end);
+ gpt->DiskGUID = gpt_disk_data->uuid;
+ gpt->NumberOfPartitionEntries
+ = PED_CPU_TO_LE32 (gpt_disk_data->entry_count);
+ gpt->SizeOfPartitionEntry = PED_CPU_TO_LE32 (sizeof (GuidPartitionEntry_t));
+ gpt->PartitionEntryArrayCRC32 = PED_CPU_TO_LE32 (ptes_crc);
+
+ uint32_t crc;
+ if (pth_crc32 (disk->dev, gpt, &crc) != 0)
+ return 1;
+
+ gpt->HeaderCRC32 = PED_CPU_TO_LE32 (crc);
+ return 0;
+}
+
+static void
+_partition_generate_part_entry (PedPartition *part, GuidPartitionEntry_t *pte)
+{
+ GPTPartitionData *gpt_part_data = part->disk_specific;
+ unsigned int i;
+
+ PED_ASSERT (gpt_part_data != NULL);
+
+ pte->PartitionTypeGuid = gpt_part_data->type;
+ pte->UniquePartitionGuid = gpt_part_data->uuid;
+ pte->StartingLBA = PED_CPU_TO_LE64 (part->geom.start);
+ pte->EndingLBA = PED_CPU_TO_LE64 (part->geom.end);
+ pte->Attributes = gpt_part_data->attributes;
+
+ for (i = 0; i < 36; i++)
+ pte->PartitionName[i] = gpt_part_data->name[i];
+}
+
+static int
+gpt_write (const PedDisk *disk)
+{
+ GPTDiskData *gpt_disk_data;
+ uint32_t ptes_crc;
+ uint8_t *pth_raw;
+ GuidPartitionTableHeader_t *gpt;
+ PedPartition *part;
+
+ PED_ASSERT (disk != NULL);
+ PED_ASSERT (disk->dev != NULL);
+ PED_ASSERT (disk->disk_specific != NULL);
+
+ gpt_disk_data = disk->disk_specific;
+
+ size_t ptes_bytes = (gpt_disk_data->entry_count
+ * sizeof (GuidPartitionEntry_t));
+ size_t ss = disk->dev->sector_size;
+ PedSector ptes_sectors = (ptes_bytes + ss - 1) / ss;
+ /* Note that we allocate a little more than ptes_bytes,
+ when that number is not a multiple of sector size. */
+ GuidPartitionEntry_t *ptes = calloc (ptes_sectors, ss);
+ if (!ptes)
+ goto error;
+ for (part = ped_disk_next_partition (disk, NULL); part;
+ part = ped_disk_next_partition (disk, part))
+ {
+ if (part->type != 0)
+ continue;
+ _partition_generate_part_entry (part, &ptes[part->num - 1]);
+ }
+
+ ptes_crc = efi_crc32 (ptes, ptes_bytes);
+
+ /* Write protective MBR */
+ if (!_write_pmbr (disk->dev, gpt_disk_data->pmbr_boot))
+ goto error_free_ptes;
+
+ /* Write PTH and PTEs */
+ /* FIXME: Caution: this code is nearly identical to what's just below. */
+ if (_generate_header (disk, 0, ptes_crc, &gpt) != 0) {
+ pth_free(gpt);
+ goto error_free_ptes;
+ }
+ pth_raw = pth_get_raw (disk->dev, gpt);
+ pth_free (gpt);
+ if (pth_raw == NULL)
+ goto error_free_ptes;
+ int write_ok = ped_device_write (disk->dev, pth_raw, 1, 1);
+ free (pth_raw);
+ if (!write_ok)
+ goto error_free_ptes;
+ if (!ped_device_write (disk->dev, ptes, 2, ptes_sectors))
+ goto error_free_ptes;
+
+ /* Write Alternate PTH & PTEs */
+ /* FIXME: Caution: this code is nearly identical to what's just above. */
+ if (_generate_header (disk, 1, ptes_crc, &gpt) != 0) {
+ pth_free(gpt);
+ goto error_free_ptes;
+ }
+ pth_raw = pth_get_raw (disk->dev, gpt);
+ pth_free (gpt);
+ if (pth_raw == NULL)
+ goto error_free_ptes;
+ write_ok = ped_device_write (disk->dev, pth_raw, gpt_disk_data->AlternateLBA, 1);
+ free (pth_raw);
+ if (!write_ok)
+ goto error_free_ptes;
+ if (!ped_device_write (disk->dev, ptes,
+ gpt_disk_data->AlternateLBA - ptes_sectors, ptes_sectors))
+ goto error_free_ptes;
+
+ free (ptes);
+ return ped_device_sync (disk->dev);
+
+error_free_ptes:
+ free (ptes);
+error:
+ return 0;
+}
+#endif /* !DISCOVER_ONLY */
+
+static int
+add_metadata_part (PedDisk *disk, PedSector start, PedSector length)
+{
+ PedPartition *part;
+ PedConstraint *constraint_exact;
+ PED_ASSERT (disk != NULL);
+
+ part = ped_partition_new (disk, PED_PARTITION_METADATA, NULL,
+ start, start + length - 1);
+ if (!part)
+ goto error;
+
+ constraint_exact = ped_constraint_exact (&part->geom);
+ if (!ped_disk_add_partition (disk, part, constraint_exact))
+ goto error_destroy_constraint;
+ ped_constraint_destroy (constraint_exact);
+ return 1;
+
+error_destroy_constraint:
+ ped_constraint_destroy (constraint_exact);
+ ped_partition_destroy (part);
+error:
+ return 0;
+}
+
+static PedPartition *
+gpt_partition_new (const PedDisk *disk,
+ PedPartitionType part_type,
+ const PedFileSystemType *fs_type, PedSector start,
+ PedSector end)
+{
+ PedPartition *part;
+ GPTPartitionData *gpt_part_data;
+
+ part = _ped_partition_alloc (disk, part_type, fs_type, start, end);
+ if (!part)
+ goto error;
+
+ if (part_type != 0)
+ return part;
+
+ gpt_part_data = part->disk_specific =
+ ped_malloc (sizeof (GPTPartitionData));
+ if (!gpt_part_data)
+ goto error_free_part;
+
+ gpt_part_data->type = PARTITION_LINUX_DATA_GUID;
+ gpt_part_data->translated_name = 0;
+ uuid_generate ((unsigned char *) &gpt_part_data->uuid);
+ swap_uuid_and_efi_guid (&gpt_part_data->uuid);
+ memset (gpt_part_data->name, 0, sizeof gpt_part_data->name);
+ memset (&gpt_part_data->attributes, 0, sizeof gpt_part_data->attributes);
+ return part;
+
+error_free_part:
+ _ped_partition_free (part);
+error:
+ return NULL;
+}
+
+static PedPartition *
+gpt_partition_duplicate (const PedPartition *part)
+{
+ PedPartition *result;
+ GPTPartitionData *part_data = part->disk_specific;
+ GPTPartitionData *result_data;
+
+ result = _ped_partition_alloc (part->disk, part->type, part->fs_type,
+ part->geom.start, part->geom.end);
+ if (!result)
+ goto error;
+ result->num = part->num;
+
+ if (result->type != 0)
+ return result;
+
+ result_data = result->disk_specific =
+ ped_malloc (sizeof (GPTPartitionData));
+ if (!result_data)
+ goto error_free_part;
+
+ *result_data = *part_data;
+ if (part_data->translated_name) {
+ result_data->translated_name = xstrdup (part_data->translated_name);
+ } else {
+ result_data->translated_name = 0;
+ }
+ return result;
+
+error_free_part:
+ _ped_partition_free (result);
+error:
+ return NULL;
+}
+
+static void
+gpt_partition_destroy (PedPartition *part)
+{
+ if (part->type == 0)
+ {
+ PED_ASSERT (part->disk_specific != NULL);
+ GPTPartitionData *gpt_part_data = part->disk_specific;
+ free (gpt_part_data->translated_name);
+ free (part->disk_specific);
+ }
+
+ _ped_partition_free (part);
+}
+
+/* is_skip_guid checks the guid against the list of guids that should not be
+ * overridden by set_system. It returns a 1 if it is in the list.
+*/
+static bool
+is_skip_guid(efi_guid_t guid) {
+ int n = sizeof(skip_set_system_guids) / sizeof(skip_set_system_guids[0]);
+ for (int i = 0; i < n; ++i) {
+ if (guid_cmp(guid, skip_set_system_guids[i]) == 0) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static int
+gpt_partition_set_system (PedPartition *part,
+ const PedFileSystemType *fs_type)
+{
+ GPTPartitionData *gpt_part_data = part->disk_specific;
+
+ PED_ASSERT (gpt_part_data != NULL);
+
+ part->fs_type = fs_type;
+
+ // Is this a GUID that should skip fs_type checking?
+ if (is_skip_guid(gpt_part_data->type)) {
+ return 1;
+ }
+
+ if (fs_type)
+ {
+ if (strncmp (fs_type->name, "fat", 3) == 0
+ || strcmp (fs_type->name, "udf") == 0
+ || strcmp (fs_type->name, "ntfs") == 0)
+ {
+ gpt_part_data->type = PARTITION_BASIC_DATA_GUID;
+ return 1;
+ }
+ if (strncmp (fs_type->name, "hfs", 3) == 0)
+ {
+ gpt_part_data->type = PARTITION_APPLE_HFS_GUID;
+ return 1;
+ }
+ if (strstr (fs_type->name, "swap"))
+ {
+ gpt_part_data->type = PARTITION_SWAP_GUID;
+ return 1;
+ }
+ }
+
+ gpt_part_data->type = PARTITION_LINUX_DATA_GUID;
+ return 1;
+}
+
+/* Allocate metadata partitions for the GPTH and PTES */
+static int
+gpt_alloc_metadata (PedDisk *disk)
+{
+ PedSector gptlength, pteslength = 0;
+ GPTDiskData *gpt_disk_data;
+
+ PED_ASSERT (disk != NULL);
+ PED_ASSERT (disk->dev != NULL);
+ PED_ASSERT (disk->disk_specific != NULL);
+ gpt_disk_data = disk->disk_specific;
+
+ gptlength = ped_div_round_up (sizeof (GuidPartitionTableHeader_t),
+ disk->dev->sector_size);
+ pteslength = ped_div_round_up (gpt_disk_data->entry_count
+ * sizeof (GuidPartitionEntry_t),
+ disk->dev->sector_size);
+
+ /* metadata at the start of the disk includes the MBR */
+ if (!add_metadata_part (disk, GPT_PMBR_LBA,
+ GPT_PMBR_SECTORS + gptlength + pteslength))
+ return 0;
+
+ /* metadata at the end of the disk */
+ if (!add_metadata_part (disk, disk->dev->length - gptlength - pteslength,
+ gptlength + pteslength))
+ return 0;
+
+ return 1;
+}
+
+/* Does nothing, as the read/new/destroy functions maintain part->num */
+static int
+gpt_partition_enumerate (PedPartition *part)
+{
+ GPTDiskData *gpt_disk_data = part->disk->disk_specific;
+ int i;
+
+ /* never change the partition numbers */
+ if (part->num != -1)
+ return 1;
+
+ for (i = 1; i <= gpt_disk_data->entry_count; i++)
+ {
+ if (!ped_disk_get_partition (part->disk, i))
+ {
+ part->num = i;
+ return 1;
+ }
+ }
+
+ PED_ASSERT (0);
+
+ return 0; /* used if debug is disabled */
+}
+
+static int
+gpt_disk_set_flag (PedDisk *disk, PedDiskFlag flag, int state)
+{
+ GPTDiskData *gpt_disk_data = disk->disk_specific;
+ switch (flag)
+ {
+ case PED_DISK_GPT_PMBR_BOOT:
+ gpt_disk_data->pmbr_boot = state;
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+static int
+gpt_disk_is_flag_available(const PedDisk *disk, PedDiskFlag flag)
+{
+ switch (flag)
+ {
+ case PED_DISK_GPT_PMBR_BOOT:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+static uint8_t*
+gpt_disk_get_uuid (const PedDisk *disk)
+{
+ GPTDiskData *gpt_disk_data = disk->disk_specific;
+
+ efi_guid_t uuid = gpt_disk_data->uuid;
+
+ /* uuid is always LE, while uint8_t is always kind of BE */
+
+ uuid.time_low = PED_SWAP32(uuid.time_low);
+ uuid.time_mid = PED_SWAP16(uuid.time_mid);
+ uuid.time_hi_and_version = PED_SWAP16(uuid.time_hi_and_version);
+
+ uint8_t *buf = ped_malloc(sizeof (uuid_t));
+ memcpy(buf, &uuid, sizeof (uuid_t));
+ return buf;
+}
+
+static int
+gpt_disk_get_flag (const PedDisk *disk, PedDiskFlag flag)
+{
+ GPTDiskData *gpt_disk_data = disk->disk_specific;
+ switch (flag)
+ {
+ case PED_DISK_GPT_PMBR_BOOT:
+ return gpt_disk_data->pmbr_boot;
+ break;
+ default:
+ return 0;
+ }
+}
+
+static int
+gpt_partition_set_flag (PedPartition *part, PedPartitionFlag flag, int state)
+{
+ GPTPartitionData *gpt_part_data;
+ PED_ASSERT (part != NULL);
+ PED_ASSERT (part->disk_specific != NULL);
+ gpt_part_data = part->disk_specific;
+
+ const struct flag_uuid_mapping_t* p = gpt_find_flag_uuid_mapping (flag);
+ if (p)
+ {
+ if (state) {
+ gpt_part_data->type = p->type_uuid;
+ } else if (guid_cmp (gpt_part_data->type, p->type_uuid) == 0) {
+ // Clear the GUID so that fs_type will be used to return it to the default
+ gpt_part_data->type = PARTITION_LINUX_DATA_GUID;
+ return gpt_partition_set_system (part, part->fs_type);
+ }
+ return 1;
+ }
+
+ switch (flag)
+ {
+ case PED_PARTITION_HIDDEN:
+ gpt_part_data->attributes.RequiredToFunction = state;
+ return 1;
+ case PED_PARTITION_LEGACY_BOOT:
+ gpt_part_data->attributes.LegacyBIOSBootable = state;
+ return 1;
+ case PED_PARTITION_NO_AUTOMOUNT:
+ gpt_part_data->attributes.NoAutomount = state;
+ return 1;
+ case PED_PARTITION_ROOT:
+ case PED_PARTITION_LBA:
+ default:
+ return 0;
+ }
+ return 1;
+}
+
+static int _GL_ATTRIBUTE_PURE
+gpt_partition_get_flag (const PedPartition *part, PedPartitionFlag flag)
+{
+ GPTPartitionData *gpt_part_data;
+ PED_ASSERT (part->disk_specific != NULL);
+ gpt_part_data = part->disk_specific;
+
+ const struct flag_uuid_mapping_t* p = gpt_find_flag_uuid_mapping (flag);
+ if (p)
+ return guid_cmp (gpt_part_data->type, p->type_uuid) == 0;
+
+ switch (flag)
+ {
+ case PED_PARTITION_HIDDEN:
+ return gpt_part_data->attributes.RequiredToFunction;
+ case PED_PARTITION_LEGACY_BOOT:
+ return gpt_part_data->attributes.LegacyBIOSBootable;
+ case PED_PARTITION_NO_AUTOMOUNT:
+ return gpt_part_data->attributes.NoAutomount;
+ case PED_PARTITION_LBA:
+ case PED_PARTITION_ROOT:
+ default:
+ return 0;
+ }
+ return 0;
+}
+
+static int
+gpt_partition_is_flag_available (const PedPartition *part,
+ PedPartitionFlag flag)
+{
+ if (gpt_find_flag_uuid_mapping (flag))
+ return 1;
+
+ switch (flag)
+ {
+ case PED_PARTITION_HIDDEN:
+ case PED_PARTITION_LEGACY_BOOT:
+ case PED_PARTITION_NO_AUTOMOUNT:
+ return 1;
+ case PED_PARTITION_ROOT:
+ case PED_PARTITION_LBA:
+ default:
+ return 0;
+ }
+ return 0;
+}
+
+static void
+gpt_partition_set_name (PedPartition *part, const char *name)
+{
+ GPTPartitionData *gpt_part_data = part->disk_specific;
+
+ free(gpt_part_data->translated_name);
+ gpt_part_data->translated_name = xstrdup(name);
+ iconv_t conv = iconv_open ("UCS-2LE", nl_langinfo (CODESET));
+ if (conv == (iconv_t)-1)
+ goto err;
+ char *inbuff = gpt_part_data->translated_name;
+ char *outbuff = (char *)&gpt_part_data->name;
+ size_t inbuffsize = strlen (inbuff) + 1;
+ size_t outbuffsize = 72;
+ if (iconv (conv, &inbuff, &inbuffsize, &outbuff, &outbuffsize) == -1)
+ goto err;
+ iconv_close (conv);
+ return;
+ err:
+ ped_exception_throw (PED_EXCEPTION_WARNING,
+ PED_EXCEPTION_IGNORE,
+ _("failed to translate partition name"));
+ iconv_close (conv);
+}
+
+static const char *
+gpt_partition_get_name (const PedPartition *part)
+{
+ GPTPartitionData *gpt_part_data = part->disk_specific;
+ if (gpt_part_data->translated_name == NULL)
+ {
+ char buffer[200];
+ iconv_t conv = iconv_open (nl_langinfo (CODESET), "UCS-2LE");
+ if (conv == (iconv_t)-1)
+ goto err;
+ char *inbuff = (char *)&gpt_part_data->name;
+ char *outbuff = buffer;
+ size_t inbuffsize = 72;
+ size_t outbuffsize = sizeof(buffer);
+ if (iconv (conv, &inbuff, &inbuffsize, &outbuff, &outbuffsize) == -1)
+ goto err;
+ iconv_close (conv);
+ *outbuff = 0;
+ gpt_part_data->translated_name = xstrdup (buffer);
+ return gpt_part_data->translated_name;
+ err:
+ ped_exception_throw (PED_EXCEPTION_WARNING,
+ PED_EXCEPTION_IGNORE,
+ _("failed to translate partition name"));
+ iconv_close (conv);
+ return "";
+ }
+ return gpt_part_data->translated_name;
+}
+
+
+static int
+gpt_partition_set_type_uuid (PedPartition *part, const uint8_t *uuid)
+{
+ GPTPartitionData *gpt_part_data = part->disk_specific;
+
+ efi_guid_t* type_uuid = &gpt_part_data->type;
+ memcpy(type_uuid, uuid, sizeof (efi_guid_t));
+
+ /* type_uuid is always LE, while uint8_t is always kind of BE */
+
+ type_uuid->time_low = PED_SWAP32(type_uuid->time_low);
+ type_uuid->time_mid = PED_SWAP16(type_uuid->time_mid);
+ type_uuid->time_hi_and_version = PED_SWAP16(type_uuid->time_hi_and_version);
+
+ return 1;
+}
+
+
+static uint8_t*
+gpt_partition_get_type_uuid (const PedPartition *part)
+{
+ const GPTPartitionData *gpt_part_data = part->disk_specific;
+
+ efi_guid_t type_uuid = gpt_part_data->type;
+
+ /* type_uuid is always LE, while uint8_t is always kind of BE */
+
+ type_uuid.time_low = PED_SWAP32(type_uuid.time_low);
+ type_uuid.time_mid = PED_SWAP16(type_uuid.time_mid);
+ type_uuid.time_hi_and_version = PED_SWAP16(type_uuid.time_hi_and_version);
+
+ uint8_t *buf = ped_malloc(sizeof (uuid_t));
+ memcpy(buf, &type_uuid, sizeof (uuid_t));
+ return buf;
+}
+
+static uint8_t*
+gpt_partition_get_uuid (const PedPartition *part)
+{
+ const GPTPartitionData *gpt_part_data = part->disk_specific;
+
+ efi_guid_t uuid = gpt_part_data->uuid;
+
+ /* uuid is always LE, while uint8_t is always kind of BE */
+
+ uuid.time_low = PED_SWAP32(uuid.time_low);
+ uuid.time_mid = PED_SWAP16(uuid.time_mid);
+ uuid.time_hi_and_version = PED_SWAP16(uuid.time_hi_and_version);
+
+ uint8_t *buf = ped_malloc(sizeof (uuid_t));
+ memcpy(buf, &uuid, sizeof (uuid_t));
+ return buf;
+}
+
+static int
+gpt_get_max_primary_partition_count (const PedDisk *disk)
+{
+ const GPTDiskData *gpt_disk_data = disk->disk_specific;
+ return gpt_disk_data->entry_count;
+}
+
+/*
+ * From (http://developer.apple.com/technotes/tn2006/tn2166.html Chapter 5).
+ * According to the specs the first LBA (LBA0) is not relevant (it exists
+ * to maintain compatibility). on the second LBA(LBA1) gpt places the
+ * header. The header is as big as the block size. After the header we
+ * find the Entry array. Each element of said array, describes each
+ * partition. One can have as much elements as can fit between the end of
+ * the second LBA (where the header ends) and the FirstUsableLBA.
+ * FirstUsableLBA is the first logical block that is used for contents
+ * and is defined in header.
+ *
+ * /---------------------------------------------------\
+ * | BLOCK0 | HEADER | Entry Array | First Usable LBA |
+ * | | BLOCK1 | | |
+ * \---------------------------------------------------/
+ * / \
+ * /----------/ \----------\
+ * /-----------------------------------------\
+ * | E1 | E2 | E3 |...............| EN |
+ * \-----------------------------------------/
+ *
+ * The number of possible partitions or supported partitions is:
+ * SP = FirstUsableLBA*Blocksize - 2*Blocksize / SizeOfPartitionEntry
+ * SP = Blocksize(FirstusableLBA - 2) / SizeOfPartitoinEntry
+ */
+static bool
+gpt_get_max_supported_partition_count (const PedDisk *disk, int *max_n)
+{
+ GuidPartitionTableHeader_t *pth = NULL;
+ uint8_t *pth_raw = ped_malloc (pth_get_size (disk->dev));
+
+ if (ped_device_read (disk->dev, pth_raw, 1, GPT_HEADER_SECTORS)
+ || ped_device_read (disk->dev, pth_raw,
+ disk->dev->length, GPT_HEADER_SECTORS))
+ pth = pth_new_from_raw (disk->dev, pth_raw);
+ free (pth_raw);
+
+ if (pth == NULL)
+ return false;
+
+ if (!_header_is_valid (disk, pth, 1))
+ {
+ pth->FirstUsableLBA = PED_CPU_TO_LE64 (34);
+ pth->SizeOfPartitionEntry
+ = PED_CPU_TO_LE32 (sizeof (GuidPartitionEntry_t));
+ }
+
+ *max_n = (disk->dev->sector_size * (PED_LE64_TO_CPU (pth->FirstUsableLBA) - 2)
+ / PED_LE32_TO_CPU (pth->SizeOfPartitionEntry));
+ pth_free (pth);
+ return true;
+}
+
+static PedConstraint *
+_non_metadata_constraint (const PedDisk *disk)
+{
+ GPTDiskData *gpt_disk_data = disk->disk_specific;
+
+ return ped_constraint_new_from_max (&gpt_disk_data->data_area);
+}
+
+static int
+gpt_partition_align (PedPartition *part, const PedConstraint *constraint)
+{
+ PED_ASSERT (part != NULL);
+
+ if (_ped_partition_attempt_align (part, constraint,
+ _non_metadata_constraint (part->disk)))
+ return 1;
+
+#ifndef DISCOVER_ONLY
+ ped_exception_throw (PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_CANCEL,
+ _("Unable to satisfy all constraints on the partition."));
+#endif
+ return 0;
+}
+
+#include "pt-common.h"
+PT_define_limit_functions (gpt)
+
+static PedDiskOps gpt_disk_ops =
+{
+ clobber: NULL,
+ write: NULL_IF_DISCOVER_ONLY (gpt_write),
+
+ partition_set_name: gpt_partition_set_name,
+ partition_get_name: gpt_partition_get_name,
+ partition_set_type_id: NULL,
+ partition_get_type_id: NULL,
+ partition_set_type_uuid: gpt_partition_set_type_uuid,
+ partition_get_type_uuid: gpt_partition_get_type_uuid,
+ partition_get_uuid: gpt_partition_get_uuid,
+ disk_set_flag: gpt_disk_set_flag,
+ disk_get_flag: gpt_disk_get_flag,
+ disk_is_flag_available: gpt_disk_is_flag_available,
+ disk_get_uuid: gpt_disk_get_uuid,
+
+ PT_op_function_initializers (gpt)
+};
+
+static PedDiskType gpt_disk_type =
+{
+ next: NULL,
+ name: "gpt",
+ ops: &gpt_disk_ops,
+ features: PED_DISK_TYPE_PARTITION_NAME | PED_DISK_TYPE_PARTITION_TYPE_UUID |
+ PED_DISK_TYPE_DISK_UUID | PED_DISK_TYPE_PARTITION_UUID
+};
+
+void
+ped_disk_gpt_init ()
+{
+ ped_disk_type_register (&gpt_disk_type);
+}
+
+void
+ped_disk_gpt_done ()
+{
+ ped_disk_type_unregister (&gpt_disk_type);
+}
+
+verify (sizeof (GuidPartitionEntryAttributes_t) == 8);
+verify (sizeof (GuidPartitionEntry_t) == 128);