gpt.cc revision 1d1448a82d62ad32a8d597ed9ade46b4f37d8eb5
1/* gpt.cc -- Functions for loading, saving, and manipulating legacy MBR and GPT partition
2   data. */
3
4/* By Rod Smith, initial coding January to February, 2009 */
5
6/* This program is copyright (c) 2009 by Roderick W. Smith. It is distributed
7  under the terms of the GNU GPL version 2, as detailed in the COPYING file. */
8
9#define __STDC_LIMIT_MACROS
10#define __STDC_CONSTANT_MACROS
11
12#include <stdio.h>
13#include <unistd.h>
14#include <stdlib.h>
15#include <stdint.h>
16#include <fcntl.h>
17#include <string.h>
18#include <time.h>
19#include <sys/stat.h>
20#include <errno.h>
21#include "crc32.h"
22#include "gpt.h"
23#include "bsd.h"
24#include "support.h"
25#include "parttypes.h"
26#include "attributes.h"
27
28using namespace std;
29
30/****************************************
31 *                                      *
32 * GPTData class and related structures *
33 *                                      *
34 ****************************************/
35
36// Default constructor
37GPTData::GPTData(void) {
38   blockSize = SECTOR_SIZE; // set a default
39   diskSize = 0;
40   partitions = NULL;
41   state = gpt_valid;
42   strcpy(device, "");
43   mainCrcOk = 0;
44   secondCrcOk = 0;
45   mainPartsCrcOk = 0;
46   secondPartsCrcOk = 0;
47   apmFound = 0;
48   bsdFound = 0;
49   sectorAlignment = 8; // Align partitions on 4096-byte boundaries by default
50   srand((unsigned int) time(NULL));
51   SetGPTSize(NUM_GPT_ENTRIES);
52} // GPTData default constructor
53
54// The following constructor loads GPT data from a device file
55GPTData::GPTData(char* filename) {
56   blockSize = SECTOR_SIZE; // set a default
57   diskSize = 0;
58   partitions = NULL;
59   state = gpt_invalid;
60   strcpy(device, "");
61   mainCrcOk = 0;
62   secondCrcOk = 0;
63   mainPartsCrcOk = 0;
64   secondPartsCrcOk = 0;
65   apmFound = 0;
66   bsdFound = 0;
67   sectorAlignment = 8; // Align partitions on 4096-byte boundaries by default
68   srand((unsigned int) time(NULL));
69   LoadPartitions(filename);
70} // GPTData(char* filename) constructor
71
72// Destructor
73GPTData::~GPTData(void) {
74   free(partitions);
75} // GPTData destructor
76
77/*********************************************************************
78 *                                                                   *
79 * Begin functions that verify data, or that adjust the verification *
80 * information (compute CRCs, rebuild headers)                       *
81 *                                                                   *
82 *********************************************************************/
83
84// Perform detailed verification, reporting on any problems found, but
85// do *NOT* recover from these problems. Returns the total number of
86// problems identified.
87int GPTData::Verify(void) {
88   int problems = 0, numSegments, i;
89   uint64_t totalFree, largestSegment, firstSector;
90   char tempStr[255], siTotal[255], siLargest[255];
91
92   // First, check for CRC errors in the GPT data....
93   if (!mainCrcOk) {
94      problems++;
95      printf("\nProblem: The CRC for the main GPT header is invalid. The main GPT header may\n"
96            "be corrupt. Consider loading the backup GPT header to rebuild the main GPT\n"
97            "header\n");
98   } // if
99   if (!mainPartsCrcOk) {
100      problems++;
101      printf("\nProblem: The CRC for the main partition table is invalid. This table may be\n"
102            "corrupt. Consider loading the backup partition table.\n");
103   } // if
104   if (!secondCrcOk) {
105      problems++;
106      printf("\nProblem: The CRC for the backup GPT header is invalid. The backup GPT header\n"
107            "may be corrupt. Consider using the main GPT header to rebuild the backup GPT\n"
108            "header.\n");
109   } // if
110   if (!secondPartsCrcOk) {
111      problems++;
112      printf("\nCaution: The CRC for the backup partition table is invalid. This table may\n"
113            "be corrupt. This program will automatically create a new backup partition\n"
114            "table when you save your partitions.\n");
115   } // if
116
117   // Now check that the main and backup headers both point to themselves....
118   if (mainHeader.currentLBA != 1) {
119      problems++;
120      printf("\nProblem: The main header's self-pointer doesn't point to itself. This problem\n"
121             "is being automatically corrected, but it may be a symptom of more serious\n"
122             "problems. Think carefully before saving changes with 'w' or using this disk.\n");
123      mainHeader.currentLBA = 1;
124   } // if
125   if (secondHeader.currentLBA != (diskSize - UINT64_C(1))) {
126      problems++;
127      printf("\nProblem: The secondary header's self-pointer indicates that it doesn't reside\n"
128             "at the end of the disk. If you've added a disk to a RAID array, use the 'e'\n"
129             "option on the experts' menu to adjust the secondary header's and partition\n"
130             "table's locations.\n");
131   } // if
132
133   // Now check that critical main and backup GPT entries match each other
134   if (mainHeader.currentLBA != secondHeader.backupLBA) {
135      problems++;
136      printf("\nProblem: main GPT header's current LBA pointer (%llu) doesn't\n"
137            "match the backup GPT header's LBA pointer(%llu)\n",
138            (unsigned long long) mainHeader.currentLBA,
139             (unsigned long long) secondHeader.backupLBA);
140   } // if
141   if (mainHeader.backupLBA != secondHeader.currentLBA) {
142      problems++;
143      printf("\nProblem: main GPT header's backup LBA pointer (%llu) doesn't\n"
144            "match the backup GPT header's current LBA pointer (%llu)\n",
145            (unsigned long long) mainHeader.backupLBA,
146             (unsigned long long) secondHeader.currentLBA);
147   } // if
148   if (mainHeader.firstUsableLBA != secondHeader.firstUsableLBA) {
149      problems++;
150      printf("\nProblem: main GPT header's first usable LBA pointer (%llu) doesn't\n"
151            "match the backup GPT header's first usable LBA pointer (%llu)\n",
152            (unsigned long long) mainHeader.firstUsableLBA,
153             (unsigned long long) secondHeader.firstUsableLBA);
154   } // if
155   if (mainHeader.lastUsableLBA != secondHeader.lastUsableLBA) {
156      problems++;
157      printf("\nProblem: main GPT header's last usable LBA pointer (%llu) doesn't\n"
158            "match the backup GPT header's last usable LBA pointer (%llu)\n",
159            (unsigned long long) mainHeader.lastUsableLBA,
160             (unsigned long long) secondHeader.lastUsableLBA);
161   } // if
162   if ((mainHeader.diskGUID.data1 != secondHeader.diskGUID.data1) ||
163        (mainHeader.diskGUID.data2 != secondHeader.diskGUID.data2)) {
164      problems++;
165      printf("\nProblem: main header's disk GUID (%s) doesn't\n",
166             GUIDToStr(mainHeader.diskGUID, tempStr));
167      printf("match the backup GPT header's disk GUID (%s)\n",
168             GUIDToStr(secondHeader.diskGUID, tempStr));
169   } // if
170   if (mainHeader.numParts != secondHeader.numParts) {
171      problems++;
172      printf("\nProblem: main GPT header's number of partitions (%lu) doesn't\n"
173            "match the backup GPT header's number of partitions (%lu)\n",
174            (unsigned long) mainHeader.numParts,
175            (unsigned long) secondHeader.numParts);
176   } // if
177   if (mainHeader.sizeOfPartitionEntries != secondHeader.sizeOfPartitionEntries) {
178      problems++;
179      printf("\nProblem: main GPT header's size of partition entries (%lu) doesn't\n"
180            "match the backup GPT header's size of partition entries (%lu)\n",
181            (unsigned long) mainHeader.sizeOfPartitionEntries,
182            (unsigned long) secondHeader.sizeOfPartitionEntries);
183   } // if
184
185   // Now check for a few other miscellaneous problems...
186   // Check that the disk size will hold the data...
187   if (mainHeader.backupLBA > diskSize) {
188      problems++;
189      printf("\nProblem: Disk is too small to hold all the data!\n");
190      printf("(Disk size is %llu sectors, needs to be %llu sectors.)\n",
191            (unsigned long long) diskSize,
192               (unsigned long long) mainHeader.backupLBA);
193   } // if
194
195   // Check for overlapping partitions....
196   problems += FindOverlaps();
197
198   // Check for mismatched MBR and GPT partitions...
199   problems += FindHybridMismatches();
200
201   // Verify that partitions don't run into GPT data areas....
202   problems += CheckGPTSize();
203
204   // Check that partitions are aligned on proper boundaries (for WD Advanced
205   // Format and similar disks)....
206   for (i = 0; i < mainHeader.numParts; i++) {
207      if ((partitions[i].GetFirstLBA() % sectorAlignment) != 0) {
208         printf("\nCaution: Partition %d doesn't begin on a %d-sector boundary. This may\n"
209                "result in degraded performance on some modern (2010 and later) hard disks.\n",
210                i + 1, sectorAlignment);
211      } // if
212   } // for
213
214   // Now compute available space, but only if no problems found, since
215   // problems could affect the results
216   if (problems == 0) {
217      totalFree = FindFreeBlocks(&numSegments, &largestSegment);
218      BytesToSI(totalFree * (uint64_t) blockSize, siTotal);
219      BytesToSI(largestSegment * (uint64_t) blockSize, siLargest);
220      printf("No problems found. %llu free sectors (%s) available in %u\n"
221             "segments, the largest of which is %llu sectors (%s) in size\n",
222             (unsigned long long) totalFree,
223              siTotal, numSegments, (unsigned long long) largestSegment,
224                                     siLargest);
225   } else {
226      printf("\nIdentified %d problems!\n", problems);
227   } // if/else
228
229   return (problems);
230} // GPTData::Verify()
231
232// Checks to see if the GPT tables overrun existing partitions; if they
233// do, issues a warning but takes no action. Returns number of problems
234// detected (0 if OK, 1 to 2 if problems).
235int GPTData::CheckGPTSize(void) {
236   uint64_t overlap, firstUsedBlock, lastUsedBlock;
237   uint32_t i;
238   int numProbs = 0;
239
240   // first, locate the first & last used blocks
241   firstUsedBlock = UINT64_MAX;
242   lastUsedBlock = 0;
243   for (i = 0; i < mainHeader.numParts; i++) {
244      if ((partitions[i].GetFirstLBA() < firstUsedBlock) &&
245           (partitions[i].GetFirstLBA() != 0))
246         firstUsedBlock = partitions[i].GetFirstLBA();
247      if (partitions[i].GetLastLBA() > lastUsedBlock)
248         lastUsedBlock = partitions[i].GetLastLBA();
249   } // for
250
251   // If the disk size is 0 (the default), then it means that various
252   // variables aren't yet set, so the below tests will be useless;
253   // therefore we should skip everything
254   if (diskSize != 0) {
255      if (mainHeader.firstUsableLBA > firstUsedBlock) {
256         overlap = mainHeader.firstUsableLBA - firstUsedBlock;
257         printf("Warning! Main partition table overlaps the first partition by %lu blocks!\n",
258                (unsigned long) overlap);
259         if (firstUsedBlock > 2) {
260            printf("Try reducing the partition table size by %lu entries.\n",
261                   (unsigned long) (overlap * 4));
262            printf("(Use the 's' item on the experts' menu.)\n");
263         } else {
264            printf("You will need to delete this partition or resize it in another utility.\n");
265         } // if/else
266         numProbs++;
267      } // Problem at start of disk
268      if (mainHeader.lastUsableLBA < lastUsedBlock) {
269         overlap = lastUsedBlock - mainHeader.lastUsableLBA;
270         printf("Warning! Secondary partition table overlaps the last partition by %lu blocks\n",
271                (unsigned long) overlap);
272         if (lastUsedBlock > (diskSize - 2)) {
273            printf("You will need to delete this partition or resize it in another utility.\n");
274         } else {
275            printf("Try reducing the partition table size by %lu entries.\n",
276                   (unsigned long) (overlap * 4));
277            printf("(Use the 's' item on the experts' menu.)\n");
278         } // if/else
279         numProbs++;
280      } // Problem at end of disk
281   } // if (diskSize != 0)
282   return numProbs;
283} // GPTData::CheckGPTSize()
284
285// Check the validity of the GPT header. Returns 1 if the main header
286// is valid, 2 if the backup header is valid, 3 if both are valid, and
287// 0 if neither is valid. Note that this function just checks the GPT
288// signature and revision numbers, not CRCs or other data.
289int GPTData::CheckHeaderValidity(void) {
290   int valid = 3;
291
292   if (mainHeader.signature != GPT_SIGNATURE) {
293      valid -= 1;
294//      printf("Main GPT signature invalid; read 0x%016llX, should be\n0x%016llX\n",
295//             (unsigned long long) mainHeader.signature, (unsigned long long) GPT_SIGNATURE);
296   } else if ((mainHeader.revision != 0x00010000) && valid) {
297      valid -= 1;
298      printf("Unsupported GPT version in main header; read 0x%08lX, should be\n0x%08lX\n",
299             (unsigned long) mainHeader.revision, UINT32_C(0x00010000));
300   } // if/else/if
301
302   if (secondHeader.signature != GPT_SIGNATURE) {
303      valid -= 2;
304//      printf("Secondary GPT signature invalid; read 0x%016llX, should be\n0x%016llX\n",
305//             (unsigned long long) secondHeader.signature, (unsigned long long) GPT_SIGNATURE);
306   } else if ((secondHeader.revision != 0x00010000) && valid) {
307      valid -= 2;
308      printf("Unsupported GPT version in backup header; read 0x%08lX, should be\n0x%08lX\n",
309             (unsigned long) mainHeader.revision, UINT32_C(0x00010000));
310   } // if/else/if
311
312   // If MBR bad, check for an Apple disk signature
313   if ((protectiveMBR.GetValidity() == invalid) &&
314        (((mainHeader.signature << 32) == APM_SIGNATURE1) ||
315        (mainHeader.signature << 32) == APM_SIGNATURE2)) {
316      apmFound = 1; // Will display warning message later
317   } // if
318
319        return valid;
320} // GPTData::CheckHeaderValidity()
321
322// Check the header CRC to see if it's OK...
323// Note: Must be called BEFORE byte-order reversal on big-endian
324// systems!
325int GPTData::CheckHeaderCRC(struct GPTHeader* header) {
326   uint32_t oldCRC, newCRC, hSize;
327
328   // Back up old header CRC and then blank it, since it must be 0 for
329   // computation to be valid
330   oldCRC = header->headerCRC;
331   header->headerCRC = UINT32_C(0);
332   hSize = header->headerSize;
333
334   // If big-endian system, reverse byte order
335   if (IsLittleEndian() == 0) {
336      ReverseBytes(&oldCRC, 4);
337   } // if
338
339   // Initialize CRC functions...
340   chksum_crc32gentab();
341
342   // Compute CRC, restore original, and return result of comparison
343   newCRC = chksum_crc32((unsigned char*) header, HEADER_SIZE);
344   header->headerCRC = oldCRC;
345   return (oldCRC == newCRC);
346} // GPTData::CheckHeaderCRC()
347
348// Recompute all the CRCs. Must be called before saving (but after reversing
349// byte order on big-endian systems) if any changes have been made.
350void GPTData::RecomputeCRCs(void) {
351   uint32_t crc, hSize, trueNumParts;
352   int littleEndian = 1;
353
354   // Initialize CRC functions...
355   chksum_crc32gentab();
356
357   hSize = mainHeader.headerSize;
358   littleEndian = IsLittleEndian();
359
360   // Compute CRC of partition tables & store in main and secondary headers
361   trueNumParts = mainHeader.numParts;
362   if (littleEndian == 0)
363      ReverseBytes(&trueNumParts, 4); // unreverse this key piece of data....
364   crc = chksum_crc32((unsigned char*) partitions, trueNumParts * GPT_SIZE);
365   mainHeader.partitionEntriesCRC = crc;
366   secondHeader.partitionEntriesCRC = crc;
367   if (littleEndian == 0) {
368      ReverseBytes(&mainHeader.partitionEntriesCRC, 4);
369      ReverseBytes(&secondHeader.partitionEntriesCRC, 4);
370   } // if
371
372   // Zero out GPT tables' own CRCs (required for correct computation)
373   mainHeader.headerCRC = 0;
374   secondHeader.headerCRC = 0;
375
376   // Compute & store CRCs of main & secondary headers...
377   crc = chksum_crc32((unsigned char*) &mainHeader, hSize);
378   if (littleEndian == 0)
379      ReverseBytes(&crc, 4);
380   mainHeader.headerCRC = crc;
381   crc = chksum_crc32((unsigned char*) &secondHeader, hSize);
382   if (littleEndian == 0)
383      ReverseBytes(&crc, 4);
384   secondHeader.headerCRC = crc;
385} // GPTData::RecomputeCRCs()
386
387// Rebuild the main GPT header, using the secondary header as a model.
388// Typically called when the main header has been found to be corrupt.
389void GPTData::RebuildMainHeader(void) {
390   int i;
391
392   mainHeader.signature = GPT_SIGNATURE;
393   mainHeader.revision = secondHeader.revision;
394   mainHeader.headerSize = secondHeader.headerSize;
395   mainHeader.headerCRC = UINT32_C(0);
396   mainHeader.reserved = secondHeader.reserved;
397   mainHeader.currentLBA = secondHeader.backupLBA;
398   mainHeader.backupLBA = secondHeader.currentLBA;
399   mainHeader.firstUsableLBA = secondHeader.firstUsableLBA;
400   mainHeader.lastUsableLBA = secondHeader.lastUsableLBA;
401   mainHeader.diskGUID.data1 = secondHeader.diskGUID.data1;
402   mainHeader.diskGUID.data2 = secondHeader.diskGUID.data2;
403   mainHeader.partitionEntriesLBA = UINT64_C(2);
404   mainHeader.numParts = secondHeader.numParts;
405   mainHeader.sizeOfPartitionEntries = secondHeader.sizeOfPartitionEntries;
406   mainHeader.partitionEntriesCRC = secondHeader.partitionEntriesCRC;
407   for (i = 0 ; i < GPT_RESERVED; i++)
408      mainHeader.reserved2[i] = secondHeader.reserved2[i];
409} // GPTData::RebuildMainHeader()
410
411// Rebuild the secondary GPT header, using the main header as a model.
412void GPTData::RebuildSecondHeader(void) {
413   int i;
414
415   secondHeader.signature = GPT_SIGNATURE;
416   secondHeader.revision = mainHeader.revision;
417   secondHeader.headerSize = mainHeader.headerSize;
418   secondHeader.headerCRC = UINT32_C(0);
419   secondHeader.reserved = mainHeader.reserved;
420   secondHeader.currentLBA = mainHeader.backupLBA;
421   secondHeader.backupLBA = mainHeader.currentLBA;
422   secondHeader.firstUsableLBA = mainHeader.firstUsableLBA;
423   secondHeader.lastUsableLBA = mainHeader.lastUsableLBA;
424   secondHeader.diskGUID.data1 = mainHeader.diskGUID.data1;
425   secondHeader.diskGUID.data2 = mainHeader.diskGUID.data2;
426   secondHeader.partitionEntriesLBA = secondHeader.lastUsableLBA + UINT64_C(1);
427   secondHeader.numParts = mainHeader.numParts;
428   secondHeader.sizeOfPartitionEntries = mainHeader.sizeOfPartitionEntries;
429   secondHeader.partitionEntriesCRC = mainHeader.partitionEntriesCRC;
430   for (i = 0 ; i < GPT_RESERVED; i++)
431      secondHeader.reserved2[i] = mainHeader.reserved2[i];
432} // GPTData::RebuildSecondHeader()
433
434// Search for hybrid MBR entries that have no corresponding GPT partition.
435// Returns number of such mismatches found
436int GPTData::FindHybridMismatches(void) {
437   int i, j, found, numFound = 0;
438   uint64_t mbrFirst, mbrLast;
439
440   for (i = 0; i < 4; i++) {
441      if ((protectiveMBR.GetType(i) != 0xEE) && (protectiveMBR.GetType(i) != 0x00)) {
442         j = 0;
443         found = 0;
444         do {
445            mbrFirst = (uint64_t) protectiveMBR.GetFirstSector(i);
446            mbrLast = mbrFirst + (uint64_t) protectiveMBR.GetLength(i) - UINT64_C(1);
447            if ((partitions[j].GetFirstLBA() == mbrFirst) &&
448                (partitions[j].GetLastLBA() == mbrLast))
449               found = 1;
450            j++;
451         } while ((!found) && (j < mainHeader.numParts));
452         if (!found) {
453            numFound++;
454            printf("\nWarning! Mismatched GPT and MBR partitions! MBR partition "
455                   "%d, of type 0x%02X,\nhas no corresponding GPT partition! "
456                   "You may continue, but this condition\nmight cause data loss"
457                   " in the future!\a\n", i + 1, protectiveMBR.GetType(i));
458         } // if
459      } // if
460   } // for
461   return numFound;
462} // GPTData::FindHybridMismatches
463
464// Find overlapping partitions and warn user about them. Returns number of
465// overlapping partitions.
466int GPTData::FindOverlaps(void) {
467   int i, j, problems = 0;
468
469   for (i = 1; i < mainHeader.numParts; i++) {
470      for (j = 0; j < i; j++) {
471         if (partitions[i].DoTheyOverlap(&partitions[j])) {
472            problems++;
473            printf("\nProblem: partitions %d and %d overlap:\n", i + 1, j + 1);
474            printf("  Partition %d: %llu to %llu\n", i,
475                   (unsigned long long) partitions[i].GetFirstLBA(),
476                    (unsigned long long) partitions[i].GetLastLBA());
477            printf("  Partition %d: %llu to %llu\n", j,
478                   (unsigned long long) partitions[j].GetFirstLBA(),
479                    (unsigned long long) partitions[j].GetLastLBA());
480         } // if
481      } // for j...
482   } // for i...
483   return problems;
484} // GPTData::FindOverlaps()
485
486/******************************************************************
487 *                                                                *
488 * Begin functions that load data from disk or save data to disk. *
489 *                                                                *
490 ******************************************************************/
491
492// Scan for partition data. This function loads the MBR data (regular MBR or
493// protective MBR) and loads BSD disklabel data (which is probably invalid).
494// It also looks for APM data, forces a load of GPT data, and summarizes
495// the results.
496void GPTData::PartitionScan(int fd) {
497   BSDData bsdDisklabel;
498//   int bsdFound;
499
500   printf("Partition table scan:\n");
501
502   // Read the MBR & check for BSD disklabel
503   protectiveMBR.ReadMBRData(fd);
504   protectiveMBR.ShowState();
505   bsdDisklabel.ReadBSDData(fd, 0, diskSize - 1);
506   bsdFound = bsdDisklabel.ShowState();
507//   bsdDisklabel.DisplayBSDData();
508
509   // Load the GPT data, whether or not it's valid
510   ForceLoadGPTData(fd);
511   ShowAPMState(); // Show whether there's an Apple Partition Map present
512   ShowGPTState(); // Show GPT status
513   printf("\n");
514
515   if (apmFound) {
516      printf("\n*******************************************************************\n");
517      printf("This disk appears to contain an Apple-format (APM) partition table!\n");
518      printf("It will be destroyed if you continue!\n");
519      printf("*******************************************************************\n\n\a");
520   } // if
521} // GPTData::PartitionScan()
522
523// Read GPT data from a disk.
524int GPTData::LoadPartitions(char* deviceFilename) {
525   int fd, err;
526   int allOK = 1, i;
527   uint64_t firstBlock, lastBlock;
528   BSDData bsdDisklabel;
529
530   // First, do a test to see if writing will be possible later....
531   fd = OpenForWrite(deviceFilename);
532   if (fd == -1)
533      printf("\aNOTE: Write test failed with error number %d. It will be "
534             "impossible to save\nchanges to this disk's partition table!\n\n",
535             errno);
536   close(fd);
537
538   if ((fd = open(deviceFilename, O_RDONLY)) != -1) {
539      // store disk information....
540      diskSize = disksize(fd, &err);
541      blockSize = (uint32_t) GetBlockSize(fd);
542      strcpy(device, deviceFilename);
543      PartitionScan(fd); // Check for partition types & print summary
544
545      switch (UseWhichPartitions()) {
546         case use_mbr:
547            XFormPartitions();
548            break;
549         case use_bsd:
550            bsdDisklabel.ReadBSDData(fd, 0, diskSize - 1);
551//            bsdDisklabel.DisplayBSDData();
552            ClearGPTData();
553            protectiveMBR.MakeProtectiveMBR(1); // clear boot area (option 1)
554            XFormDisklabel(&bsdDisklabel, 0);
555            break;
556         case use_gpt:
557            break;
558         case use_new:
559            ClearGPTData();
560            protectiveMBR.MakeProtectiveMBR();
561            break;
562      } // switch
563
564      // Now find the first and last sectors used by partitions...
565      if (allOK) {
566         firstBlock = mainHeader.backupLBA; // start high
567         lastBlock = 0; // start low
568         for (i = 0; i < mainHeader.numParts; i++) {
569            if ((partitions[i].GetFirstLBA() < firstBlock) &&
570                 (partitions[i].GetFirstLBA() > 0))
571               firstBlock = partitions[i].GetFirstLBA();
572            if (partitions[i].GetLastLBA() > lastBlock)
573               lastBlock = partitions[i].GetLastLBA();
574         } // for
575      } // if
576      CheckGPTSize();
577   } else {
578      allOK = 0;
579      fprintf(stderr, "Problem opening %s for reading! Error is %d\n",
580              deviceFilename, errno);
581      if (errno == EACCES) { // User is probably not running as root
582         fprintf(stderr, "You must run this program as root or use sudo!\n");
583      } // if
584   } // if/else
585   return (allOK);
586} // GPTData::LoadPartitions()
587
588// Loads the GPT, as much as possible. Returns 1 if this seems to have
589// succeeded, 0 if there are obvious problems....
590int GPTData::ForceLoadGPTData(int fd) {
591   int allOK = 1, validHeaders;
592   off_t seekTo;
593   char* storage;
594   uint32_t newCRC, sizeOfParts;
595
596   // Seek to and read the main GPT header
597   lseek64(fd, 512, SEEK_SET);
598   read(fd, &mainHeader, 512); // read main GPT header
599   mainCrcOk = CheckHeaderCRC(&mainHeader);
600   if (IsLittleEndian() == 0) // big-endian system; adjust header byte order....
601      ReverseHeaderBytes(&mainHeader);
602
603   // Load backup header, check its CRC, and store the results of the
604   // check for future reference. Load backup header using pointer in main
605   // header if possible; but if main header has a CRC error, or if it
606   // points to beyond the end of the disk, load the last sector of the
607   // disk instead.
608   if (mainCrcOk) {
609      if (mainHeader.backupLBA < diskSize) {
610         seekTo = mainHeader.backupLBA * blockSize;
611      } else {
612         seekTo = (diskSize * blockSize) - UINT64_C(512);
613         printf("Warning! Disk size is smaller than the main header indicates! Loading\n"
614                "secondary header from the last sector of the disk! You should use 'v' to\n"
615                "verify disk integrity, and perhaps options on the experts' menu to repair\n"
616                "the disk.\n");
617      } // else
618   } else {
619      seekTo = (diskSize * blockSize) - UINT64_C(512);
620   } // if/else (mainCrcOk)
621
622   if (lseek64(fd, seekTo, SEEK_SET) != (off_t) -1) {
623      read(fd, &secondHeader, 512); // read secondary GPT header
624      secondCrcOk = CheckHeaderCRC(&secondHeader);
625      if (IsLittleEndian() == 0) // big-endian system; adjust header byte order....
626         ReverseHeaderBytes(&secondHeader);
627   } else {
628      allOK = 0;
629      state = gpt_invalid;
630      fprintf(stderr, "Unable to seek to secondary GPT header at sector %llu!\n",
631              diskSize - (UINT64_C(1)));
632   } // if/else lseek
633
634   // Return valid headers code: 0 = both headers bad; 1 = main header
635   // good, backup bad; 2 = backup header good, main header bad;
636   // 3 = both headers good. Note these codes refer to valid GPT
637   // signatures and version numbers; more subtle problems will elude
638   // this check!
639   validHeaders = CheckHeaderValidity();
640
641   // Read partitions (from primary array)
642   if (validHeaders > 0) { // if at least one header is OK....
643      // GPT appears to be valid....
644      state = gpt_valid;
645
646      // We're calling the GPT valid, but there's a possibility that one
647      // of the two headers is corrupt. If so, use the one that seems to
648      // be in better shape to regenerate the bad one
649      if (validHeaders == 2) { // valid backup header, invalid main header
650         printf("Caution: invalid main GPT header, but valid backup; regenerating main header\n"
651               "from backup!\n");
652         RebuildMainHeader();
653         mainCrcOk = secondCrcOk; // Since copied, use CRC validity of backup
654      } else if (validHeaders == 1) { // valid main header, invalid backup
655         printf("Caution: invalid backup GPT header, but valid main header; regenerating\n"
656               "backup header from main header.\n");
657         RebuildSecondHeader();
658         secondCrcOk = mainCrcOk; // Since regenerated, use CRC validity of main
659      } // if/else/if
660
661      // Load the main partition table, including storing results of its
662      // CRC check
663      if (LoadMainTable() == 0)
664         allOK = 0;
665
666      // Load backup partition table into temporary storage to check
667      // its CRC and store the results, then discard this temporary
668      // storage, since we don't use it in any but recovery operations
669      seekTo = secondHeader.partitionEntriesLBA * (off_t) blockSize;
670      if ((lseek64(fd, seekTo, SEEK_SET) != (off_t) -1) && (secondCrcOk)) {
671         sizeOfParts = secondHeader.numParts * secondHeader.sizeOfPartitionEntries;
672         storage = (char*) malloc(sizeOfParts);
673         read(fd, storage, sizeOfParts);
674         newCRC = chksum_crc32((unsigned char*) storage,  sizeOfParts);
675         free(storage);
676         secondPartsCrcOk = (newCRC == secondHeader.partitionEntriesCRC);
677      } // if
678
679      // Check for valid CRCs and warn if there are problems
680      if ((mainCrcOk == 0) || (secondCrcOk == 0) || (mainPartsCrcOk == 0) ||
681           (secondPartsCrcOk == 0)) {
682         printf("Warning! One or more CRCs don't match. You should repair the disk!\n");
683         state = gpt_corrupt;
684           } // if
685   } else {
686      state = gpt_invalid;
687   } // if/else
688   return allOK;
689} // GPTData::ForceLoadGPTData()
690
691// Loads the partition table pointed to by the main GPT header. The
692// main GPT header in memory MUST be valid for this call to do anything
693// sensible!
694int GPTData::LoadMainTable(void) {
695   int fd, retval = 0;
696   uint32_t newCRC, sizeOfParts;
697
698   if ((fd = open(device, O_RDONLY)) != -1) {
699      // Set internal data structures for number of partitions on the disk
700      SetGPTSize(mainHeader.numParts);
701
702      // Load main partition table, and record whether its CRC
703      // matches the stored value
704      lseek64(fd, mainHeader.partitionEntriesLBA * blockSize, SEEK_SET);
705      sizeOfParts = mainHeader.numParts * mainHeader.sizeOfPartitionEntries;
706      read(fd, partitions, sizeOfParts);
707      newCRC = chksum_crc32((unsigned char*) partitions, sizeOfParts);
708      mainPartsCrcOk = (newCRC == mainHeader.partitionEntriesCRC);
709      if (IsLittleEndian() == 0)
710         ReversePartitionBytes();
711      retval = 1;
712   } // if
713   return retval;
714} // GPTData::LoadMainTable()
715
716// Load the second (backup) partition table as the primary partition
717// table. Used in repair functions
718void GPTData::LoadSecondTableAsMain(void) {
719   int fd;
720   off_t seekTo;
721   uint32_t sizeOfParts, newCRC;
722
723   if ((fd = open(device, O_RDONLY)) != -1) {
724      seekTo = secondHeader.partitionEntriesLBA * (off_t) blockSize;
725      if (lseek64(fd, seekTo, SEEK_SET) != (off_t) -1) {
726         SetGPTSize(secondHeader.numParts);
727         sizeOfParts = secondHeader.numParts * secondHeader.sizeOfPartitionEntries;
728         read(fd, partitions, sizeOfParts);
729         newCRC = chksum_crc32((unsigned char*) partitions, sizeOfParts);
730         secondPartsCrcOk = (newCRC == secondHeader.partitionEntriesCRC);
731         mainPartsCrcOk = secondPartsCrcOk;
732         if (IsLittleEndian() == 0)
733            ReversePartitionBytes();
734         if (!secondPartsCrcOk) {
735            printf("Error! After loading backup partitions, the CRC still doesn't check out!\n");
736         } // if
737      } else {
738         printf("Error! Couldn't seek to backup partition table!\n");
739      } // if/else
740   } else {
741      printf("Error! Couldn't open device %s when recovering backup partition table!\n", device);
742   } // if/else
743} // GPTData::LoadSecondTableAsMain()
744
745// Writes GPT (and protective MBR) to disk. Returns 1 on successful
746// write, 0 if there was a problem.
747int GPTData::SaveGPTData(void) {
748   int allOK = 1;
749   char answer, line[256];
750   int fd;
751   uint64_t secondTable;
752   uint32_t numParts;
753   off_t offset;
754
755   if (strlen(device) == 0) {
756      printf("Device not defined.\n");
757   } // if
758
759   // First do some final sanity checks....
760   // Is there enough space to hold the GPT headers and partition tables,
761   // given the partition sizes?
762   if (CheckGPTSize() > 0) {
763      allOK = 0;
764   } // if
765
766   // Check that disk is really big enough to handle this...
767   if (mainHeader.backupLBA > diskSize) {
768      fprintf(stderr, "Error! Disk is too small! The 'e' option on the experts' menu might fix the\n"
769              "problem (or it might not). Aborting!\n");
770      printf("(Disk size is %llu sectors, needs to be %llu sectors.)\n", diskSize,
771             mainHeader.backupLBA);
772      allOK = 0;
773   } // if
774   // Check that second header is properly placed. Warn and ask if this should
775   // be corrected if the test fails....
776   if (mainHeader.backupLBA < (diskSize - UINT64_C(1))) {
777      printf("Warning! Secondary header is placed too early on the disk! Do you want to\n"
778             "correct this problem? ");
779      if (GetYN() == 'Y') {
780         MoveSecondHeaderToEnd();
781         printf("Have moved second header and partition table to correct location.\n");
782      } else {
783         printf("Have not corrected the problem. Strange problems may occur in the future!\n");
784      } // if correction requested
785   } // if
786
787   // Check for overlapping partitions....
788   if (FindOverlaps() > 0) {
789      allOK = 0;
790      fprintf(stderr, "Aborting write operation!\n");
791   } // if
792
793   // Check for mismatched MBR and GPT data, but let it pass if found
794   // (function displays warning message)
795   FindHybridMismatches();
796
797   // Pull out some data that's needed before doing byte-order reversal on
798   // big-endian systems....
799   numParts = mainHeader.numParts;
800   secondTable = secondHeader.partitionEntriesLBA;
801   if (IsLittleEndian() == 0) {
802      // Reverse partition bytes first, since that function requires non-reversed
803      // data from the main header....
804      ReversePartitionBytes();
805      ReverseHeaderBytes(&mainHeader);
806      ReverseHeaderBytes(&secondHeader);
807   } // if
808   RecomputeCRCs();
809
810   if (allOK) {
811      printf("\nFinal checks complete. About to write GPT data. THIS WILL OVERWRITE EXISTING\n");
812      printf("MBR PARTITIONS!! THIS PROGRAM IS BETA QUALITY AT BEST. IF YOU LOSE ALL YOUR\n");
813      printf("DATA, YOU HAVE ONLY YOURSELF TO BLAME IF YOU ANSWER 'Y' BELOW!\n\n");
814      printf("Do you want to proceed, possibly destroying your data? (Y/N) ");
815      fgets(line, 255, stdin);
816      sscanf(line, "%c", &answer);
817      if ((answer == 'Y') || (answer == 'y')) {
818         printf("OK; writing new GPT partition table.\n");
819      } else {
820         allOK = 0;
821      } // if/else
822   } // if
823
824   // Do it!
825   if (allOK) {
826      fd = OpenForWrite(device);
827      if (fd != -1) {
828         // First, write the protective MBR...
829         protectiveMBR.WriteMBRData(fd);
830
831         // Now write the main GPT header...
832         if (allOK)
833            if (write(fd, &mainHeader, 512) == -1)
834               allOK = 0;
835
836         // Now write the main partition tables...
837         if (allOK) {
838            if (write(fd, partitions, GPT_SIZE * numParts) == -1)
839               allOK = 0;
840         } // if
841
842         // Now seek to near the end to write the secondary GPT....
843         if (allOK) {
844            offset = (off_t) secondTable * (off_t) (blockSize);
845            if (lseek64(fd, offset, SEEK_SET) == (off_t) - 1) {
846               allOK = 0;
847               printf("Unable to seek to end of disk!\n");
848            } // if
849         } // if
850
851         // Now write the secondary partition tables....
852         if (allOK)
853            if (write(fd, partitions, GPT_SIZE * numParts) == -1)
854               allOK = 0;
855
856         // Now write the secondary GPT header...
857         if (allOK)
858            if (write(fd, &secondHeader, 512) == -1)
859               allOK = 0;
860
861         // re-read the partition table
862         if (allOK) {
863            DiskSync(fd);
864         } // if
865
866         if (allOK) { // writes completed OK
867            printf("The operation has completed successfully.\n");
868         } else {
869            printf("Warning! An error was reported when writing the partition table! This error\n");
870            printf("MIGHT be harmless, but you may have trashed the disk! Use parted and, if\n");
871            printf("necessary, restore your original partition table.\n");
872         } // if/else
873         close(fd);
874      } else {
875         fprintf(stderr, "Unable to open device %s for writing! Errno is %d! Aborting write!\n",
876                 device, errno);
877         allOK = 0;
878      } // if/else
879   } else {
880      printf("Aborting write of new partition table.\n");
881   } // if
882
883   if (IsLittleEndian() == 0) {
884      // Reverse (normalize) header bytes first, since ReversePartitionBytes()
885      // requires non-reversed data in mainHeader...
886      ReverseHeaderBytes(&mainHeader);
887      ReverseHeaderBytes(&secondHeader);
888      ReversePartitionBytes();
889   } // if
890
891   return (allOK);
892} // GPTData::SaveGPTData()
893
894// Save GPT data to a backup file. This function does much less error
895// checking than SaveGPTData(). It can therefore preserve many types of
896// corruption for later analysis; however, it preserves only the MBR,
897// the main GPT header, the backup GPT header, and the main partition
898// table; it discards the backup partition table, since it should be
899// identical to the main partition table on healthy disks.
900int GPTData::SaveGPTBackup(char* filename) {
901   int fd, allOK = 1;
902   uint32_t numParts;
903
904   if ((fd = open(filename, O_WRONLY | O_CREAT, S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH)) != -1) {
905      // Reverse the byte order, if necessary....
906      numParts = mainHeader.numParts;
907      if (IsLittleEndian() == 0) {
908         ReversePartitionBytes();
909         ReverseHeaderBytes(&mainHeader);
910         ReverseHeaderBytes(&secondHeader);
911      } // if
912
913      // Recomputing the CRCs is likely to alter them, which could be bad
914      // if the intent is to save a potentially bad GPT for later analysis;
915      // but if we don't do this, we get bogus errors when we load the
916      // backup. I'm favoring misses over false alarms....
917      RecomputeCRCs();
918
919      // Now write the protective MBR...
920      protectiveMBR.WriteMBRData(fd);
921
922      // Now write the main GPT header...
923      if (allOK)
924         if (write(fd, &mainHeader, 512) == -1)
925            allOK = 0;
926
927      // Now write the secondary GPT header...
928      if (allOK)
929         if (write(fd, &secondHeader, 512) == -1)
930            allOK = 0;
931
932      // Now write the main partition tables...
933      if (allOK) {
934         if (write(fd, partitions, GPT_SIZE * numParts) == -1)
935            allOK = 0;
936      } // if
937
938      if (allOK) { // writes completed OK
939         printf("The operation has completed successfully.\n");
940      } else {
941         printf("Warning! An error was reported when writing the backup file.\n");
942         printf("It may not be usable!\n");
943      } // if/else
944      close(fd);
945
946      // Now reverse the byte-order reversal, if necessary....
947      if (IsLittleEndian() == 0) {
948         ReverseHeaderBytes(&mainHeader);
949         ReverseHeaderBytes(&secondHeader);
950         ReversePartitionBytes();
951      } // if
952   } else {
953      fprintf(stderr, "Unable to open file %s for writing! Aborting!\n", filename);
954      allOK = 0;
955   } // if/else
956   return allOK;
957} // GPTData::SaveGPTBackup()
958
959// Load GPT data from a backup file created by SaveGPTBackup(). This function
960// does minimal error checking. It returns 1 if it completed successfully,
961// 0 if there was a problem. In the latter case, it creates a new empty
962// set of partitions.
963int GPTData::LoadGPTBackup(char* filename) {
964   int fd, allOK = 1, val;
965   uint32_t numParts, sizeOfEntries, sizeOfParts, newCRC;
966   int littleEndian = 1;
967
968   if ((fd = open(filename, O_RDONLY)) != -1) {
969      if (IsLittleEndian() == 0)
970         littleEndian = 0;
971
972      // Let the MBRData class load the saved MBR...
973      protectiveMBR.ReadMBRData(fd, 0); // 0 = don't check block size
974
975      // Load the main GPT header, check its vaility, and set the GPT
976      // size based on the data
977      read(fd, &mainHeader, 512);
978      mainCrcOk = CheckHeaderCRC(&mainHeader);
979
980      // Reverse byte order, if necessary
981      if (littleEndian == 0) {
982         ReverseHeaderBytes(&mainHeader);
983      } // if
984
985      // Load the backup GPT header in much the same way as the main
986      // GPT header....
987      read(fd, &secondHeader, 512);
988      secondCrcOk = CheckHeaderCRC(&secondHeader);
989
990      // Reverse byte order, if necessary
991      if (littleEndian == 0) {
992         ReverseHeaderBytes(&secondHeader);
993      } // if
994
995      // Return valid headers code: 0 = both headers bad; 1 = main header
996      // good, backup bad; 2 = backup header good, main header bad;
997      // 3 = both headers good. Note these codes refer to valid GPT
998      // signatures and version numbers; more subtle problems will elude
999      // this check!
1000      if ((val = CheckHeaderValidity()) > 0) {
1001         if (val == 2) { // only backup header seems to be good
1002            numParts = secondHeader.numParts;
1003            sizeOfEntries = secondHeader.sizeOfPartitionEntries;
1004         } else { // main header is OK
1005            numParts = mainHeader.numParts;
1006            sizeOfEntries = mainHeader.sizeOfPartitionEntries;
1007         } // if/else
1008
1009         SetGPTSize(numParts);
1010
1011         // If current disk size doesn't match that of backup....
1012         if (secondHeader.currentLBA != diskSize - UINT64_C(1)) {
1013            printf("Warning! Current disk size doesn't match that of the backup!\n"
1014                  "Adjusting sizes to match, but subsequent problems are possible!\n");
1015            MoveSecondHeaderToEnd();
1016         } // if
1017
1018         // Load main partition table, and record whether its CRC
1019         // matches the stored value
1020         sizeOfParts = numParts * sizeOfEntries;
1021         read(fd, partitions, sizeOfParts);
1022
1023         newCRC = chksum_crc32((unsigned char*) partitions, sizeOfParts);
1024         mainPartsCrcOk = (newCRC == mainHeader.partitionEntriesCRC);
1025         secondPartsCrcOk = (newCRC == secondHeader.partitionEntriesCRC);
1026         // Reverse byte order, if necessary
1027         if (littleEndian == 0) {
1028            ReversePartitionBytes();
1029         } // if
1030
1031      } else {
1032         allOK = 0;
1033      } // if/else
1034   } else {
1035      allOK = 0;
1036      fprintf(stderr, "Unable to open file %s for reading! Aborting!\n", filename);
1037   } // if/else
1038
1039   // Something went badly wrong, so blank out partitions
1040   if (allOK == 0) {
1041      ClearGPTData();
1042      protectiveMBR.MakeProtectiveMBR();
1043   } // if
1044   return allOK;
1045} // GPTData::LoadGPTBackup()
1046
1047// Tell user whether Apple Partition Map (APM) was discovered....
1048void GPTData::ShowAPMState(void) {
1049   if (apmFound)
1050      printf("  APM: present\n");
1051   else
1052      printf("  APM: not present\n");
1053} // GPTData::ShowAPMState()
1054
1055// Tell user about the state of the GPT data....
1056void GPTData::ShowGPTState(void) {
1057   switch (state) {
1058      case gpt_invalid:
1059         printf("  GPT: not present\n");
1060         break;
1061      case gpt_valid:
1062         printf("  GPT: present\n");
1063         break;
1064      case gpt_corrupt:
1065         printf("  GPT: damaged\n");
1066         break;
1067      default:
1068         printf("\a  GPT: unknown -- bug!\n");
1069         break;
1070   } // switch
1071} // GPTData::ShowGPTState()
1072
1073// Display the basic GPT data
1074void GPTData::DisplayGPTData(void) {
1075   int i;
1076   char sizeInSI[255]; // String to hold size of disk in SI units
1077   char tempStr[255];
1078   uint64_t temp, totalFree;
1079
1080   BytesToSI(diskSize * blockSize, sizeInSI);
1081   printf("Disk %s: %llu sectors, %s\n", device,
1082          (unsigned long long) diskSize, sizeInSI);
1083   printf("Disk identifier (GUID): %s\n", GUIDToStr(mainHeader.diskGUID, tempStr));
1084   printf("Partition table holds up to %lu entries\n", (unsigned long) mainHeader.numParts);
1085   printf("First usable sector is %lu, last usable sector is %lu\n",
1086          (unsigned long) mainHeader.firstUsableLBA,
1087           (unsigned long) mainHeader.lastUsableLBA);
1088   totalFree = FindFreeBlocks(&i, &temp);
1089   printf("Total free space is %llu sectors (%s)\n", totalFree,
1090          BytesToSI(totalFree * (uint64_t) blockSize, sizeInSI));
1091   printf("\nNumber  Start (sector)    End (sector)  Size       Code  Name\n");
1092   for (i = 0; i < mainHeader.numParts; i++) {
1093      partitions[i].ShowSummary(i, blockSize);
1094   } // for
1095} // GPTData::DisplayGPTData()
1096
1097// Get partition number from user and then call ShowPartDetails(partNum)
1098// to show its detailed information
1099void GPTData::ShowDetails(void) {
1100   int partNum;
1101   uint32_t low, high;
1102
1103   if (GetPartRange(&low, &high) > 0) {
1104      partNum = GetPartNum();
1105      ShowPartDetails(partNum);
1106   } else {
1107      printf("No partitions\n");
1108   } // if/else
1109} // GPTData::ShowDetails()
1110
1111// Show detailed information on the specified partition
1112void GPTData::ShowPartDetails(uint32_t partNum) {
1113   if (partitions[partNum].GetFirstLBA() != 0) {
1114      partitions[partNum].ShowDetails(blockSize);
1115   } else {
1116      printf("Partition #%d does not exist.", (int) (partNum + 1));
1117   } // if
1118} // GPTData::ShowPartDetails()
1119
1120/*********************************************************************
1121 *                                                                   *
1122 * Begin functions that obtain information from the users, and often *
1123 * do something with that information (call other functions)         *
1124 *                                                                   *
1125 *********************************************************************/
1126
1127// Prompts user for partition number and returns the result.
1128uint32_t GPTData::GetPartNum(void) {
1129   uint32_t partNum;
1130   uint32_t low, high;
1131   char prompt[255];
1132
1133   if (GetPartRange(&low, &high) > 0) {
1134      sprintf(prompt, "Partition number (%d-%d): ", low + 1, high + 1);
1135      partNum = GetNumber(low + 1, high + 1, low, prompt);
1136   } else partNum = 1;
1137   return (partNum - 1);
1138} // GPTData::GetPartNum()
1139
1140// What it says: Resize the partition table. (Default is 128 entries.)
1141void GPTData::ResizePartitionTable(void) {
1142   int newSize;
1143   char prompt[255];
1144   uint32_t curLow, curHigh;
1145
1146   printf("Current partition table size is %lu.\n",
1147          (unsigned long) mainHeader.numParts);
1148   GetPartRange(&curLow, &curHigh);
1149   curHigh++; // since GetPartRange() returns numbers starting from 0...
1150   // There's no point in having fewer than four partitions....
1151   if (curHigh < 4)
1152      curHigh = 4;
1153   sprintf(prompt, "Enter new size (%d up, default %d): ", (int) curHigh,
1154           (int) NUM_GPT_ENTRIES);
1155   newSize = GetNumber(4, 65535, 128, prompt);
1156   if (newSize < 128) {
1157      printf("Caution: The partition table size should officially be 16KB or larger,\n"
1158            "which works out to 128 entries. In practice, smaller tables seem to\n"
1159            "work with most OSes, but this practice is risky. I'm proceeding with\n"
1160            "the resize, but you may want to reconsider this action and undo it.\n\n");
1161   } // if
1162   SetGPTSize(newSize);
1163} // GPTData::ResizePartitionTable()
1164
1165// Interactively create a partition
1166void GPTData::CreatePartition(void) {
1167   uint64_t firstBlock, firstInLargest, lastBlock, sector;
1168   char prompt[255];
1169   int partNum, firstFreePart = 0;
1170
1171   // Find first free partition...
1172   while (partitions[firstFreePart].GetFirstLBA() != 0) {
1173      firstFreePart++;
1174   } // while
1175
1176   if (((firstBlock = FindFirstAvailable()) != 0) &&
1177         (firstFreePart < mainHeader.numParts)) {
1178      lastBlock = FindLastAvailable(firstBlock);
1179      firstInLargest = FindFirstInLargest();
1180
1181      // Get partition number....
1182      do {
1183         sprintf(prompt, "Partition number (%d-%d, default %d): ", firstFreePart + 1,
1184                 mainHeader.numParts, firstFreePart + 1);
1185         partNum = GetNumber(firstFreePart + 1, mainHeader.numParts,
1186                             firstFreePart + 1, prompt) - 1;
1187         if (partitions[partNum].GetFirstLBA() != 0)
1188            printf("partition %d is in use.\n", partNum + 1);
1189      } while (partitions[partNum].GetFirstLBA() != 0);
1190
1191      // Get first block for new partition...
1192      sprintf(prompt,
1193              "First sector (%llu-%llu, default = %llu) or {+-}size{KMGT}: ",
1194              firstBlock, lastBlock, firstInLargest);
1195      do {
1196         sector = GetSectorNum(firstBlock, lastBlock, firstInLargest, prompt);
1197      } while (IsFree(sector) == 0);
1198      Align(&sector); // Align sector to correct multiple
1199      firstBlock = sector;
1200
1201      // Get last block for new partitions...
1202      lastBlock = FindLastInFree(firstBlock);
1203      sprintf(prompt,
1204              "Last sector (%llu-%llu, default = %llu) or {+-}size{KMGT}: ",
1205              firstBlock, lastBlock, lastBlock);
1206      do {
1207         sector = GetSectorNum(firstBlock, lastBlock, lastBlock, prompt);
1208      } while (IsFree(sector) == 0);
1209      lastBlock = sector;
1210
1211      partitions[partNum].SetFirstLBA(firstBlock);
1212      partitions[partNum].SetLastLBA(lastBlock);
1213
1214      partitions[partNum].SetUniqueGUID(1);
1215      partitions[partNum].ChangeType();
1216      partitions[partNum].SetName((unsigned char*) partitions[partNum].GetNameType(prompt));
1217         } else {
1218            printf("No free sectors available\n");
1219         } // if/else
1220} // GPTData::CreatePartition()
1221
1222// Interactively delete a partition (duh!)
1223void GPTData::DeletePartition(void) {
1224   int partNum;
1225   uint32_t low, high;
1226   uint64_t startSector, length;
1227   char prompt[255];
1228
1229   if (GetPartRange(&low, &high) > 0) {
1230      sprintf(prompt, "Partition number (%d-%d): ", low + 1, high + 1);
1231      partNum = GetNumber(low + 1, high + 1, low, prompt);
1232
1233      // In case there's a protective MBR, look for & delete matching
1234      // MBR partition....
1235      startSector = partitions[partNum - 1].GetFirstLBA();
1236      length = partitions[partNum - 1].GetLengthLBA();
1237      protectiveMBR.DeleteByLocation(startSector, length);
1238
1239      // Now delete the GPT partition
1240      partitions[partNum - 1].BlankPartition();
1241   } else {
1242      printf("No partitions\n");
1243   } // if/else
1244} // GPTData::DeletePartition()
1245
1246// Prompt user for a partition number, then change its type code
1247// using ChangeGPTType(struct GPTPartition*) function.
1248void GPTData::ChangePartType(void) {
1249   int partNum;
1250   uint32_t low, high;
1251
1252   if (GetPartRange(&low, &high) > 0) {
1253      partNum = GetPartNum();
1254      partitions[partNum].ChangeType();
1255   } else {
1256      printf("No partitions\n");
1257   } // if/else
1258} // GPTData::ChangePartType()
1259
1260// Partition attributes seem to be rarely used, but I want a way to
1261// adjust them for completeness....
1262void GPTData::SetAttributes(uint32_t partNum) {
1263   Attributes theAttr;
1264
1265   theAttr.SetAttributes(partitions[partNum].GetAttributes());
1266   theAttr.DisplayAttributes();
1267   theAttr.ChangeAttributes();
1268   partitions[partNum].SetAttributes(theAttr.GetAttributes());
1269} // GPTData::SetAttributes()
1270
1271// This function destroys the on-disk GPT structures. Returns 1 if the
1272// user confirms destruction, 0 if the user aborts.
1273// If prompt == 0, don't ask user about proceeding and do NOT wipe out
1274// MBR. (Set prompt == 0 when doing a GPT-to-MBR conversion.)
1275int GPTData::DestroyGPT(int prompt) {
1276   int fd, i;
1277   char blankSector[512], goOn = 'Y', blank = 'N';
1278
1279   for (i = 0; i < 512; i++) {
1280      blankSector[i] = '\0';
1281   } // for
1282
1283   if (((apmFound) || (bsdFound)) && prompt) {
1284      printf("WARNING: APM or BSD disklabel structures detected! This operation could\n"
1285             "damage any APM or BSD partitions on this disk!\n");
1286   } // if APM or BSD
1287   if (prompt) {
1288      printf("\a\aAbout to wipe out GPT on %s. Proceed? ", device);
1289      goOn = GetYN();
1290   } // if
1291   if (goOn == 'Y') {
1292      fd = open(device, O_WRONLY);
1293#ifdef __APPLE__
1294      // MacOS X requires a shared lock under some circumstances....
1295      if (fd < 0) {
1296         fd = open(device, O_WRONLY|O_SHLOCK);
1297      } // if
1298#endif
1299      if (fd != -1) {
1300         lseek64(fd, mainHeader.currentLBA * 512, SEEK_SET); // seek to GPT header
1301         write(fd, blankSector, 512); // blank it out
1302         lseek64(fd, mainHeader.partitionEntriesLBA * 512, SEEK_SET); // seek to partition table
1303         for (i = 0; i < GetBlocksInPartTable(); i++)
1304            write(fd, blankSector, 512);
1305         lseek64(fd, secondHeader.partitionEntriesLBA * 512, SEEK_SET); // seek to partition table
1306         for (i = 0; i < GetBlocksInPartTable(); i++)
1307            write(fd, blankSector, 512);
1308         lseek64(fd, secondHeader.currentLBA * 512, SEEK_SET); // seek to GPT header
1309         write(fd, blankSector, 512); // blank it out
1310         if (prompt) {
1311            printf("Blank out MBR? ");
1312            blank = GetYN();
1313         }// if
1314         // Note on below: Touch the MBR only if the user wants it completely
1315         // blanked out. Version 0.4.2 deleted the 0xEE partition and re-wrote
1316         // the MBR, but this could wipe out a valid MBR that the program
1317         // had subsequently discarded (say, if it conflicted with older GPT
1318         // structures).
1319         if (blank == 'Y') {
1320            lseek64(fd, 0, SEEK_SET);
1321            write(fd, blankSector, 512); // blank it out
1322         } else {
1323            printf("MBR is unchanged. You may need to delete an EFI GPT (0xEE) partition\n"
1324                   "with fdisk or another tool.\n");
1325         } // if/else
1326         DiskSync(fd);
1327         close(fd);
1328         printf("GPT data structures destroyed! You may now partition the disk using fdisk or\n"
1329               "other utilities. Program will now terminate.\n");
1330      } else {
1331         printf("Problem opening %s for writing! Program will now terminate.\n", device);
1332      } // if/else (fd != -1)
1333   } // if (goOn == 'Y')
1334   return (goOn == 'Y');
1335} // GPTData::DestroyGPT()
1336
1337/**************************************************************************
1338 *                                                                        *
1339 * Partition table transformation functions (MBR or BSD disklabel to GPT) *
1340 * (some of these functions may require user interaction)                 *
1341 *                                                                        *
1342 **************************************************************************/
1343
1344// Examines the MBR & GPT data, and perhaps asks the user questions, to
1345// determine which set of data to use: the MBR (use_mbr), the GPT (use_gpt),
1346// or create a new set of partitions (use_new)
1347WhichToUse GPTData::UseWhichPartitions(void) {
1348   WhichToUse which = use_new;
1349   MBRValidity mbrState;
1350   int answer;
1351
1352   mbrState = protectiveMBR.GetValidity();
1353
1354   if ((state == gpt_invalid) && ((mbrState == mbr) || (mbrState == hybrid))) {
1355      printf("\n\a***************************************************************\n"
1356            "Found invalid GPT and valid MBR; converting MBR to GPT format.\n"
1357            "THIS OPERATON IS POTENTIALLY DESTRUCTIVE! Exit by typing 'q' if\n"
1358            "you don't want to convert your MBR partitions to GPT format!\n"
1359            "***************************************************************\n\n");
1360      which = use_mbr;
1361   } // if
1362
1363   if ((state == gpt_invalid) && bsdFound) {
1364      printf("\n\a**********************************************************************\n"
1365            "Found invalid GPT and valid BSD disklabel; converting BSD disklabel\n"
1366            "to GPT format. THIS OPERATON IS POTENTIALLY DESTRUCTIVE! Your first\n"
1367            "BSD partition will likely be unusable. Exit by typing 'q' if you don't\n"
1368            "want to convert your BSD partitions to GPT format!\n"
1369            "**********************************************************************\n\n");
1370      which = use_bsd;
1371   } // if
1372
1373   if ((state == gpt_valid) && (mbrState == gpt)) {
1374      printf("Found valid GPT with protective MBR; using GPT.\n");
1375      which = use_gpt;
1376   } // if
1377   if ((state == gpt_valid) && (mbrState == hybrid)) {
1378      printf("Found valid GPT with hybrid MBR; using GPT.\n");
1379      which = use_gpt;
1380   } // if
1381   if ((state == gpt_valid) && (mbrState == invalid)) {
1382      printf("\aFound valid GPT with corrupt MBR; using GPT and will create new\nprotective MBR on save.\n");
1383      which = use_gpt;
1384      protectiveMBR.MakeProtectiveMBR();
1385   } // if
1386   if ((state == gpt_valid) && (mbrState == mbr)) {
1387      printf("Found valid MBR and GPT. Which do you want to use?\n");
1388      answer = GetNumber(1, 3, 2, (char*) " 1 - MBR\n 2 - GPT\n 3 - Create blank GPT\n\nYour answer: ");
1389      if (answer == 1) {
1390         which = use_mbr;
1391      } else if (answer == 2) {
1392         which = use_gpt;
1393         protectiveMBR.MakeProtectiveMBR();
1394         printf("Using GPT and creating fresh protective MBR.\n");
1395      } else which = use_new;
1396   } // if
1397
1398   // Nasty decisions here -- GPT is present, but corrupt (bad CRCs or other
1399   // problems)
1400   if (state == gpt_corrupt) {
1401      if ((mbrState == mbr) || (mbrState == hybrid)) {
1402         printf("Found valid MBR and corrupt GPT. Which do you want to use? (Using the\n"
1403               "GPT MAY permit recovery of GPT data.)\n");
1404         answer = GetNumber(1, 3, 2, (char*) " 1 - MBR\n 2 - GPT\n 3 - Create blank GPT\n\nYour answer: ");
1405         if (answer == 1) {
1406            which = use_mbr;
1407//            protectiveMBR.MakeProtectiveMBR();
1408         } else if (answer == 2) {
1409            which = use_gpt;
1410         } else which = use_new;
1411      } else if (mbrState == invalid) {
1412         printf("Found invalid MBR and corrupt GPT. What do you want to do? (Using the\n"
1413               "GPT MAY permit recovery of GPT data.)\n");
1414         answer = GetNumber(1, 2, 1, (char*) " 1 - GPT\n 2 - Create blank GPT\n\nYour answer: ");
1415         if (answer == 1) {
1416            which = use_gpt;
1417         } else which = use_new;
1418      } else { // corrupt GPT, MBR indicates it's a GPT disk....
1419         printf("\a\a****************************************************************************\n"
1420               "Caution: Found protective or hybrid MBR and corrupt GPT. Using GPT, but disk\n"
1421               "verification and recovery are STRONGLY recommended.\n"
1422               "****************************************************************************\n");
1423         which = use_gpt;
1424      } // if/else/else
1425   } // if (corrupt GPT)
1426
1427   if (which == use_new)
1428      printf("Creating new GPT entries.\n");
1429
1430   return which;
1431} // UseWhichPartitions()
1432
1433// Convert MBR partition table into GPT form
1434int GPTData::XFormPartitions(void) {
1435   int i, numToConvert;
1436   uint8_t origType;
1437   struct newGUID;
1438
1439   // Clear out old data & prepare basics....
1440   ClearGPTData();
1441
1442   // Convert the smaller of the # of GPT or MBR partitions
1443   if (mainHeader.numParts > (MAX_MBR_PARTS))
1444      numToConvert = MAX_MBR_PARTS;
1445   else
1446      numToConvert = mainHeader.numParts;
1447
1448   for (i = 0; i < numToConvert; i++) {
1449      origType = protectiveMBR.GetType(i);
1450      // don't waste CPU time trying to convert extended, hybrid protective, or
1451      // null (non-existent) partitions
1452      if ((origType != 0x05) && (origType != 0x0f) && (origType != 0x85) &&
1453           (origType != 0x00) && (origType != 0xEE))
1454         partitions[i] = protectiveMBR.AsGPT(i);
1455   } // for
1456
1457   // Convert MBR into protective MBR
1458   protectiveMBR.MakeProtectiveMBR();
1459
1460   // Record that all original CRCs were OK so as not to raise flags
1461   // when doing a disk verification
1462   mainCrcOk = secondCrcOk = mainPartsCrcOk = secondPartsCrcOk = 1;
1463
1464   return (1);
1465} // GPTData::XFormPartitions()
1466
1467// Transforms BSD disklabel on the specified partition (numbered from 0).
1468// If an invalid partition number is given, the program prompts for one.
1469// Returns the number of new partitions created.
1470int GPTData::XFormDisklabel(int i) {
1471   uint32_t low, high, partNum, startPart;
1472   uint16_t hexCode;
1473   int goOn = 1, numDone = 0;
1474   BSDData disklabel;
1475
1476   if (GetPartRange(&low, &high) != 0) {
1477      if ((i < low) || (i > high))
1478         partNum = GetPartNum();
1479      else
1480         partNum = (uint32_t) i;
1481
1482      // Find the partition after the last used one
1483      startPart = high + 1;
1484
1485      // Now see if the specified partition has a BSD type code....
1486      hexCode = partitions[partNum].GetHexType();
1487      if ((hexCode != 0xa500) && (hexCode != 0xa900)) {
1488         printf("Specified partition doesn't have a disklabel partition type "
1489               "code.\nContinue anyway?");
1490         goOn = (GetYN() == 'Y');
1491      } // if
1492
1493      // If all is OK, read the disklabel and convert it.
1494      if (goOn) {
1495         goOn = disklabel.ReadBSDData(device, partitions[partNum].GetFirstLBA(),
1496                                      partitions[partNum].GetLastLBA());
1497         if ((goOn) && (disklabel.IsDisklabel())) {
1498            numDone = XFormDisklabel(&disklabel, startPart);
1499            if (numDone == 1)
1500               printf("Converted %d BSD partition.\n", numDone);
1501            else
1502               printf("Converted %d BSD partitions.\n", numDone);
1503         } else {
1504            printf("Unable to convert partitions! Unrecognized BSD disklabel.\n");
1505         } // if/else
1506      } // if
1507      if (numDone > 0) { // converted partitions; delete carrier
1508         partitions[partNum].BlankPartition();
1509      } // if
1510   } else {
1511      printf("No partitions\n");
1512   } // if/else
1513   return numDone;
1514} // GPTData::XFormDisklable(int i)
1515
1516// Transform the partitions on an already-loaded BSD disklabel...
1517int GPTData::XFormDisklabel(BSDData* disklabel, int startPart) {
1518   int i, numDone = 0;
1519
1520   if ((disklabel->IsDisklabel()) && (startPart >= 0) &&
1521        (startPart < mainHeader.numParts)) {
1522      for (i = 0; i < disklabel->GetNumParts(); i++) {
1523         partitions[i + startPart] = disklabel->AsGPT(i);
1524         if (partitions[i + startPart].GetFirstLBA() != UINT64_C(0))
1525            numDone++;
1526      } // for
1527   } // if
1528
1529   // Record that all original CRCs were OK so as not to raise flags
1530   // when doing a disk verification
1531   mainCrcOk = secondCrcOk = mainPartsCrcOk = secondPartsCrcOk = 1;
1532
1533   return numDone;
1534} // GPTData::XFormDisklabel(BSDData* disklabel)
1535
1536// Add one GPT partition to MBR. Used by XFormToMBR() and MakeHybrid()
1537// functions. Returns 1 if operation was successful.
1538int GPTData::OnePartToMBR(uint32_t gptPart, int mbrPart) {
1539   int allOK = 1, typeCode, bootable;
1540   uint64_t length;
1541   char line[255];
1542
1543   if ((mbrPart < 0) || (mbrPart > 3)) {
1544      printf("MBR partition %d is out of range; omitting it.\n", mbrPart + 1);
1545      allOK = 0;
1546   } // if
1547   if (gptPart >= mainHeader.numParts) {
1548      printf("GPT partition %d is out of range; omitting it.\n", gptPart + 1);
1549      allOK = 0;
1550   } // if
1551   if (allOK && (partitions[gptPart].GetLastLBA() == UINT64_C(0))) {
1552      printf("GPT partition %d is undefined; omitting it.\n", gptPart + 1);
1553      allOK = 0;
1554   } // if
1555   if (allOK && (partitions[gptPart].GetFirstLBA() <= UINT32_MAX) &&
1556       (partitions[gptPart].GetLengthLBA() <= UINT32_MAX)) {
1557      if (partitions[gptPart].GetLastLBA() > UINT32_MAX) {
1558         printf("Caution: Partition end point past 32-bit pointer boundary;"
1559                " some OSes may\nreact strangely.\n");
1560      } // if partition ends past 32-bit (usually 2TiB) boundary
1561      do {
1562         printf("Enter an MBR hex code (default %02X): ",
1563                  typeHelper.GUIDToID(partitions[gptPart].GetType()) / 256);
1564         fgets(line, 255, stdin);
1565         sscanf(line, "%x", &typeCode);
1566         if (line[0] == '\n')
1567            typeCode = partitions[gptPart].GetHexType() / 256;
1568      } while ((typeCode <= 0) || (typeCode > 255));
1569      printf("Set the bootable flag? ");
1570      bootable = (GetYN() == 'Y');
1571      length = partitions[gptPart].GetLengthLBA();
1572      protectiveMBR.MakePart(mbrPart, (uint32_t) partitions[gptPart].GetFirstLBA(),
1573                             (uint32_t) length, typeCode, bootable);
1574   } else { // partition out of range
1575      printf("Partition %d begins beyond the 32-bit pointer limit of MBR "
1576             "partitions, or is\n too big; omitting it.\n", gptPart + 1);
1577      allOK = 0;
1578   } // if/else
1579   return allOK;
1580} // GPTData::OnePartToMBR()
1581
1582// Convert the GPT to MBR form. This function is necessarily limited; it
1583// handles at most four partitions and creates layouts that ignore CHS
1584// geometries. Returns the number of converted partitions; if this value
1585// is over 0, the calling function should call DestroyGPT() to destroy
1586// the GPT data, and then exit.
1587int GPTData::XFormToMBR(void) {
1588   char line[255];
1589   int i, j, numParts, numConverted = 0;
1590   uint32_t partNums[4];
1591
1592   // Get the numbers of up to four partitions to add to the
1593   // hybrid MBR....
1594   numParts = CountParts();
1595   printf("Counted %d partitions.\n", numParts);
1596
1597   // Prepare the MBR for conversion (empty it of existing partitions).
1598   protectiveMBR.EmptyMBR(0);
1599   protectiveMBR.SetDiskSize(diskSize);
1600
1601   if (numParts > 4) { // Over four partitions; engage in triage
1602      printf("Type from one to four GPT partition numbers, separated by spaces, to be\n"
1603            "used in the MBR, in sequence: ");
1604      fgets(line, 255, stdin);
1605      numParts = sscanf(line, "%d %d %d %d", &partNums[0], &partNums[1],
1606                        &partNums[2], &partNums[3]);
1607   } else { // Four or fewer partitions; convert them all
1608      i = j = 0;
1609      while ((j < numParts) && (i < mainHeader.numParts)) {
1610         if (partitions[i].GetFirstLBA() > 0) { // if GPT part. is defined
1611            partNums[j++] = ++i; // flag it for conversion
1612         } else i++;
1613      } // while
1614   } // if/else
1615
1616   for (i = 0; i < numParts; i++) {
1617      j = partNums[i] - 1;
1618      printf("\nCreating entry for partition #%d\n", j + 1);
1619      numConverted += OnePartToMBR(j, i);
1620   } // for
1621   return numConverted;
1622} // GPTData::XFormToMBR()
1623
1624// Create a hybrid MBR -- an ugly, funky thing that helps GPT work with
1625// OSes that don't understand GPT.
1626void GPTData::MakeHybrid(void) {
1627   uint32_t partNums[3];
1628   char line[255];
1629   int numParts, numConverted = 0, i, j, typeCode, mbrNum;
1630   char fillItUp = 'M'; // fill extra partition entries? (Yes/No/Maybe)
1631   char eeFirst = 'Y'; // Whether EFI GPT (0xEE) partition comes first in table
1632
1633   printf("\nWARNING! Hybrid MBRs are flaky and potentially dangerous! If you decide not\n"
1634         "to use one, just hit the Enter key at the below prompt and your MBR\n"
1635         "partition table will be untouched.\n\n\a");
1636
1637   // Now get the numbers of up to three partitions to add to the
1638   // hybrid MBR....
1639   printf("Type from one to three GPT partition numbers, separated by spaces, to be\n"
1640         "added to the hybrid MBR, in sequence: ");
1641   fgets(line, 255, stdin);
1642   numParts = sscanf(line, "%d %d %d", &partNums[0], &partNums[1], &partNums[2]);
1643
1644   if (numParts > 0) {
1645      // Blank out the protective MBR, but leave the boot loader code
1646      // alone....
1647      protectiveMBR.EmptyMBR(0);
1648      protectiveMBR.SetDiskSize(diskSize);
1649      printf("Place EFI GPT (0xEE) partition first in MBR (good for GRUB)? ");
1650      eeFirst = GetYN();
1651   } // if
1652
1653   for (i = 0; i < numParts; i++) {
1654      j = partNums[i] - 1;
1655      printf("\nCreating entry for partition #%d\n", j + 1);
1656      if (eeFirst == 'Y')
1657         mbrNum = i + 1;
1658      else
1659         mbrNum = i;
1660      numConverted += OnePartToMBR(j, mbrNum);
1661   } // for
1662
1663   if ((numParts > 0) && (numConverted > 0)) { // User opted to create a hybrid MBR....
1664      // Create EFI protective partition that covers the start of the disk.
1665      // If this location (covering the main GPT data structures) is omitted,
1666      // Linux won't find any partitions on the disk. Note that this is
1667      // NUMBERED AFTER the hybrid partitions, contrary to what the
1668      // gptsync utility does. This is because Windows seems to choke on
1669      // disks with a 0xEE partition in the first slot and subsequent
1670      // additional partitions, unless it boots from the disk.
1671      if (eeFirst == 'Y')
1672         mbrNum = 0;
1673      else
1674         mbrNum = numParts;
1675      protectiveMBR.MakePart(mbrNum, 1, protectiveMBR.FindLastInFree(1), 0xEE);
1676      protectiveMBR.SetHybrid();
1677
1678      // ... and for good measure, if there are any partition spaces left,
1679      // optionally create another protective EFI partition to cover as much
1680      // space as possible....
1681      for (i = 0; i < 4; i++) {
1682         if (protectiveMBR.GetType(i) == 0x00) { // unused entry....
1683            if (fillItUp == 'M') {
1684               printf("\nUnused partition space(s) found. Use one to protect more partitions? ");
1685               fillItUp = GetYN();
1686               typeCode = 0x00; // use this to flag a need to get type code
1687            } // if
1688            if (fillItUp == 'Y') {
1689               while ((typeCode <= 0) || (typeCode > 255)) {
1690                  printf("Enter an MBR hex code (EE is EFI GPT, but may confuse MacOS): ");
1691                  // Comment on above: Mac OS treats disks with more than one
1692                  // 0xEE MBR partition as MBR disks, not as GPT disks.
1693                  fgets(line, 255, stdin);
1694                  sscanf(line, "%x", &typeCode);
1695                  if (line[0] == '\n')
1696                     typeCode = 0;
1697               } // while
1698               protectiveMBR.MakeBiggestPart(i, typeCode); // make a partition
1699            } // if (fillItUp == 'Y')
1700         } // if unused entry
1701      } // for (i = 0; i < 4; i++)
1702   } // if (numParts > 0)
1703} // GPTData::MakeHybrid()
1704
1705/**********************************************************************
1706 *                                                                    *
1707 * Functions that adjust GPT data structures WITHOUT user interaction *
1708 * (they may display information for the user's benefit, though)      *
1709 *                                                                    *
1710 **********************************************************************/
1711
1712// Resizes GPT to specified number of entries. Creates a new table if
1713// necessary, copies data if it already exists.
1714int GPTData::SetGPTSize(uint32_t numEntries) {
1715   struct GPTPart* newParts;
1716   struct GPTPart* trash;
1717   uint32_t i, high, copyNum;
1718   int allOK = 1;
1719
1720   // First, adjust numEntries upward, if necessary, to get a number
1721   // that fills the allocated sectors
1722   i = blockSize / GPT_SIZE;
1723   if ((numEntries % i) != 0) {
1724      printf("Adjusting GPT size from %lu ", (unsigned long) numEntries);
1725      numEntries = ((numEntries / i) + 1) * i;
1726      printf("to %lu to fill the sector\n", (unsigned long) numEntries);
1727   } // if
1728
1729   // Do the work only if the # of partitions is changing. Along with being
1730   // efficient, this prevents mucking the with location of the secondary
1731   // partition table, which causes problems when loading data from a RAID
1732   // array that's been expanded because this function is called when loading
1733   // data.
1734   if ((numEntries != mainHeader.numParts) || (partitions == NULL)) {
1735      newParts = (GPTPart*) calloc(numEntries, sizeof (GPTPart));
1736      if (newParts != NULL) {
1737         if (partitions != NULL) { // existing partitions; copy them over
1738            GetPartRange(&i, &high);
1739            if (numEntries < (high + 1)) { // Highest entry too high for new #
1740               printf("The highest-numbered partition is %lu, which is greater than the requested\n"
1741                     "partition table size of %d; cannot resize. Perhaps sorting will help.\n",
1742                     (unsigned long) (high + 1), numEntries);
1743               allOK = 0;
1744            } else { // go ahead with copy
1745               if (numEntries < mainHeader.numParts)
1746                  copyNum = numEntries;
1747               else
1748                  copyNum = mainHeader.numParts;
1749               for (i = 0; i < copyNum; i++) {
1750                  newParts[i] = partitions[i];
1751               } // for
1752               trash = partitions;
1753               partitions = newParts;
1754               free(trash);
1755            } // if
1756         } else { // No existing partition table; just create it
1757            partitions = newParts;
1758         } // if/else existing partitions
1759         mainHeader.numParts = numEntries;
1760         secondHeader.numParts = numEntries;
1761         mainHeader.firstUsableLBA = ((numEntries * GPT_SIZE) / blockSize) + 2 ;
1762         secondHeader.firstUsableLBA = mainHeader.firstUsableLBA;
1763         MoveSecondHeaderToEnd();
1764         if (diskSize > 0)
1765            CheckGPTSize();
1766      } else { // Bad memory allocation
1767         fprintf(stderr, "Error allocating memory for partition table!\n");
1768         allOK = 0;
1769      } // if/else
1770   } // if/else
1771   return (allOK);
1772} // GPTData::SetGPTSize()
1773
1774// Blank the partition array
1775void GPTData::BlankPartitions(void) {
1776   uint32_t i;
1777
1778   for (i = 0; i < mainHeader.numParts; i++) {
1779      partitions[i].BlankPartition();
1780   } // for
1781} // GPTData::BlankPartitions()
1782
1783// Sort the GPT entries, eliminating gaps and making for a logical
1784// ordering. Relies on QuickSortGPT() for the bulk of the work
1785void GPTData::SortGPT(void) {
1786   int i, lastPart = 0;
1787   GPTPart temp;
1788
1789   // First, find the last partition with data, so as not to
1790   // spend needless time sorting empty entries....
1791   for (i = 0; i < mainHeader.numParts; i++) {
1792      if (partitions[i].GetFirstLBA() > 0)
1793         lastPart = i;
1794   } // for
1795
1796   // Now swap empties with the last partitions, to simplify the logic
1797   // in the Quicksort function....
1798   i = 0;
1799   while (i < lastPart) {
1800      if (partitions[i].GetFirstLBA() == 0) {
1801         temp = partitions[i];
1802         partitions[i] = partitions[lastPart];
1803         partitions[lastPart] = temp;
1804         lastPart--;
1805      } // if
1806      i++;
1807   } // while
1808
1809   // Now call the recursive quick sort routine to do the real work....
1810   QuickSortGPT(partitions, 0, lastPart);
1811} // GPTData::SortGPT()
1812
1813// Set up data structures for entirely new set of partitions on the
1814// specified device. Returns 1 if OK, 0 if there were problems.
1815// Note that this function does NOT clear the protectiveMBR data
1816// structure, since it may hold the original MBR partitions if the
1817// program was launched on an MBR disk, and those may need to be
1818// converted to GPT format.
1819int GPTData::ClearGPTData(void) {
1820   int goOn = 1, i;
1821
1822   // Set up the partition table....
1823   free(partitions);
1824   partitions = NULL;
1825   SetGPTSize(NUM_GPT_ENTRIES);
1826
1827   // Now initialize a bunch of stuff that's static....
1828   mainHeader.signature = GPT_SIGNATURE;
1829   mainHeader.revision = 0x00010000;
1830   mainHeader.headerSize = HEADER_SIZE;
1831   mainHeader.reserved = 0;
1832   mainHeader.currentLBA = UINT64_C(1);
1833   mainHeader.partitionEntriesLBA = (uint64_t) 2;
1834   mainHeader.sizeOfPartitionEntries = GPT_SIZE;
1835   for (i = 0; i < GPT_RESERVED; i++) {
1836      mainHeader.reserved2[i] = '\0';
1837   } // for
1838
1839   // Now some semi-static items (computed based on end of disk)
1840   mainHeader.backupLBA = diskSize - UINT64_C(1);
1841   mainHeader.lastUsableLBA = diskSize - mainHeader.firstUsableLBA;
1842
1843   // Set a unique GUID for the disk, based on random numbers
1844   // rand() is only 32 bits, so multiply together to fill a 64-bit value
1845   mainHeader.diskGUID.data1 = (uint64_t) rand() * (uint64_t) rand();
1846   mainHeader.diskGUID.data2 = (uint64_t) rand() * (uint64_t) rand();
1847
1848   // Copy main header to backup header
1849   RebuildSecondHeader();
1850
1851   // Blank out the partitions array....
1852   BlankPartitions();
1853
1854   // Flag all CRCs as being OK....
1855   mainCrcOk = 1;
1856   secondCrcOk = 1;
1857   mainPartsCrcOk = 1;
1858   secondPartsCrcOk = 1;
1859
1860   return (goOn);
1861} // GPTData::ClearGPTData()
1862
1863// Set the location of the second GPT header data to the end of the disk.
1864// Used internally and called by the 'e' option on the recovery &
1865// transformation menu, to help users of RAID arrays who add disk space
1866// to their arrays.
1867void GPTData::MoveSecondHeaderToEnd() {
1868   mainHeader.backupLBA = secondHeader.currentLBA = diskSize - UINT64_C(1);
1869   mainHeader.lastUsableLBA = secondHeader.lastUsableLBA = diskSize - mainHeader.firstUsableLBA;
1870   secondHeader.partitionEntriesLBA = secondHeader.lastUsableLBA + UINT64_C(1);
1871} // GPTData::FixSecondHeaderLocation()
1872
1873void GPTData::SetName(uint32_t partNum, char* theName) {
1874   if ((partNum >= 0) && (partNum < mainHeader.numParts))
1875      if (partitions[partNum].GetFirstLBA() > 0)
1876         partitions[partNum].SetName((unsigned char*) theName);
1877} // GPTData::SetName
1878
1879// Set the disk GUID to the specified value. Note that the header CRCs must
1880// be recomputed after calling this function.
1881void GPTData::SetDiskGUID(GUIDData newGUID) {
1882   mainHeader.diskGUID = newGUID;
1883   secondHeader.diskGUID = newGUID;
1884} // SetDiskGUID()
1885
1886// Set the unique GUID of the specified partition. Returns 1 on
1887// successful completion, 0 if there were problems (invalid
1888// partition number).
1889int GPTData::SetPartitionGUID(uint32_t pn, GUIDData theGUID) {
1890   int retval = 0;
1891
1892   if (pn < mainHeader.numParts) {
1893      if (partitions[pn].GetFirstLBA() != UINT64_C(0)) {
1894         partitions[pn].SetUniqueGUID(theGUID);
1895         retval = 1;
1896      } // if
1897   } // if
1898   return retval;
1899} // GPTData::SetPartitionGUID()
1900
1901// Adjust sector number so that it falls on a sector boundary that's a
1902// multiple of sectorAlignment. This is done to improve the performance
1903// of Western Digital Advanced Format disks and disks with similar
1904// technology from other companies, which use 4096-byte sectors
1905// internally although they translate to 512-byte sectors for the
1906// benefit of the OS. If partitions aren't properly aligned on these
1907// disks, some filesystem data structures can span multiple physical
1908// sectors, degrading performance. This function should be called
1909// only on the FIRST sector of the partition, not the last!
1910// This function returns 1 if the alignment was altered, 0 if it
1911// was unchanged.
1912int GPTData::Align(uint64_t* sector) {
1913   int retval = 0, sectorOK = 0;
1914   uint64_t earlier, later, testSector, original;
1915
1916   if ((*sector % sectorAlignment) != 0) {
1917      original = *sector;
1918      retval = 1;
1919      earlier = (*sector / sectorAlignment) * sectorAlignment;
1920      later = earlier + (uint64_t) sectorAlignment;
1921
1922      // Check to see that every sector between the earlier one and the
1923      // requested one is clear, and that it's not too early....
1924      if (earlier >= mainHeader.firstUsableLBA) {
1925//         printf("earlier is %llu, first usable is %llu\n", earlier, mainHeader.firstUsableLBA);
1926         sectorOK = 1;
1927         testSector = earlier;
1928         do {
1929            sectorOK = IsFree(testSector++);
1930         } while ((sectorOK == 1) && (testSector < *sector));
1931         if (sectorOK == 1) {
1932            *sector = earlier;
1933//            printf("Moved sector earlier.\n");
1934         } // if
1935      } // if firstUsableLBA check
1936
1937      // If couldn't move the sector earlier, try to move it later instead....
1938      if ((sectorOK != 1) && (later <= mainHeader.lastUsableLBA)) {
1939         sectorOK = 1;
1940         testSector = later;
1941         do {
1942            sectorOK = IsFree(testSector--);
1943         } while ((sectorOK == 1) && (testSector > *sector));
1944         if (sectorOK == 1) {
1945            *sector = later;
1946//            printf("Moved sector later\n");
1947         } // if
1948      } // if
1949
1950      // If sector was changed successfully, inform the user of this fact.
1951      // Otherwise, notify the user that it couldn't be done....
1952      if (sectorOK == 1) {
1953         printf("Information: Moved requested sector from %llu to %llu for\n"
1954               "alignment purposes. Use 'l' on the experts' menu to adjust alignment.\n",
1955               original, *sector);
1956      } else {
1957         printf("Information: Sector not aligned on %d-sector boundary and could not be moved.\n"
1958                "If you're using a Western Digital Advanced Format or similar disk with\n"
1959                "underlying 4096-byte sectors, performance may suffer.\n", sectorAlignment);
1960         retval = 0;
1961      } // if/else
1962   } // if
1963   return retval;
1964} // GPTData::Align()
1965
1966/********************************************************
1967 *                                                      *
1968 * Functions that return data about GPT data structures *
1969 * (most of these are inline in gpt.h)                  *
1970 *                                                      *
1971 ********************************************************/
1972
1973// Find the low and high used partition numbers (numbered from 0).
1974// Return value is the number of partitions found. Note that the
1975// *low and *high values are both set to 0 when no partitions
1976// are found, as well as when a single partition in the first
1977// position exists. Thus, the return value is the only way to
1978// tell when no partitions exist.
1979int GPTData::GetPartRange(uint32_t *low, uint32_t *high) {
1980   uint32_t i;
1981   int numFound = 0;
1982
1983   *low = mainHeader.numParts + 1; // code for "not found"
1984   *high = 0;
1985   if (mainHeader.numParts > 0) { // only try if partition table exists...
1986      for (i = 0; i < mainHeader.numParts; i++) {
1987         if (partitions[i].GetFirstLBA() != UINT64_C(0)) { // it exists
1988            *high = i; // since we're counting up, set the high value
1989	    // Set the low value only if it's not yet found...
1990            if (*low == (mainHeader.numParts + 1)) *low = i;
1991            numFound++;
1992         } // if
1993      } // for
1994   } // if
1995
1996   // Above will leave *low pointing to its "not found" value if no partitions
1997   // are defined, so reset to 0 if this is the case....
1998   if (*low == (mainHeader.numParts + 1))
1999      *low = 0;
2000   return numFound;
2001} // GPTData::GetPartRange()
2002
2003// Returns the number of defined partitions.
2004uint32_t GPTData::CountParts(void) {
2005   int i, counted = 0;
2006
2007   for (i = 0; i < mainHeader.numParts; i++) {
2008      if (partitions[i].GetFirstLBA() > 0)
2009         counted++;
2010   } // for
2011   return counted;
2012} // GPTData::CountParts()
2013
2014/****************************************************
2015 *                                                  *
2016 * Functions that return data about disk free space *
2017 *                                                  *
2018 ****************************************************/
2019
2020// Find the first available block after the starting point; returns 0 if
2021// there are no available blocks left
2022uint64_t GPTData::FindFirstAvailable(uint64_t start) {
2023   uint64_t first;
2024   uint32_t i;
2025   int firstMoved = 0;
2026
2027   // Begin from the specified starting point or from the first usable
2028   // LBA, whichever is greater...
2029   if (start < mainHeader.firstUsableLBA)
2030      first = mainHeader.firstUsableLBA;
2031   else
2032      first = start;
2033
2034   // ...now search through all partitions; if first is within an
2035   // existing partition, move it to the next sector after that
2036   // partition and repeat. If first was moved, set firstMoved
2037   // flag; repeat until firstMoved is not set, so as to catch
2038   // cases where partitions are out of sequential order....
2039   do {
2040      firstMoved = 0;
2041      for (i = 0; i < mainHeader.numParts; i++) {
2042         if ((first >= partitions[i].GetFirstLBA()) &&
2043              (first <= partitions[i].GetLastLBA())) { // in existing part.
2044            first = partitions[i].GetLastLBA() + 1;
2045            firstMoved = 1;
2046              } // if
2047      } // for
2048   } while (firstMoved == 1);
2049   if (first > mainHeader.lastUsableLBA)
2050      first = 0;
2051   return (first);
2052} // GPTData::FindFirstAvailable()
2053
2054// Finds the first available sector in the largest block of unallocated
2055// space on the disk. Returns 0 if there are no available blocks left
2056uint64_t GPTData::FindFirstInLargest(void) {
2057   uint64_t start, firstBlock, lastBlock, segmentSize, selectedSize = 0, selectedSegment = 0;
2058
2059   start = 0;
2060   do {
2061      firstBlock = FindFirstAvailable(start);
2062      if (firstBlock != UINT32_C(0)) { // something's free...
2063         lastBlock = FindLastInFree(firstBlock);
2064         segmentSize = lastBlock - firstBlock + UINT32_C(1);
2065         if (segmentSize > selectedSize) {
2066            selectedSize = segmentSize;
2067            selectedSegment = firstBlock;
2068         } // if
2069         start = lastBlock + 1;
2070      } // if
2071   } while (firstBlock != 0);
2072   return selectedSegment;
2073} // GPTData::FindFirstInLargest()
2074
2075// Find the last available block on the disk at or after the start
2076// block. Returns 0 if there are no available partitions after
2077// (or including) start.
2078uint64_t GPTData::FindLastAvailable(uint64_t start) {
2079   uint64_t last;
2080   uint32_t i;
2081   int lastMoved = 0;
2082
2083   // Start by assuming the last usable LBA is available....
2084   last = mainHeader.lastUsableLBA;
2085
2086   // ...now, similar to algorithm in FindFirstAvailable(), search
2087   // through all partitions, moving last when it's in an existing
2088   // partition. Set the lastMoved flag so we repeat to catch cases
2089   // where partitions are out of logical order.
2090   do {
2091      lastMoved = 0;
2092      for (i = 0; i < mainHeader.numParts; i++) {
2093         if ((last >= partitions[i].GetFirstLBA()) &&
2094              (last <= partitions[i].GetLastLBA())) { // in existing part.
2095            last = partitions[i].GetFirstLBA() - 1;
2096            lastMoved = 1;
2097              } // if
2098      } // for
2099   } while (lastMoved == 1);
2100   if (last < mainHeader.firstUsableLBA)
2101      last = 0;
2102   return (last);
2103} // GPTData::FindLastAvailable()
2104
2105// Find the last available block in the free space pointed to by start.
2106uint64_t GPTData::FindLastInFree(uint64_t start) {
2107   uint64_t nearestStart;
2108   uint32_t i;
2109
2110   nearestStart = mainHeader.lastUsableLBA;
2111   for (i = 0; i < mainHeader.numParts; i++) {
2112      if ((nearestStart > partitions[i].GetFirstLBA()) &&
2113           (partitions[i].GetFirstLBA() > start)) {
2114         nearestStart = partitions[i].GetFirstLBA() - 1;
2115           } // if
2116   } // for
2117   return (nearestStart);
2118} // GPTData::FindLastInFree()
2119
2120// Finds the total number of free blocks, the number of segments in which
2121// they reside, and the size of the largest of those segments
2122uint64_t GPTData::FindFreeBlocks(int *numSegments, uint64_t *largestSegment) {
2123   uint64_t start = UINT64_C(0); // starting point for each search
2124   uint64_t totalFound = UINT64_C(0); // running total
2125   uint64_t firstBlock; // first block in a segment
2126   uint64_t lastBlock; // last block in a segment
2127   uint64_t segmentSize; // size of segment in blocks
2128   int num = 0;
2129
2130   *largestSegment = UINT64_C(0);
2131   do {
2132      firstBlock = FindFirstAvailable(start);
2133      if (firstBlock != UINT64_C(0)) { // something's free...
2134         lastBlock = FindLastInFree(firstBlock);
2135         segmentSize = lastBlock - firstBlock + UINT64_C(1);
2136         if (segmentSize > *largestSegment) {
2137            *largestSegment = segmentSize;
2138         } // if
2139         totalFound += segmentSize;
2140         num++;
2141         start = lastBlock + 1;
2142      } // if
2143   } while (firstBlock != 0);
2144   *numSegments = num;
2145   return totalFound;
2146} // GPTData::FindFreeBlocks()
2147
2148// Returns 1 if sector is unallocated, 0 if it's allocated to a partition
2149int GPTData::IsFree(uint64_t sector) {
2150   int isFree = 1;
2151   uint32_t i;
2152
2153   for (i = 0; i < mainHeader.numParts; i++) {
2154      if ((sector >= partitions[i].GetFirstLBA()) &&
2155           (sector <= partitions[i].GetLastLBA())) {
2156         isFree = 0;
2157           } // if
2158   } // for
2159   if ((sector < mainHeader.firstUsableLBA) ||
2160        (sector > mainHeader.lastUsableLBA)) {
2161      isFree = 0;
2162        } // if
2163        return (isFree);
2164} // GPTData::IsFree()
2165
2166/********************************
2167 *                              *
2168 * Endianness support functions *
2169 *                              *
2170 ********************************/
2171
2172void GPTData::ReverseHeaderBytes(struct GPTHeader* header) {
2173   ReverseBytes(&header->signature, 8);
2174   ReverseBytes(&header->revision, 4);
2175   ReverseBytes(&header->headerSize, 4);
2176   ReverseBytes(&header->headerCRC, 4);
2177   ReverseBytes(&header->reserved, 4);
2178   ReverseBytes(&header->currentLBA, 8);
2179   ReverseBytes(&header->backupLBA, 8);
2180   ReverseBytes(&header->firstUsableLBA, 8);
2181   ReverseBytes(&header->lastUsableLBA, 8);
2182   ReverseBytes(&header->partitionEntriesLBA, 8);
2183   ReverseBytes(&header->numParts, 4);
2184   ReverseBytes(&header->sizeOfPartitionEntries, 4);
2185   ReverseBytes(&header->partitionEntriesCRC, 4);
2186   ReverseBytes(&header->reserved2, GPT_RESERVED);
2187   ReverseBytes(&header->diskGUID.data1, 8);
2188   ReverseBytes(&header->diskGUID.data2, 8);
2189} // GPTData::ReverseHeaderBytes()
2190
2191// IMPORTANT NOTE: This function requires non-reversed mainHeader
2192// structure!
2193void GPTData::ReversePartitionBytes() {
2194   uint32_t i;
2195
2196   // Check GPT signature on big-endian systems; this will mismatch
2197   // if the function is called out of order. Unfortunately, it'll also
2198   // mismatch if there's data corruption.
2199   if ((mainHeader.signature != GPT_SIGNATURE) && (IsLittleEndian() == 0)) {
2200      printf("GPT signature mismatch in GPTData::ReversePartitionBytes(). This indicates\n"
2201            "data corruption or a misplaced call to this function.\n");
2202   } // if signature mismatch....
2203   for (i = 0; i < mainHeader.numParts; i++) {
2204      partitions[i].ReversePartBytes();
2205   } // for
2206} // GPTData::ReversePartitionBytes()
2207
2208/******************************************
2209 *                                        *
2210 * Additional non-class support functions *
2211 *                                        *
2212 ******************************************/
2213
2214// Check to be sure that data type sizes are correct. The basic types (uint*_t) should
2215// never fail these tests, but the struct types may fail depending on compile options.
2216// Specifically, the -fpack-struct option to gcc may be required to ensure proper structure
2217// sizes.
2218int SizesOK(void) {
2219   int allOK = 1;
2220
2221   if (sizeof(uint8_t) != 1) {
2222      fprintf(stderr, "uint8_t is %lu bytes, should be 1 byte; aborting!\n", sizeof(uint8_t));
2223      allOK = 0;
2224   } // if
2225   if (sizeof(uint16_t) != 2) {
2226      fprintf(stderr, "uint16_t is %lu bytes, should be 2 bytes; aborting!\n", sizeof(uint16_t));
2227      allOK = 0;
2228   } // if
2229   if (sizeof(uint32_t) != 4) {
2230      fprintf(stderr, "uint32_t is %lu bytes, should be 4 bytes; aborting!\n", sizeof(uint32_t));
2231      allOK = 0;
2232   } // if
2233   if (sizeof(uint64_t) != 8) {
2234      fprintf(stderr, "uint64_t is %lu bytes, should be 8 bytes; aborting!\n", sizeof(uint64_t));
2235      allOK = 0;
2236   } // if
2237   if (sizeof(struct MBRRecord) != 16) {
2238      fprintf(stderr, "MBRRecord is %lu bytes, should be 16 bytes; aborting!\n", sizeof(MBRRecord));
2239      allOK = 0;
2240   } // if
2241   if (sizeof(struct TempMBR) != 512) {
2242      fprintf(stderr, "TempMBR is %lu bytes, should be 512 bytes; aborting!\n", sizeof(TempMBR));
2243      allOK = 0;
2244   } // if
2245   if (sizeof(struct GPTHeader) != 512) {
2246      fprintf(stderr, "GPTHeader is %lu bytes, should be 512 bytes; aborting!\n", sizeof(GPTHeader));
2247      allOK = 0;
2248   } // if
2249   if (sizeof(GPTPart) != 128) {
2250      fprintf(stderr, "GPTPart is %lu bytes, should be 128 bytes; aborting!\n", sizeof(GPTPart));
2251      allOK = 0;
2252   } // if
2253// Determine endianness; set allOK = 0 if running on big-endian hardware
2254   if (IsLittleEndian() == 0) {
2255      fprintf(stderr, "\aRunning on big-endian hardware. Big-endian support is new and poorly"
2256            " tested!\nBeware!\n");
2257      // allOK = 0;
2258   } // if
2259   return (allOK);
2260} // SizesOK()
2261
2262