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