OSDN Git Service

Fix ChangeLog
[android-x86/external-parted.git] / libparted / labels / gpt.c
1 /*
2     libparted - a library for manipulating disk partitions
3
4     original version by Matt Domsch <Matt_Domsch@dell.com>
5     Disclaimed into the Public Domain
6
7     Portions Copyright (C) 2001, 2002, 2003, 2005, 2006
8         Free Software Foundation, Inc.
9
10     EFI GUID Partition Table handling
11     Per Intel EFI Specification v1.02
12     http://developer.intel.com/technology/efi/efi.htm
13
14     This program is free software; you can redistribute it and/or modify
15     it under the terms of the GNU General Public License as published by
16     the Free Software Foundation; either version 2 of the License, or
17     (at your option) any later version.
18
19     This program is distributed in the hope that it will be useful,
20     but WITHOUT ANY WARRANTY; without even the implied warranty of
21     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22     GNU General Public License for more details.
23
24     You should have received a copy of the GNU General Public License
25     along with this program; if not, write to the Free Software
26     Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
27 */
28
29 #include "config.h"
30
31 #include <parted/parted.h>
32 #include <parted/debug.h>
33 #include <parted/endian.h>
34 #include <parted/crc32.h>
35 #include <inttypes.h>
36 #include <stdio.h>
37 #include <string.h>
38 #include <stdlib.h>
39 #include <sys/types.h>
40 #include <sys/ioctl.h>
41 #include <fcntl.h>
42 #include <unistd.h>
43 #include <uuid/uuid.h>
44
45 #if ENABLE_NLS
46 #  include <libintl.h>
47 #  define _(String) gettext (String)
48 #else
49 #  define _(String) (String)
50 #endif                          /* ENABLE_NLS */
51
52 #define EFI_PMBR_OSTYPE_EFI 0xEE
53 #define MSDOS_MBR_SIGNATURE 0xaa55
54
55 #define GPT_HEADER_SIGNATURE 0x5452415020494645LL
56
57 /* NOTE: the document that describes revision 1.00 is labelled "version 1.02",
58  * so some implementors got confused...
59  */
60 #define GPT_HEADER_REVISION_V1_02 0x00010200
61 #define GPT_HEADER_REVISION_V1_00 0x00010000
62 #define GPT_HEADER_REVISION_V0_99 0x00009900
63
64 typedef uint16_t efi_char16_t;  /* UNICODE character */
65
66 typedef struct {
67         uint32_t time_low;
68         uint16_t time_mid;
69         uint16_t time_hi_and_version;
70         uint8_t  clock_seq_hi_and_reserved;
71         uint8_t  clock_seq_low;
72         uint8_t  node[6];
73 } /* __attribute__ ((packed)) */ efi_guid_t;
74 /* commented out "__attribute__ ((packed))" to work around gcc bug (fixed
75  * in gcc3.1): __attribute__ ((packed)) breaks addressing on initialized
76  * data.  It turns out we don't need it in this case, so it doesn't break
77  * anything :)
78  */
79
80 #define UNUSED_ENTRY_GUID    \
81     ((efi_guid_t) { 0x00000000, 0x0000, 0x0000, 0x00, 0x00, \
82                     { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }})
83 #define PARTITION_SYSTEM_GUID \
84     ((efi_guid_t) { PED_CPU_TO_LE32 (0xC12A7328), PED_CPU_TO_LE16 (0xF81F), \
85                     PED_CPU_TO_LE16 (0x11d2), 0xBA, 0x4B, \
86                     { 0x00, 0xA0, 0xC9, 0x3E, 0xC9, 0x3B }})
87 #define LEGACY_MBR_PARTITION_GUID \
88     ((efi_guid_t) { PED_CPU_TO_LE32 (0x024DEE41), PED_CPU_TO_LE16 (0x33E7), \
89                     PED_CPU_TO_LE16 (0x11d3, 0x9D, 0x69, \
90                     { 0x00, 0x08, 0xC7, 0x81, 0xF3, 0x9F }})
91 #define PARTITION_MSFT_RESERVED_GUID \
92     ((efi_guid_t) { PED_CPU_TO_LE32 (0xE3C9E316), PED_CPU_TO_LE16 (0x0B5C), \
93                     PED_CPU_TO_LE16 (0x4DB8), 0x81, 0x7D, \
94                     { 0xF9, 0x2D, 0xF0, 0x02, 0x15, 0xAE }})
95 #define PARTITION_BASIC_DATA_GUID \
96     ((efi_guid_t) { PED_CPU_TO_LE32 (0xEBD0A0A2), PED_CPU_TO_LE16 (0xB9E5), \
97                     PED_CPU_TO_LE16 (0x4433), 0x87, 0xC0, \
98                     { 0x68, 0xB6, 0xB7, 0x26, 0x99, 0xC7 }})
99 #define PARTITION_RAID_GUID \
100     ((efi_guid_t) { PED_CPU_TO_LE32 (0xa19d880f), PED_CPU_TO_LE16 (0x05fc), \
101                     PED_CPU_TO_LE16 (0x4d3b), 0xa0, 0x06, \
102                     { 0x74, 0x3f, 0x0f, 0x84, 0x91, 0x1e }})
103 #define PARTITION_SWAP_GUID \
104     ((efi_guid_t) { PED_CPU_TO_LE32 (0x0657fd6d), PED_CPU_TO_LE16 (0xa4ab), \
105                     PED_CPU_TO_LE16 (0x43c4), 0x84, 0xe5, \
106                     { 0x09, 0x33, 0xc8, 0x4b, 0x4f, 0x4f }})
107 #define PARTITION_LVM_GUID \
108     ((efi_guid_t) { PED_CPU_TO_LE32 (0xe6d6d379), PED_CPU_TO_LE16 (0xf507), \
109                     PED_CPU_TO_LE16 (0x44c2), 0xa2, 0x3c, \
110                     { 0x23, 0x8f, 0x2a, 0x3d, 0xf9, 0x28 }})
111 #define PARTITION_RESERVED_GUID \
112     ((efi_guid_t) { PED_CPU_TO_LE32 (0x8da63339), PED_CPU_TO_LE16 (0x0007), \
113                     PED_CPU_TO_LE16 (0x60c0), 0xc4, 0x36, \
114                     { 0x08, 0x3a, 0xc8, 0x23, 0x09, 0x08 }})
115 #define PARTITION_HPSERVICE_GUID \
116     ((efi_guid_t) { PED_CPU_TO_LE32 (0xe2a1e728), PED_CPU_TO_LE16 (0x32e3), \
117                     PED_CPU_TO_LE16 (0x11d6), 0xa6, 0x82, \
118                     { 0x7b, 0x03, 0xa0, 0x00, 0x00, 0x00 }})
119 #define PARTITION_APPLE_HFS_GUID \
120     ((efi_guid_t) { PED_CPU_TO_LE32 (0x48465300), PED_CPU_TO_LE16 (0x0000), \
121                     PED_CPU_TO_LE16 (0x11AA), 0xaa, 0x11, \
122                     { 0x00, 0x30, 0x65, 0x43, 0xEC, 0xAC }})
123
124 typedef struct _GuidPartitionTableHeader_t {
125         uint64_t Signature;
126         uint32_t Revision;
127         uint32_t HeaderSize;
128         uint32_t HeaderCRC32;
129         uint32_t Reserved1;
130         uint64_t MyLBA;
131         uint64_t AlternateLBA;
132         uint64_t FirstUsableLBA;
133         uint64_t LastUsableLBA;
134         efi_guid_t DiskGUID;
135         uint64_t PartitionEntryLBA;
136         uint32_t NumberOfPartitionEntries;
137         uint32_t SizeOfPartitionEntry;
138         uint32_t PartitionEntryArrayCRC32;
139         uint8_t* Reserved2;
140 } __attribute__ ((packed)) GuidPartitionTableHeader_t;
141
142 typedef struct _GuidPartitionEntryAttributes_t {
143 #ifdef __GNUC__ /* XXX narrow this down to !TinyCC */
144         uint64_t RequiredToFunction:1;
145         uint64_t Reserved:47;
146         uint64_t GuidSpecific:16;
147 #else
148 #       warning "Using crippled partition entry type"
149         uint32_t RequiredToFunction:1;
150         uint32_t Reserved:32;
151         uint32_t LOST:5;
152         uint32_t GuidSpecific:16;
153 #endif
154 } __attribute__ ((packed)) GuidPartitionEntryAttributes_t;
155
156 typedef struct _GuidPartitionEntry_t {
157         efi_guid_t PartitionTypeGuid;
158         efi_guid_t UniquePartitionGuid;
159         uint64_t StartingLBA;
160         uint64_t EndingLBA;
161         GuidPartitionEntryAttributes_t Attributes;
162         efi_char16_t PartitionName[72 / sizeof(efi_char16_t)];
163 } __attribute__ ((packed)) GuidPartitionEntry_t;
164
165 #define GPT_PMBR_LBA 0
166 #define GPT_PMBR_SECTORS 1
167 #define GPT_PRIMARY_HEADER_LBA 1
168 #define GPT_HEADER_SECTORS 1
169 #define GPT_PRIMARY_PART_TABLE_LBA 2 
170
171 /* 
172    These values are only defaults.  The actual on-disk structures
173    may define different sizes, so use those unless creating a new GPT disk!
174 */
175
176 #define GPT_DEFAULT_PARTITION_ENTRY_ARRAY_SIZE 16384
177
178 /* Number of actual partition entries should be calculated as: */
179 #define GPT_DEFAULT_PARTITION_ENTRIES \
180         (GPT_DEFAULT_PARTITION_ENTRY_ARRAY_SIZE / \
181          sizeof(GuidPartitionEntry_t))
182
183
184 typedef struct _PartitionRecord_t {
185         /* Not used by EFI firmware. Set to 0x80 to indicate that this
186            is the bootable legacy partition. */
187         uint8_t BootIndicator;
188
189         /* Start of partition in CHS address, not used by EFI firmware. */
190         uint8_t StartHead;
191
192         /* Start of partition in CHS address, not used by EFI firmware. */
193         uint8_t StartSector;
194
195         /* Start of partition in CHS address, not used by EFI firmware. */
196         uint8_t StartTrack;
197
198         /* OS type. A value of 0xEF defines an EFI system partition.
199            Other values are reserved for legacy operating systems, and
200            allocated independently of the EFI specification. */
201         uint8_t OSType;
202
203         /* End of partition in CHS address, not used by EFI firmware. */
204         uint8_t EndHead;
205
206         /* End of partition in CHS address, not used by EFI firmware. */
207         uint8_t EndSector;
208
209         /* End of partition in CHS address, not used by EFI firmware. */
210         uint8_t EndTrack;
211
212         /* Starting LBA address of the partition on the disk. Used by
213            EFI firmware to define the start of the partition. */
214         uint32_t StartingLBA;
215
216         /* Size of partition in LBA. Used by EFI firmware to determine
217            the size of the partition. */
218         uint32_t SizeInLBA;
219 } __attribute__ ((packed)) PartitionRecord_t;
220
221 /* Protected Master Boot Record  & Legacy MBR share same structure */
222 /* Needs to be packed because the u16s force misalignment. */
223 typedef struct _LegacyMBR_t {
224         uint8_t BootCode[440];
225         uint32_t UniqueMBRSignature;
226         uint16_t Unknown;
227         PartitionRecord_t PartitionRecord[4];
228         uint16_t Signature;
229 } __attribute__ ((packed)) LegacyMBR_t;
230
231 /* uses libparted's disk_specific field in PedDisk, to store our info */
232 typedef struct _GPTDiskData {
233         PedGeometry     data_area;
234         int             entry_count;
235         efi_guid_t      uuid;
236 } GPTDiskData;
237
238 /* uses libparted's disk_specific field in PedPartition, to store our info */
239 typedef struct _GPTPartitionData {
240         efi_guid_t      type;
241         efi_guid_t      uuid;
242         char            name[37];
243         int             lvm;
244         int             raid;
245         int             boot;
246         int             hp_service;
247         int             hidden;
248         int             msftres;
249 } GPTPartitionData;
250
251 static PedDiskType gpt_disk_type;
252
253
254 static inline uint32_t
255 pth_get_size (const PedDevice* dev)
256 {
257         return GPT_HEADER_SECTORS * dev->sector_size;
258 }
259
260
261 static inline uint32_t
262 pth_get_size_static (const PedDevice* dev)
263 {
264         return sizeof (GuidPartitionTableHeader_t) - sizeof (uint8_t*);
265 }
266
267
268 static inline uint32_t
269 pth_get_size_rsv2 (const PedDevice* dev)
270 {
271         return pth_get_size(dev) - pth_get_size_static(dev);
272 }
273
274
275 static GuidPartitionTableHeader_t*
276 pth_new (const PedDevice* dev)
277 {
278         GuidPartitionTableHeader_t* pth = ped_malloc (
279                         sizeof (GuidPartitionTableHeader_t)
280                         + sizeof (uint8_t));
281         
282         pth->Reserved2 = ped_malloc ( pth_get_size_rsv2 (dev) );
283
284         return pth;
285 }
286
287
288 static GuidPartitionTableHeader_t*
289 pth_new_zeroed (const PedDevice* dev)
290 {
291         GuidPartitionTableHeader_t* pth = pth_new (dev);
292         
293         memset (pth, 0, pth_get_size_static (dev));
294         memset (pth->Reserved2, 0, pth_get_size_rsv2 (dev));        
295         
296         return (pth);
297 }
298                         
299
300 static GuidPartitionTableHeader_t*
301 pth_new_from_raw (const PedDevice* dev, const uint8_t* pth_raw)
302 {
303         GuidPartitionTableHeader_t* pth = pth_new (dev); 
304
305         PED_ASSERT (pth_raw != NULL, return 0);
306         
307         memcpy (pth, pth_raw, pth_get_size_static (dev));
308         memcpy (pth->Reserved2, pth_raw + pth_get_size_static (dev),
309                         pth_get_size_rsv2 (dev));
310         
311         return pth;
312 }
313
314 static void
315 pth_free (GuidPartitionTableHeader_t* pth)
316 {
317         PED_ASSERT (pth != NULL, return);
318         PED_ASSERT (pth->Reserved2 != NULL, return);
319
320         ped_free (pth->Reserved2);
321         ped_free (pth);
322 }
323
324 static uint8_t*
325 pth_get_raw (const PedDevice* dev, const GuidPartitionTableHeader_t* pth)
326 {
327         uint8_t* pth_raw = ped_malloc (pth_get_size (dev));
328         int size_static = pth_get_size_static (dev);
329         
330         PED_ASSERT (pth != NULL, return 0);
331         PED_ASSERT (pth->Reserved2 != NULL, return 0);
332        
333         memcpy (pth_raw, pth, size_static);
334         memcpy (pth_raw + size_static, pth->Reserved2, pth_get_size_rsv2 (dev));
335         
336         return pth_raw;
337 }
338
339
340 /**
341  * swap_uuid_and_efi_guid() - converts between uuid formats
342  * @uuid - uuid_t in either format (converts it to the other)
343  *
344  * There are two different representations for Globally Unique Identifiers
345  * (GUIDs or UUIDs).
346  * 
347  * The RFC specifies a UUID as a string of 16 bytes, essentially
348  * a big-endian array of char.
349  * Intel, in their EFI Specification, references the same RFC, but
350  * then defines a GUID as a structure of little-endian fields.
351  * Coincidentally, both structures have the same format when unparsed.
352  *
353  * When read from disk, EFI GUIDs are in struct of little endian format,
354  * and need to be converted to be treated as uuid_t in memory.
355  *
356  * When writing to disk, uuid_ts need to be converted into EFI GUIDs.
357  *
358  * Blame Intel.
359  */
360 static void
361 swap_uuid_and_efi_guid(uuid_t uuid)
362 {
363         int i;
364         efi_guid_t *guid = (efi_guid_t *)uuid;
365
366         PED_ASSERT(uuid != NULL, return);
367         guid->time_low            = PED_SWAP32(guid->time_low);
368         guid->time_mid            = PED_SWAP16(guid->time_mid);
369         guid->time_hi_and_version = PED_SWAP16(guid->time_hi_and_version);
370 }
371
372 /* returns the EFI-style CRC32 value for buf
373  *      This function uses the crc32 function by Gary S. Brown,
374  * but seeds the function with ~0, and xor's with ~0 at the end.
375  */
376 static inline uint32_t
377 efi_crc32(const void *buf, unsigned long len)
378 {
379         return (__efi_crc32(buf, len, ~0L) ^ ~0L);
380 }
381
382 static inline uint32_t
383 pth_crc32(const PedDevice* dev, const GuidPartitionTableHeader_t* pth)
384 {
385         uint8_t* pth_raw = pth_get_raw (dev, pth);
386         uint32_t crc32 = 0;
387        
388         PED_ASSERT (dev != NULL, return 0);
389         PED_ASSERT (pth != NULL, return 0);
390        
391         crc32 = efi_crc32 (pth_raw, pth_get_size_static (dev));
392
393         ped_free (pth_raw);
394       
395         return crc32;
396 }
397
398 static inline int
399 guid_cmp (efi_guid_t left, efi_guid_t right)
400 {
401         return memcmp(&left, &right, sizeof(efi_guid_t));
402 }
403
404 /* checks if 'mbr' is a protective MBR partition table */
405 static inline int
406 _pmbr_is_valid (const LegacyMBR_t* mbr)
407 {
408         int i;
409
410         PED_ASSERT(mbr != NULL, return 0);
411
412         if (mbr->Signature != PED_CPU_TO_LE16(MSDOS_MBR_SIGNATURE))
413                 return 0;
414         for (i = 0; i < 4; i++) {
415                 if (mbr->PartitionRecord[i].OSType == EFI_PMBR_OSTYPE_EFI)
416                         return 1;
417         }
418         return 0;
419 }
420
421 static int
422 gpt_probe (const PedDevice * dev)
423 {
424         GuidPartitionTableHeader_t* gpt = NULL;
425         uint8_t* pth_raw = ped_malloc (pth_get_size (dev));
426         LegacyMBR_t legacy_mbr;
427         int gpt_sig_found = 0;
428
429         PED_ASSERT (dev != NULL, return 0);
430
431         if (ped_device_read(dev, pth_raw, 1, GPT_HEADER_SECTORS)
432         || ped_device_read(dev, pth_raw, dev->length - 1, GPT_HEADER_SECTORS)) {
433                 gpt = pth_new_from_raw (dev, pth_raw);
434                 if (gpt->Signature == PED_CPU_TO_LE64(GPT_HEADER_SIGNATURE))
435                         gpt_sig_found = 1;
436         }
437         
438         ped_free (pth_raw);
439
440         if (gpt)
441                 pth_free (gpt);
442
443         
444         if (!gpt_sig_found)
445                 return 0;
446
447         if (ped_device_read(dev, &legacy_mbr, 0, GPT_HEADER_SECTORS)) {
448                 if (!_pmbr_is_valid (&legacy_mbr)) {
449                         int ex_status = ped_exception_throw (
450                                 PED_EXCEPTION_WARNING,
451                                 PED_EXCEPTION_YES_NO,
452                         _("%s contains GPT signatures, indicating that it has "
453                           "a GPT table.  However, it does not have a valid "
454                           "fake msdos partition table, as it should.  Perhaps "
455                           "it was corrupted -- possibly by a program that "
456                           "doesn't understand GPT partition tables.  Or "
457                           "perhaps you deleted the GPT table, and are now "
458                           "using an msdos partition table.  Is this a GPT "
459                           "partition table?"),
460                                 dev->path);
461                         if (ex_status == PED_EXCEPTION_NO)
462                                 return 0;
463                 }
464         }
465
466         return 1;
467 }
468
469 #ifndef DISCOVER_ONLY
470 /* writes zeros to the PMBR and the primary and alternate GPTHs and PTEs */
471 static int
472 gpt_clobber(PedDevice * dev)
473 {
474         LegacyMBR_t pmbr;
475         uint8_t* zeroed_pth_raw = ped_malloc (pth_get_size (dev));
476         uint8_t* pth_raw = ped_malloc (pth_get_size (dev));
477         GuidPartitionTableHeader_t* gpt;
478         GuidPartitionEntry_t ptes[GPT_DEFAULT_PARTITION_ENTRIES];
479
480         PED_ASSERT (dev != NULL, return 0);
481
482         memset(&pmbr, 0, sizeof(pmbr));
483         memset(zeroed_pth_raw, 0, pth_get_size (dev));
484         
485         /*
486          * TO DISCUSS: check whether checksum is correct?
487          * If not, we might get a wrong AlternateLBA field and destroy
488          * one sector of random data.
489          */
490         if (!ped_device_read(dev, pth_raw,
491                              GPT_PRIMARY_HEADER_LBA, GPT_HEADER_SECTORS))
492                 goto error_free;
493
494         gpt = pth_new_from_raw (dev, pth_raw);
495         
496         if (!ped_device_write(dev, &pmbr, GPT_PMBR_LBA, GPT_PMBR_SECTORS))
497                 goto error_free_with_gpt;
498         if (!ped_device_write(dev, &zeroed_pth_raw,
499                               GPT_PRIMARY_HEADER_LBA, GPT_HEADER_SECTORS))
500                 goto error_free_with_gpt;
501         if (!ped_device_write(dev, &zeroed_pth_raw, dev->length - GPT_HEADER_SECTORS,
502                               GPT_HEADER_SECTORS))
503                 goto error_free_with_gpt;
504
505         if ((PedSector) PED_LE64_TO_CPU (gpt->AlternateLBA) < dev->length - 1) {
506                 if (!ped_device_write(dev, gpt,
507                                       PED_LE64_TO_CPU (gpt->AlternateLBA),
508                                       GPT_HEADER_SECTORS))
509                         return 0;
510         }
511         
512         pth_free (gpt);
513
514         return 1;
515         
516 error_free_with_gpt:
517         pth_free (gpt);
518 error_free:
519         ped_free (pth_raw);
520         ped_free (zeroed_pth_raw);
521         return 0;
522 }
523 #endif /* !DISCOVER_ONLY */
524
525 static PedDisk *
526 gpt_alloc (const PedDevice * dev)
527 {
528         PedDisk* disk;
529         GPTDiskData *gpt_disk_data;
530         PedSector data_start, data_end;
531
532         disk = _ped_disk_alloc ((PedDevice*)dev, &gpt_disk_type);
533         if (!disk)
534                 goto error;
535         disk->disk_specific = gpt_disk_data = ped_malloc (sizeof (GPTDiskData));
536         if (!disk->disk_specific)
537                 goto error_free_disk;
538
539         data_start = 2 + GPT_DEFAULT_PARTITION_ENTRY_ARRAY_SIZE / dev->sector_size;
540         data_end = dev->length - 2
541                    - GPT_DEFAULT_PARTITION_ENTRY_ARRAY_SIZE / dev->sector_size;
542         ped_geometry_init (&gpt_disk_data->data_area, dev, data_start,
543                            data_end - data_start + 1);
544         gpt_disk_data->entry_count = GPT_DEFAULT_PARTITION_ENTRIES;
545         uuid_generate ((unsigned char*) &gpt_disk_data->uuid);
546         swap_uuid_and_efi_guid((unsigned char*)(&gpt_disk_data->uuid));
547         return disk;
548
549 error_free_disk:
550         ped_free (disk);
551 error:
552         return NULL;
553 }
554
555 static PedDisk*
556 gpt_duplicate (const PedDisk* disk)
557 {
558         PedDisk* new_disk;
559         GPTDiskData* new_disk_data;
560         GPTDiskData* old_disk_data;
561
562         new_disk = ped_disk_new_fresh (disk->dev, &gpt_disk_type);
563         if (!new_disk)
564                 return NULL;
565
566         old_disk_data = disk->disk_specific;
567         new_disk_data = new_disk->disk_specific;
568
569         ped_geometry_init (&new_disk_data->data_area, disk->dev,
570                            old_disk_data->data_area.start,
571                            old_disk_data->data_area.length);
572         new_disk_data->entry_count = old_disk_data->entry_count;
573         new_disk_data->uuid = old_disk_data->uuid;
574         return new_disk;
575 }
576
577 static void
578 gpt_free(PedDisk * disk)
579 {
580         ped_disk_delete_all (disk);
581         ped_free (disk->disk_specific);
582         _ped_disk_free (disk);
583 }
584
585 static int
586 _header_is_valid (const PedDevice* dev, GuidPartitionTableHeader_t* gpt)
587 {
588         uint32_t crc, origcrc;
589
590         if (PED_LE64_TO_CPU (gpt->Signature) != GPT_HEADER_SIGNATURE)
591                 return 0;
592         if (PED_LE32_TO_CPU (gpt->HeaderSize)
593                         > pth_get_size_static (dev))
594                 return 0;
595
596         origcrc = gpt->HeaderCRC32;
597         gpt->HeaderCRC32 = 0;
598         crc = pth_crc32 (dev, gpt);
599         gpt->HeaderCRC32 = origcrc;
600
601         return crc == PED_LE32_TO_CPU (origcrc);
602 }
603
604 static int
605 _read_header (const PedDevice* dev, GuidPartitionTableHeader_t** gpt,
606               PedSector where)
607 {
608         uint8_t* pth_raw = ped_malloc (pth_get_size (dev));
609
610         PED_ASSERT (dev != NULL, return 0);
611
612         if (!ped_device_read (dev, pth_raw, where, GPT_HEADER_SECTORS)) {
613                 ped_free (pth_raw); 
614                 return 0;
615         }
616  
617         *gpt = pth_new_from_raw (dev, pth_raw);
618         
619         ped_free (pth_raw);
620
621         if (_header_is_valid (dev, *gpt))
622                 return 1;
623
624         pth_free (*gpt);
625         return 0;
626 }
627
628 static int
629 _parse_header (PedDisk* disk, GuidPartitionTableHeader_t* gpt, 
630                int *update_needed)
631
632         GPTDiskData* gpt_disk_data = disk->disk_specific;
633         PedSector first_usable;
634         PedSector last_usable;
635         PedSector last_usable_if_grown, last_usable_min_default;
636         static int asked_already;
637
638         PED_ASSERT (_header_is_valid (disk->dev, gpt), return 0);
639
640 #ifndef DISCOVER_ONLY
641         if (PED_CPU_TO_LE32 (gpt->Revision) > GPT_HEADER_REVISION_V1_02
642             || PED_CPU_TO_LE32 (gpt->HeaderSize) != pth_get_size_static (
643                                                         disk->dev)) {
644                 if (ped_exception_throw (
645                         PED_EXCEPTION_WARNING,
646                         PED_EXCEPTION_IGNORE_CANCEL,
647                         _("The format of the GPT partition table is version "
648                           "%x, which is newer than what Parted can "
649                           "recognise.  Please tell us!  bug-parted@gnu.org"),
650                         PED_CPU_TO_LE32 (gpt->Revision))
651                                 != PED_EXCEPTION_IGNORE)
652                         return 0;
653         }
654 #endif
655
656         first_usable = PED_CPU_TO_LE64 (gpt->FirstUsableLBA);
657         last_usable = PED_CPU_TO_LE64 (gpt->LastUsableLBA);
658
659
660 /*
661    Need to check whether the volume has grown, the LastUsableLBA is
662    normally set to disk->dev->length - 2 - ptes_size (at least for parted
663    created volumes), where ptes_size is the number of entries * 
664    size of each entry / sector size or 16k / sector size, whatever the greater.
665    If the volume has grown, offer the user the chance to use the new
666    space or continue with the current usable area.  Only ask once per 
667    parted invocation.
668 */
669    
670         last_usable_if_grown 
671                 = PED_CPU_TO_LE64 (disk->dev->length - 2 - 
672                 ((PedSector)(PED_CPU_TO_LE32(gpt->NumberOfPartitionEntries)) * 
673                 (PedSector)(PED_CPU_TO_LE32(gpt->SizeOfPartitionEntry)) / 
674                 disk->dev->sector_size));
675
676         last_usable_min_default = disk->dev->length - 2 - 
677                 GPT_DEFAULT_PARTITION_ENTRY_ARRAY_SIZE / disk->dev->sector_size;
678
679         if ( last_usable_if_grown > last_usable_min_default ) {
680
681                 last_usable_if_grown = last_usable_min_default;
682         }
683
684
685         PED_ASSERT (last_usable > first_usable, return 0);
686         PED_ASSERT (last_usable <= disk->dev->length, return 0);
687
688         PED_ASSERT (last_usable_if_grown > first_usable, return 0);
689         PED_ASSERT (last_usable_if_grown <= disk->dev->length, return 0);
690
691         if ( !asked_already && last_usable < last_usable_if_grown ) {
692
693                 PedExceptionOption q;
694
695                 q = ped_exception_throw (PED_EXCEPTION_WARNING,
696                         PED_EXCEPTION_FIX | PED_EXCEPTION_IGNORE,
697                         _("Not all of the space available to %s appears "
698                         "to be used, you can fix the GPT to use all of the "
699                         "space (an extra %llu blocks) or continue with the "
700                         "current setting? "), disk->dev->path, 
701                         (uint64_t)(last_usable_if_grown - last_usable));
702
703
704                 if (q == PED_EXCEPTION_FIX) {
705
706                         last_usable = last_usable_if_grown;
707                         *update_needed = 1;
708
709                 }
710                 else if (q != PED_EXCEPTION_UNHANDLED ) {
711
712                         asked_already = 1;
713                 }
714         }
715
716         ped_geometry_init (&gpt_disk_data->data_area, disk->dev,
717                            first_usable, last_usable - first_usable + 1);
718
719
720         gpt_disk_data->entry_count
721                 = PED_CPU_TO_LE32 (gpt->NumberOfPartitionEntries);
722         PED_ASSERT (gpt_disk_data->entry_count > 0, return 0);
723         PED_ASSERT (gpt_disk_data->entry_count <= 8192, return 0);
724
725         gpt_disk_data->uuid = gpt->DiskGUID;
726
727         return 1;
728 }
729
730 static PedPartition*
731 _parse_part_entry (PedDisk* disk, GuidPartitionEntry_t* pte)
732 {
733         PedPartition* part;
734         GPTPartitionData* gpt_part_data;
735         unsigned int i;
736
737         part = ped_partition_new (disk, 0, NULL,
738                         PED_LE64_TO_CPU(pte->StartingLBA),
739                         PED_LE64_TO_CPU(pte->EndingLBA));
740         if (!part)
741                 return NULL;
742
743         gpt_part_data = part->disk_specific;
744         gpt_part_data->type = pte->PartitionTypeGuid;
745         gpt_part_data->uuid = pte->UniquePartitionGuid;
746         for (i = 0; i < 72 / sizeof (efi_char16_t); i++)
747                 gpt_part_data->name[i] = (efi_char16_t) PED_LE16_TO_CPU(
748                                            (uint16_t) pte->PartitionName[i]);
749         gpt_part_data->name[i] = 0;
750         
751         gpt_part_data->lvm = gpt_part_data->raid 
752                 = gpt_part_data->boot = gpt_part_data->hp_service
753                 = gpt_part_data->hidden = gpt_part_data->msftres = 0;
754
755         if (pte->Attributes.RequiredToFunction & 0x1)
756                 gpt_part_data->hidden = 1;
757        
758         if (!guid_cmp (gpt_part_data->type, PARTITION_SYSTEM_GUID))
759                 gpt_part_data->boot = 1;
760         else if (!guid_cmp (gpt_part_data->type, PARTITION_RAID_GUID))
761                 gpt_part_data->raid = 1;
762         else if (!guid_cmp (gpt_part_data->type, PARTITION_LVM_GUID))
763                 gpt_part_data->lvm = 1;
764         else if (!guid_cmp (gpt_part_data->type, PARTITION_HPSERVICE_GUID))
765                 gpt_part_data->hp_service = 1;
766         else if (!guid_cmp (gpt_part_data->type, PARTITION_MSFT_RESERVED_GUID))
767                 gpt_part_data->msftres = 1;
768         
769         return part;
770 }
771
772 /************************************************************
773  *  Intel is changing the EFI Spec. (after v1.02) to say that a
774  *  disk is considered to have a GPT label only if the GPT
775  *  structures are correct, and the MBR is actually a Protective
776  *  MBR (has one 0xEE type partition).
777  *  Problem occurs when a GPT-partitioned disk is then
778  *  edited with a legacy (non-GPT-aware) application, such as
779  *  fdisk (which doesn't generally erase the PGPT or AGPT).
780  *  How should such a disk get handled?  As a GPT disk (throwing
781  *  away the fdisk changes), or as an MSDOS disk (throwing away
782  *  the GPT information).  Previously, I've taken the GPT-is-right,
783  *  MBR is wrong, approach, to stay consistent with the EFI Spec.
784  *  Intel disagrees, saying the disk should then be treated
785  *  as having a msdos label, not a GPT label.  If this is true,
786  *  then what's the point of having an AGPT, since if the PGPT
787  *  is screwed up, likely the PMBR is too, and the PMBR becomes
788  *  a single point of failure.
789  *  So, in the Linux kernel, I'm going to test for PMBR, and
790  *  warn if it's not there, and treat the disk as MSDOS, with a note
791  *  for users to use Parted to "fix up" their disk if they
792  *  really want it to be considered GPT.
793  ************************************************************/
794 static int
795 gpt_read (PedDisk * disk)
796 {
797         GPTDiskData *gpt_disk_data = disk->disk_specific;
798         GuidPartitionTableHeader_t* gpt;
799         GuidPartitionEntry_t* ptes;
800         int ptes_size;
801         int i;
802 #ifndef DISCOVER_ONLY
803         int write_back = 0;
804 #endif
805
806         ped_disk_delete_all (disk);
807
808         /* 
809          * motivation: let the user decide about the pmbr... during
810          * ped_disk_probe(), they probably didn't get a choice...
811          */
812         if (!gpt_probe (disk->dev))
813                 goto error;
814
815         if (_read_header (disk->dev, &gpt, 1)) {
816                 PED_ASSERT ((PedSector) PED_LE64_TO_CPU (gpt->AlternateLBA)
817                                 <= disk->dev->length - 1, goto error_free_gpt);
818                 if ((PedSector) PED_LE64_TO_CPU (gpt->AlternateLBA)
819                                 < disk->dev->length - 1) {
820                         char* zeros = ped_malloc (pth_get_size (disk->dev));
821
822 #ifndef DISCOVER_ONLY
823                         if (ped_exception_throw (
824                                 PED_EXCEPTION_ERROR,
825                                 PED_EXCEPTION_FIX | PED_EXCEPTION_CANCEL,
826                 _("The backup GPT table is not at the end of the disk, as it "
827                   "should be.  This might mean that another operating system "
828                   "believes the disk is smaller.  Fix, by moving the backup "
829                   "to the end (and removing the old backup)?"))
830                                         == PED_EXCEPTION_CANCEL)
831                                 goto error_free_gpt;
832
833                         write_back = 1;
834                         memset (zeros, 0, disk->dev->sector_size);
835                         ped_device_write (disk->dev, zeros,
836                                           PED_LE64_TO_CPU (gpt->AlternateLBA),
837                                           1);
838 #endif /* !DISCOVER_ONLY */
839                 }
840         } else { /* primary GPT *not* ok */
841                 int alternate_ok = 0;
842
843 #ifndef DISCOVER_ONLY
844                 write_back = 1;
845 #endif
846
847                 if ((PedSector) PED_LE64_TO_CPU (gpt->AlternateLBA)
848                                 < disk->dev->length - 1) {
849                         alternate_ok = _read_header (disk->dev, &gpt,
850                                             PED_LE64_TO_CPU(gpt->AlternateLBA));
851                 }
852                 if (!alternate_ok) {
853                         alternate_ok = _read_header (disk->dev, &gpt,
854                                                      disk->dev->length - 1);
855                 }
856
857                 if (alternate_ok) {
858                         if (ped_exception_throw (
859                                 PED_EXCEPTION_ERROR,
860                                 PED_EXCEPTION_OK_CANCEL,
861                                 _("The primary GPT table is corrupt, but the "
862                                   "backup appears OK, so that will be used."))
863                                     == PED_EXCEPTION_CANCEL)
864                                 goto error_free_gpt;
865                 } else {
866                         ped_exception_throw (
867                                 PED_EXCEPTION_ERROR,
868                                 PED_EXCEPTION_CANCEL,
869                                 _("Both the primary and backup GPT tables "
870                                   "are corrupt.  Try making a fresh table, "
871                                   "and using Parted's rescue feature to "
872                                   "recover partitions."));
873                         goto error;
874                 }
875         }
876
877         if (!_parse_header (disk, gpt, &write_back))
878                 goto error_free_gpt;
879
880
881         ptes_size = sizeof (GuidPartitionEntry_t) * gpt_disk_data->entry_count;
882         ptes = (GuidPartitionEntry_t*) ped_malloc (ptes_size);
883         if (!ped_device_read (disk->dev, ptes,
884                               PED_LE64_TO_CPU(gpt->PartitionEntryLBA),
885                               ptes_size / disk->dev->sector_size))
886                 goto error_free_ptes;
887
888         for (i = 0; i < gpt_disk_data->entry_count; i++) {
889                 PedPartition* part;
890                 PedConstraint* constraint_exact;
891
892                 if (!guid_cmp (ptes[i].PartitionTypeGuid, UNUSED_ENTRY_GUID))
893                         continue;
894
895                 part = _parse_part_entry (disk, &ptes[i]);
896                 if (!part)
897                         goto error_delete_all;
898
899                 part->fs_type = ped_file_system_probe (&part->geom);
900                 part->num = i + 1;
901
902                 constraint_exact = ped_constraint_exact (&part->geom);
903                 if (!ped_disk_add_partition(disk, part, constraint_exact)) {
904                         ped_partition_destroy (part);
905                         goto error_delete_all;
906                 }
907                 ped_constraint_destroy (constraint_exact);
908         }
909         ped_free (ptes);
910
911 #ifndef DISCOVER_ONLY
912         if (write_back)
913                 ped_disk_commit_to_dev (disk);
914 #endif
915
916         return 1;
917
918 error_delete_all:
919         ped_disk_delete_all (disk);
920 error_free_ptes:
921         ped_free (ptes);
922 error_free_gpt:
923         pth_free (gpt);
924 error:
925         return 0;
926 }
927
928 #ifndef DISCOVER_ONLY
929 /* Writes the protective MBR (to keep DOS happy) */
930 static int
931 _write_pmbr (PedDevice * dev)
932 {
933         LegacyMBR_t pmbr;
934
935         memset(&pmbr, 0, sizeof(pmbr));
936         pmbr.Signature = PED_CPU_TO_LE16(MSDOS_MBR_SIGNATURE);
937         pmbr.PartitionRecord[0].OSType      = EFI_PMBR_OSTYPE_EFI;
938         pmbr.PartitionRecord[0].StartSector = 1;
939         pmbr.PartitionRecord[0].EndHead     = 0xFE;
940         pmbr.PartitionRecord[0].EndSector   = 0xFF;
941         pmbr.PartitionRecord[0].EndTrack    = 0xFF;
942         pmbr.PartitionRecord[0].StartingLBA = PED_CPU_TO_LE32(1);
943         if ((dev->length - 1ULL) > 0xFFFFFFFFULL) 
944                 pmbr.PartitionRecord[0].SizeInLBA = PED_CPU_TO_LE32(0xFFFFFFFF);
945         else
946                 pmbr.PartitionRecord[0].SizeInLBA = PED_CPU_TO_LE32(dev->length - 1UL);
947
948         return ped_device_write (dev, &pmbr, GPT_PMBR_LBA, GPT_PMBR_SECTORS);
949 }
950
951 static void
952 _generate_header (PedDisk* disk, int alternate, uint32_t ptes_crc,
953                   GuidPartitionTableHeader_t** gpt_p)
954 {
955         GPTDiskData* gpt_disk_data = disk->disk_specific;
956         GuidPartitionTableHeader_t* gpt;
957
958         *gpt_p = pth_new_zeroed (disk->dev);
959         
960         gpt = *gpt_p;
961         
962         gpt->Signature = PED_CPU_TO_LE64 (GPT_HEADER_SIGNATURE);
963         gpt->Revision = PED_CPU_TO_LE32 (GPT_HEADER_REVISION_V1_00);
964
965         /* per 1.00 spec */
966         gpt->HeaderSize = PED_CPU_TO_LE32 (pth_get_size_static (disk->dev));
967         gpt->HeaderCRC32 = 0;
968         gpt->Reserved1 = 0;
969
970         if (alternate) {
971                 PedSector ptes_size = gpt_disk_data->entry_count
972                                       * sizeof (GuidPartitionEntry_t) / disk->dev->sector_size;
973
974                 gpt->MyLBA = PED_CPU_TO_LE64 (disk->dev->length - 1);
975                 gpt->AlternateLBA = PED_CPU_TO_LE64 (1);
976                 gpt->PartitionEntryLBA
977                         = PED_CPU_TO_LE64 (disk->dev->length - 1 - ptes_size);
978         } else {
979                 gpt->MyLBA = PED_CPU_TO_LE64 (1);
980                 gpt->AlternateLBA = PED_CPU_TO_LE64 (disk->dev->length - 1);
981                 gpt->PartitionEntryLBA = PED_CPU_TO_LE64 (2);
982         }
983
984         gpt->FirstUsableLBA = PED_CPU_TO_LE64 (gpt_disk_data->data_area.start);
985         gpt->LastUsableLBA = PED_CPU_TO_LE64 (gpt_disk_data->data_area.end);
986         gpt->DiskGUID = gpt_disk_data->uuid;
987         gpt->NumberOfPartitionEntries
988                 = PED_CPU_TO_LE32 (gpt_disk_data->entry_count);
989         gpt->SizeOfPartitionEntry
990                 = PED_CPU_TO_LE32 (sizeof (GuidPartitionEntry_t));
991         gpt->PartitionEntryArrayCRC32 = PED_CPU_TO_LE32 (ptes_crc);
992         gpt->HeaderCRC32 = PED_CPU_TO_LE32 (pth_crc32 (disk->dev, gpt));
993 }
994
995 static void
996 _partition_generate_part_entry (PedPartition* part, GuidPartitionEntry_t* pte)
997 {
998         GPTPartitionData* gpt_part_data = part->disk_specific;
999         unsigned int i;
1000
1001         PED_ASSERT (gpt_part_data != NULL, return);
1002
1003         pte->PartitionTypeGuid = gpt_part_data->type;
1004         pte->UniquePartitionGuid = gpt_part_data->uuid;
1005         pte->StartingLBA = PED_CPU_TO_LE64(part->geom.start);
1006         pte->EndingLBA = PED_CPU_TO_LE64(part->geom.end);
1007         memset (&pte->Attributes, 0, sizeof (GuidPartitionEntryAttributes_t));
1008
1009         if (gpt_part_data->hidden)
1010                 pte->Attributes.RequiredToFunction = 1;
1011         
1012         for (i = 0; i < 72 / sizeof(efi_char16_t); i++)
1013                 pte->PartitionName[i]
1014                         = (efi_char16_t) PED_CPU_TO_LE16(
1015                                 (uint16_t) gpt_part_data->name[i]);
1016 }
1017
1018 static int
1019 gpt_write(PedDisk * disk)
1020 {
1021         GPTDiskData* gpt_disk_data;
1022         GuidPartitionEntry_t* ptes;
1023         uint32_t ptes_crc;
1024         uint8_t* pth_raw = ped_malloc (pth_get_size (disk->dev));
1025         GuidPartitionTableHeader_t* gpt;
1026         PedPartition* part;
1027         int ptes_size;
1028         unsigned int i;
1029
1030         PED_ASSERT (disk != NULL, goto error);
1031         PED_ASSERT (disk->dev != NULL, goto error);
1032         PED_ASSERT (disk->disk_specific != NULL, goto error);
1033
1034         gpt_disk_data = disk->disk_specific;
1035
1036         ptes_size = sizeof (GuidPartitionEntry_t) * gpt_disk_data->entry_count;
1037         ptes = (GuidPartitionEntry_t*) ped_malloc (ptes_size);
1038         if (!ptes)
1039                 goto error;
1040         memset (ptes, 0, ptes_size);
1041         for (part = ped_disk_next_partition (disk, NULL); part;
1042              part = ped_disk_next_partition (disk, part)) {
1043                 if (part->type != 0)
1044                         continue;
1045                 _partition_generate_part_entry (part, &ptes[part->num - 1]);
1046         }
1047
1048         ptes_crc = efi_crc32 (ptes, ptes_size);
1049
1050         /* Write protective MBR */
1051         if (!_write_pmbr (disk->dev))
1052                 goto error_free_ptes;
1053
1054         /* Write PTH and PTEs */
1055         _generate_header (disk, 0, ptes_crc, &gpt);
1056         pth_raw = pth_get_raw (disk->dev, gpt);
1057         if (!ped_device_write (disk->dev, pth_raw, 1, 1))
1058                 goto error_free_ptes;
1059         if (!ped_device_write (disk->dev, ptes, 2, ptes_size / disk->dev->sector_size))
1060                 goto error_free_ptes;
1061
1062         /* Write Alternate PTH & PTEs */
1063         _generate_header (disk, 1, ptes_crc, &gpt);
1064         pth_raw = pth_get_raw (disk->dev, gpt);
1065         if (!ped_device_write (disk->dev, pth_raw, disk->dev->length - 1, 1))
1066                 goto error_free_ptes;
1067         if (!ped_device_write (disk->dev, ptes,
1068                                disk->dev->length - 1 - ptes_size / disk->dev->sector_size,
1069                                ptes_size / disk->dev->sector_size))
1070                 goto error_free_ptes;
1071
1072         ped_free (ptes);
1073         return ped_device_sync (disk->dev);
1074
1075 error_free_ptes:
1076         ped_free (ptes);
1077 error:
1078         return 0;
1079 }
1080 #endif /* !DISCOVER_ONLY */
1081
1082 static int
1083 add_metadata_part(PedDisk * disk, PedSector start, PedSector length)
1084 {
1085         PedPartition* part;
1086         PedConstraint* constraint_exact;
1087         PED_ASSERT(disk != NULL, return 0);
1088
1089         part = ped_partition_new (disk, PED_PARTITION_METADATA, NULL,
1090                                   start, start + length - 1);
1091         if (!part)
1092                 goto error;
1093
1094         constraint_exact = ped_constraint_exact (&part->geom);
1095         if (!ped_disk_add_partition (disk, part, constraint_exact))
1096                 goto error_destroy_constraint;
1097         ped_constraint_destroy (constraint_exact);
1098         return 1;
1099
1100 error_destroy_constraint:
1101         ped_constraint_destroy (constraint_exact);
1102 error_destroy_part:
1103         ped_partition_destroy (part);
1104 error:
1105         return 0;
1106 }
1107
1108 static PedPartition*
1109 gpt_partition_new (const PedDisk* disk,
1110                   PedPartitionType part_type, const PedFileSystemType* fs_type,
1111                   PedSector start, PedSector end)
1112 {
1113         PedPartition* part;
1114         GPTPartitionData* gpt_part_data;
1115
1116         part = _ped_partition_alloc (disk, part_type, fs_type, start, end);
1117         if (!part)
1118                 goto error;
1119
1120         if (part_type != 0)
1121                 return part;
1122
1123         gpt_part_data = part->disk_specific =
1124                 ped_malloc (sizeof (GPTPartitionData));
1125         if (!gpt_part_data)
1126                 goto error_free_part;
1127
1128         gpt_part_data->type = PARTITION_BASIC_DATA_GUID;
1129         gpt_part_data->lvm = 0;
1130         gpt_part_data->raid = 0;
1131         gpt_part_data->boot = 0;
1132         gpt_part_data->hp_service = 0;
1133         gpt_part_data->hidden = 0;
1134         gpt_part_data->msftres = 0;
1135         uuid_generate ((unsigned char*) &gpt_part_data->uuid);
1136         swap_uuid_and_efi_guid((unsigned char*)(&gpt_part_data->uuid));
1137         strcpy (gpt_part_data->name, "");
1138         return part;
1139
1140 error_free_part:
1141         _ped_partition_free (part);
1142 error:
1143         return NULL;
1144 }
1145
1146 static PedPartition*
1147 gpt_partition_duplicate (const PedPartition* part)
1148 {
1149         PedPartition* result;
1150         GPTPartitionData* part_data = part->disk_specific;
1151         GPTPartitionData* result_data;
1152
1153         result = _ped_partition_alloc (part->disk, part->type, part->fs_type,
1154                                        part->geom.start, part->geom.end);
1155         if (!result)
1156                 goto error;
1157         result->num = part->num;
1158
1159         if (result->type != 0)
1160                 return result;
1161
1162         result_data = result->disk_specific =
1163                 ped_malloc (sizeof (GPTPartitionData));
1164         if (!result_data)
1165                 goto error_free_part;
1166
1167         result_data->type = part_data->type;
1168         result_data->uuid = part_data->uuid;
1169         strcpy (result_data->name, part_data->name);
1170         return result;
1171
1172 error_free_part:
1173         _ped_partition_free (result);
1174 error:
1175         return NULL;
1176 }
1177
1178 static void
1179 gpt_partition_destroy (PedPartition *part)
1180 {
1181         if (part->type == 0) {
1182                 PED_ASSERT (part->disk_specific != NULL, return);
1183                 ped_free (part->disk_specific);
1184         }
1185
1186         _ped_partition_free (part);
1187 }
1188
1189 static int
1190 gpt_partition_set_system (PedPartition* part, const PedFileSystemType* fs_type)
1191 {
1192         GPTPartitionData* gpt_part_data = part->disk_specific;
1193
1194         PED_ASSERT (gpt_part_data != NULL, return 0);
1195
1196         part->fs_type = fs_type;
1197
1198         if (gpt_part_data->lvm) {
1199                 gpt_part_data->type = PARTITION_LVM_GUID;
1200                 return 1;
1201         }
1202         if (gpt_part_data->raid) {
1203                 gpt_part_data->type = PARTITION_RAID_GUID;
1204                 return 1;
1205         }
1206         if (gpt_part_data->boot) {
1207                 gpt_part_data->type = PARTITION_SYSTEM_GUID;
1208                 return 1;
1209         }
1210         if (gpt_part_data->hp_service) {
1211                 gpt_part_data->type = PARTITION_HPSERVICE_GUID;
1212                 return 1;
1213         }
1214         if (gpt_part_data->msftres) {
1215                 gpt_part_data->type = PARTITION_MSFT_RESERVED_GUID;
1216                 return 1;
1217         }
1218         
1219         if (fs_type) {
1220                 if (strncmp (fs_type->name, "fat", 3) == 0
1221                     || strcmp (fs_type->name, "ntfs") == 0) {
1222                         gpt_part_data->type = PARTITION_MSFT_RESERVED_GUID;
1223                         return 1;
1224                 }
1225                 if (strncmp (fs_type->name, "hfs", 3) == 0) {
1226                         gpt_part_data->type = PARTITION_APPLE_HFS_GUID;
1227                         return 1;
1228                 }
1229                 if (strstr (fs_type->name, "swap")) {
1230                         gpt_part_data->type = PARTITION_SWAP_GUID;
1231                         return 1;
1232                 }
1233         }
1234
1235         gpt_part_data->type = PARTITION_BASIC_DATA_GUID;
1236         return 1;
1237 }
1238
1239 /* Allocate metadata partitions for the GPTH and PTES */
1240 static int
1241 gpt_alloc_metadata (PedDisk * disk)
1242 {
1243         PedSector gptlength, pteslength = 0;
1244         GPTDiskData *gpt_disk_data;
1245
1246         PED_ASSERT(disk != NULL, return 0);
1247         PED_ASSERT(disk->dev != NULL, return 0);
1248         PED_ASSERT(disk->disk_specific != NULL, return 0);
1249         gpt_disk_data = disk->disk_specific;
1250
1251         gptlength = ped_div_round_up (sizeof (GuidPartitionTableHeader_t),
1252                                       disk->dev->sector_size);
1253         pteslength = ped_div_round_up (gpt_disk_data->entry_count
1254                                        * sizeof (GuidPartitionEntry_t), disk->dev->sector_size);
1255
1256         /* metadata at the start of the disk includes the MBR */
1257         if (!add_metadata_part(disk, GPT_PMBR_LBA,
1258                                GPT_PMBR_SECTORS + gptlength + pteslength))
1259                 return 0;
1260
1261         /* metadata at the end of the disk */
1262         if (!add_metadata_part(disk, disk->dev->length - gptlength - pteslength,
1263                                gptlength + pteslength))
1264                 return 0;
1265
1266         return 1;
1267 }
1268
1269 /* Does nothing, as the read/new/destroy functions maintain part->num */
1270 static int
1271 gpt_partition_enumerate (PedPartition* part)
1272 {
1273         GPTDiskData* gpt_disk_data = part->disk->disk_specific;
1274         int i;
1275
1276         /* never change the partition numbers */
1277         if (part->num != -1)
1278                 return 1;
1279
1280         for (i = 1; i <= gpt_disk_data->entry_count; i++) {
1281                 if (!ped_disk_get_partition (part->disk, i)) {
1282                         part->num = i;
1283                         return 1;
1284                 }
1285         }
1286
1287         PED_ASSERT (0, return 0); return 0;
1288 }
1289
1290 static int
1291 gpt_partition_set_flag(PedPartition *part,
1292                        PedPartitionFlag flag,
1293                        int state)
1294 {
1295         GPTPartitionData *gpt_part_data;
1296         PED_ASSERT(part != NULL, return 0);
1297         PED_ASSERT(part->disk_specific != NULL, return 0);
1298         gpt_part_data = part->disk_specific;
1299
1300         switch (flag) {
1301         case PED_PARTITION_BOOT:
1302                 gpt_part_data->boot = state;
1303                 if (state)
1304                         gpt_part_data->raid 
1305                                 = gpt_part_data->lvm
1306                                 = gpt_part_data->hp_service
1307                                 = gpt_part_data->msftres = 0;
1308                 return gpt_partition_set_system (part, part->fs_type);
1309         case PED_PARTITION_RAID:
1310                 gpt_part_data->raid = state;
1311                 if (state)
1312                         gpt_part_data->boot
1313                                 = gpt_part_data->lvm
1314                                 = gpt_part_data->hp_service
1315                                 = gpt_part_data->msftres = 0;
1316                 return gpt_partition_set_system (part, part->fs_type);
1317         case PED_PARTITION_LVM:
1318                 gpt_part_data->lvm = state;
1319                 if (state)
1320                         gpt_part_data->boot
1321                                 = gpt_part_data->raid
1322                                 = gpt_part_data->hp_service
1323                                 = gpt_part_data->msftres = 0;
1324                 return gpt_partition_set_system (part, part->fs_type);
1325         case PED_PARTITION_HPSERVICE:
1326                 gpt_part_data->hp_service = state;
1327                 if (state)
1328                         gpt_part_data->boot
1329                                 = gpt_part_data->raid
1330                                 = gpt_part_data->lvm
1331                                 = gpt_part_data->msftres = 0;
1332                 return gpt_partition_set_system (part, part->fs_type);
1333         case PED_PARTITION_MSFT_RESERVED:
1334                 gpt_part_data->msftres = state;
1335                 if (state)
1336                         gpt_part_data->boot
1337                                 = gpt_part_data->raid
1338                                 = gpt_part_data->lvm
1339                                 = gpt_part_data->hp_service = 0;
1340                 return gpt_partition_set_system (part, part->fs_type);
1341         case PED_PARTITION_HIDDEN:
1342                 gpt_part_data->hidden = state;
1343                 return 1;
1344         case PED_PARTITION_SWAP:
1345         case PED_PARTITION_ROOT:
1346         case PED_PARTITION_LBA:
1347         default:
1348                 return 0;
1349         }
1350         return 1;
1351 }
1352
1353 static int
1354 gpt_partition_get_flag(const PedPartition *part, PedPartitionFlag flag)
1355 {
1356         GPTPartitionData *gpt_part_data;
1357         PED_ASSERT(part->disk_specific != NULL, return 0);
1358         gpt_part_data = part->disk_specific;
1359
1360         switch (flag) {
1361         case PED_PARTITION_RAID:
1362                 return gpt_part_data->raid;
1363         case PED_PARTITION_LVM:
1364                 return gpt_part_data->lvm;
1365         case PED_PARTITION_BOOT:
1366                 return gpt_part_data->boot;
1367         case PED_PARTITION_HPSERVICE:
1368                 return gpt_part_data->hp_service;
1369         case PED_PARTITION_MSFT_RESERVED:
1370                 return gpt_part_data->msftres;
1371         case PED_PARTITION_HIDDEN:
1372                        return gpt_part_data->hidden;
1373         case PED_PARTITION_SWAP:
1374         case PED_PARTITION_LBA:
1375         case PED_PARTITION_ROOT:
1376         default:
1377                 return 0;
1378         }
1379         return 0;
1380 }
1381
1382 static int
1383 gpt_partition_is_flag_available(const PedPartition * part,
1384                                 PedPartitionFlag flag)
1385 {
1386         switch (flag) {
1387         case PED_PARTITION_RAID:
1388         case PED_PARTITION_LVM:
1389         case PED_PARTITION_BOOT:
1390         case PED_PARTITION_HPSERVICE:
1391         case PED_PARTITION_MSFT_RESERVED:
1392         case PED_PARTITION_HIDDEN:        
1393                 return 1;
1394         case PED_PARTITION_SWAP:
1395         case PED_PARTITION_ROOT:
1396         case PED_PARTITION_LBA:
1397         default:
1398                 return 0;
1399         }
1400         return 0;
1401 }
1402
1403 static void
1404 gpt_partition_set_name (PedPartition *part, const char *name)
1405 {
1406         GPTPartitionData *gpt_part_data = part->disk_specific;
1407
1408         strncpy (gpt_part_data->name, name, 36);
1409         gpt_part_data->name [36] = 0;
1410 }
1411
1412 static const char *
1413 gpt_partition_get_name (const PedPartition * part)
1414 {
1415         GPTPartitionData* gpt_part_data = part->disk_specific;
1416         return gpt_part_data->name;
1417 }
1418
1419 static int
1420 gpt_get_max_primary_partition_count (const PedDisk *disk)
1421 {
1422         const GPTDiskData* gpt_disk_data = disk->disk_specific;
1423         return gpt_disk_data->entry_count;
1424 }
1425
1426 static PedConstraint*
1427 _non_metadata_constraint (const PedDisk* disk)
1428 {
1429         GPTDiskData* gpt_disk_data = disk->disk_specific;
1430
1431         return ped_constraint_new_from_max (&gpt_disk_data->data_area);
1432 }
1433
1434 static int
1435 gpt_partition_align (PedPartition* part, const PedConstraint* constraint)
1436 {
1437         PED_ASSERT (part != NULL, return 0);
1438
1439         if (_ped_partition_attempt_align (part, constraint,
1440                         _non_metadata_constraint (part->disk)))
1441                 return 1;
1442
1443 #ifndef DISCOVER_ONLY
1444         ped_exception_throw (
1445                 PED_EXCEPTION_ERROR,
1446                 PED_EXCEPTION_CANCEL,
1447                 _("Unable to satisfy all constraints on the partition."));
1448 #endif
1449         return 0;
1450 }
1451
1452 static PedDiskOps gpt_disk_ops = {
1453         probe:          gpt_probe,
1454 #ifndef DISCOVER_ONLY
1455         clobber:        gpt_clobber,
1456 #else
1457         clobber:        NULL,
1458 #endif
1459         alloc:          gpt_alloc,
1460         duplicate:      gpt_duplicate,
1461         free:           gpt_free,
1462         read:           gpt_read,
1463 #ifndef DISCOVER_ONLY
1464         write:          gpt_write,
1465 #else
1466         write:          NULL,
1467 #endif
1468         partition_new:                  gpt_partition_new,
1469         partition_duplicate:            gpt_partition_duplicate,
1470         partition_destroy:              gpt_partition_destroy,
1471         partition_set_system:           gpt_partition_set_system,
1472         partition_set_flag:             gpt_partition_set_flag,
1473         partition_get_flag:             gpt_partition_get_flag,
1474         partition_is_flag_available:    gpt_partition_is_flag_available,
1475         partition_set_name:             gpt_partition_set_name,
1476         partition_get_name:             gpt_partition_get_name,
1477         partition_align:                gpt_partition_align,
1478         partition_enumerate:            gpt_partition_enumerate,
1479         alloc_metadata:                 gpt_alloc_metadata,
1480         get_max_primary_partition_count: gpt_get_max_primary_partition_count
1481 };
1482
1483 static PedDiskType gpt_disk_type = {
1484         next:           NULL,
1485         name:           "gpt",
1486         ops:            &gpt_disk_ops,
1487         features:       PED_DISK_TYPE_PARTITION_NAME
1488 };
1489
1490 void
1491 ped_disk_gpt_init()
1492 {
1493         PED_ASSERT(sizeof(GuidPartitionEntryAttributes_t) == 8, return);
1494         PED_ASSERT(sizeof(GuidPartitionEntry_t) == 128, return);
1495
1496         ped_register_disk_type(&gpt_disk_type);
1497 }
1498
1499 void
1500 ped_disk_gpt_done()
1501 {
1502         ped_unregister_disk_type(&gpt_disk_type);
1503 }
1504