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