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-2013 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#ifndef __STDC_CONSTANT_MACROS
11#define __STDC_CONSTANT_MACROS
12#endif
13
14#include <stdio.h>
15#include <stdlib.h>
16#include <stdint.h>
17#include <fcntl.h>
18#include <string.h>
19#include <math.h>
20#include <time.h>
21#include <sys/stat.h>
22#include <errno.h>
23#include <iostream>
24#include <algorithm>
25#include "crc32.h"
26#include "gpt.h"
27#include "bsd.h"
28#include "support.h"
29#include "parttypes.h"
30#include "attributes.h"
31#include "diskio.h"
32
33using namespace std;
34
35#ifdef __FreeBSD__
36#define log2(x) (log(x) / M_LN2)
37#endif // __FreeBSD__
38
39#ifdef _MSC_VER
40#define log2(x) (log((double) x) / log(2.0))
41#endif // Microsoft Visual C++
42
43#ifdef EFI
44// in UEFI mode MMX registers are not yet available so using the
45// x86_64 ABI to move "double" values around is not an option.
46#ifdef log2
47#undef log2
48#endif
49#define log2(x) log2_32( x )
50static inline uint32_t log2_32(uint32_t v) {
51   int r = -1;
52   while (v >= 1) {
53      r++;
54      v >>= 1;
55   }
56   return r;
57}
58#endif
59
60/****************************************
61 *                                      *
62 * GPTData class and related structures *
63 *                                      *
64 ****************************************/
65
66// Default constructor
67GPTData::GPTData(void) {
68   blockSize = SECTOR_SIZE; // set a default
69   diskSize = 0;
70   partitions = NULL;
71   state = gpt_valid;
72   device = "";
73   justLooking = 0;
74   syncing = 1;
75   mainCrcOk = 0;
76   secondCrcOk = 0;
77   mainPartsCrcOk = 0;
78   secondPartsCrcOk = 0;
79   apmFound = 0;
80   bsdFound = 0;
81   sectorAlignment = MIN_AF_ALIGNMENT; // Align partitions on 4096-byte boundaries by default
82   beQuiet = 0;
83   whichWasUsed = use_new;
84   mainHeader.numParts = 0;
85   numParts = 0;
86   SetGPTSize(NUM_GPT_ENTRIES);
87   // Initialize CRC functions...
88   chksum_crc32gentab();
89} // GPTData default constructor
90
91// The following constructor loads GPT data from a device file
92GPTData::GPTData(string filename) {
93   blockSize = SECTOR_SIZE; // set a default
94   diskSize = 0;
95   partitions = NULL;
96   state = gpt_invalid;
97   device = "";
98   justLooking = 0;
99   syncing = 1;
100   mainCrcOk = 0;
101   secondCrcOk = 0;
102   mainPartsCrcOk = 0;
103   secondPartsCrcOk = 0;
104   apmFound = 0;
105   bsdFound = 0;
106   sectorAlignment = MIN_AF_ALIGNMENT; // Align partitions on 4096-byte boundaries by default
107   beQuiet = 0;
108   whichWasUsed = use_new;
109   mainHeader.numParts = 0;
110   numParts = 0;
111   // Initialize CRC functions...
112   chksum_crc32gentab();
113   if (!LoadPartitions(filename))
114      exit(2);
115} // GPTData(string filename) constructor
116
117// Destructor
118GPTData::~GPTData(void) {
119   delete[] partitions;
120} // GPTData destructor
121
122// Assignment operator
123GPTData & GPTData::operator=(const GPTData & orig) {
124   uint32_t i;
125
126   mainHeader = orig.mainHeader;
127   numParts = orig.numParts;
128   secondHeader = orig.secondHeader;
129   protectiveMBR = orig.protectiveMBR;
130   device = orig.device;
131   blockSize = orig.blockSize;
132   diskSize = orig.diskSize;
133   state = orig.state;
134   justLooking = orig.justLooking;
135   syncing = orig.syncing;
136   mainCrcOk = orig.mainCrcOk;
137   secondCrcOk = orig.secondCrcOk;
138   mainPartsCrcOk = orig.mainPartsCrcOk;
139   secondPartsCrcOk = orig.secondPartsCrcOk;
140   apmFound = orig.apmFound;
141   bsdFound = orig.bsdFound;
142   sectorAlignment = orig.sectorAlignment;
143   beQuiet = orig.beQuiet;
144   whichWasUsed = orig.whichWasUsed;
145
146   myDisk.OpenForRead(orig.myDisk.GetName());
147
148   delete[] partitions;
149   partitions = new GPTPart [numParts];
150   if (partitions == NULL) {
151      cerr << "Error! Could not allocate memory for partitions in GPTData::operator=()!\n"
152           << "Terminating!\n";
153      exit(1);
154   } // if
155   for (i = 0; i < numParts; i++) {
156      partitions[i] = orig.partitions[i];
157   } // for
158
159   return *this;
160} // GPTData::operator=()
161
162/*********************************************************************
163 *                                                                   *
164 * Begin functions that verify data, or that adjust the verification *
165 * information (compute CRCs, rebuild headers)                       *
166 *                                                                   *
167 *********************************************************************/
168
169// Perform detailed verification, reporting on any problems found, but
170// do *NOT* recover from these problems. Returns the total number of
171// problems identified.
172int GPTData::Verify(void) {
173   int problems = 0, alignProbs = 0;
174   uint32_t i, numSegments;
175   uint64_t totalFree, largestSegment;
176
177   // First, check for CRC errors in the GPT data....
178   if (!mainCrcOk) {
179      problems++;
180      cout << "\nProblem: The CRC for the main GPT header is invalid. The main GPT header may\n"
181           << "be corrupt. Consider loading the backup GPT header to rebuild the main GPT\n"
182           << "header ('b' on the recovery & transformation menu). This report may be a false\n"
183           << "alarm if you've already corrected other problems.\n";
184   } // if
185   if (!mainPartsCrcOk) {
186      problems++;
187      cout << "\nProblem: The CRC for the main partition table is invalid. This table may be\n"
188           << "corrupt. Consider loading the backup partition table ('c' on the recovery &\n"
189           << "transformation menu). This report may be a false alarm if you've already\n"
190           << "corrected other problems.\n";
191   } // if
192   if (!secondCrcOk) {
193      problems++;
194      cout << "\nProblem: The CRC for the backup GPT header is invalid. The backup GPT header\n"
195           << "may be corrupt. Consider using the main GPT header to rebuild the backup GPT\n"
196           << "header ('d' on the recovery & transformation menu). This report may be a false\n"
197           << "alarm if you've already corrected other problems.\n";
198   } // if
199   if (!secondPartsCrcOk) {
200      problems++;
201      cout << "\nCaution: The CRC for the backup partition table is invalid. This table may\n"
202           << "be corrupt. This program will automatically create a new backup partition\n"
203           << "table when you save your partitions.\n";
204   } // if
205
206   // Now check that the main and backup headers both point to themselves....
207   if (mainHeader.currentLBA != 1) {
208      problems++;
209      cout << "\nProblem: The main header's self-pointer doesn't point to itself. This problem\n"
210           << "is being automatically corrected, but it may be a symptom of more serious\n"
211           << "problems. Think carefully before saving changes with 'w' or using this disk.\n";
212      mainHeader.currentLBA = 1;
213   } // if
214   if (secondHeader.currentLBA != (diskSize - UINT64_C(1))) {
215      problems++;
216      cout << "\nProblem: The secondary header's self-pointer indicates that it doesn't reside\n"
217           << "at the end of the disk. If you've added a disk to a RAID array, use the 'e'\n"
218           << "option on the experts' menu to adjust the secondary header's and partition\n"
219           << "table's locations.\n";
220   } // if
221
222   // Now check that critical main and backup GPT entries match each other
223   if (mainHeader.currentLBA != secondHeader.backupLBA) {
224      problems++;
225      cout << "\nProblem: main GPT header's current LBA pointer (" << mainHeader.currentLBA
226           << ") doesn't\nmatch the backup GPT header's alternate LBA pointer("
227           << secondHeader.backupLBA << ").\n";
228   } // if
229   if (mainHeader.backupLBA != secondHeader.currentLBA) {
230      problems++;
231      cout << "\nProblem: main GPT header's backup LBA pointer (" << mainHeader.backupLBA
232           << ") doesn't\nmatch the backup GPT header's current LBA pointer ("
233           << secondHeader.currentLBA << ").\n"
234           << "The 'e' option on the experts' menu may fix this problem.\n";
235   } // if
236   if (mainHeader.firstUsableLBA != secondHeader.firstUsableLBA) {
237      problems++;
238      cout << "\nProblem: main GPT header's first usable LBA pointer (" << mainHeader.firstUsableLBA
239           << ") doesn't\nmatch the backup GPT header's first usable LBA pointer ("
240           << secondHeader.firstUsableLBA << ")\n";
241   } // if
242   if (mainHeader.lastUsableLBA != secondHeader.lastUsableLBA) {
243      problems++;
244      cout << "\nProblem: main GPT header's last usable LBA pointer (" << mainHeader.lastUsableLBA
245           << ") doesn't\nmatch the backup GPT header's last usable LBA pointer ("
246           << secondHeader.lastUsableLBA << ")\n"
247           << "The 'e' option on the experts' menu can probably fix this problem.\n";
248   } // if
249   if ((mainHeader.diskGUID != secondHeader.diskGUID)) {
250      problems++;
251      cout << "\nProblem: main header's disk GUID (" << mainHeader.diskGUID
252           << ") doesn't\nmatch the backup GPT header's disk GUID ("
253           << secondHeader.diskGUID << ")\n"
254           << "You should use the 'b' or 'd' option on the recovery & transformation menu to\n"
255           << "select one or the other header.\n";
256   } // if
257   if (mainHeader.numParts != secondHeader.numParts) {
258      problems++;
259      cout << "\nProblem: main GPT header's number of partitions (" << mainHeader.numParts
260           << ") doesn't\nmatch the backup GPT header's number of partitions ("
261           << secondHeader.numParts << ")\n"
262           << "Resizing the partition table ('s' on the experts' menu) may help.\n";
263   } // if
264   if (mainHeader.sizeOfPartitionEntries != secondHeader.sizeOfPartitionEntries) {
265      problems++;
266      cout << "\nProblem: main GPT header's size of partition entries ("
267           << mainHeader.sizeOfPartitionEntries << ") doesn't\n"
268           << "match the backup GPT header's size of partition entries ("
269           << secondHeader.sizeOfPartitionEntries << ")\n"
270           << "You should use the 'b' or 'd' option on the recovery & transformation menu to\n"
271           << "select one or the other header.\n";
272   } // if
273
274   // Now check for a few other miscellaneous problems...
275   // Check that the disk size will hold the data...
276   if (mainHeader.backupLBA >= diskSize) {
277      problems++;
278      cout << "\nProblem: Disk is too small to hold all the data!\n"
279           << "(Disk size is " << diskSize << " sectors, needs to be "
280           << mainHeader.backupLBA + UINT64_C(1) << " sectors.)\n"
281           << "The 'e' option on the experts' menu may fix this problem.\n";
282   } // if
283
284   if ((mainHeader.lastUsableLBA >= diskSize) || (mainHeader.lastUsableLBA > mainHeader.backupLBA)) {
285      problems++;
286      cout << "\nProblem: GPT claims the disk is larger than it is! (Claimed last usable\n"
287           << "sector is " << mainHeader.lastUsableLBA << ", but backup header is at\n"
288           << mainHeader.backupLBA << " and disk size is " << diskSize << " sectors.\n"
289           << "The 'e' option on the experts' menu will probably fix this problem\n";
290   }
291
292   // Check for overlapping partitions....
293   problems += FindOverlaps();
294
295   // Check for insane partitions (start after end, hugely big, etc.)
296   problems += FindInsanePartitions();
297
298   // Check for mismatched MBR and GPT partitions...
299   problems += FindHybridMismatches();
300
301   // Check for MBR-specific problems....
302   problems += VerifyMBR();
303
304   // Check for a 0xEE protective partition that's marked as active....
305   if (protectiveMBR.IsEEActive()) {
306      cout << "\nWarning: The 0xEE protective partition in the MBR is marked as active. This is\n"
307           << "technically a violation of the GPT specification, and can cause some EFIs to\n"
308           << "ignore the disk, but it is required to boot from a GPT disk on some BIOS-based\n"
309           << "computers. You can clear this flag by creating a fresh protective MBR using\n"
310           << "the 'n' option on the experts' menu.\n";
311   }
312
313   // Verify that partitions don't run into GPT data areas....
314   problems += CheckGPTSize();
315
316   if (!protectiveMBR.DoTheyFit()) {
317      cout << "\nPartition(s) in the protective MBR are too big for the disk! Creating a\n"
318           << "fresh protective or hybrid MBR is recommended.\n";
319      problems++;
320   }
321
322   // Check that partitions are aligned on proper boundaries (for WD Advanced
323   // Format and similar disks)....
324   for (i = 0; i < numParts; i++) {
325      if ((partitions[i].IsUsed()) && (partitions[i].GetFirstLBA() % sectorAlignment) != 0) {
326         cout << "\nCaution: Partition " << i + 1 << " doesn't begin on a "
327              << sectorAlignment << "-sector boundary. This may\nresult "
328              << "in degraded performance on some modern (2009 and later) hard disks.\n";
329         alignProbs++;
330      } // if
331   } // for
332   if (alignProbs > 0)
333      cout << "\nConsult http://www.ibm.com/developerworks/linux/library/l-4kb-sector-disks/\n"
334      << "for information on disk alignment.\n";
335
336   // Now compute available space, but only if no problems found, since
337   // problems could affect the results
338   if (problems == 0) {
339      totalFree = FindFreeBlocks(&numSegments, &largestSegment);
340      cout << "\nNo problems found. " << totalFree << " free sectors ("
341           << BytesToIeee(totalFree, blockSize) << ") available in "
342           << numSegments << "\nsegments, the largest of which is "
343           << largestSegment << " (" << BytesToIeee(largestSegment, blockSize)
344           << ") in size.\n";
345   } else {
346      cout << "\nIdentified " << problems << " problems!\n";
347   } // if/else
348
349   return (problems);
350} // GPTData::Verify()
351
352// Checks to see if the GPT tables overrun existing partitions; if they
353// do, issues a warning but takes no action. Returns number of problems
354// detected (0 if OK, 1 to 2 if problems).
355int GPTData::CheckGPTSize(void) {
356   uint64_t overlap, firstUsedBlock, lastUsedBlock;
357   uint32_t i;
358   int numProbs = 0;
359
360   // first, locate the first & last used blocks
361   firstUsedBlock = UINT64_MAX;
362   lastUsedBlock = 0;
363   for (i = 0; i < numParts; i++) {
364      if (partitions[i].IsUsed()) {
365         if (partitions[i].GetFirstLBA() < firstUsedBlock)
366            firstUsedBlock = partitions[i].GetFirstLBA();
367         if (partitions[i].GetLastLBA() > lastUsedBlock) {
368            lastUsedBlock = partitions[i].GetLastLBA();
369         } // if
370      } // if
371   } // for
372
373   // If the disk size is 0 (the default), then it means that various
374   // variables aren't yet set, so the below tests will be useless;
375   // therefore we should skip everything
376   if (diskSize != 0) {
377      if (mainHeader.firstUsableLBA > firstUsedBlock) {
378         overlap = mainHeader.firstUsableLBA - firstUsedBlock;
379         cout << "Warning! Main partition table overlaps the first partition by "
380              << overlap << " blocks!\n";
381         if (firstUsedBlock > 2) {
382            cout << "Try reducing the partition table size by " << overlap * 4
383                 << " entries.\n(Use the 's' item on the experts' menu.)\n";
384         } else {
385            cout << "You will need to delete this partition or resize it in another utility.\n";
386         } // if/else
387         numProbs++;
388      } // Problem at start of disk
389      if (mainHeader.lastUsableLBA < lastUsedBlock) {
390         overlap = lastUsedBlock - mainHeader.lastUsableLBA;
391         cout << "\nWarning! Secondary partition table overlaps the last partition by\n"
392              << overlap << " blocks!\n";
393         if (lastUsedBlock > (diskSize - 2)) {
394            cout << "You will need to delete this partition or resize it in another utility.\n";
395         } else {
396            cout << "Try reducing the partition table size by " << overlap * 4
397                 << " entries.\n(Use the 's' item on the experts' menu.)\n";
398         } // if/else
399         numProbs++;
400      } // Problem at end of disk
401   } // if (diskSize != 0)
402   return numProbs;
403} // GPTData::CheckGPTSize()
404
405// Check the validity of the GPT header. Returns 1 if the main header
406// is valid, 2 if the backup header is valid, 3 if both are valid, and
407// 0 if neither is valid. Note that this function checks the GPT signature,
408// revision value, and CRCs in both headers.
409int GPTData::CheckHeaderValidity(void) {
410   int valid = 3;
411
412   cout.setf(ios::uppercase);
413   cout.fill('0');
414
415   // Note: failed GPT signature checks produce no error message because
416   // a message is displayed in the ReversePartitionBytes() function
417   if ((mainHeader.signature != GPT_SIGNATURE) || (!CheckHeaderCRC(&mainHeader, 1))) {
418      valid -= 1;
419   } else if ((mainHeader.revision != 0x00010000) && valid) {
420      valid -= 1;
421      cout << "Unsupported GPT version in main header; read 0x";
422      cout.width(8);
423      cout << hex << mainHeader.revision << ", should be\n0x";
424      cout.width(8);
425      cout << UINT32_C(0x00010000) << dec << "\n";
426   } // if/else/if
427
428   if ((secondHeader.signature != GPT_SIGNATURE) || (!CheckHeaderCRC(&secondHeader))) {
429      valid -= 2;
430   } else if ((secondHeader.revision != 0x00010000) && valid) {
431      valid -= 2;
432      cout << "Unsupported GPT version in backup header; read 0x";
433      cout.width(8);
434      cout << hex << secondHeader.revision << ", should be\n0x";
435      cout.width(8);
436      cout << UINT32_C(0x00010000) << dec << "\n";
437   } // if/else/if
438
439   // Check for an Apple disk signature
440   if (((mainHeader.signature << 32) == APM_SIGNATURE1) ||
441        (mainHeader.signature << 32) == APM_SIGNATURE2) {
442      apmFound = 1; // Will display warning message later
443   } // if
444   cout.fill(' ');
445
446   return valid;
447} // GPTData::CheckHeaderValidity()
448
449// Check the header CRC to see if it's OK...
450// Note: Must be called with header in platform-ordered byte order.
451// Returns 1 if header's computed CRC matches the stored value, 0 if the
452// computed and stored values don't match
453int GPTData::CheckHeaderCRC(struct GPTHeader* header, int warn) {
454   uint32_t oldCRC, newCRC, hSize;
455   uint8_t *temp;
456
457   // Back up old header CRC and then blank it, since it must be 0 for
458   // computation to be valid
459   oldCRC = header->headerCRC;
460   header->headerCRC = UINT32_C(0);
461
462   hSize = header->headerSize;
463
464   if (IsLittleEndian() == 0)
465      ReverseHeaderBytes(header);
466
467   if ((hSize > blockSize) || (hSize < HEADER_SIZE)) {
468      if (warn) {
469         cerr << "\aWarning! Header size is specified as " << hSize << ", which is invalid.\n";
470         cerr << "Setting the header size for CRC computation to " << HEADER_SIZE << "\n";
471      } // if
472      hSize = HEADER_SIZE;
473   } else if ((hSize > sizeof(GPTHeader)) && warn) {
474      cout << "\aCaution! Header size for CRC check is " << hSize << ", which is greater than " << sizeof(GPTHeader) << ".\n";
475      cout << "If stray data exists after the header on the header sector, it will be ignored,\n"
476           << "which may result in a CRC false alarm.\n";
477   } // if/elseif
478   temp = new uint8_t[hSize];
479   if (temp != NULL) {
480      memset(temp, 0, hSize);
481      if (hSize < sizeof(GPTHeader))
482         memcpy(temp, header, hSize);
483      else
484         memcpy(temp, header, sizeof(GPTHeader));
485
486      newCRC = chksum_crc32((unsigned char*) temp, hSize);
487      delete[] temp;
488   } else {
489      cerr << "Could not allocate memory in GPTData::CheckHeaderCRC()! Aborting!\n";
490      exit(1);
491   }
492   if (IsLittleEndian() == 0)
493      ReverseHeaderBytes(header);
494   header->headerCRC = oldCRC;
495   return (oldCRC == newCRC);
496} // GPTData::CheckHeaderCRC()
497
498// Recompute all the CRCs. Must be called before saving if any changes have
499// been made. Must be called on platform-ordered data (this function reverses
500// byte order and then undoes that reversal.)
501void GPTData::RecomputeCRCs(void) {
502   uint32_t crc, hSize;
503   int littleEndian = 1;
504
505   // If the header size is bigger than the GPT header data structure, reset it;
506   // otherwise, set both header sizes to whatever the main one is....
507   if (mainHeader.headerSize > sizeof(GPTHeader))
508      hSize = secondHeader.headerSize = mainHeader.headerSize = HEADER_SIZE;
509   else
510      hSize = secondHeader.headerSize = mainHeader.headerSize;
511
512   if ((littleEndian = IsLittleEndian()) == 0) {
513      ReversePartitionBytes();
514      ReverseHeaderBytes(&mainHeader);
515      ReverseHeaderBytes(&secondHeader);
516   } // if
517
518   // Compute CRC of partition tables & store in main and secondary headers
519   crc = chksum_crc32((unsigned char*) partitions, numParts * GPT_SIZE);
520   mainHeader.partitionEntriesCRC = crc;
521   secondHeader.partitionEntriesCRC = crc;
522   if (littleEndian == 0) {
523      ReverseBytes(&mainHeader.partitionEntriesCRC, 4);
524      ReverseBytes(&secondHeader.partitionEntriesCRC, 4);
525   } // if
526
527   // Zero out GPT headers' own CRCs (required for correct computation)
528   mainHeader.headerCRC = 0;
529   secondHeader.headerCRC = 0;
530
531   crc = chksum_crc32((unsigned char*) &mainHeader, hSize);
532   if (littleEndian == 0)
533      ReverseBytes(&crc, 4);
534   mainHeader.headerCRC = crc;
535   crc = chksum_crc32((unsigned char*) &secondHeader, hSize);
536   if (littleEndian == 0)
537      ReverseBytes(&crc, 4);
538   secondHeader.headerCRC = crc;
539
540   if (littleEndian == 0) {
541      ReverseHeaderBytes(&mainHeader);
542      ReverseHeaderBytes(&secondHeader);
543      ReversePartitionBytes();
544   } // if
545} // GPTData::RecomputeCRCs()
546
547// Rebuild the main GPT header, using the secondary header as a model.
548// Typically called when the main header has been found to be corrupt.
549void GPTData::RebuildMainHeader(void) {
550   mainHeader.signature = GPT_SIGNATURE;
551   mainHeader.revision = secondHeader.revision;
552   mainHeader.headerSize = secondHeader.headerSize;
553   mainHeader.headerCRC = UINT32_C(0);
554   mainHeader.reserved = secondHeader.reserved;
555   mainHeader.currentLBA = secondHeader.backupLBA;
556   mainHeader.backupLBA = secondHeader.currentLBA;
557   mainHeader.firstUsableLBA = secondHeader.firstUsableLBA;
558   mainHeader.lastUsableLBA = secondHeader.lastUsableLBA;
559   mainHeader.diskGUID = secondHeader.diskGUID;
560   mainHeader.partitionEntriesLBA = UINT64_C(2);
561   mainHeader.numParts = secondHeader.numParts;
562   mainHeader.sizeOfPartitionEntries = secondHeader.sizeOfPartitionEntries;
563   mainHeader.partitionEntriesCRC = secondHeader.partitionEntriesCRC;
564   memcpy(mainHeader.reserved2, secondHeader.reserved2, sizeof(mainHeader.reserved2));
565   mainCrcOk = secondCrcOk;
566   SetGPTSize(mainHeader.numParts, 0);
567} // GPTData::RebuildMainHeader()
568
569// Rebuild the secondary GPT header, using the main header as a model.
570void GPTData::RebuildSecondHeader(void) {
571   secondHeader.signature = GPT_SIGNATURE;
572   secondHeader.revision = mainHeader.revision;
573   secondHeader.headerSize = mainHeader.headerSize;
574   secondHeader.headerCRC = UINT32_C(0);
575   secondHeader.reserved = mainHeader.reserved;
576   secondHeader.currentLBA = mainHeader.backupLBA;
577   secondHeader.backupLBA = mainHeader.currentLBA;
578   secondHeader.firstUsableLBA = mainHeader.firstUsableLBA;
579   secondHeader.lastUsableLBA = mainHeader.lastUsableLBA;
580   secondHeader.diskGUID = mainHeader.diskGUID;
581   secondHeader.partitionEntriesLBA = secondHeader.lastUsableLBA + UINT64_C(1);
582   secondHeader.numParts = mainHeader.numParts;
583   secondHeader.sizeOfPartitionEntries = mainHeader.sizeOfPartitionEntries;
584   secondHeader.partitionEntriesCRC = mainHeader.partitionEntriesCRC;
585   memcpy(secondHeader.reserved2, mainHeader.reserved2, sizeof(secondHeader.reserved2));
586   secondCrcOk = mainCrcOk;
587   SetGPTSize(secondHeader.numParts, 0);
588} // GPTData::RebuildSecondHeader()
589
590// Search for hybrid MBR entries that have no corresponding GPT partition.
591// Returns number of such mismatches found
592int GPTData::FindHybridMismatches(void) {
593   int i, found, numFound = 0;
594   uint32_t j;
595   uint64_t mbrFirst, mbrLast;
596
597   for (i = 0; i < 4; i++) {
598      if ((protectiveMBR.GetType(i) != 0xEE) && (protectiveMBR.GetType(i) != 0x00)) {
599         j = 0;
600         found = 0;
601         mbrFirst = (uint64_t) protectiveMBR.GetFirstSector(i);
602         mbrLast = mbrFirst + (uint64_t) protectiveMBR.GetLength(i) - UINT64_C(1);
603         do {
604            if ((j < numParts) && (partitions[j].GetFirstLBA() == mbrFirst) &&
605                (partitions[j].GetLastLBA() == mbrLast) && (partitions[j].IsUsed()))
606               found = 1;
607            j++;
608         } while ((!found) && (j < numParts));
609         if (!found) {
610            numFound++;
611            cout << "\nWarning! Mismatched GPT and MBR partition! MBR partition "
612                 << i + 1 << ", of type 0x";
613            cout.fill('0');
614            cout.setf(ios::uppercase);
615            cout.width(2);
616            cout << hex << (int) protectiveMBR.GetType(i) << ",\n"
617                 << "has no corresponding GPT partition! You may continue, but this condition\n"
618                 << "might cause data loss in the future!\a\n" << dec;
619            cout.fill(' ');
620         } // if
621      } // if
622   } // for
623   return numFound;
624} // GPTData::FindHybridMismatches
625
626// Find overlapping partitions and warn user about them. Returns number of
627// overlapping partitions.
628// Returns number of overlapping segments found.
629int GPTData::FindOverlaps(void) {
630   int problems = 0;
631   uint32_t i, j;
632
633   for (i = 1; i < numParts; i++) {
634      for (j = 0; j < i; j++) {
635         if ((partitions[i].IsUsed()) && (partitions[j].IsUsed()) &&
636             (partitions[i].DoTheyOverlap(partitions[j]))) {
637            problems++;
638            cout << "\nProblem: partitions " << i + 1 << " and " << j + 1 << " overlap:\n";
639            cout << "  Partition " << i + 1 << ": " << partitions[i].GetFirstLBA()
640                 << " to " << partitions[i].GetLastLBA() << "\n";
641            cout << "  Partition " << j + 1 << ": " << partitions[j].GetFirstLBA()
642                 << " to " << partitions[j].GetLastLBA() << "\n";
643         } // if
644      } // for j...
645   } // for i...
646   return problems;
647} // GPTData::FindOverlaps()
648
649// Find partitions that are insane -- they start after they end or are too
650// big for the disk. (The latter should duplicate detection of overlaps
651// with GPT backup data structures, but better to err on the side of
652// redundant tests than to miss something....)
653// Returns number of problems found.
654int GPTData::FindInsanePartitions(void) {
655   uint32_t i;
656   int problems = 0;
657
658   for (i = 0; i < numParts; i++) {
659      if (partitions[i].IsUsed()) {
660         if (partitions[i].GetFirstLBA() > partitions[i].GetLastLBA()) {
661            problems++;
662            cout << "\nProblem: partition " << i + 1 << " ends before it begins.\n";
663         } // if
664         if (partitions[i].GetLastLBA() >= diskSize) {
665            problems++;
666         cout << "\nProblem: partition " << i + 1 << " is too big for the disk.\n";
667         } // if
668      } // if
669   } // for
670   return problems;
671} // GPTData::FindInsanePartitions(void)
672
673
674/******************************************************************
675 *                                                                *
676 * Begin functions that load data from disk or save data to disk. *
677 *                                                                *
678 ******************************************************************/
679
680// Change the filename associated with the GPT. Used for duplicating
681// the partition table to a new disk and saving backups.
682// Returns 1 on success, 0 on failure.
683int GPTData::SetDisk(const string & deviceFilename) {
684   int err, allOK = 1;
685
686   device = deviceFilename;
687   if (allOK && myDisk.OpenForRead(deviceFilename)) {
688      // store disk information....
689      diskSize = myDisk.DiskSize(&err);
690      blockSize = (uint32_t) myDisk.GetBlockSize();
691   } // if
692   protectiveMBR.SetDisk(&myDisk);
693   protectiveMBR.SetDiskSize(diskSize);
694   protectiveMBR.SetBlockSize(blockSize);
695   return allOK;
696} // GPTData::SetDisk()
697
698// Scan for partition data. This function loads the MBR data (regular MBR or
699// protective MBR) and loads BSD disklabel data (which is probably invalid).
700// It also looks for APM data, forces a load of GPT data, and summarizes
701// the results.
702void GPTData::PartitionScan(void) {
703   BSDData bsdDisklabel;
704
705   // Read the MBR & check for BSD disklabel
706   protectiveMBR.ReadMBRData(&myDisk);
707   bsdDisklabel.ReadBSDData(&myDisk, 0, diskSize - 1);
708
709   // Load the GPT data, whether or not it's valid
710   ForceLoadGPTData();
711
712   // Some tools create a 0xEE partition that's too big. If this is detected,
713   // normalize it....
714   if ((state == gpt_valid) && !protectiveMBR.DoTheyFit() && (protectiveMBR.GetValidity() == gpt)) {
715      if (!beQuiet) {
716         cerr << "\aThe protective MBR's 0xEE partition is oversized! Auto-repairing.\n\n";
717      } // if
718      protectiveMBR.MakeProtectiveMBR();
719   } // if
720
721   if (!beQuiet) {
722      cout << "Partition table scan:\n";
723      protectiveMBR.ShowState();
724      bsdDisklabel.ShowState();
725      ShowAPMState(); // Show whether there's an Apple Partition Map present
726      ShowGPTState(); // Show GPT status
727      cout << "\n";
728   } // if
729
730   if (apmFound) {
731      cout << "\n*******************************************************************\n"
732           << "This disk appears to contain an Apple-format (APM) partition table!\n";
733      if (!justLooking) {
734         cout << "It will be destroyed if you continue!\n";
735      } // if
736      cout << "*******************************************************************\n\n\a";
737   } // if
738} // GPTData::PartitionScan()
739
740// Read GPT data from a disk.
741int GPTData::LoadPartitions(const string & deviceFilename) {
742   BSDData bsdDisklabel;
743   int err, allOK = 1;
744   MBRValidity mbrState;
745
746   if (myDisk.OpenForRead(deviceFilename)) {
747      err = myDisk.OpenForWrite(deviceFilename);
748      if ((err == 0) && (!justLooking)) {
749         cout << "\aNOTE: Write test failed with error number " << errno
750              << ". It will be impossible to save\nchanges to this disk's partition table!\n";
751#if defined (__FreeBSD__) || defined (__FreeBSD_kernel__)
752         cout << "You may be able to enable writes by exiting this program, typing\n"
753              << "'sysctl kern.geom.debugflags=16' at a shell prompt, and re-running this\n"
754              << "program.\n";
755#endif
756         cout << "\n";
757      } // if
758      myDisk.Close(); // Close and re-open read-only in case of bugs
759   } else allOK = 0; // if
760
761   if (allOK && myDisk.OpenForRead(deviceFilename)) {
762      // store disk information....
763      diskSize = myDisk.DiskSize(&err);
764      blockSize = (uint32_t) myDisk.GetBlockSize();
765      device = deviceFilename;
766      PartitionScan(); // Check for partition types, load GPT, & print summary
767
768      whichWasUsed = UseWhichPartitions();
769      switch (whichWasUsed) {
770         case use_mbr:
771            XFormPartitions();
772            break;
773         case use_bsd:
774            bsdDisklabel.ReadBSDData(&myDisk, 0, diskSize - 1);
775//            bsdDisklabel.DisplayBSDData();
776            ClearGPTData();
777            protectiveMBR.MakeProtectiveMBR(1); // clear boot area (option 1)
778            XFormDisklabel(&bsdDisklabel);
779            break;
780         case use_gpt:
781            mbrState = protectiveMBR.GetValidity();
782            if ((mbrState == invalid) || (mbrState == mbr))
783               protectiveMBR.MakeProtectiveMBR();
784            break;
785         case use_new:
786            ClearGPTData();
787            protectiveMBR.MakeProtectiveMBR();
788            break;
789         case use_abort:
790            allOK = 0;
791            cerr << "Invalid partition data!\n";
792            break;
793      } // switch
794
795      if (allOK)
796         CheckGPTSize();
797      myDisk.Close();
798      ComputeAlignment();
799   } else {
800      allOK = 0;
801   } // if/else
802   return (allOK);
803} // GPTData::LoadPartitions()
804
805// Loads the GPT, as much as possible. Returns 1 if this seems to have
806// succeeded, 0 if there are obvious problems....
807int GPTData::ForceLoadGPTData(void) {
808   int allOK, validHeaders, loadedTable = 1;
809
810   allOK = LoadHeader(&mainHeader, myDisk, 1, &mainCrcOk);
811
812   if (mainCrcOk && (mainHeader.backupLBA < diskSize)) {
813      allOK = LoadHeader(&secondHeader, myDisk, mainHeader.backupLBA, &secondCrcOk) && allOK;
814   } else {
815      allOK = LoadHeader(&secondHeader, myDisk, diskSize - UINT64_C(1), &secondCrcOk) && allOK;
816      if (mainCrcOk && (mainHeader.backupLBA >= diskSize))
817         cout << "Warning! Disk size is smaller than the main header indicates! Loading\n"
818              << "secondary header from the last sector of the disk! You should use 'v' to\n"
819              << "verify disk integrity, and perhaps options on the experts' menu to repair\n"
820              << "the disk.\n";
821   } // if/else
822   if (!allOK)
823      state = gpt_invalid;
824
825   // Return valid headers code: 0 = both headers bad; 1 = main header
826   // good, backup bad; 2 = backup header good, main header bad;
827   // 3 = both headers good. Note these codes refer to valid GPT
828   // signatures, version numbers, and CRCs.
829   validHeaders = CheckHeaderValidity();
830
831   // Read partitions (from primary array)
832   if (validHeaders > 0) { // if at least one header is OK....
833      // GPT appears to be valid....
834      state = gpt_valid;
835
836      // We're calling the GPT valid, but there's a possibility that one
837      // of the two headers is corrupt. If so, use the one that seems to
838      // be in better shape to regenerate the bad one
839      if (validHeaders == 1) { // valid main header, invalid backup header
840         cerr << "\aCaution: invalid backup GPT header, but valid main header; regenerating\n"
841              << "backup header from main header.\n\n";
842         RebuildSecondHeader();
843         state = gpt_corrupt;
844         secondCrcOk = mainCrcOk; // Since regenerated, use CRC validity of main
845      } else if (validHeaders == 2) { // valid backup header, invalid main header
846         cerr << "\aCaution: invalid main GPT header, but valid backup; regenerating main header\n"
847              << "from backup!\n\n";
848         RebuildMainHeader();
849         state = gpt_corrupt;
850         mainCrcOk = secondCrcOk; // Since copied, use CRC validity of backup
851      } // if/else/if
852
853      // Figure out which partition table to load....
854      // Load the main partition table, since either its header's CRC is OK or the
855      // backup header's CRC is not OK....
856      if (mainCrcOk || !secondCrcOk) {
857         if (LoadMainTable() == 0)
858            allOK = 0;
859      } else { // bad main header CRC and backup header CRC is OK
860         state = gpt_corrupt;
861         if (LoadSecondTableAsMain()) {
862            loadedTable = 2;
863            cerr << "\aWarning: Invalid CRC on main header data; loaded backup partition table.\n";
864         } else { // backup table bad, bad main header CRC, but try main table in desperation....
865            if (LoadMainTable() == 0) {
866               allOK = 0;
867               loadedTable = 0;
868               cerr << "\a\aWarning! Unable to load either main or backup partition table!\n";
869            } // if
870         } // if/else (LoadSecondTableAsMain())
871      } // if/else (load partition table)
872
873      if (loadedTable == 1)
874         secondPartsCrcOk = CheckTable(&secondHeader);
875      else if (loadedTable == 2)
876         mainPartsCrcOk = CheckTable(&mainHeader);
877      else
878         mainPartsCrcOk = secondPartsCrcOk = 0;
879
880      // Problem with main partition table; if backup is OK, use it instead....
881      if (secondPartsCrcOk && secondCrcOk && !mainPartsCrcOk) {
882         state = gpt_corrupt;
883         allOK = allOK && LoadSecondTableAsMain();
884         mainPartsCrcOk = 0; // LoadSecondTableAsMain() resets this, so re-flag as bad
885         cerr << "\aWarning! Main partition table CRC mismatch! Loaded backup "
886              << "partition table\ninstead of main partition table!\n\n";
887      } // if */
888
889      // Check for valid CRCs and warn if there are problems
890      if ((mainCrcOk == 0) || (secondCrcOk == 0) || (mainPartsCrcOk == 0) ||
891           (secondPartsCrcOk == 0)) {
892         cerr << "Warning! One or more CRCs don't match. You should repair the disk!\n\n";
893         state = gpt_corrupt;
894      } // if
895   } else {
896      state = gpt_invalid;
897   } // if/else
898   return allOK;
899} // GPTData::ForceLoadGPTData()
900
901// Loads the partition table pointed to by the main GPT header. The
902// main GPT header in memory MUST be valid for this call to do anything
903// sensible!
904// Returns 1 on success, 0 on failure. CRC errors do NOT count as failure.
905int GPTData::LoadMainTable(void) {
906   return LoadPartitionTable(mainHeader, myDisk);
907} // GPTData::LoadMainTable()
908
909// Load the second (backup) partition table as the primary partition
910// table. Used in repair functions, and when starting up if the main
911// partition table is damaged.
912// Returns 1 on success, 0 on failure. CRC errors do NOT count as failure.
913int GPTData::LoadSecondTableAsMain(void) {
914   return LoadPartitionTable(secondHeader, myDisk);
915} // GPTData::LoadSecondTableAsMain()
916
917// Load a single GPT header (main or backup) from the specified disk device and
918// sector. Applies byte-order corrections on big-endian platforms. Sets crcOk
919// value appropriately.
920// Returns 1 on success, 0 on failure. Note that CRC errors do NOT qualify as
921// failure.
922int GPTData::LoadHeader(struct GPTHeader *header, DiskIO & disk, uint64_t sector, int *crcOk) {
923   int allOK = 1;
924   GPTHeader tempHeader;
925
926   disk.Seek(sector);
927   if (disk.Read(&tempHeader, 512) != 512) {
928      cerr << "Warning! Read error " << errno << "; strange behavior now likely!\n";
929      allOK = 0;
930   } // if
931
932   // Reverse byte order, if necessary
933   if (IsLittleEndian() == 0) {
934      ReverseHeaderBytes(&tempHeader);
935   } // if
936   *crcOk = CheckHeaderCRC(&tempHeader);
937
938   if (allOK && (numParts != tempHeader.numParts) && *crcOk) {
939      allOK = SetGPTSize(tempHeader.numParts, 0);
940   }
941
942   *header = tempHeader;
943   return allOK;
944} // GPTData::LoadHeader
945
946// Load a partition table (either main or secondary) from the specified disk,
947// using header as a reference for what to load. If sector != 0 (the default
948// is 0), loads from the specified sector; otherwise loads from the sector
949// indicated in header.
950// Returns 1 on success, 0 on failure. CRC errors do NOT count as failure.
951int GPTData::LoadPartitionTable(const struct GPTHeader & header, DiskIO & disk, uint64_t sector) {
952   uint32_t sizeOfParts, newCRC;
953   int retval;
954
955   if (disk.OpenForRead()) {
956      if (sector == 0) {
957         retval = disk.Seek(header.partitionEntriesLBA);
958      } else {
959         retval = disk.Seek(sector);
960      } // if/else
961      if (retval == 1)
962         retval = SetGPTSize(header.numParts, 0);
963      if (retval == 1) {
964         sizeOfParts = header.numParts * header.sizeOfPartitionEntries;
965         if (disk.Read(partitions, sizeOfParts) != (int) sizeOfParts) {
966            cerr << "Warning! Read error " << errno << "! Misbehavior now likely!\n";
967            retval = 0;
968         } // if
969         newCRC = chksum_crc32((unsigned char*) partitions, sizeOfParts);
970         mainPartsCrcOk = secondPartsCrcOk = (newCRC == header.partitionEntriesCRC);
971         if (IsLittleEndian() == 0)
972            ReversePartitionBytes();
973         if (!mainPartsCrcOk) {
974            cout << "Caution! After loading partitions, the CRC doesn't check out!\n";
975         } // if
976      } else {
977         cerr << "Error! Couldn't seek to partition table!\n";
978      } // if/else
979   } else {
980      cerr << "Error! Couldn't open device " << device
981           << " when reading partition table!\n";
982      retval = 0;
983   } // if/else
984   return retval;
985} // GPTData::LoadPartitionsTable()
986
987// Check the partition table pointed to by header, but don't keep it
988// around.
989// Returns 1 if the CRC is OK & this table matches the one already in memory,
990// 0 if not or if there was a read error.
991int GPTData::CheckTable(struct GPTHeader *header) {
992   uint32_t sizeOfParts, newCRC;
993   GPTPart *partsToCheck;
994   GPTHeader *otherHeader;
995   int allOK = 0;
996
997   // Load partition table into temporary storage to check
998   // its CRC and store the results, then discard this temporary
999   // storage, since we don't use it in any but recovery operations
1000   if (myDisk.Seek(header->partitionEntriesLBA)) {
1001      partsToCheck = new GPTPart[header->numParts];
1002      sizeOfParts = header->numParts * header->sizeOfPartitionEntries;
1003      if (partsToCheck == NULL) {
1004         cerr << "Could not allocate memory in GPTData::CheckTable()! Terminating!\n";
1005         exit(1);
1006      } // if
1007      if (myDisk.Read(partsToCheck, sizeOfParts) != (int) sizeOfParts) {
1008         cerr << "Warning! Error " << errno << " reading partition table for CRC check!\n";
1009      } else {
1010         newCRC = chksum_crc32((unsigned char*) partsToCheck, sizeOfParts);
1011         allOK = (newCRC == header->partitionEntriesCRC);
1012         if (header == &mainHeader)
1013            otherHeader = &secondHeader;
1014         else
1015            otherHeader = &mainHeader;
1016         if (newCRC != otherHeader->partitionEntriesCRC) {
1017            cerr << "Warning! Main and backup partition tables differ! Use the 'c' and 'e' options\n"
1018                 << "on the recovery & transformation menu to examine the two tables.\n\n";
1019            allOK = 0;
1020         } // if
1021      } // if/else
1022      delete[] partsToCheck;
1023   } // if
1024   return allOK;
1025} // GPTData::CheckTable()
1026
1027// Writes GPT (and protective MBR) to disk. If quiet==1, moves the second
1028// header later on the disk without asking for permission, if necessary, and
1029// doesn't confirm the operation before writing. If quiet==0, asks permission
1030// before moving the second header and asks for final confirmation of any
1031// write.
1032// Returns 1 on successful write, 0 if there was a problem.
1033int GPTData::SaveGPTData(int quiet) {
1034   int allOK = 1, syncIt = 1;
1035   char answer;
1036
1037   // First do some final sanity checks....
1038
1039   // This test should only fail on read-only disks....
1040   if (justLooking) {
1041      cout << "The justLooking flag is set. This probably means you can't write to the disk.\n";
1042      allOK = 0;
1043   } // if
1044
1045   // Check that disk is really big enough to handle the second header...
1046   if (mainHeader.backupLBA >= diskSize) {
1047      cerr << "Caution! Secondary header was placed beyond the disk's limits! Moving the\n"
1048           << "header, but other problems may occur!\n";
1049      MoveSecondHeaderToEnd();
1050   } // if
1051
1052   // Is there enough space to hold the GPT headers and partition tables,
1053   // given the partition sizes?
1054   if (CheckGPTSize() > 0) {
1055      allOK = 0;
1056   } // if
1057
1058   // Check that second header is properly placed. Warn and ask if this should
1059   // be corrected if the test fails....
1060   if (mainHeader.backupLBA < (diskSize - UINT64_C(1))) {
1061      if (quiet == 0) {
1062         cout << "Warning! Secondary header is placed too early on the disk! Do you want to\n"
1063              << "correct this problem? ";
1064         if (GetYN() == 'Y') {
1065            MoveSecondHeaderToEnd();
1066            cout << "Have moved second header and partition table to correct location.\n";
1067         } else {
1068            cout << "Have not corrected the problem. Strange problems may occur in the future!\n";
1069         } // if correction requested
1070      } else { // Go ahead and do correction automatically
1071         MoveSecondHeaderToEnd();
1072      } // if/else quiet
1073   } // if
1074
1075   if ((mainHeader.lastUsableLBA >= diskSize) || (mainHeader.lastUsableLBA > mainHeader.backupLBA)) {
1076      if (quiet == 0) {
1077         cout << "Warning! The claimed last usable sector is incorrect! Do you want to correct\n"
1078              << "this problem? ";
1079         if (GetYN() == 'Y') {
1080            MoveSecondHeaderToEnd();
1081            cout << "Have adjusted the second header and last usable sector value.\n";
1082         } else {
1083            cout << "Have not corrected the problem. Strange problems may occur in the future!\n";
1084         } // if correction requested
1085      } else { // go ahead and do correction automatically
1086         MoveSecondHeaderToEnd();
1087      } // if/else quiet
1088   } // if
1089
1090   // Check for overlapping or insane partitions....
1091   if ((FindOverlaps() > 0) || (FindInsanePartitions() > 0)) {
1092      allOK = 0;
1093      cerr << "Aborting write operation!\n";
1094   } // if
1095
1096   // Check that protective MBR fits, and warn if it doesn't....
1097   if (!protectiveMBR.DoTheyFit()) {
1098      cerr << "\nPartition(s) in the protective MBR are too big for the disk! Creating a\n"
1099           << "fresh protective or hybrid MBR is recommended.\n";
1100   }
1101
1102   // Check for mismatched MBR and GPT data, but let it pass if found
1103   // (function displays warning message)
1104   FindHybridMismatches();
1105
1106   RecomputeCRCs();
1107
1108   if ((allOK) && (!quiet)) {
1109      cout << "\nFinal checks complete. About to write GPT data. THIS WILL OVERWRITE EXISTING\n"
1110           << "PARTITIONS!!\n\nDo you want to proceed? ";
1111      answer = GetYN();
1112      if (answer == 'Y') {
1113         cout << "OK; writing new GUID partition table (GPT) to " << myDisk.GetName() << ".\n";
1114      } else {
1115         allOK = 0;
1116      } // if/else
1117   } // if
1118
1119   // Do it!
1120   if (allOK) {
1121      if (myDisk.OpenForWrite()) {
1122         // As per UEFI specs, write the secondary table and GPT first....
1123         allOK = SavePartitionTable(myDisk, secondHeader.partitionEntriesLBA);
1124         if (!allOK) {
1125            cerr << "Unable to save backup partition table! Perhaps the 'e' option on the experts'\n"
1126                 << "menu will resolve this problem.\n";
1127            syncIt = 0;
1128         } // if
1129
1130         // Now write the secondary GPT header...
1131         allOK = allOK && SaveHeader(&secondHeader, myDisk, mainHeader.backupLBA);
1132
1133         // Now write the main partition tables...
1134         allOK = allOK && SavePartitionTable(myDisk, mainHeader.partitionEntriesLBA);
1135
1136         // Now write the main GPT header...
1137         allOK = allOK && SaveHeader(&mainHeader, myDisk, 1);
1138
1139         // To top it off, write the protective MBR...
1140         allOK = allOK && protectiveMBR.WriteMBRData(&myDisk);
1141
1142         // re-read the partition table
1143         // Note: Done even if some write operations failed, but not if all of them failed.
1144         // Done this way because I've received one problem report from a user one whose
1145         // system the MBR write failed but everything else was OK (on a GPT disk under
1146         // Windows), and the failure to sync therefore caused Windows to restore the
1147         // original partition table from its cache. OTOH, such restoration might be
1148         // desirable if the error occurs later; but that seems unlikely unless the initial
1149         // write fails....
1150         if (syncIt && syncing)
1151            myDisk.DiskSync();
1152
1153         if (allOK) { // writes completed OK
1154            cout << "The operation has completed successfully.\n";
1155         } else {
1156            cerr << "Warning! An error was reported when writing the partition table! This error\n"
1157                 << "MIGHT be harmless, or the disk might be damaged! Checking it is advisable.\n";
1158         } // if/else
1159
1160         myDisk.Close();
1161      } else {
1162         cerr << "Unable to open device '" << myDisk.GetName() << "' for writing! Errno is "
1163              << errno << "! Aborting write!\n";
1164         allOK = 0;
1165      } // if/else
1166   } else {
1167      cout << "Aborting write of new partition table.\n";
1168   } // if
1169
1170   return (allOK);
1171} // GPTData::SaveGPTData()
1172
1173// Save GPT data to a backup file. This function does much less error
1174// checking than SaveGPTData(). It can therefore preserve many types of
1175// corruption for later analysis; however, it preserves only the MBR,
1176// the main GPT header, the backup GPT header, and the main partition
1177// table; it discards the backup partition table, since it should be
1178// identical to the main partition table on healthy disks.
1179int GPTData::SaveGPTBackup(const string & filename) {
1180   int allOK = 1;
1181   DiskIO backupFile;
1182
1183   if (backupFile.OpenForWrite(filename)) {
1184      // Recomputing the CRCs is likely to alter them, which could be bad
1185      // if the intent is to save a potentially bad GPT for later analysis;
1186      // but if we don't do this, we get bogus errors when we load the
1187      // backup. I'm favoring misses over false alarms....
1188      RecomputeCRCs();
1189
1190      protectiveMBR.WriteMBRData(&backupFile);
1191      protectiveMBR.SetDisk(&myDisk);
1192
1193      if (allOK) {
1194         // MBR write closed disk, so re-open and seek to end....
1195         backupFile.OpenForWrite();
1196         allOK = SaveHeader(&mainHeader, backupFile, 1);
1197      } // if (allOK)
1198
1199      if (allOK)
1200         allOK = SaveHeader(&secondHeader, backupFile, 2);
1201
1202      if (allOK)
1203         allOK = SavePartitionTable(backupFile, 3);
1204
1205      if (allOK) { // writes completed OK
1206         cout << "The operation has completed successfully.\n";
1207      } else {
1208         cerr << "Warning! An error was reported when writing the backup file.\n"
1209              << "It may not be usable!\n";
1210      } // if/else
1211      backupFile.Close();
1212   } else {
1213      cerr << "Unable to open file '" << filename << "' for writing! Aborting!\n";
1214      allOK = 0;
1215   } // if/else
1216   return allOK;
1217} // GPTData::SaveGPTBackup()
1218
1219// Write a GPT header (main or backup) to the specified sector. Used by both
1220// the SaveGPTData() and SaveGPTBackup() functions.
1221// Should be passed an architecture-appropriate header (DO NOT call
1222// ReverseHeaderBytes() on the header before calling this function)
1223// Returns 1 on success, 0 on failure
1224int GPTData::SaveHeader(struct GPTHeader *header, DiskIO & disk, uint64_t sector) {
1225   int littleEndian, allOK = 1;
1226
1227   littleEndian = IsLittleEndian();
1228   if (!littleEndian)
1229      ReverseHeaderBytes(header);
1230   if (disk.Seek(sector)) {
1231      if (disk.Write(header, 512) == -1)
1232         allOK = 0;
1233   } else allOK = 0; // if (disk.Seek()...)
1234   if (!littleEndian)
1235      ReverseHeaderBytes(header);
1236   return allOK;
1237} // GPTData::SaveHeader()
1238
1239// Save the partitions to the specified sector. Used by both the SaveGPTData()
1240// and SaveGPTBackup() functions.
1241// Should be passed an architecture-appropriate header (DO NOT call
1242// ReverseHeaderBytes() on the header before calling this function)
1243// Returns 1 on success, 0 on failure
1244int GPTData::SavePartitionTable(DiskIO & disk, uint64_t sector) {
1245   int littleEndian, allOK = 1;
1246
1247   littleEndian = IsLittleEndian();
1248   if (disk.Seek(sector)) {
1249      if (!littleEndian)
1250         ReversePartitionBytes();
1251      if (disk.Write(partitions, mainHeader.sizeOfPartitionEntries * numParts) == -1)
1252         allOK = 0;
1253      if (!littleEndian)
1254         ReversePartitionBytes();
1255   } else allOK = 0; // if (myDisk.Seek()...)
1256   return allOK;
1257} // GPTData::SavePartitionTable()
1258
1259// Load GPT data from a backup file created by SaveGPTBackup(). This function
1260// does minimal error checking. It returns 1 if it completed successfully,
1261// 0 if there was a problem. In the latter case, it creates a new empty
1262// set of partitions.
1263int GPTData::LoadGPTBackup(const string & filename) {
1264   int allOK = 1, val, err;
1265   int shortBackup = 0;
1266   DiskIO backupFile;
1267
1268   if (backupFile.OpenForRead(filename)) {
1269      // Let the MBRData class load the saved MBR...
1270      protectiveMBR.ReadMBRData(&backupFile, 0); // 0 = don't check block size
1271      protectiveMBR.SetDisk(&myDisk);
1272
1273      LoadHeader(&mainHeader, backupFile, 1, &mainCrcOk);
1274
1275      // Check backup file size and rebuild second header if file is right
1276      // size to be direct dd copy of MBR, main header, and main partition
1277      // table; if other size, treat it like a GPT fdisk-generated backup
1278      // file
1279      shortBackup = ((backupFile.DiskSize(&err) * backupFile.GetBlockSize()) ==
1280                     (mainHeader.numParts * mainHeader.sizeOfPartitionEntries) + 1024);
1281      if (shortBackup) {
1282         RebuildSecondHeader();
1283         secondCrcOk = mainCrcOk;
1284      } else {
1285         LoadHeader(&secondHeader, backupFile, 2, &secondCrcOk);
1286      } // if/else
1287
1288      // Return valid headers code: 0 = both headers bad; 1 = main header
1289      // good, backup bad; 2 = backup header good, main header bad;
1290      // 3 = both headers good. Note these codes refer to valid GPT
1291      // signatures and version numbers; more subtle problems will elude
1292      // this check!
1293      if ((val = CheckHeaderValidity()) > 0) {
1294         if (val == 2) { // only backup header seems to be good
1295            SetGPTSize(secondHeader.numParts, 0);
1296         } else { // main header is OK
1297            SetGPTSize(mainHeader.numParts, 0);
1298         } // if/else
1299
1300         if (secondHeader.currentLBA != diskSize - UINT64_C(1)) {
1301            cout << "Warning! Current disk size doesn't match that of the backup!\n"
1302                 << "Adjusting sizes to match, but subsequent problems are possible!\n";
1303            MoveSecondHeaderToEnd();
1304         } // if
1305
1306         if (!LoadPartitionTable(mainHeader, backupFile, (uint64_t) (3 - shortBackup)))
1307            cerr << "Warning! Read error " << errno
1308                 << " loading partition table; strange behavior now likely!\n";
1309      } else {
1310         allOK = 0;
1311      } // if/else
1312      // Something went badly wrong, so blank out partitions
1313      if (allOK == 0) {
1314         cerr << "Improper backup file! Clearing all partition data!\n";
1315         ClearGPTData();
1316         protectiveMBR.MakeProtectiveMBR();
1317      } // if
1318   } else {
1319      allOK = 0;
1320      cerr << "Unable to open file '" << filename << "' for reading! Aborting!\n";
1321   } // if/else
1322
1323   return allOK;
1324} // GPTData::LoadGPTBackup()
1325
1326int GPTData::SaveMBR(void) {
1327   return protectiveMBR.WriteMBRData(&myDisk);
1328} // GPTData::SaveMBR()
1329
1330// This function destroys the on-disk GPT structures, but NOT the on-disk
1331// MBR.
1332// Returns 1 if the operation succeeds, 0 if not.
1333int GPTData::DestroyGPT(void) {
1334   int sum, tableSize, allOK = 1;
1335   uint8_t blankSector[512];
1336   uint8_t* emptyTable;
1337
1338   memset(blankSector, 0, sizeof(blankSector));
1339   ClearGPTData();
1340
1341   if (myDisk.OpenForWrite()) {
1342      if (!myDisk.Seek(mainHeader.currentLBA))
1343         allOK = 0;
1344      if (myDisk.Write(blankSector, 512) != 512) { // blank it out
1345         cerr << "Warning! GPT main header not overwritten! Error is " << errno << "\n";
1346         allOK = 0;
1347      } // if
1348      if (!myDisk.Seek(mainHeader.partitionEntriesLBA))
1349         allOK = 0;
1350      tableSize = numParts * mainHeader.sizeOfPartitionEntries;
1351      emptyTable = new uint8_t[tableSize];
1352      if (emptyTable == NULL) {
1353         cerr << "Could not allocate memory in GPTData::DestroyGPT()! Terminating!\n";
1354         exit(1);
1355      } // if
1356      memset(emptyTable, 0, tableSize);
1357      if (allOK) {
1358         sum = myDisk.Write(emptyTable, tableSize);
1359         if (sum != tableSize) {
1360            cerr << "Warning! GPT main partition table not overwritten! Error is " << errno << "\n";
1361            allOK = 0;
1362         } // if write failed
1363      } // if
1364      if (!myDisk.Seek(secondHeader.partitionEntriesLBA))
1365         allOK = 0;
1366      if (allOK) {
1367         sum = myDisk.Write(emptyTable, tableSize);
1368         if (sum != tableSize) {
1369            cerr << "Warning! GPT backup partition table not overwritten! Error is "
1370                 << errno << "\n";
1371            allOK = 0;
1372         } // if wrong size written
1373      } // if
1374      if (!myDisk.Seek(secondHeader.currentLBA))
1375         allOK = 0;
1376      if (allOK) {
1377         if (myDisk.Write(blankSector, 512) != 512) { // blank it out
1378            cerr << "Warning! GPT backup header not overwritten! Error is " << errno << "\n";
1379            allOK = 0;
1380         } // if
1381      } // if
1382      if (syncing) {
1383         myDisk.DiskSync();
1384      }
1385      myDisk.Close();
1386      cout << "GPT data structures destroyed! You may now partition the disk using fdisk or\n"
1387           << "other utilities.\n";
1388      delete[] emptyTable;
1389   } else {
1390      cerr << "Problem opening '" << device << "' for writing! Program will now terminate.\n";
1391   } // if/else (fd != -1)
1392   return (allOK);
1393} // GPTDataTextUI::DestroyGPT()
1394
1395// Wipe MBR data from the disk (zero it out completely)
1396// Returns 1 on success, 0 on failure.
1397int GPTData::DestroyMBR(void) {
1398   int allOK;
1399   uint8_t blankSector[512];
1400
1401   memset(blankSector, 0, sizeof(blankSector));
1402
1403   allOK = myDisk.OpenForWrite() && myDisk.Seek(0) && (myDisk.Write(blankSector, 512) == 512);
1404
1405   if (!allOK)
1406      cerr << "Warning! MBR not overwritten! Error is " << errno << "!\n";
1407   return allOK;
1408} // GPTData::DestroyMBR(void)
1409
1410// Tell user whether Apple Partition Map (APM) was discovered....
1411void GPTData::ShowAPMState(void) {
1412   if (apmFound)
1413      cout << "  APM: present\n";
1414   else
1415      cout << "  APM: not present\n";
1416} // GPTData::ShowAPMState()
1417
1418// Tell user about the state of the GPT data....
1419void GPTData::ShowGPTState(void) {
1420   switch (state) {
1421      case gpt_invalid:
1422         cout << "  GPT: not present\n";
1423         break;
1424      case gpt_valid:
1425         cout << "  GPT: present\n";
1426         break;
1427      case gpt_corrupt:
1428         cout << "  GPT: damaged\n";
1429         break;
1430      default:
1431         cout << "\a  GPT: unknown -- bug!\n";
1432         break;
1433   } // switch
1434} // GPTData::ShowGPTState()
1435
1436// Display the basic GPT data
1437void GPTData::DisplayGPTData(void) {
1438   uint32_t i;
1439   uint64_t temp, totalFree;
1440
1441   cout << "Disk " << device << ": " << diskSize << " sectors, "
1442        << BytesToIeee(diskSize, blockSize) << "\n";
1443   cout << "Logical sector size: " << blockSize << " bytes\n";
1444   cout << "Disk identifier (GUID): " << mainHeader.diskGUID << "\n";
1445   cout << "Partition table holds up to " << numParts << " entries\n";
1446   cout << "First usable sector is " << mainHeader.firstUsableLBA
1447        << ", last usable sector is " << mainHeader.lastUsableLBA << "\n";
1448   totalFree = FindFreeBlocks(&i, &temp);
1449   cout << "Partitions will be aligned on " << sectorAlignment << "-sector boundaries\n";
1450   cout << "Total free space is " << totalFree << " sectors ("
1451        << BytesToIeee(totalFree, blockSize) << ")\n";
1452   cout << "\nNumber  Start (sector)    End (sector)  Size       Code  Name\n";
1453   for (i = 0; i < numParts; i++) {
1454      partitions[i].ShowSummary(i, blockSize);
1455   } // for
1456} // GPTData::DisplayGPTData()
1457
1458// Show detailed information on the specified partition
1459void GPTData::ShowPartDetails(uint32_t partNum) {
1460   if ((partNum < numParts) && !IsFreePartNum(partNum)) {
1461      partitions[partNum].ShowDetails(blockSize);
1462   } else {
1463      cout << "Partition #" << partNum + 1 << " does not exist.\n";
1464   } // if
1465} // GPTData::ShowPartDetails()
1466
1467/**************************************************************************
1468 *                                                                        *
1469 * Partition table transformation functions (MBR or BSD disklabel to GPT) *
1470 * (some of these functions may require user interaction)                 *
1471 *                                                                        *
1472 **************************************************************************/
1473
1474// Examines the MBR & GPT data to determine which set of data to use: the
1475// MBR (use_mbr), the GPT (use_gpt), the BSD disklabel (use_bsd), or create
1476// a new set of partitions (use_new). A return value of use_abort indicates
1477// that this function couldn't determine what to do. Overriding functions
1478// in derived classes may ask users questions in such cases.
1479WhichToUse GPTData::UseWhichPartitions(void) {
1480   WhichToUse which = use_new;
1481   MBRValidity mbrState;
1482
1483   mbrState = protectiveMBR.GetValidity();
1484
1485   if ((state == gpt_invalid) && ((mbrState == mbr) || (mbrState == hybrid))) {
1486      cout << "\n***************************************************************\n"
1487           << "Found invalid GPT and valid MBR; converting MBR to GPT format\n"
1488           << "in memory. ";
1489      if (!justLooking) {
1490         cout << "\aTHIS OPERATION IS POTENTIALLY DESTRUCTIVE! Exit by\n"
1491              << "typing 'q' if you don't want to convert your MBR partitions\n"
1492              << "to GPT format!";
1493      } // if
1494      cout << "\n***************************************************************\n\n";
1495      which = use_mbr;
1496   } // if
1497
1498   if ((state == gpt_invalid) && bsdFound) {
1499      cout << "\n**********************************************************************\n"
1500           << "Found invalid GPT and valid BSD disklabel; converting BSD disklabel\n"
1501           << "to GPT format.";
1502      if ((!justLooking) && (!beQuiet)) {
1503      cout << "\a THIS OPERATION IS POTENTIALLY DESTRUCTIVE! Your first\n"
1504           << "BSD partition will likely be unusable. Exit by typing 'q' if you don't\n"
1505           << "want to convert your BSD partitions to GPT format!";
1506      } // if
1507      cout << "\n**********************************************************************\n\n";
1508      which = use_bsd;
1509   } // if
1510
1511   if ((state == gpt_valid) && (mbrState == gpt)) {
1512      which = use_gpt;
1513      if (!beQuiet)
1514         cout << "Found valid GPT with protective MBR; using GPT.\n";
1515   } // if
1516   if ((state == gpt_valid) && (mbrState == hybrid)) {
1517      which = use_gpt;
1518      if (!beQuiet)
1519         cout << "Found valid GPT with hybrid MBR; using GPT.\n";
1520   } // if
1521   if ((state == gpt_valid) && (mbrState == invalid)) {
1522      cout << "\aFound valid GPT with corrupt MBR; using GPT and will write new\n"
1523           << "protective MBR on save.\n";
1524      which = use_gpt;
1525   } // if
1526   if ((state == gpt_valid) && (mbrState == mbr)) {
1527      which = use_abort;
1528   } // if
1529
1530   if (state == gpt_corrupt) {
1531      if (mbrState == gpt) {
1532         cout << "\a\a****************************************************************************\n"
1533              << "Caution: Found protective or hybrid MBR and corrupt GPT. Using GPT, but disk\n"
1534              << "verification and recovery are STRONGLY recommended.\n"
1535              << "****************************************************************************\n";
1536         which = use_gpt;
1537      } else {
1538         which = use_abort;
1539      } // if/else MBR says disk is GPT
1540   } // if GPT corrupt
1541
1542   if (which == use_new)
1543      cout << "Creating new GPT entries.\n";
1544
1545   return which;
1546} // UseWhichPartitions()
1547
1548// Convert MBR partition table into GPT form.
1549void GPTData::XFormPartitions(void) {
1550   int i, numToConvert;
1551   uint8_t origType;
1552
1553   // Clear out old data & prepare basics....
1554   ClearGPTData();
1555
1556   // Convert the smaller of the # of GPT or MBR partitions
1557   if (numParts > MAX_MBR_PARTS)
1558      numToConvert = MAX_MBR_PARTS;
1559   else
1560      numToConvert = numParts;
1561
1562   for (i = 0; i < numToConvert; i++) {
1563      origType = protectiveMBR.GetType(i);
1564      // don't waste CPU time trying to convert extended, hybrid protective, or
1565      // null (non-existent) partitions
1566      if ((origType != 0x05) && (origType != 0x0f) && (origType != 0x85) &&
1567          (origType != 0x00) && (origType != 0xEE))
1568         partitions[i] = protectiveMBR.AsGPT(i);
1569   } // for
1570
1571   // Convert MBR into protective MBR
1572   protectiveMBR.MakeProtectiveMBR();
1573
1574   // Record that all original CRCs were OK so as not to raise flags
1575   // when doing a disk verification
1576   mainCrcOk = secondCrcOk = mainPartsCrcOk = secondPartsCrcOk = 1;
1577} // GPTData::XFormPartitions()
1578
1579// Transforms BSD disklabel on the specified partition (numbered from 0).
1580// If an invalid partition number is given, the program does nothing.
1581// Returns the number of new partitions created.
1582int GPTData::XFormDisklabel(uint32_t partNum) {
1583   uint32_t low, high;
1584   int goOn = 1, numDone = 0;
1585   BSDData disklabel;
1586
1587   if (GetPartRange(&low, &high) == 0) {
1588      goOn = 0;
1589      cout << "No partitions!\n";
1590   } // if
1591   if (partNum > high) {
1592      goOn = 0;
1593      cout << "Specified partition is invalid!\n";
1594   } // if
1595
1596   // If all is OK, read the disklabel and convert it.
1597   if (goOn) {
1598      goOn = disklabel.ReadBSDData(&myDisk, partitions[partNum].GetFirstLBA(),
1599                                   partitions[partNum].GetLastLBA());
1600      if ((goOn) && (disklabel.IsDisklabel())) {
1601         numDone = XFormDisklabel(&disklabel);
1602         if (numDone == 1)
1603            cout << "Converted 1 BSD partition.\n";
1604         else
1605            cout << "Converted " << numDone << " BSD partitions.\n";
1606      } else {
1607         cout << "Unable to convert partitions! Unrecognized BSD disklabel.\n";
1608      } // if/else
1609   } // if
1610   if (numDone > 0) { // converted partitions; delete carrier
1611      partitions[partNum].BlankPartition();
1612   } // if
1613   return numDone;
1614} // GPTData::XFormDisklabel(uint32_t i)
1615
1616// Transform the partitions on an already-loaded BSD disklabel...
1617int GPTData::XFormDisklabel(BSDData* disklabel) {
1618   int i, partNum = 0, numDone = 0;
1619
1620   if (disklabel->IsDisklabel()) {
1621      for (i = 0; i < disklabel->GetNumParts(); i++) {
1622         partNum = FindFirstFreePart();
1623         if (partNum >= 0) {
1624            partitions[partNum] = disklabel->AsGPT(i);
1625            if (partitions[partNum].IsUsed())
1626               numDone++;
1627         } // if
1628      } // for
1629      if (partNum == -1)
1630         cerr << "Warning! Too many partitions to convert!\n";
1631   } // if
1632
1633   // Record that all original CRCs were OK so as not to raise flags
1634   // when doing a disk verification
1635   mainCrcOk = secondCrcOk = mainPartsCrcOk = secondPartsCrcOk = 1;
1636
1637   return numDone;
1638} // GPTData::XFormDisklabel(BSDData* disklabel)
1639
1640// Add one GPT partition to MBR. Used by PartsToMBR() functions. Created
1641// partition has the active/bootable flag UNset and uses the GPT fdisk
1642// type code divided by 0x0100 as the MBR type code.
1643// Returns 1 if operation was 100% successful, 0 if there were ANY
1644// problems.
1645int GPTData::OnePartToMBR(uint32_t gptPart, int mbrPart) {
1646   int allOK = 1;
1647
1648   if ((mbrPart < 0) || (mbrPart > 3)) {
1649      cout << "MBR partition " << mbrPart + 1 << " is out of range; omitting it.\n";
1650      allOK = 0;
1651   } // if
1652   if (gptPart >= numParts) {
1653      cout << "GPT partition " << gptPart + 1 << " is out of range; omitting it.\n";
1654      allOK = 0;
1655   } // if
1656   if (allOK && (partitions[gptPart].GetLastLBA() == UINT64_C(0))) {
1657      cout << "GPT partition " << gptPart + 1 << " is undefined; omitting it.\n";
1658      allOK = 0;
1659   } // if
1660   if (allOK && (partitions[gptPart].GetFirstLBA() <= UINT32_MAX) &&
1661       (partitions[gptPart].GetLengthLBA() <= UINT32_MAX)) {
1662      if (partitions[gptPart].GetLastLBA() > UINT32_MAX) {
1663         cout << "Caution: Partition end point past 32-bit pointer boundary;"
1664              << " some OSes may\nreact strangely.\n";
1665      } // if
1666      protectiveMBR.MakePart(mbrPart, (uint32_t) partitions[gptPart].GetFirstLBA(),
1667                             (uint32_t) partitions[gptPart].GetLengthLBA(),
1668                             partitions[gptPart].GetHexType() / 256, 0);
1669   } else { // partition out of range
1670      if (allOK) // Display only if "else" triggered by out-of-bounds condition
1671         cout << "Partition " << gptPart + 1 << " begins beyond the 32-bit pointer limit of MBR "
1672              << "partitions, or is\n too big; omitting it.\n";
1673      allOK = 0;
1674   } // if/else
1675   return allOK;
1676} // GPTData::OnePartToMBR()
1677
1678
1679/**********************************************************************
1680 *                                                                    *
1681 * Functions that adjust GPT data structures WITHOUT user interaction *
1682 * (they may display information for the user's benefit, though)      *
1683 *                                                                    *
1684 **********************************************************************/
1685
1686// Resizes GPT to specified number of entries. Creates a new table if
1687// necessary, copies data if it already exists. If fillGPTSectors is 1
1688// (the default), rounds numEntries to fill all the sectors necessary to
1689// hold the GPT.
1690// Returns 1 if all goes well, 0 if an error is encountered.
1691int GPTData::SetGPTSize(uint32_t numEntries, int fillGPTSectors) {
1692   GPTPart* newParts;
1693   uint32_t i, high, copyNum, entriesPerSector;
1694   int allOK = 1;
1695
1696   // First, adjust numEntries upward, if necessary, to get a number
1697   // that fills the allocated sectors
1698   entriesPerSector = blockSize / GPT_SIZE;
1699   if (fillGPTSectors && ((numEntries % entriesPerSector) != 0)) {
1700      cout << "Adjusting GPT size from " << numEntries << " to ";
1701      numEntries = ((numEntries / entriesPerSector) + 1) * entriesPerSector;
1702      cout << numEntries << " to fill the sector\n";
1703   } // if
1704
1705   // Do the work only if the # of partitions is changing. Along with being
1706   // efficient, this prevents mucking with the location of the secondary
1707   // partition table, which causes problems when loading data from a RAID
1708   // array that's been expanded because this function is called when loading
1709   // data.
1710   if (((numEntries != numParts) || (partitions == NULL)) && (numEntries > 0)) {
1711      newParts = new GPTPart [numEntries];
1712      if (newParts != NULL) {
1713         if (partitions != NULL) { // existing partitions; copy them over
1714            GetPartRange(&i, &high);
1715            if (numEntries < (high + 1)) { // Highest entry too high for new #
1716               cout << "The highest-numbered partition is " << high + 1
1717                    << ", which is greater than the requested\n"
1718                    << "partition table size of " << numEntries
1719                    << "; cannot resize. Perhaps sorting will help.\n";
1720               allOK = 0;
1721               delete[] newParts;
1722            } else { // go ahead with copy
1723               if (numEntries < numParts)
1724                  copyNum = numEntries;
1725               else
1726                  copyNum = numParts;
1727               for (i = 0; i < copyNum; i++) {
1728                  newParts[i] = partitions[i];
1729               } // for
1730               delete[] partitions;
1731               partitions = newParts;
1732            } // if
1733         } else { // No existing partition table; just create it
1734            partitions = newParts;
1735         } // if/else existing partitions
1736         numParts = numEntries;
1737         mainHeader.firstUsableLBA = ((numEntries * GPT_SIZE) / blockSize) + (((numEntries * GPT_SIZE) % blockSize) != 0) + 2 ;
1738         secondHeader.firstUsableLBA = mainHeader.firstUsableLBA;
1739         MoveSecondHeaderToEnd();
1740         if (diskSize > 0)
1741            CheckGPTSize();
1742      } else { // Bad memory allocation
1743         cerr << "Error allocating memory for partition table! Size is unchanged!\n";
1744         allOK = 0;
1745      } // if/else
1746   } // if/else
1747   mainHeader.numParts = numParts;
1748   secondHeader.numParts = numParts;
1749   return (allOK);
1750} // GPTData::SetGPTSize()
1751
1752// Blank the partition array
1753void GPTData::BlankPartitions(void) {
1754   uint32_t i;
1755
1756   for (i = 0; i < numParts; i++) {
1757      partitions[i].BlankPartition();
1758   } // for
1759} // GPTData::BlankPartitions()
1760
1761// Delete a partition by number. Returns 1 if successful,
1762// 0 if there was a problem. Returns 1 if partition was in
1763// range, 0 if it was out of range.
1764int GPTData::DeletePartition(uint32_t partNum) {
1765   uint64_t startSector, length;
1766   uint32_t low, high, numUsedParts, retval = 1;;
1767
1768   numUsedParts = GetPartRange(&low, &high);
1769   if ((numUsedParts > 0) && (partNum >= low) && (partNum <= high)) {
1770      // In case there's a protective MBR, look for & delete matching
1771      // MBR partition....
1772      startSector = partitions[partNum].GetFirstLBA();
1773      length = partitions[partNum].GetLengthLBA();
1774      protectiveMBR.DeleteByLocation(startSector, length);
1775
1776      // Now delete the GPT partition
1777      partitions[partNum].BlankPartition();
1778   } else {
1779      cerr << "Partition number " << partNum + 1 << " out of range!\n";
1780      retval = 0;
1781   } // if/else
1782   return retval;
1783} // GPTData::DeletePartition(uint32_t partNum)
1784
1785// Non-interactively create a partition.
1786// Returns 1 if the operation was successful, 0 if a problem was discovered.
1787uint32_t GPTData::CreatePartition(uint32_t partNum, uint64_t startSector, uint64_t endSector) {
1788   int retval = 1; // assume there'll be no problems
1789   uint64_t origSector = startSector;
1790
1791   if (IsFreePartNum(partNum)) {
1792      if (Align(&startSector)) {
1793         cout << "Information: Moved requested sector from " << origSector << " to "
1794              << startSector << " in\norder to align on " << sectorAlignment
1795              << "-sector boundaries.\n";
1796      } // if
1797      if (IsFree(startSector) && (startSector <= endSector)) {
1798         if (FindLastInFree(startSector) >= endSector) {
1799            partitions[partNum].SetFirstLBA(startSector);
1800            partitions[partNum].SetLastLBA(endSector);
1801            partitions[partNum].SetType(DEFAULT_GPT_TYPE);
1802            partitions[partNum].RandomizeUniqueGUID();
1803         } else retval = 0; // if free space until endSector
1804      } else retval = 0; // if startSector is free
1805   } else retval = 0; // if legal partition number
1806   return retval;
1807} // GPTData::CreatePartition(partNum, startSector, endSector)
1808
1809// Sort the GPT entries, eliminating gaps and making for a logical
1810// ordering.
1811void GPTData::SortGPT(void) {
1812   if (numParts > 0)
1813      sort(partitions, partitions + numParts);
1814} // GPTData::SortGPT()
1815
1816// Swap the contents of two partitions.
1817// Returns 1 if successful, 0 if either partition is out of range
1818// (that is, not a legal number; either or both can be empty).
1819// Note that if partNum1 = partNum2 and this number is in range,
1820// it will be considered successful.
1821int GPTData::SwapPartitions(uint32_t partNum1, uint32_t partNum2) {
1822   GPTPart temp;
1823   int allOK = 1;
1824
1825   if ((partNum1 < numParts) && (partNum2 < numParts)) {
1826      if (partNum1 != partNum2) {
1827         temp = partitions[partNum1];
1828         partitions[partNum1] = partitions[partNum2];
1829         partitions[partNum2] = temp;
1830      } // if
1831   } else allOK = 0; // partition numbers are valid
1832   return allOK;
1833} // GPTData::SwapPartitions()
1834
1835// Set up data structures for entirely new set of partitions on the
1836// specified device. Returns 1 if OK, 0 if there were problems.
1837// Note that this function does NOT clear the protectiveMBR data
1838// structure, since it may hold the original MBR partitions if the
1839// program was launched on an MBR disk, and those may need to be
1840// converted to GPT format.
1841int GPTData::ClearGPTData(void) {
1842   int goOn = 1, i;
1843
1844   // Set up the partition table....
1845   delete[] partitions;
1846   partitions = NULL;
1847   SetGPTSize(NUM_GPT_ENTRIES);
1848
1849   // Now initialize a bunch of stuff that's static....
1850   mainHeader.signature = GPT_SIGNATURE;
1851   mainHeader.revision = 0x00010000;
1852   mainHeader.headerSize = HEADER_SIZE;
1853   mainHeader.reserved = 0;
1854   mainHeader.currentLBA = UINT64_C(1);
1855   mainHeader.partitionEntriesLBA = (uint64_t) 2;
1856   mainHeader.sizeOfPartitionEntries = GPT_SIZE;
1857   for (i = 0; i < GPT_RESERVED; i++) {
1858      mainHeader.reserved2[i] = '\0';
1859   } // for
1860   if (blockSize > 0)
1861      sectorAlignment = DEFAULT_ALIGNMENT * SECTOR_SIZE / blockSize;
1862   else
1863      sectorAlignment = DEFAULT_ALIGNMENT;
1864
1865   // Now some semi-static items (computed based on end of disk)
1866   mainHeader.backupLBA = diskSize - UINT64_C(1);
1867   mainHeader.lastUsableLBA = diskSize - mainHeader.firstUsableLBA;
1868
1869   // Set a unique GUID for the disk, based on random numbers
1870   mainHeader.diskGUID.Randomize();
1871
1872   // Copy main header to backup header
1873   RebuildSecondHeader();
1874
1875   // Blank out the partitions array....
1876   BlankPartitions();
1877
1878   // Flag all CRCs as being OK....
1879   mainCrcOk = 1;
1880   secondCrcOk = 1;
1881   mainPartsCrcOk = 1;
1882   secondPartsCrcOk = 1;
1883
1884   return (goOn);
1885} // GPTData::ClearGPTData()
1886
1887// Set the location of the second GPT header data to the end of the disk.
1888// If the disk size has actually changed, this also adjusts the protective
1889// entry in the MBR, since it's probably no longer correct.
1890// Used internally and called by the 'e' option on the recovery &
1891// transformation menu, to help users of RAID arrays who add disk space
1892// to their arrays or to adjust data structures in restore operations
1893// involving unequal-sized disks.
1894void GPTData::MoveSecondHeaderToEnd() {
1895   mainHeader.backupLBA = secondHeader.currentLBA = diskSize - UINT64_C(1);
1896   if (mainHeader.lastUsableLBA != diskSize - mainHeader.firstUsableLBA) {
1897      if (protectiveMBR.GetValidity() == hybrid) {
1898         protectiveMBR.OptimizeEESize();
1899         RecomputeCHS();
1900      } // if
1901      if (protectiveMBR.GetValidity() == gpt)
1902         MakeProtectiveMBR();
1903   } // if
1904   mainHeader.lastUsableLBA = secondHeader.lastUsableLBA = diskSize - mainHeader.firstUsableLBA;
1905   secondHeader.partitionEntriesLBA = secondHeader.lastUsableLBA + UINT64_C(1);
1906} // GPTData::FixSecondHeaderLocation()
1907
1908// Sets the partition's name to the specified UnicodeString without
1909// user interaction.
1910// Returns 1 on success, 0 on failure (invalid partition number).
1911int GPTData::SetName(uint32_t partNum, const UnicodeString & theName) {
1912   int retval = 1;
1913
1914   if (IsUsedPartNum(partNum))
1915      partitions[partNum].SetName(theName);
1916   else
1917      retval = 0;
1918
1919   return retval;
1920} // GPTData::SetName
1921
1922// Set the disk GUID to the specified value. Note that the header CRCs must
1923// be recomputed after calling this function.
1924void GPTData::SetDiskGUID(GUIDData newGUID) {
1925   mainHeader.diskGUID = newGUID;
1926   secondHeader.diskGUID = newGUID;
1927} // SetDiskGUID()
1928
1929// Set the unique GUID of the specified partition. Returns 1 on
1930// successful completion, 0 if there were problems (invalid
1931// partition number).
1932int GPTData::SetPartitionGUID(uint32_t pn, GUIDData theGUID) {
1933   int retval = 0;
1934
1935   if (pn < numParts) {
1936      if (partitions[pn].IsUsed()) {
1937         partitions[pn].SetUniqueGUID(theGUID);
1938         retval = 1;
1939      } // if
1940   } // if
1941   return retval;
1942} // GPTData::SetPartitionGUID()
1943
1944// Set new random GUIDs for the disk and all partitions. Intended to be used
1945// after disk cloning or similar operations that don't randomize the GUIDs.
1946void GPTData::RandomizeGUIDs(void) {
1947   uint32_t i;
1948
1949   mainHeader.diskGUID.Randomize();
1950   secondHeader.diskGUID = mainHeader.diskGUID;
1951   for (i = 0; i < numParts; i++)
1952      if (partitions[i].IsUsed())
1953         partitions[i].RandomizeUniqueGUID();
1954} // GPTData::RandomizeGUIDs()
1955
1956// Change partition type code non-interactively. Returns 1 if
1957// successful, 0 if not....
1958int GPTData::ChangePartType(uint32_t partNum, PartType theGUID) {
1959   int retval = 1;
1960
1961   if (!IsFreePartNum(partNum)) {
1962      partitions[partNum].SetType(theGUID);
1963   } else retval = 0;
1964   return retval;
1965} // GPTData::ChangePartType()
1966
1967// Recompute the CHS values of all the MBR partitions. Used to reset
1968// CHS values that some BIOSes require, despite the fact that the
1969// resulting CHS values violate the GPT standard.
1970void GPTData::RecomputeCHS(void) {
1971   int i;
1972
1973   for (i = 0; i < 4; i++)
1974      protectiveMBR.RecomputeCHS(i);
1975} // GPTData::RecomputeCHS()
1976
1977// Adjust sector number so that it falls on a sector boundary that's a
1978// multiple of sectorAlignment. This is done to improve the performance
1979// of Western Digital Advanced Format disks and disks with similar
1980// technology from other companies, which use 4096-byte sectors
1981// internally although they translate to 512-byte sectors for the
1982// benefit of the OS. If partitions aren't properly aligned on these
1983// disks, some filesystem data structures can span multiple physical
1984// sectors, degrading performance. This function should be called
1985// only on the FIRST sector of the partition, not the last!
1986// This function returns 1 if the alignment was altered, 0 if it
1987// was unchanged.
1988int GPTData::Align(uint64_t* sector) {
1989   int retval = 0, sectorOK = 0;
1990   uint64_t earlier, later, testSector;
1991
1992   if ((*sector % sectorAlignment) != 0) {
1993      earlier = (*sector / sectorAlignment) * sectorAlignment;
1994      later = earlier + (uint64_t) sectorAlignment;
1995
1996      // Check to see that every sector between the earlier one and the
1997      // requested one is clear, and that it's not too early....
1998      if (earlier >= mainHeader.firstUsableLBA) {
1999         sectorOK = 1;
2000         testSector = earlier;
2001         do {
2002            sectorOK = IsFree(testSector++);
2003         } while ((sectorOK == 1) && (testSector < *sector));
2004         if (sectorOK == 1) {
2005            *sector = earlier;
2006            retval = 1;
2007         } // if
2008      } // if firstUsableLBA check
2009
2010      // If couldn't move the sector earlier, try to move it later instead....
2011      if ((sectorOK != 1) && (later <= mainHeader.lastUsableLBA)) {
2012         sectorOK = 1;
2013         testSector = later;
2014         do {
2015            sectorOK = IsFree(testSector--);
2016         } while ((sectorOK == 1) && (testSector > *sector));
2017         if (sectorOK == 1) {
2018            *sector = later;
2019            retval = 1;
2020         } // if
2021      } // if
2022   } // if
2023   return retval;
2024} // GPTData::Align()
2025
2026/********************************************************
2027 *                                                      *
2028 * Functions that return data about GPT data structures *
2029 * (most of these are inline in gpt.h)                  *
2030 *                                                      *
2031 ********************************************************/
2032
2033// Find the low and high used partition numbers (numbered from 0).
2034// Return value is the number of partitions found. Note that the
2035// *low and *high values are both set to 0 when no partitions
2036// are found, as well as when a single partition in the first
2037// position exists. Thus, the return value is the only way to
2038// tell when no partitions exist.
2039int GPTData::GetPartRange(uint32_t *low, uint32_t *high) {
2040   uint32_t i;
2041   int numFound = 0;
2042
2043   *low = numParts + 1; // code for "not found"
2044   *high = 0;
2045   for (i = 0; i < numParts; i++) {
2046      if (partitions[i].IsUsed()) { // it exists
2047         *high = i; // since we're counting up, set the high value
2048         // Set the low value only if it's not yet found...
2049         if (*low == (numParts + 1)) *low = i;
2050            numFound++;
2051      } // if
2052   } // for
2053
2054   // Above will leave *low pointing to its "not found" value if no partitions
2055   // are defined, so reset to 0 if this is the case....
2056   if (*low == (numParts + 1))
2057      *low = 0;
2058   return numFound;
2059} // GPTData::GetPartRange()
2060
2061// Returns the value of the first free partition, or -1 if none is
2062// unused.
2063int GPTData::FindFirstFreePart(void) {
2064   int i = 0;
2065
2066   if (partitions != NULL) {
2067      while ((i < (int) numParts) && (partitions[i].IsUsed()))
2068         i++;
2069      if (i >= (int) numParts)
2070         i = -1;
2071   } else i = -1;
2072   return i;
2073} // GPTData::FindFirstFreePart()
2074
2075// Returns the number of defined partitions.
2076uint32_t GPTData::CountParts(void) {
2077   uint32_t i, counted = 0;
2078
2079   for (i = 0; i < numParts; i++) {
2080      if (partitions[i].IsUsed())
2081         counted++;
2082   } // for
2083   return counted;
2084} // GPTData::CountParts()
2085
2086/****************************************************
2087 *                                                  *
2088 * Functions that return data about disk free space *
2089 *                                                  *
2090 ****************************************************/
2091
2092// Find the first available block after the starting point; returns 0 if
2093// there are no available blocks left
2094uint64_t GPTData::FindFirstAvailable(uint64_t start) {
2095   uint64_t first;
2096   uint32_t i;
2097   int firstMoved = 0;
2098
2099   // Begin from the specified starting point or from the first usable
2100   // LBA, whichever is greater...
2101   if (start < mainHeader.firstUsableLBA)
2102      first = mainHeader.firstUsableLBA;
2103   else
2104      first = start;
2105
2106   // ...now search through all partitions; if first is within an
2107   // existing partition, move it to the next sector after that
2108   // partition and repeat. If first was moved, set firstMoved
2109   // flag; repeat until firstMoved is not set, so as to catch
2110   // cases where partitions are out of sequential order....
2111   do {
2112      firstMoved = 0;
2113      for (i = 0; i < numParts; i++) {
2114         if ((partitions[i].IsUsed()) && (first >= partitions[i].GetFirstLBA()) &&
2115             (first <= partitions[i].GetLastLBA())) { // in existing part.
2116            first = partitions[i].GetLastLBA() + 1;
2117            firstMoved = 1;
2118         } // if
2119      } // for
2120   } while (firstMoved == 1);
2121   if (first > mainHeader.lastUsableLBA)
2122      first = 0;
2123   return (first);
2124} // GPTData::FindFirstAvailable()
2125
2126// Finds the first available sector in the largest block of unallocated
2127// space on the disk. Returns 0 if there are no available blocks left
2128uint64_t GPTData::FindFirstInLargest(void) {
2129   uint64_t start, firstBlock, lastBlock, segmentSize, selectedSize = 0, selectedSegment = 0;
2130
2131   start = 0;
2132   do {
2133      firstBlock = FindFirstAvailable(start);
2134      if (firstBlock != UINT32_C(0)) { // something's free...
2135         lastBlock = FindLastInFree(firstBlock);
2136         segmentSize = lastBlock - firstBlock + UINT32_C(1);
2137         if (segmentSize > selectedSize) {
2138            selectedSize = segmentSize;
2139            selectedSegment = firstBlock;
2140         } // if
2141         start = lastBlock + 1;
2142      } // if
2143   } while (firstBlock != 0);
2144   return selectedSegment;
2145} // GPTData::FindFirstInLargest()
2146
2147// Find the last available block on the disk.
2148// Returns 0 if there are no available sectors
2149uint64_t GPTData::FindLastAvailable(void) {
2150   uint64_t last;
2151   uint32_t i;
2152   int lastMoved = 0;
2153
2154   // Start by assuming the last usable LBA is available....
2155   last = mainHeader.lastUsableLBA;
2156
2157   // ...now, similar to algorithm in FindFirstAvailable(), search
2158   // through all partitions, moving last when it's in an existing
2159   // partition. Set the lastMoved flag so we repeat to catch cases
2160   // where partitions are out of logical order.
2161   do {
2162      lastMoved = 0;
2163      for (i = 0; i < numParts; i++) {
2164         if ((last >= partitions[i].GetFirstLBA()) &&
2165             (last <= partitions[i].GetLastLBA())) { // in existing part.
2166            last = partitions[i].GetFirstLBA() - 1;
2167            lastMoved = 1;
2168         } // if
2169      } // for
2170   } while (lastMoved == 1);
2171   if (last < mainHeader.firstUsableLBA)
2172      last = 0;
2173   return (last);
2174} // GPTData::FindLastAvailable()
2175
2176// Find the last available block in the free space pointed to by start.
2177uint64_t GPTData::FindLastInFree(uint64_t start) {
2178   uint64_t nearestStart;
2179   uint32_t i;
2180
2181   nearestStart = mainHeader.lastUsableLBA;
2182   for (i = 0; i < numParts; i++) {
2183      if ((nearestStart > partitions[i].GetFirstLBA()) &&
2184          (partitions[i].GetFirstLBA() > start)) {
2185         nearestStart = partitions[i].GetFirstLBA() - 1;
2186      } // if
2187   } // for
2188   return (nearestStart);
2189} // GPTData::FindLastInFree()
2190
2191// Finds the total number of free blocks, the number of segments in which
2192// they reside, and the size of the largest of those segments
2193uint64_t GPTData::FindFreeBlocks(uint32_t *numSegments, uint64_t *largestSegment) {
2194   uint64_t start = UINT64_C(0); // starting point for each search
2195   uint64_t totalFound = UINT64_C(0); // running total
2196   uint64_t firstBlock; // first block in a segment
2197   uint64_t lastBlock; // last block in a segment
2198   uint64_t segmentSize; // size of segment in blocks
2199   uint32_t num = 0;
2200
2201   *largestSegment = UINT64_C(0);
2202   if (diskSize > 0) {
2203      do {
2204         firstBlock = FindFirstAvailable(start);
2205         if (firstBlock != UINT64_C(0)) { // something's free...
2206            lastBlock = FindLastInFree(firstBlock);
2207            segmentSize = lastBlock - firstBlock + UINT64_C(1);
2208            if (segmentSize > *largestSegment) {
2209               *largestSegment = segmentSize;
2210            } // if
2211            totalFound += segmentSize;
2212            num++;
2213            start = lastBlock + 1;
2214         } // if
2215      } while (firstBlock != 0);
2216   } // if
2217   *numSegments = num;
2218   return totalFound;
2219} // GPTData::FindFreeBlocks()
2220
2221// Returns 1 if sector is unallocated, 0 if it's allocated to a partition.
2222// If it's allocated, return the partition number to which it's allocated
2223// in partNum, if that variable is non-NULL. (A value of UINT32_MAX is
2224// returned in partNum if the sector is in use by basic GPT data structures.)
2225int GPTData::IsFree(uint64_t sector, uint32_t *partNum) {
2226   int isFree = 1;
2227   uint32_t i;
2228
2229   for (i = 0; i < numParts; i++) {
2230      if ((sector >= partitions[i].GetFirstLBA()) &&
2231           (sector <= partitions[i].GetLastLBA())) {
2232         isFree = 0;
2233         if (partNum != NULL)
2234            *partNum = i;
2235      } // if
2236   } // for
2237   if ((sector < mainHeader.firstUsableLBA) ||
2238        (sector > mainHeader.lastUsableLBA)) {
2239      isFree = 0;
2240      if (partNum != NULL)
2241         *partNum = UINT32_MAX;
2242   } // if
2243   return (isFree);
2244} // GPTData::IsFree()
2245
2246// Returns 1 if partNum is unused AND if it's a legal value.
2247int GPTData::IsFreePartNum(uint32_t partNum) {
2248   return ((partNum < numParts) && (partitions != NULL) &&
2249           (!partitions[partNum].IsUsed()));
2250} // GPTData::IsFreePartNum()
2251
2252// Returns 1 if partNum is in use.
2253int GPTData::IsUsedPartNum(uint32_t partNum) {
2254   return ((partNum < numParts) && (partitions != NULL) &&
2255           (partitions[partNum].IsUsed()));
2256} // GPTData::IsUsedPartNum()
2257
2258/***********************************************************
2259 *                                                         *
2260 * Change how functions work or return information on them *
2261 *                                                         *
2262 ***********************************************************/
2263
2264// Set partition alignment value; partitions will begin on multiples of
2265// the specified value
2266void GPTData::SetAlignment(uint32_t n) {
2267   if (n > 0)
2268      sectorAlignment = n;
2269   else
2270      cerr << "Attempt to set partition alignment to 0!\n";
2271} // GPTData::SetAlignment()
2272
2273// Compute sector alignment based on the current partitions (if any). Each
2274// partition's starting LBA is examined, and if it's divisible by a power-of-2
2275// value less than or equal to the DEFAULT_ALIGNMENT value (adjusted for the
2276// sector size), but not by the previously-located alignment value, then the
2277// alignment value is adjusted down. If the computed alignment is less than 8
2278// and the disk is bigger than SMALLEST_ADVANCED_FORMAT, resets it to 8. This
2279// is a safety measure for Advanced Format drives. If no partitions are
2280// defined, the alignment value is set to DEFAULT_ALIGNMENT (2048) (or an
2281// adjustment of that based on the current sector size). The result is that new
2282// drives are aligned to 2048-sector multiples but the program won't complain
2283// about other alignments on existing disks unless a smaller-than-8 alignment
2284// is used on big disks (as safety for Advanced Format drives).
2285// Returns the computed alignment value.
2286uint32_t GPTData::ComputeAlignment(void) {
2287   uint32_t i = 0, found, exponent = 31;
2288   uint32_t align = DEFAULT_ALIGNMENT;
2289
2290   if (blockSize > 0)
2291      align = DEFAULT_ALIGNMENT * SECTOR_SIZE / blockSize;
2292   exponent = (uint32_t) log2(align);
2293   for (i = 0; i < numParts; i++) {
2294      if (partitions[i].IsUsed()) {
2295         found = 0;
2296         while (!found) {
2297            align = UINT64_C(1) << exponent;
2298            if ((partitions[i].GetFirstLBA() % align) == 0) {
2299               found = 1;
2300            } else {
2301               exponent--;
2302            } // if/else
2303         } // while
2304      } // if
2305   } // for
2306   if ((align < MIN_AF_ALIGNMENT) && (diskSize >= SMALLEST_ADVANCED_FORMAT))
2307      align = MIN_AF_ALIGNMENT;
2308   sectorAlignment = align;
2309   return align;
2310} // GPTData::ComputeAlignment()
2311
2312/********************************
2313 *                              *
2314 * Endianness support functions *
2315 *                              *
2316 ********************************/
2317
2318void GPTData::ReverseHeaderBytes(struct GPTHeader* header) {
2319   ReverseBytes(&header->signature, 8);
2320   ReverseBytes(&header->revision, 4);
2321   ReverseBytes(&header->headerSize, 4);
2322   ReverseBytes(&header->headerCRC, 4);
2323   ReverseBytes(&header->reserved, 4);
2324   ReverseBytes(&header->currentLBA, 8);
2325   ReverseBytes(&header->backupLBA, 8);
2326   ReverseBytes(&header->firstUsableLBA, 8);
2327   ReverseBytes(&header->lastUsableLBA, 8);
2328   ReverseBytes(&header->partitionEntriesLBA, 8);
2329   ReverseBytes(&header->numParts, 4);
2330   ReverseBytes(&header->sizeOfPartitionEntries, 4);
2331   ReverseBytes(&header->partitionEntriesCRC, 4);
2332   ReverseBytes(header->reserved2, GPT_RESERVED);
2333} // GPTData::ReverseHeaderBytes()
2334
2335// Reverse byte order for all partitions.
2336void GPTData::ReversePartitionBytes() {
2337   uint32_t i;
2338
2339   for (i = 0; i < numParts; i++) {
2340      partitions[i].ReversePartBytes();
2341   } // for
2342} // GPTData::ReversePartitionBytes()
2343
2344// Validate partition number
2345bool GPTData::ValidPartNum (const uint32_t partNum) {
2346   if (partNum >= numParts) {
2347      cerr << "Partition number out of range: " << partNum << "\n";
2348      return false;
2349   } // if
2350   return true;
2351} // GPTData::ValidPartNum
2352
2353// Return a single partition for inspection (not modification!) by other
2354// functions.
2355const GPTPart & GPTData::operator[](uint32_t partNum) const {
2356   if (partNum >= numParts) {
2357      cerr << "Partition number out of range (" << partNum << " requested, but only "
2358           << numParts << " available)\n";
2359      exit(1);
2360   } // if
2361   if (partitions == NULL) {
2362      cerr << "No partitions defined in GPTData::operator[]; fatal error!\n";
2363      exit(1);
2364   } // if
2365   return partitions[partNum];
2366} // operator[]
2367
2368// Return (not for modification!) the disk's GUID value
2369const GUIDData & GPTData::GetDiskGUID(void) const {
2370   return mainHeader.diskGUID;
2371} // GPTData::GetDiskGUID()
2372
2373// Manage attributes for a partition, based on commands passed to this function.
2374// (Function is non-interactive.)
2375// Returns 1 if a modification command succeeded, 0 if the command should not have
2376// modified data, and -1 if a modification command failed.
2377int GPTData::ManageAttributes(int partNum, const string & command, const string & bits) {
2378   int retval = 0;
2379   Attributes theAttr;
2380
2381   if (partNum >= (int) numParts) {
2382      cerr << "Invalid partition number (" << partNum + 1 << ")\n";
2383      retval = -1;
2384   } else {
2385      if (command == "show") {
2386         ShowAttributes(partNum);
2387      } else if (command == "get") {
2388         GetAttribute(partNum, bits);
2389      } else {
2390         theAttr = partitions[partNum].GetAttributes();
2391         if (theAttr.OperateOnAttributes(partNum, command, bits)) {
2392            partitions[partNum].SetAttributes(theAttr.GetAttributes());
2393            retval = 1;
2394         } else {
2395            retval = -1;
2396         } // if/else
2397      } // if/elseif/else
2398   } // if/else invalid partition #
2399
2400   return retval;
2401} // GPTData::ManageAttributes()
2402
2403// Show all attributes for a specified partition....
2404void GPTData::ShowAttributes(const uint32_t partNum) {
2405   if ((partNum < numParts) && partitions[partNum].IsUsed())
2406      partitions[partNum].ShowAttributes(partNum);
2407} // GPTData::ShowAttributes
2408
2409// Show whether a single attribute bit is set (terse output)...
2410void GPTData::GetAttribute(const uint32_t partNum, const string& attributeBits) {
2411   if (partNum < numParts)
2412      partitions[partNum].GetAttributes().OperateOnAttributes(partNum, "get", attributeBits);
2413} // GPTData::GetAttribute
2414
2415
2416/******************************************
2417 *                                        *
2418 * Additional non-class support functions *
2419 *                                        *
2420 ******************************************/
2421
2422// Check to be sure that data type sizes are correct. The basic types (uint*_t) should
2423// never fail these tests, but the struct types may fail depending on compile options.
2424// Specifically, the -fpack-struct option to gcc may be required to ensure proper structure
2425// sizes.
2426int SizesOK(void) {
2427   int allOK = 1;
2428
2429   if (sizeof(uint8_t) != 1) {
2430      cerr << "uint8_t is " << sizeof(uint8_t) << " bytes, should be 1 byte; aborting!\n";
2431      allOK = 0;
2432   } // if
2433   if (sizeof(uint16_t) != 2) {
2434      cerr << "uint16_t is " << sizeof(uint16_t) << " bytes, should be 2 bytes; aborting!\n";
2435      allOK = 0;
2436   } // if
2437   if (sizeof(uint32_t) != 4) {
2438      cerr << "uint32_t is " << sizeof(uint32_t) << " bytes, should be 4 bytes; aborting!\n";
2439      allOK = 0;
2440   } // if
2441   if (sizeof(uint64_t) != 8) {
2442      cerr << "uint64_t is " << sizeof(uint64_t) << " bytes, should be 8 bytes; aborting!\n";
2443      allOK = 0;
2444   } // if
2445   if (sizeof(struct MBRRecord) != 16) {
2446      cerr << "MBRRecord is " << sizeof(MBRRecord) << " bytes, should be 16 bytes; aborting!\n";
2447      allOK = 0;
2448   } // if
2449   if (sizeof(struct TempMBR) != 512) {
2450      cerr << "TempMBR is " <<  sizeof(TempMBR) << " bytes, should be 512 bytes; aborting!\n";
2451      allOK = 0;
2452   } // if
2453   if (sizeof(struct GPTHeader) != 512) {
2454      cerr << "GPTHeader is " << sizeof(GPTHeader) << " bytes, should be 512 bytes; aborting!\n";
2455      allOK = 0;
2456   } // if
2457   if (sizeof(GPTPart) != 128) {
2458      cerr << "GPTPart is " << sizeof(GPTPart) << " bytes, should be 128 bytes; aborting!\n";
2459      allOK = 0;
2460   } // if
2461   if (sizeof(GUIDData) != 16) {
2462      cerr << "GUIDData is " << sizeof(GUIDData) << " bytes, should be 16 bytes; aborting!\n";
2463      allOK = 0;
2464   } // if
2465   if (sizeof(PartType) != 16) {
2466      cerr << "PartType is " << sizeof(PartType) << " bytes, should be 16 bytes; aborting!\n";
2467      allOK = 0;
2468   } // if
2469   return (allOK);
2470} // SizesOK()
2471
2472