1 /* gpt.cc -- Functions for loading, saving, and manipulating legacy MBR and GPT partition
4 /* By Rod Smith, initial coding January to February, 2009 */
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. */
9 #define __STDC_LIMIT_MACROS
10 #define __STDC_CONSTANT_MACROS
27 #include "parttypes.h"
28 #include "attributes.h"
34 #define log2(x) (log(x) / M_LN2)
38 #define log2(x) (log((double) x) / log(2.0))
39 #endif // Microsoft Visual C++
41 /****************************************
43 * GPTData class and related structures *
45 ****************************************/
47 // Default constructor
48 GPTData::GPTData(void) {
49 blockSize = SECTOR_SIZE; // set a default
61 sectorAlignment = MIN_AF_ALIGNMENT; // Align partitions on 4096-byte boundaries by default
63 whichWasUsed = use_new;
64 mainHeader.numParts = 0;
66 SetGPTSize(NUM_GPT_ENTRIES);
67 // Initialize CRC functions...
69 } // GPTData default constructor
71 // The following constructor loads GPT data from a device file
72 GPTData::GPTData(string filename) {
73 blockSize = SECTOR_SIZE; // set a default
85 sectorAlignment = MIN_AF_ALIGNMENT; // Align partitions on 4096-byte boundaries by default
87 whichWasUsed = use_new;
88 mainHeader.numParts = 0;
90 // Initialize CRC functions...
92 if (!LoadPartitions(filename))
94 } // GPTData(string filename) constructor
97 GPTData::~GPTData(void) {
99 } // GPTData destructor
101 // Assignment operator
102 GPTData & GPTData::operator=(const GPTData & orig) {
105 mainHeader = orig.mainHeader;
106 numParts = orig.numParts;
107 secondHeader = orig.secondHeader;
108 protectiveMBR = orig.protectiveMBR;
109 device = orig.device;
110 blockSize = orig.blockSize;
111 diskSize = orig.diskSize;
113 justLooking = orig.justLooking;
114 mainCrcOk = orig.mainCrcOk;
115 secondCrcOk = orig.secondCrcOk;
116 mainPartsCrcOk = orig.mainPartsCrcOk;
117 secondPartsCrcOk = orig.secondPartsCrcOk;
118 apmFound = orig.apmFound;
119 bsdFound = orig.bsdFound;
120 sectorAlignment = orig.sectorAlignment;
121 beQuiet = orig.beQuiet;
122 whichWasUsed = orig.whichWasUsed;
124 myDisk.OpenForRead(orig.myDisk.GetName());
127 partitions = new GPTPart [numParts];
128 if (partitions == NULL) {
129 cerr << "Error! Could not allocate memory for partitions in GPTData::operator=()!\n"
133 for (i = 0; i < numParts; i++) {
134 partitions[i] = orig.partitions[i];
138 } // GPTData::operator=()
140 /*********************************************************************
142 * Begin functions that verify data, or that adjust the verification *
143 * information (compute CRCs, rebuild headers) *
145 *********************************************************************/
147 // Perform detailed verification, reporting on any problems found, but
148 // do *NOT* recover from these problems. Returns the total number of
149 // problems identified.
150 int GPTData::Verify(void) {
151 int problems = 0, alignProbs = 0;
152 uint32_t i, numSegments;
153 uint64_t totalFree, largestSegment;
155 // First, check for CRC errors in the GPT data....
158 cout << "\nProblem: The CRC for the main GPT header is invalid. The main GPT header may\n"
159 << "be corrupt. Consider loading the backup GPT header to rebuild the main GPT\n"
160 << "header ('b' on the recovery & transformation menu). This report may be a false\n"
161 << "alarm if you've already corrected other problems.\n";
163 if (!mainPartsCrcOk) {
165 cout << "\nProblem: The CRC for the main partition table is invalid. This table may be\n"
166 << "corrupt. Consider loading the backup partition table ('c' on the recovery &\n"
167 << "transformation menu). This report may be a false alarm if you've already\n"
168 << "corrected other problems.\n";
172 cout << "\nProblem: The CRC for the backup GPT header is invalid. The backup GPT header\n"
173 << "may be corrupt. Consider using the main GPT header to rebuild the backup GPT\n"
174 << "header ('d' on the recovery & transformation menu). This report may be a false\n"
175 << "alarm if you've already corrected other problems.\n";
177 if (!secondPartsCrcOk) {
179 cout << "\nCaution: The CRC for the backup partition table is invalid. This table may\n"
180 << "be corrupt. This program will automatically create a new backup partition\n"
181 << "table when you save your partitions.\n";
184 // Now check that the main and backup headers both point to themselves....
185 if (mainHeader.currentLBA != 1) {
187 cout << "\nProblem: The main header's self-pointer doesn't point to itself. This problem\n"
188 << "is being automatically corrected, but it may be a symptom of more serious\n"
189 << "problems. Think carefully before saving changes with 'w' or using this disk.\n";
190 mainHeader.currentLBA = 1;
192 if (secondHeader.currentLBA != (diskSize - UINT64_C(1))) {
194 cout << "\nProblem: The secondary header's self-pointer indicates that it doesn't reside\n"
195 << "at the end of the disk. If you've added a disk to a RAID array, use the 'e'\n"
196 << "option on the experts' menu to adjust the secondary header's and partition\n"
197 << "table's locations.\n";
200 // Now check that critical main and backup GPT entries match each other
201 if (mainHeader.currentLBA != secondHeader.backupLBA) {
203 cout << "\nProblem: main GPT header's current LBA pointer (" << mainHeader.currentLBA
204 << ") doesn't\nmatch the backup GPT header's alternate LBA pointer("
205 << secondHeader.backupLBA << ").\n";
207 if (mainHeader.backupLBA != secondHeader.currentLBA) {
209 cout << "\nProblem: main GPT header's backup LBA pointer (" << mainHeader.backupLBA
210 << ") doesn't\nmatch the backup GPT header's current LBA pointer ("
211 << secondHeader.currentLBA << ").\n"
212 << "The 'e' option on the experts' menu may fix this problem.\n";
214 if (mainHeader.firstUsableLBA != secondHeader.firstUsableLBA) {
216 cout << "\nProblem: main GPT header's first usable LBA pointer (" << mainHeader.firstUsableLBA
217 << ") doesn't\nmatch the backup GPT header's first usable LBA pointer ("
218 << secondHeader.firstUsableLBA << ")\n";
220 if (mainHeader.lastUsableLBA != secondHeader.lastUsableLBA) {
222 cout << "\nProblem: main GPT header's last usable LBA pointer (" << mainHeader.lastUsableLBA
223 << ") doesn't\nmatch the backup GPT header's last usable LBA pointer ("
224 << secondHeader.lastUsableLBA << ")\n"
225 << "The 'e' option on the experts' menu can probably fix this problem.\n";
227 if ((mainHeader.diskGUID != secondHeader.diskGUID)) {
229 cout << "\nProblem: main header's disk GUID (" << mainHeader.diskGUID
230 << ") doesn't\nmatch the backup GPT header's disk GUID ("
231 << secondHeader.diskGUID << ")\n"
232 << "You should use the 'b' or 'd' option on the recovery & transformation menu to\n"
233 << "select one or the other header.\n";
235 if (mainHeader.numParts != secondHeader.numParts) {
237 cout << "\nProblem: main GPT header's number of partitions (" << mainHeader.numParts
238 << ") doesn't\nmatch the backup GPT header's number of partitions ("
239 << secondHeader.numParts << ")\n"
240 << "Resizing the partition table ('s' on the experts' menu) may help.\n";
242 if (mainHeader.sizeOfPartitionEntries != secondHeader.sizeOfPartitionEntries) {
244 cout << "\nProblem: main GPT header's size of partition entries ("
245 << mainHeader.sizeOfPartitionEntries << ") doesn't\n"
246 << "match the backup GPT header's size of partition entries ("
247 << secondHeader.sizeOfPartitionEntries << ")\n"
248 << "You should use the 'b' or 'd' option on the recovery & transformation menu to\n"
249 << "select one or the other header.\n";
252 // Now check for a few other miscellaneous problems...
253 // Check that the disk size will hold the data...
254 if (mainHeader.backupLBA >= diskSize) {
256 cout << "\nProblem: Disk is too small to hold all the data!\n"
257 << "(Disk size is " << diskSize << " sectors, needs to be "
258 << mainHeader.backupLBA + UINT64_C(1) << " sectors.)\n"
259 << "The 'e' option on the experts' menu may fix this problem.\n";
262 // Check for overlapping partitions....
263 problems += FindOverlaps();
265 // Check for insane partitions (start after end, hugely big, etc.)
266 problems += FindInsanePartitions();
268 // Check for mismatched MBR and GPT partitions...
269 problems += FindHybridMismatches();
271 // Check for MBR-specific problems....
272 problems += VerifyMBR();
274 // Verify that partitions don't run into GPT data areas....
275 problems += CheckGPTSize();
277 // Check that partitions are aligned on proper boundaries (for WD Advanced
278 // Format and similar disks)....
279 for (i = 0; i < numParts; i++) {
280 if ((partitions[i].IsUsed()) && (partitions[i].GetFirstLBA() % sectorAlignment) != 0) {
281 cout << "\nCaution: Partition " << i + 1 << " doesn't begin on a "
282 << sectorAlignment << "-sector boundary. This may\nresult "
283 << "in degraded performance on some modern (2009 and later) hard disks.\n";
288 cout << "\nConsult http://www.ibm.com/developerworks/linux/library/l-4kb-sector-disks/\n"
289 << "for information on disk alignment.\n";
291 // Now compute available space, but only if no problems found, since
292 // problems could affect the results
294 totalFree = FindFreeBlocks(&numSegments, &largestSegment);
295 cout << "\nNo problems found. " << totalFree << " free sectors ("
296 << BytesToIeee(totalFree, blockSize) << ") available in "
297 << numSegments << "\nsegments, the largest of which is "
298 << largestSegment << " (" << BytesToIeee(largestSegment, blockSize)
301 cout << "\nIdentified " << problems << " problems!\n";
305 } // GPTData::Verify()
307 // Checks to see if the GPT tables overrun existing partitions; if they
308 // do, issues a warning but takes no action. Returns number of problems
309 // detected (0 if OK, 1 to 2 if problems).
310 int GPTData::CheckGPTSize(void) {
311 uint64_t overlap, firstUsedBlock, lastUsedBlock;
315 // first, locate the first & last used blocks
316 firstUsedBlock = UINT64_MAX;
318 for (i = 0; i < numParts; i++) {
319 if (partitions[i].IsUsed()) {
320 if (partitions[i].GetFirstLBA() < firstUsedBlock)
321 firstUsedBlock = partitions[i].GetFirstLBA();
322 if (partitions[i].GetLastLBA() > lastUsedBlock) {
323 lastUsedBlock = partitions[i].GetLastLBA();
328 // If the disk size is 0 (the default), then it means that various
329 // variables aren't yet set, so the below tests will be useless;
330 // therefore we should skip everything
332 if (mainHeader.firstUsableLBA > firstUsedBlock) {
333 overlap = mainHeader.firstUsableLBA - firstUsedBlock;
334 cout << "Warning! Main partition table overlaps the first partition by "
335 << overlap << " blocks!\n";
336 if (firstUsedBlock > 2) {
337 cout << "Try reducing the partition table size by " << overlap * 4
338 << " entries.\n(Use the 's' item on the experts' menu.)\n";
340 cout << "You will need to delete this partition or resize it in another utility.\n";
343 } // Problem at start of disk
344 if (mainHeader.lastUsableLBA < lastUsedBlock) {
345 overlap = lastUsedBlock - mainHeader.lastUsableLBA;
346 cout << "\nWarning! Secondary partition table overlaps the last partition by\n"
347 << overlap << " blocks!\n";
348 if (lastUsedBlock > (diskSize - 2)) {
349 cout << "You will need to delete this partition or resize it in another utility.\n";
351 cout << "Try reducing the partition table size by " << overlap * 4
352 << " entries.\n(Use the 's' item on the experts' menu.)\n";
355 } // Problem at end of disk
356 } // if (diskSize != 0)
358 } // GPTData::CheckGPTSize()
360 // Check the validity of the GPT header. Returns 1 if the main header
361 // is valid, 2 if the backup header is valid, 3 if both are valid, and
362 // 0 if neither is valid. Note that this function checks the GPT signature,
363 // revision value, and CRCs in both headers.
364 int GPTData::CheckHeaderValidity(void) {
367 cout.setf(ios::uppercase);
370 // Note: failed GPT signature checks produce no error message because
371 // a message is displayed in the ReversePartitionBytes() function
372 if ((mainHeader.signature != GPT_SIGNATURE) || (!CheckHeaderCRC(&mainHeader, 1))) {
374 } else if ((mainHeader.revision != 0x00010000) && valid) {
376 cout << "Unsupported GPT version in main header; read 0x";
378 cout << hex << mainHeader.revision << ", should be\n0x";
380 cout << UINT32_C(0x00010000) << dec << "\n";
383 if ((secondHeader.signature != GPT_SIGNATURE) || (!CheckHeaderCRC(&secondHeader))) {
385 } else if ((secondHeader.revision != 0x00010000) && valid) {
387 cout << "Unsupported GPT version in backup header; read 0x";
389 cout << hex << secondHeader.revision << ", should be\n0x";
391 cout << UINT32_C(0x00010000) << dec << "\n";
394 // Check for an Apple disk signature
395 if (((mainHeader.signature << 32) == APM_SIGNATURE1) ||
396 (mainHeader.signature << 32) == APM_SIGNATURE2) {
397 apmFound = 1; // Will display warning message later
402 } // GPTData::CheckHeaderValidity()
404 // Check the header CRC to see if it's OK...
405 // Note: Must be called with header in platform-ordered byte order.
406 // Returns 1 if header's computed CRC matches the stored value, 0 if the
407 // computed and stored values don't match
408 int GPTData::CheckHeaderCRC(struct GPTHeader* header, int warn) {
409 uint32_t oldCRC, newCRC, hSize;
412 // Back up old header CRC and then blank it, since it must be 0 for
413 // computation to be valid
414 oldCRC = header->headerCRC;
415 header->headerCRC = UINT32_C(0);
417 hSize = header->headerSize;
419 if (IsLittleEndian() == 0)
420 ReverseHeaderBytes(header);
422 if ((hSize > blockSize) || (hSize < HEADER_SIZE)) {
424 cerr << "\aWarning! Header size is specified as " << hSize << ", which is invalid.\n";
425 cerr << "Setting the header size for CRC computation to " << HEADER_SIZE << "\n";
428 } else if ((hSize > sizeof(GPTHeader)) && warn) {
429 cout << "\aCaution! Header size for CRC check is " << hSize << ", which is greater than " << sizeof(GPTHeader) << ".\n";
430 cout << "If stray data exists after the header on the header sector, it will be ignored,\n"
431 << "which may result in a CRC false alarm.\n";
433 temp = new uint8_t[hSize];
435 memset(temp, 0, hSize);
436 if (hSize < sizeof(GPTHeader))
437 memcpy(temp, header, hSize);
439 memcpy(temp, header, sizeof(GPTHeader));
441 newCRC = chksum_crc32((unsigned char*) temp, hSize);
444 cerr << "Could not allocate memory in GPTData::CheckHeaderCRC()! Aborting!\n";
447 if (IsLittleEndian() == 0)
448 ReverseHeaderBytes(header);
449 header->headerCRC = oldCRC;
450 return (oldCRC == newCRC);
451 } // GPTData::CheckHeaderCRC()
453 // Recompute all the CRCs. Must be called before saving if any changes have
454 // been made. Must be called on platform-ordered data (this function reverses
455 // byte order and then undoes that reversal.)
456 void GPTData::RecomputeCRCs(void) {
458 int littleEndian = 1;
460 // If the header size is bigger than the GPT header data structure, reset it;
461 // otherwise, set both header sizes to whatever the main one is....
462 if (mainHeader.headerSize > sizeof(GPTHeader))
463 hSize = secondHeader.headerSize = mainHeader.headerSize = HEADER_SIZE;
465 hSize = secondHeader.headerSize = mainHeader.headerSize;
467 if ((littleEndian = IsLittleEndian()) == 0) {
468 ReversePartitionBytes();
469 ReverseHeaderBytes(&mainHeader);
470 ReverseHeaderBytes(&secondHeader);
473 // Compute CRC of partition tables & store in main and secondary headers
474 crc = chksum_crc32((unsigned char*) partitions, numParts * GPT_SIZE);
475 mainHeader.partitionEntriesCRC = crc;
476 secondHeader.partitionEntriesCRC = crc;
477 if (littleEndian == 0) {
478 ReverseBytes(&mainHeader.partitionEntriesCRC, 4);
479 ReverseBytes(&secondHeader.partitionEntriesCRC, 4);
482 // Zero out GPT headers' own CRCs (required for correct computation)
483 mainHeader.headerCRC = 0;
484 secondHeader.headerCRC = 0;
486 crc = chksum_crc32((unsigned char*) &mainHeader, hSize);
487 if (littleEndian == 0)
488 ReverseBytes(&crc, 4);
489 mainHeader.headerCRC = crc;
490 crc = chksum_crc32((unsigned char*) &secondHeader, hSize);
491 if (littleEndian == 0)
492 ReverseBytes(&crc, 4);
493 secondHeader.headerCRC = crc;
495 if (littleEndian == 0) {
496 ReverseHeaderBytes(&mainHeader);
497 ReverseHeaderBytes(&secondHeader);
498 ReversePartitionBytes();
500 } // GPTData::RecomputeCRCs()
502 // Rebuild the main GPT header, using the secondary header as a model.
503 // Typically called when the main header has been found to be corrupt.
504 void GPTData::RebuildMainHeader(void) {
505 mainHeader.signature = GPT_SIGNATURE;
506 mainHeader.revision = secondHeader.revision;
507 mainHeader.headerSize = secondHeader.headerSize;
508 mainHeader.headerCRC = UINT32_C(0);
509 mainHeader.reserved = secondHeader.reserved;
510 mainHeader.currentLBA = secondHeader.backupLBA;
511 mainHeader.backupLBA = secondHeader.currentLBA;
512 mainHeader.firstUsableLBA = secondHeader.firstUsableLBA;
513 mainHeader.lastUsableLBA = secondHeader.lastUsableLBA;
514 mainHeader.diskGUID = secondHeader.diskGUID;
515 mainHeader.partitionEntriesLBA = UINT64_C(2);
516 mainHeader.numParts = secondHeader.numParts;
517 mainHeader.sizeOfPartitionEntries = secondHeader.sizeOfPartitionEntries;
518 mainHeader.partitionEntriesCRC = secondHeader.partitionEntriesCRC;
519 memcpy(mainHeader.reserved2, secondHeader.reserved2, sizeof(mainHeader.reserved2));
520 mainCrcOk = secondCrcOk;
521 SetGPTSize(mainHeader.numParts, 0);
522 } // GPTData::RebuildMainHeader()
524 // Rebuild the secondary GPT header, using the main header as a model.
525 void GPTData::RebuildSecondHeader(void) {
526 secondHeader.signature = GPT_SIGNATURE;
527 secondHeader.revision = mainHeader.revision;
528 secondHeader.headerSize = mainHeader.headerSize;
529 secondHeader.headerCRC = UINT32_C(0);
530 secondHeader.reserved = mainHeader.reserved;
531 secondHeader.currentLBA = mainHeader.backupLBA;
532 secondHeader.backupLBA = mainHeader.currentLBA;
533 secondHeader.firstUsableLBA = mainHeader.firstUsableLBA;
534 secondHeader.lastUsableLBA = mainHeader.lastUsableLBA;
535 secondHeader.diskGUID = mainHeader.diskGUID;
536 secondHeader.partitionEntriesLBA = secondHeader.lastUsableLBA + UINT64_C(1);
537 secondHeader.numParts = mainHeader.numParts;
538 secondHeader.sizeOfPartitionEntries = mainHeader.sizeOfPartitionEntries;
539 secondHeader.partitionEntriesCRC = mainHeader.partitionEntriesCRC;
540 memcpy(secondHeader.reserved2, mainHeader.reserved2, sizeof(secondHeader.reserved2));
541 secondCrcOk = mainCrcOk;
542 SetGPTSize(secondHeader.numParts, 0);
543 } // GPTData::RebuildSecondHeader()
545 // Search for hybrid MBR entries that have no corresponding GPT partition.
546 // Returns number of such mismatches found
547 int GPTData::FindHybridMismatches(void) {
548 int i, found, numFound = 0;
550 uint64_t mbrFirst, mbrLast;
552 for (i = 0; i < 4; i++) {
553 if ((protectiveMBR.GetType(i) != 0xEE) && (protectiveMBR.GetType(i) != 0x00)) {
556 mbrFirst = (uint64_t) protectiveMBR.GetFirstSector(i);
557 mbrLast = mbrFirst + (uint64_t) protectiveMBR.GetLength(i) - UINT64_C(1);
559 if ((partitions[j].GetFirstLBA() == mbrFirst) &&
560 (partitions[j].GetLastLBA() == mbrLast) && (partitions[j].IsUsed()))
563 } while ((!found) && (j < numParts));
566 cout << "\nWarning! Mismatched GPT and MBR partition! MBR partition "
567 << i + 1 << ", of type 0x";
569 cout.setf(ios::uppercase);
571 cout << hex << (int) protectiveMBR.GetType(i) << ",\n"
572 << "has no corresponding GPT partition! You may continue, but this condition\n"
573 << "might cause data loss in the future!\a\n" << dec;
579 } // GPTData::FindHybridMismatches
581 // Find overlapping partitions and warn user about them. Returns number of
582 // overlapping partitions.
583 // Returns number of overlapping segments found.
584 int GPTData::FindOverlaps(void) {
588 for (i = 1; i < numParts; i++) {
589 for (j = 0; j < i; j++) {
590 if ((partitions[i].IsUsed()) && (partitions[j].IsUsed()) &&
591 (partitions[i].DoTheyOverlap(partitions[j]))) {
593 cout << "\nProblem: partitions " << i + 1 << " and " << j + 1 << " overlap:\n";
594 cout << " Partition " << i + 1 << ": " << partitions[i].GetFirstLBA()
595 << " to " << partitions[i].GetLastLBA() << "\n";
596 cout << " Partition " << j + 1 << ": " << partitions[j].GetFirstLBA()
597 << " to " << partitions[j].GetLastLBA() << "\n";
602 } // GPTData::FindOverlaps()
604 // Find partitions that are insane -- they start after they end or are too
605 // big for the disk. (The latter should duplicate detection of overlaps
606 // with GPT backup data structures, but better to err on the side of
607 // redundant tests than to miss something....)
608 // Returns number of problems found.
609 int GPTData::FindInsanePartitions(void) {
613 for (i = 0; i < numParts; i++) {
614 if (partitions[i].IsUsed()) {
615 if (partitions[i].GetFirstLBA() > partitions[i].GetLastLBA()) {
617 cout << "\nProblem: partition " << i + 1 << " ends before it begins.\n";
619 if (partitions[i].GetLastLBA() >= diskSize) {
621 cout << "\nProblem: partition " << i + 1 << " is too big for the disk.\n";
626 } // GPTData::FindInsanePartitions(void)
629 /******************************************************************
631 * Begin functions that load data from disk or save data to disk. *
633 ******************************************************************/
635 // Change the filename associated with the GPT. Used for duplicating
636 // the partition table to a new disk and saving backups.
637 // Returns 1 on success, 0 on failure.
638 int GPTData::SetDisk(const string & deviceFilename) {
641 device = deviceFilename;
642 if (allOK && myDisk.OpenForRead(deviceFilename)) {
643 // store disk information....
644 diskSize = myDisk.DiskSize(&err);
645 blockSize = (uint32_t) myDisk.GetBlockSize();
647 protectiveMBR.SetDisk(&myDisk);
648 protectiveMBR.SetDiskSize(diskSize);
649 protectiveMBR.SetBlockSize(blockSize);
651 } // GPTData::SetDisk()
653 // Scan for partition data. This function loads the MBR data (regular MBR or
654 // protective MBR) and loads BSD disklabel data (which is probably invalid).
655 // It also looks for APM data, forces a load of GPT data, and summarizes
657 void GPTData::PartitionScan(void) {
658 BSDData bsdDisklabel;
660 // Read the MBR & check for BSD disklabel
661 protectiveMBR.ReadMBRData(&myDisk);
662 bsdDisklabel.ReadBSDData(&myDisk, 0, diskSize - 1);
664 // Load the GPT data, whether or not it's valid
668 cout << "Partition table scan:\n";
669 protectiveMBR.ShowState();
670 bsdDisklabel.ShowState();
671 ShowAPMState(); // Show whether there's an Apple Partition Map present
672 ShowGPTState(); // Show GPT status
677 cout << "\n*******************************************************************\n"
678 << "This disk appears to contain an Apple-format (APM) partition table!\n";
680 cout << "It will be destroyed if you continue!\n";
682 cout << "*******************************************************************\n\n\a";
684 } // GPTData::PartitionScan()
686 // Read GPT data from a disk.
687 int GPTData::LoadPartitions(const string & deviceFilename) {
688 BSDData bsdDisklabel;
690 MBRValidity mbrState;
692 if (myDisk.OpenForRead(deviceFilename)) {
693 err = myDisk.OpenForWrite(deviceFilename);
694 if ((err == 0) && (!justLooking)) {
695 cout << "\aNOTE: Write test failed with error number " << errno
696 << ". It will be impossible to save\nchanges to this disk's partition table!\n";
697 #if defined (__FreeBSD__) || defined (__FreeBSD_kernel__)
698 cout << "You may be able to enable writes by exiting this program, typing\n"
699 << "'sysctl kern.geom.debugflags=16' at a shell prompt, and re-running this\n"
704 myDisk.Close(); // Close and re-open read-only in case of bugs
705 } else allOK = 0; // if
707 if (allOK && myDisk.OpenForRead(deviceFilename)) {
708 // store disk information....
709 diskSize = myDisk.DiskSize(&err);
710 blockSize = (uint32_t) myDisk.GetBlockSize();
711 device = deviceFilename;
712 PartitionScan(); // Check for partition types, load GPT, & print summary
714 whichWasUsed = UseWhichPartitions();
715 switch (whichWasUsed) {
720 bsdDisklabel.ReadBSDData(&myDisk, 0, diskSize - 1);
721 // bsdDisklabel.DisplayBSDData();
723 protectiveMBR.MakeProtectiveMBR(1); // clear boot area (option 1)
724 XFormDisklabel(&bsdDisklabel);
727 mbrState = protectiveMBR.GetValidity();
728 if ((mbrState == invalid) || (mbrState == mbr))
729 protectiveMBR.MakeProtectiveMBR();
733 protectiveMBR.MakeProtectiveMBR();
737 cerr << "Invalid partition data!\n";
749 } // GPTData::LoadPartitions()
751 // Loads the GPT, as much as possible. Returns 1 if this seems to have
752 // succeeded, 0 if there are obvious problems....
753 int GPTData::ForceLoadGPTData(void) {
754 int allOK, validHeaders, loadedTable = 1;
756 allOK = LoadHeader(&mainHeader, myDisk, 1, &mainCrcOk);
758 if (mainCrcOk && (mainHeader.backupLBA < diskSize)) {
759 allOK = LoadHeader(&secondHeader, myDisk, mainHeader.backupLBA, &secondCrcOk) && allOK;
761 allOK = LoadHeader(&secondHeader, myDisk, diskSize - UINT64_C(1), &secondCrcOk) && allOK;
762 if (mainCrcOk && (mainHeader.backupLBA >= diskSize))
763 cout << "Warning! Disk size is smaller than the main header indicates! Loading\n"
764 << "secondary header from the last sector of the disk! You should use 'v' to\n"
765 << "verify disk integrity, and perhaps options on the experts' menu to repair\n"
771 // Return valid headers code: 0 = both headers bad; 1 = main header
772 // good, backup bad; 2 = backup header good, main header bad;
773 // 3 = both headers good. Note these codes refer to valid GPT
774 // signatures, version numbers, and CRCs.
775 validHeaders = CheckHeaderValidity();
777 // Read partitions (from primary array)
778 if (validHeaders > 0) { // if at least one header is OK....
779 // GPT appears to be valid....
782 // We're calling the GPT valid, but there's a possibility that one
783 // of the two headers is corrupt. If so, use the one that seems to
784 // be in better shape to regenerate the bad one
785 if (validHeaders == 1) { // valid main header, invalid backup header
786 cerr << "\aCaution: invalid backup GPT header, but valid main header; regenerating\n"
787 << "backup header from main header.\n\n";
788 RebuildSecondHeader();
790 secondCrcOk = mainCrcOk; // Since regenerated, use CRC validity of main
791 } else if (validHeaders == 2) { // valid backup header, invalid main header
792 cerr << "\aCaution: invalid main GPT header, but valid backup; regenerating main header\n"
793 << "from backup!\n\n";
796 mainCrcOk = secondCrcOk; // Since copied, use CRC validity of backup
799 // Figure out which partition table to load....
800 // Load the main partition table, since either its header's CRC is OK or the
801 // backup header's CRC is not OK....
802 if (mainCrcOk || !secondCrcOk) {
803 if (LoadMainTable() == 0)
805 } else { // bad main header CRC and backup header CRC is OK
807 if (LoadSecondTableAsMain()) {
809 cerr << "\aWarning: Invalid CRC on main header data; loaded backup partition table.\n";
810 } else { // backup table bad, bad main header CRC, but try main table in desperation....
811 if (LoadMainTable() == 0) {
814 cerr << "\a\aWarning! Unable to load either main or backup partition table!\n";
816 } // if/else (LoadSecondTableAsMain())
817 } // if/else (load partition table)
819 if (loadedTable == 1)
820 secondPartsCrcOk = CheckTable(&secondHeader);
821 else if (loadedTable == 2)
822 mainPartsCrcOk = CheckTable(&mainHeader);
824 mainPartsCrcOk = secondPartsCrcOk = 0;
826 // Problem with main partition table; if backup is OK, use it instead....
827 if (secondPartsCrcOk && secondCrcOk && !mainPartsCrcOk) {
829 allOK = allOK && LoadSecondTableAsMain();
830 mainPartsCrcOk = 0; // LoadSecondTableAsMain() resets this, so re-flag as bad
831 cerr << "\aWarning! Main partition table CRC mismatch! Loaded backup "
832 << "partition table\ninstead of main partition table!\n\n";
835 // Check for valid CRCs and warn if there are problems
836 if ((mainCrcOk == 0) || (secondCrcOk == 0) || (mainPartsCrcOk == 0) ||
837 (secondPartsCrcOk == 0)) {
838 cerr << "Warning! One or more CRCs don't match. You should repair the disk!\n\n";
845 } // GPTData::ForceLoadGPTData()
847 // Loads the partition table pointed to by the main GPT header. The
848 // main GPT header in memory MUST be valid for this call to do anything
850 // Returns 1 on success, 0 on failure. CRC errors do NOT count as failure.
851 int GPTData::LoadMainTable(void) {
852 return LoadPartitionTable(mainHeader, myDisk);
853 } // GPTData::LoadMainTable()
855 // Load the second (backup) partition table as the primary partition
856 // table. Used in repair functions, and when starting up if the main
857 // partition table is damaged.
858 // Returns 1 on success, 0 on failure. CRC errors do NOT count as failure.
859 int GPTData::LoadSecondTableAsMain(void) {
860 return LoadPartitionTable(secondHeader, myDisk);
861 } // GPTData::LoadSecondTableAsMain()
863 // Load a single GPT header (main or backup) from the specified disk device and
864 // sector. Applies byte-order corrections on big-endian platforms. Sets crcOk
865 // value appropriately.
866 // Returns 1 on success, 0 on failure. Note that CRC errors do NOT qualify as
868 int GPTData::LoadHeader(struct GPTHeader *header, DiskIO & disk, uint64_t sector, int *crcOk) {
870 GPTHeader tempHeader;
873 if (disk.Read(&tempHeader, 512) != 512) {
874 cerr << "Warning! Read error " << errno << "; strange behavior now likely!\n";
878 // Reverse byte order, if necessary
879 if (IsLittleEndian() == 0) {
880 ReverseHeaderBytes(&tempHeader);
882 *crcOk = CheckHeaderCRC(&tempHeader);
884 if (allOK && (numParts != tempHeader.numParts) && *crcOk) {
885 allOK = SetGPTSize(tempHeader.numParts, 0);
888 *header = tempHeader;
890 } // GPTData::LoadHeader
892 // Load a partition table (either main or secondary) from the specified disk,
893 // using header as a reference for what to load. If sector != 0 (the default
894 // is 0), loads from the specified sector; otherwise loads from the sector
895 // indicated in header.
896 // Returns 1 on success, 0 on failure. CRC errors do NOT count as failure.
897 int GPTData::LoadPartitionTable(const struct GPTHeader & header, DiskIO & disk, uint64_t sector) {
898 uint32_t sizeOfParts, newCRC;
901 if (disk.OpenForRead()) {
903 retval = disk.Seek(header.partitionEntriesLBA);
905 retval = disk.Seek(sector);
908 retval = SetGPTSize(header.numParts, 0);
910 sizeOfParts = header.numParts * header.sizeOfPartitionEntries;
911 if (disk.Read(partitions, sizeOfParts) != (int) sizeOfParts) {
912 cerr << "Warning! Read error " << errno << "! Misbehavior now likely!\n";
915 newCRC = chksum_crc32((unsigned char*) partitions, sizeOfParts);
916 mainPartsCrcOk = secondPartsCrcOk = (newCRC == header.partitionEntriesCRC);
917 if (IsLittleEndian() == 0)
918 ReversePartitionBytes();
919 if (!mainPartsCrcOk) {
920 cout << "Caution! After loading partitions, the CRC doesn't check out!\n";
923 cerr << "Error! Couldn't seek to partition table!\n";
926 cerr << "Error! Couldn't open device " << device
927 << " when reading partition table!\n";
931 } // GPTData::LoadPartitionsTable()
933 // Check the partition table pointed to by header, but don't keep it
935 // Returns 1 if the CRC is OK & this table matches the one already in memory,
936 // 0 if not or if there was a read error.
937 int GPTData::CheckTable(struct GPTHeader *header) {
938 uint32_t sizeOfParts, newCRC;
939 GPTPart *partsToCheck;
940 GPTHeader *otherHeader;
943 // Load partition table into temporary storage to check
944 // its CRC and store the results, then discard this temporary
945 // storage, since we don't use it in any but recovery operations
946 if (myDisk.Seek(header->partitionEntriesLBA)) {
947 partsToCheck = new GPTPart[header->numParts];
948 sizeOfParts = header->numParts * header->sizeOfPartitionEntries;
949 if (partsToCheck == NULL) {
950 cerr << "Could not allocate memory in GPTData::CheckTable()! Terminating!\n";
953 if (myDisk.Read(partsToCheck, sizeOfParts) != (int) sizeOfParts) {
954 cerr << "Warning! Error " << errno << " reading partition table for CRC check!\n";
956 newCRC = chksum_crc32((unsigned char*) partsToCheck, sizeOfParts);
957 allOK = (newCRC == header->partitionEntriesCRC);
958 if (header == &mainHeader)
959 otherHeader = &secondHeader;
961 otherHeader = &mainHeader;
962 if (newCRC != otherHeader->partitionEntriesCRC) {
963 cerr << "Warning! Main and backup partition tables differ! Use the 'c' and 'e' options\n"
964 << "on the recovery & transformation menu to examine the two tables.\n\n";
968 delete[] partsToCheck;
971 } // GPTData::CheckTable()
973 // Writes GPT (and protective MBR) to disk. If quiet==1, moves the second
974 // header later on the disk without asking for permission, if necessary, and
975 // doesn't confirm the operation before writing. If quiet==0, asks permission
976 // before moving the second header and asks for final confirmation of any
978 // Returns 1 on successful write, 0 if there was a problem.
979 int GPTData::SaveGPTData(int quiet) {
980 int allOK = 1, syncIt = 1;
983 // First do some final sanity checks....
985 // This test should only fail on read-only disks....
987 cout << "The justLooking flag is set. This probably means you can't write to the disk.\n";
991 // Check that disk is really big enough to handle the second header...
992 if (mainHeader.backupLBA >= diskSize) {
993 cerr << "Caution! Secondary header was placed beyond the disk's limits! Moving the\n"
994 << "header, but other problems may occur!\n";
995 MoveSecondHeaderToEnd();
998 // Is there enough space to hold the GPT headers and partition tables,
999 // given the partition sizes?
1000 if (CheckGPTSize() > 0) {
1004 // Check that second header is properly placed. Warn and ask if this should
1005 // be corrected if the test fails....
1006 if (mainHeader.backupLBA < (diskSize - UINT64_C(1))) {
1008 cout << "Warning! Secondary header is placed too early on the disk! Do you want to\n"
1009 << "correct this problem? ";
1010 if (GetYN() == 'Y') {
1011 MoveSecondHeaderToEnd();
1012 cout << "Have moved second header and partition table to correct location.\n";
1014 cout << "Have not corrected the problem. Strange problems may occur in the future!\n";
1015 } // if correction requested
1016 } else { // Go ahead and do correction automatically
1017 MoveSecondHeaderToEnd();
1021 // Check for overlapping or insane partitions....
1022 if ((FindOverlaps() > 0) || (FindInsanePartitions() > 0)) {
1024 cerr << "Aborting write operation!\n";
1027 // Check for mismatched MBR and GPT data, but let it pass if found
1028 // (function displays warning message)
1029 FindHybridMismatches();
1033 if ((allOK) && (!quiet)) {
1034 cout << "\nFinal checks complete. About to write GPT data. THIS WILL OVERWRITE EXISTING\n"
1035 << "PARTITIONS!!\n\nDo you want to proceed? ";
1037 if (answer == 'Y') {
1038 cout << "OK; writing new GUID partition table (GPT) to " << myDisk.GetName() << ".\n";
1046 if (myDisk.OpenForWrite()) {
1047 // As per UEFI specs, write the secondary table and GPT first....
1048 allOK = SavePartitionTable(myDisk, secondHeader.partitionEntriesLBA);
1050 cerr << "Unable to save backup partition table! Perhaps the 'e' option on the experts'\n"
1051 << "menu will resolve this problem.\n";
1055 // Now write the secondary GPT header...
1056 allOK = allOK && SaveHeader(&secondHeader, myDisk, mainHeader.backupLBA);
1058 // Now write the main partition tables...
1059 allOK = allOK && SavePartitionTable(myDisk, mainHeader.partitionEntriesLBA);
1061 // Now write the main GPT header...
1062 allOK = allOK && SaveHeader(&mainHeader, myDisk, 1);
1064 // To top it off, write the protective MBR...
1065 allOK = allOK && protectiveMBR.WriteMBRData(&myDisk);
1067 // re-read the partition table
1068 // Note: Done even if some write operations failed, but not if all of them failed.
1069 // Done this way because I've received one problem report from a user one whose
1070 // system the MBR write failed but everything else was OK (on a GPT disk under
1071 // Windows), and the failure to sync therefore caused Windows to restore the
1072 // original partition table from its cache. OTOH, such restoration might be
1073 // desirable if the error occurs later; but that seems unlikely unless the initial
1078 if (allOK) { // writes completed OK
1079 cout << "The operation has completed successfully.\n";
1081 cerr << "Warning! An error was reported when writing the partition table! This error\n"
1082 << "MIGHT be harmless, or the disk might be damaged! Checking it is advisable.\n";
1087 cerr << "Unable to open device '" << myDisk.GetName() << "' for writing! Errno is "
1088 << errno << "! Aborting write!\n";
1092 cout << "Aborting write of new partition table.\n";
1096 } // GPTData::SaveGPTData()
1098 // Save GPT data to a backup file. This function does much less error
1099 // checking than SaveGPTData(). It can therefore preserve many types of
1100 // corruption for later analysis; however, it preserves only the MBR,
1101 // the main GPT header, the backup GPT header, and the main partition
1102 // table; it discards the backup partition table, since it should be
1103 // identical to the main partition table on healthy disks.
1104 int GPTData::SaveGPTBackup(const string & filename) {
1108 if (backupFile.OpenForWrite(filename)) {
1109 // Recomputing the CRCs is likely to alter them, which could be bad
1110 // if the intent is to save a potentially bad GPT for later analysis;
1111 // but if we don't do this, we get bogus errors when we load the
1112 // backup. I'm favoring misses over false alarms....
1115 protectiveMBR.WriteMBRData(&backupFile);
1116 protectiveMBR.SetDisk(&myDisk);
1119 // MBR write closed disk, so re-open and seek to end....
1120 backupFile.OpenForWrite();
1121 allOK = SaveHeader(&mainHeader, backupFile, 1);
1125 allOK = SaveHeader(&secondHeader, backupFile, 2);
1128 allOK = SavePartitionTable(backupFile, 3);
1130 if (allOK) { // writes completed OK
1131 cout << "The operation has completed successfully.\n";
1133 cerr << "Warning! An error was reported when writing the backup file.\n"
1134 << "It may not be usable!\n";
1138 cerr << "Unable to open file '" << filename << "' for writing! Aborting!\n";
1142 } // GPTData::SaveGPTBackup()
1144 // Write a GPT header (main or backup) to the specified sector. Used by both
1145 // the SaveGPTData() and SaveGPTBackup() functions.
1146 // Should be passed an architecture-appropriate header (DO NOT call
1147 // ReverseHeaderBytes() on the header before calling this function)
1148 // Returns 1 on success, 0 on failure
1149 int GPTData::SaveHeader(struct GPTHeader *header, DiskIO & disk, uint64_t sector) {
1150 int littleEndian, allOK = 1;
1152 littleEndian = IsLittleEndian();
1154 ReverseHeaderBytes(header);
1155 if (disk.Seek(sector)) {
1156 if (disk.Write(header, 512) == -1)
1158 } else allOK = 0; // if (disk.Seek()...)
1160 ReverseHeaderBytes(header);
1162 } // GPTData::SaveHeader()
1164 // Save the partitions to the specified sector. Used by both the SaveGPTData()
1165 // and SaveGPTBackup() functions.
1166 // Should be passed an architecture-appropriate header (DO NOT call
1167 // ReverseHeaderBytes() on the header before calling this function)
1168 // Returns 1 on success, 0 on failure
1169 int GPTData::SavePartitionTable(DiskIO & disk, uint64_t sector) {
1170 int littleEndian, allOK = 1;
1172 littleEndian = IsLittleEndian();
1173 if (disk.Seek(sector)) {
1175 ReversePartitionBytes();
1176 if (disk.Write(partitions, mainHeader.sizeOfPartitionEntries * numParts) == -1)
1179 ReversePartitionBytes();
1180 } else allOK = 0; // if (myDisk.Seek()...)
1182 } // GPTData::SavePartitionTable()
1184 // Load GPT data from a backup file created by SaveGPTBackup(). This function
1185 // does minimal error checking. It returns 1 if it completed successfully,
1186 // 0 if there was a problem. In the latter case, it creates a new empty
1187 // set of partitions.
1188 int GPTData::LoadGPTBackup(const string & filename) {
1189 int allOK = 1, val, err;
1190 int shortBackup = 0;
1193 if (backupFile.OpenForRead(filename)) {
1194 // Let the MBRData class load the saved MBR...
1195 protectiveMBR.ReadMBRData(&backupFile, 0); // 0 = don't check block size
1196 protectiveMBR.SetDisk(&myDisk);
1198 LoadHeader(&mainHeader, backupFile, 1, &mainCrcOk);
1200 // Check backup file size and rebuild second header if file is right
1201 // size to be direct dd copy of MBR, main header, and main partition
1202 // table; if other size, treat it like a GPT fdisk-generated backup
1204 shortBackup = ((backupFile.DiskSize(&err) * backupFile.GetBlockSize()) ==
1205 (mainHeader.numParts * mainHeader.sizeOfPartitionEntries) + 1024);
1207 RebuildSecondHeader();
1208 secondCrcOk = mainCrcOk;
1210 LoadHeader(&secondHeader, backupFile, 2, &secondCrcOk);
1213 // Return valid headers code: 0 = both headers bad; 1 = main header
1214 // good, backup bad; 2 = backup header good, main header bad;
1215 // 3 = both headers good. Note these codes refer to valid GPT
1216 // signatures and version numbers; more subtle problems will elude
1218 if ((val = CheckHeaderValidity()) > 0) {
1219 if (val == 2) { // only backup header seems to be good
1220 SetGPTSize(secondHeader.numParts, 0);
1221 } else { // main header is OK
1222 SetGPTSize(mainHeader.numParts, 0);
1225 if (secondHeader.currentLBA != diskSize - UINT64_C(1)) {
1226 cout << "Warning! Current disk size doesn't match that of the backup!\n"
1227 << "Adjusting sizes to match, but subsequent problems are possible!\n";
1228 MoveSecondHeaderToEnd();
1231 if (!LoadPartitionTable(mainHeader, backupFile, (uint64_t) (3 - shortBackup)))
1232 cerr << "Warning! Read error " << errno
1233 << " loading partition table; strange behavior now likely!\n";
1237 // Something went badly wrong, so blank out partitions
1239 cerr << "Improper backup file! Clearing all partition data!\n";
1241 protectiveMBR.MakeProtectiveMBR();
1245 cerr << "Unable to open file '" << filename << "' for reading! Aborting!\n";
1249 } // GPTData::LoadGPTBackup()
1251 int GPTData::SaveMBR(void) {
1252 return protectiveMBR.WriteMBRData(&myDisk);
1253 } // GPTData::SaveMBR()
1255 // This function destroys the on-disk GPT structures, but NOT the on-disk
1257 // Returns 1 if the operation succeeds, 0 if not.
1258 int GPTData::DestroyGPT(void) {
1259 int sum, tableSize, allOK = 1;
1260 uint8_t blankSector[512];
1261 uint8_t* emptyTable;
1263 memset(blankSector, 0, sizeof(blankSector));
1265 if (myDisk.OpenForWrite()) {
1266 if (!myDisk.Seek(mainHeader.currentLBA))
1268 if (myDisk.Write(blankSector, 512) != 512) { // blank it out
1269 cerr << "Warning! GPT main header not overwritten! Error is " << errno << "\n";
1272 if (!myDisk.Seek(mainHeader.partitionEntriesLBA))
1274 tableSize = numParts * mainHeader.sizeOfPartitionEntries;
1275 emptyTable = new uint8_t[tableSize];
1276 if (emptyTable == NULL) {
1277 cerr << "Could not allocate memory in GPTData::DestroyGPT()! Terminating!\n";
1280 memset(emptyTable, 0, tableSize);
1282 sum = myDisk.Write(emptyTable, tableSize);
1283 if (sum != tableSize) {
1284 cerr << "Warning! GPT main partition table not overwritten! Error is " << errno << "\n";
1286 } // if write failed
1288 if (!myDisk.Seek(secondHeader.partitionEntriesLBA))
1291 sum = myDisk.Write(emptyTable, tableSize);
1292 if (sum != tableSize) {
1293 cerr << "Warning! GPT backup partition table not overwritten! Error is "
1296 } // if wrong size written
1298 if (!myDisk.Seek(secondHeader.currentLBA))
1301 if (myDisk.Write(blankSector, 512) != 512) { // blank it out
1302 cerr << "Warning! GPT backup header not overwritten! Error is " << errno << "\n";
1308 cout << "GPT data structures destroyed! You may now partition the disk using fdisk or\n"
1309 << "other utilities.\n";
1310 delete[] emptyTable;
1312 cerr << "Problem opening '" << device << "' for writing! Program will now terminate.\n";
1313 } // if/else (fd != -1)
1315 } // GPTDataTextUI::DestroyGPT()
1317 // Wipe MBR data from the disk (zero it out completely)
1318 // Returns 1 on success, 0 on failure.
1319 int GPTData::DestroyMBR(void) {
1321 uint8_t blankSector[512];
1323 memset(blankSector, 0, sizeof(blankSector));
1325 allOK = myDisk.OpenForWrite() && myDisk.Seek(0) && (myDisk.Write(blankSector, 512) == 512);
1328 cerr << "Warning! MBR not overwritten! Error is " << errno << "!\n";
1330 } // GPTData::DestroyMBR(void)
1332 // Tell user whether Apple Partition Map (APM) was discovered....
1333 void GPTData::ShowAPMState(void) {
1335 cout << " APM: present\n";
1337 cout << " APM: not present\n";
1338 } // GPTData::ShowAPMState()
1340 // Tell user about the state of the GPT data....
1341 void GPTData::ShowGPTState(void) {
1344 cout << " GPT: not present\n";
1347 cout << " GPT: present\n";
1350 cout << " GPT: damaged\n";
1353 cout << "\a GPT: unknown -- bug!\n";
1356 } // GPTData::ShowGPTState()
1358 // Display the basic GPT data
1359 void GPTData::DisplayGPTData(void) {
1361 uint64_t temp, totalFree;
1363 cout << "Disk " << device << ": " << diskSize << " sectors, "
1364 << BytesToIeee(diskSize, blockSize) << "\n";
1365 cout << "Logical sector size: " << blockSize << " bytes\n";
1366 cout << "Disk identifier (GUID): " << mainHeader.diskGUID << "\n";
1367 cout << "Partition table holds up to " << numParts << " entries\n";
1368 cout << "First usable sector is " << mainHeader.firstUsableLBA
1369 << ", last usable sector is " << mainHeader.lastUsableLBA << "\n";
1370 totalFree = FindFreeBlocks(&i, &temp);
1371 cout << "Partitions will be aligned on " << sectorAlignment << "-sector boundaries\n";
1372 cout << "Total free space is " << totalFree << " sectors ("
1373 << BytesToIeee(totalFree, blockSize) << ")\n";
1374 cout << "\nNumber Start (sector) End (sector) Size Code Name\n";
1375 for (i = 0; i < numParts; i++) {
1376 partitions[i].ShowSummary(i, blockSize);
1378 } // GPTData::DisplayGPTData()
1380 // Show detailed information on the specified partition
1381 void GPTData::ShowPartDetails(uint32_t partNum) {
1382 if (!IsFreePartNum(partNum)) {
1383 partitions[partNum].ShowDetails(blockSize);
1385 cout << "Partition #" << partNum + 1 << " does not exist.";
1387 } // GPTData::ShowPartDetails()
1389 /**************************************************************************
1391 * Partition table transformation functions (MBR or BSD disklabel to GPT) *
1392 * (some of these functions may require user interaction) *
1394 **************************************************************************/
1396 // Examines the MBR & GPT data to determine which set of data to use: the
1397 // MBR (use_mbr), the GPT (use_gpt), the BSD disklabel (use_bsd), or create
1398 // a new set of partitions (use_new). A return value of use_abort indicates
1399 // that this function couldn't determine what to do. Overriding functions
1400 // in derived classes may ask users questions in such cases.
1401 WhichToUse GPTData::UseWhichPartitions(void) {
1402 WhichToUse which = use_new;
1403 MBRValidity mbrState;
1405 mbrState = protectiveMBR.GetValidity();
1407 if ((state == gpt_invalid) && ((mbrState == mbr) || (mbrState == hybrid))) {
1408 cout << "\n***************************************************************\n"
1409 << "Found invalid GPT and valid MBR; converting MBR to GPT format.\n";
1411 cout << "\aTHIS OPERATION IS POTENTIALLY DESTRUCTIVE! Exit by typing 'q' if\n"
1412 << "you don't want to convert your MBR partitions to GPT format!\n";
1414 cout << "***************************************************************\n\n";
1418 if ((state == gpt_invalid) && bsdFound) {
1419 cout << "\n**********************************************************************\n"
1420 << "Found invalid GPT and valid BSD disklabel; converting BSD disklabel\n"
1421 << "to GPT format.";
1422 if ((!justLooking) && (!beQuiet)) {
1423 cout << "\a THIS OPERATION IS POTENTIALLY DESTRUCTIVE! Your first\n"
1424 << "BSD partition will likely be unusable. Exit by typing 'q' if you don't\n"
1425 << "want to convert your BSD partitions to GPT format!";
1427 cout << "\n**********************************************************************\n\n";
1431 if ((state == gpt_valid) && (mbrState == gpt)) {
1434 cout << "Found valid GPT with protective MBR; using GPT.\n";
1436 if ((state == gpt_valid) && (mbrState == hybrid)) {
1439 cout << "Found valid GPT with hybrid MBR; using GPT.\n";
1441 if ((state == gpt_valid) && (mbrState == invalid)) {
1442 cout << "\aFound valid GPT with corrupt MBR; using GPT and will write new\n"
1443 << "protective MBR on save.\n";
1446 if ((state == gpt_valid) && (mbrState == mbr)) {
1450 if (state == gpt_corrupt) {
1451 if (mbrState == gpt) {
1452 cout << "\a\a****************************************************************************\n"
1453 << "Caution: Found protective or hybrid MBR and corrupt GPT. Using GPT, but disk\n"
1454 << "verification and recovery are STRONGLY recommended.\n"
1455 << "****************************************************************************\n";
1459 } // if/else MBR says disk is GPT
1462 if (which == use_new)
1463 cout << "Creating new GPT entries.\n";
1466 } // UseWhichPartitions()
1468 // Convert MBR partition table into GPT form.
1469 void GPTData::XFormPartitions(void) {
1470 int i, numToConvert;
1473 // Clear out old data & prepare basics....
1476 // Convert the smaller of the # of GPT or MBR partitions
1477 if (numParts > MAX_MBR_PARTS)
1478 numToConvert = MAX_MBR_PARTS;
1480 numToConvert = numParts;
1482 for (i = 0; i < numToConvert; i++) {
1483 origType = protectiveMBR.GetType(i);
1484 // don't waste CPU time trying to convert extended, hybrid protective, or
1485 // null (non-existent) partitions
1486 if ((origType != 0x05) && (origType != 0x0f) && (origType != 0x85) &&
1487 (origType != 0x00) && (origType != 0xEE))
1488 partitions[i] = protectiveMBR.AsGPT(i);
1491 // Convert MBR into protective MBR
1492 protectiveMBR.MakeProtectiveMBR();
1494 // Record that all original CRCs were OK so as not to raise flags
1495 // when doing a disk verification
1496 mainCrcOk = secondCrcOk = mainPartsCrcOk = secondPartsCrcOk = 1;
1497 } // GPTData::XFormPartitions()
1499 // Transforms BSD disklabel on the specified partition (numbered from 0).
1500 // If an invalid partition number is given, the program does nothing.
1501 // Returns the number of new partitions created.
1502 int GPTData::XFormDisklabel(uint32_t partNum) {
1504 int goOn = 1, numDone = 0;
1507 if (GetPartRange(&low, &high) == 0) {
1509 cout << "No partitions!\n";
1511 if (partNum > high) {
1513 cout << "Specified partition is invalid!\n";
1516 // If all is OK, read the disklabel and convert it.
1518 goOn = disklabel.ReadBSDData(&myDisk, partitions[partNum].GetFirstLBA(),
1519 partitions[partNum].GetLastLBA());
1520 if ((goOn) && (disklabel.IsDisklabel())) {
1521 numDone = XFormDisklabel(&disklabel);
1523 cout << "Converted 1 BSD partition.\n";
1525 cout << "Converted " << numDone << " BSD partitions.\n";
1527 cout << "Unable to convert partitions! Unrecognized BSD disklabel.\n";
1530 if (numDone > 0) { // converted partitions; delete carrier
1531 partitions[partNum].BlankPartition();
1534 } // GPTData::XFormDisklabel(uint32_t i)
1536 // Transform the partitions on an already-loaded BSD disklabel...
1537 int GPTData::XFormDisklabel(BSDData* disklabel) {
1538 int i, partNum = 0, numDone = 0;
1540 if (disklabel->IsDisklabel()) {
1541 for (i = 0; i < disklabel->GetNumParts(); i++) {
1542 partNum = FindFirstFreePart();
1544 partitions[partNum] = disklabel->AsGPT(i);
1545 if (partitions[partNum].IsUsed())
1550 cerr << "Warning! Too many partitions to convert!\n";
1553 // Record that all original CRCs were OK so as not to raise flags
1554 // when doing a disk verification
1555 mainCrcOk = secondCrcOk = mainPartsCrcOk = secondPartsCrcOk = 1;
1558 } // GPTData::XFormDisklabel(BSDData* disklabel)
1560 // Add one GPT partition to MBR. Used by PartsToMBR() functions. Created
1561 // partition has the active/bootable flag UNset and uses the GPT fdisk
1562 // type code divided by 0x0100 as the MBR type code.
1563 // Returns 1 if operation was 100% successful, 0 if there were ANY
1565 int GPTData::OnePartToMBR(uint32_t gptPart, int mbrPart) {
1568 if ((mbrPart < 0) || (mbrPart > 3)) {
1569 cout << "MBR partition " << mbrPart + 1 << " is out of range; omitting it.\n";
1572 if (gptPart >= numParts) {
1573 cout << "GPT partition " << gptPart + 1 << " is out of range; omitting it.\n";
1576 if (allOK && (partitions[gptPart].GetLastLBA() == UINT64_C(0))) {
1577 cout << "GPT partition " << gptPart + 1 << " is undefined; omitting it.\n";
1580 if (allOK && (partitions[gptPart].GetFirstLBA() <= UINT32_MAX) &&
1581 (partitions[gptPart].GetLengthLBA() <= UINT32_MAX)) {
1582 if (partitions[gptPart].GetLastLBA() > UINT32_MAX) {
1583 cout << "Caution: Partition end point past 32-bit pointer boundary;"
1584 << " some OSes may\nreact strangely.\n";
1586 protectiveMBR.MakePart(mbrPart, (uint32_t) partitions[gptPart].GetFirstLBA(),
1587 (uint32_t) partitions[gptPart].GetLengthLBA(),
1588 partitions[gptPart].GetHexType() / 256, 0);
1589 } else { // partition out of range
1590 if (allOK) // Display only if "else" triggered by out-of-bounds condition
1591 cout << "Partition " << gptPart + 1 << " begins beyond the 32-bit pointer limit of MBR "
1592 << "partitions, or is\n too big; omitting it.\n";
1596 } // GPTData::OnePartToMBR()
1599 /**********************************************************************
1601 * Functions that adjust GPT data structures WITHOUT user interaction *
1602 * (they may display information for the user's benefit, though) *
1604 **********************************************************************/
1606 // Resizes GPT to specified number of entries. Creates a new table if
1607 // necessary, copies data if it already exists. If fillGPTSectors is 1
1608 // (the default), rounds numEntries to fill all the sectors necessary to
1610 // Returns 1 if all goes well, 0 if an error is encountered.
1611 int GPTData::SetGPTSize(uint32_t numEntries, int fillGPTSectors) {
1613 uint32_t i, high, copyNum, entriesPerSector;
1616 // First, adjust numEntries upward, if necessary, to get a number
1617 // that fills the allocated sectors
1618 entriesPerSector = blockSize / GPT_SIZE;
1619 if (fillGPTSectors && ((numEntries % entriesPerSector) != 0)) {
1620 cout << "Adjusting GPT size from " << numEntries << " to ";
1621 numEntries = ((numEntries / entriesPerSector) + 1) * entriesPerSector;
1622 cout << numEntries << " to fill the sector\n";
1625 // Do the work only if the # of partitions is changing. Along with being
1626 // efficient, this prevents mucking with the location of the secondary
1627 // partition table, which causes problems when loading data from a RAID
1628 // array that's been expanded because this function is called when loading
1630 if (((numEntries != numParts) || (partitions == NULL)) && (numEntries > 0)) {
1631 newParts = new GPTPart [numEntries];
1632 if (newParts != NULL) {
1633 if (partitions != NULL) { // existing partitions; copy them over
1634 GetPartRange(&i, &high);
1635 if (numEntries < (high + 1)) { // Highest entry too high for new #
1636 cout << "The highest-numbered partition is " << high + 1
1637 << ", which is greater than the requested\n"
1638 << "partition table size of " << numEntries
1639 << "; cannot resize. Perhaps sorting will help.\n";
1642 } else { // go ahead with copy
1643 if (numEntries < numParts)
1644 copyNum = numEntries;
1647 for (i = 0; i < copyNum; i++) {
1648 newParts[i] = partitions[i];
1650 delete[] partitions;
1651 partitions = newParts;
1653 } else { // No existing partition table; just create it
1654 partitions = newParts;
1655 } // if/else existing partitions
1656 numParts = numEntries;
1657 mainHeader.firstUsableLBA = ((numEntries * GPT_SIZE) / blockSize) + (((numEntries * GPT_SIZE) % blockSize) != 0) + 2 ;
1658 secondHeader.firstUsableLBA = mainHeader.firstUsableLBA;
1659 MoveSecondHeaderToEnd();
1662 } else { // Bad memory allocation
1663 cerr << "Error allocating memory for partition table! Size is unchanged!\n";
1667 mainHeader.numParts = numParts;
1668 secondHeader.numParts = numParts;
1670 } // GPTData::SetGPTSize()
1672 // Blank the partition array
1673 void GPTData::BlankPartitions(void) {
1676 for (i = 0; i < numParts; i++) {
1677 partitions[i].BlankPartition();
1679 } // GPTData::BlankPartitions()
1681 // Delete a partition by number. Returns 1 if successful,
1682 // 0 if there was a problem. Returns 1 if partition was in
1683 // range, 0 if it was out of range.
1684 int GPTData::DeletePartition(uint32_t partNum) {
1685 uint64_t startSector, length;
1686 uint32_t low, high, numUsedParts, retval = 1;;
1688 numUsedParts = GetPartRange(&low, &high);
1689 if ((numUsedParts > 0) && (partNum >= low) && (partNum <= high)) {
1690 // In case there's a protective MBR, look for & delete matching
1691 // MBR partition....
1692 startSector = partitions[partNum].GetFirstLBA();
1693 length = partitions[partNum].GetLengthLBA();
1694 protectiveMBR.DeleteByLocation(startSector, length);
1696 // Now delete the GPT partition
1697 partitions[partNum].BlankPartition();
1699 cerr << "Partition number " << partNum + 1 << " out of range!\n";
1703 } // GPTData::DeletePartition(uint32_t partNum)
1705 // Non-interactively create a partition.
1706 // Returns 1 if the operation was successful, 0 if a problem was discovered.
1707 uint32_t GPTData::CreatePartition(uint32_t partNum, uint64_t startSector, uint64_t endSector) {
1708 int retval = 1; // assume there'll be no problems
1709 uint64_t origSector = startSector;
1711 if (IsFreePartNum(partNum)) {
1712 if (Align(&startSector)) {
1713 cout << "Information: Moved requested sector from " << origSector << " to "
1714 << startSector << " in\norder to align on " << sectorAlignment
1715 << "-sector boundaries.\n";
1717 if (IsFree(startSector) && (startSector <= endSector)) {
1718 if (FindLastInFree(startSector) >= endSector) {
1719 partitions[partNum].SetFirstLBA(startSector);
1720 partitions[partNum].SetLastLBA(endSector);
1721 partitions[partNum].SetType(DEFAULT_TYPE);
1722 partitions[partNum].RandomizeUniqueGUID();
1723 } else retval = 0; // if free space until endSector
1724 } else retval = 0; // if startSector is free
1725 } else retval = 0; // if legal partition number
1727 } // GPTData::CreatePartition(partNum, startSector, endSector)
1729 // Sort the GPT entries, eliminating gaps and making for a logical
1731 void GPTData::SortGPT(void) {
1733 sort(partitions, partitions + numParts);
1734 } // GPTData::SortGPT()
1736 // Swap the contents of two partitions.
1737 // Returns 1 if successful, 0 if either partition is out of range
1738 // (that is, not a legal number; either or both can be empty).
1739 // Note that if partNum1 = partNum2 and this number is in range,
1740 // it will be considered successful.
1741 int GPTData::SwapPartitions(uint32_t partNum1, uint32_t partNum2) {
1745 if ((partNum1 < numParts) && (partNum2 < numParts)) {
1746 if (partNum1 != partNum2) {
1747 temp = partitions[partNum1];
1748 partitions[partNum1] = partitions[partNum2];
1749 partitions[partNum2] = temp;
1751 } else allOK = 0; // partition numbers are valid
1753 } // GPTData::SwapPartitions()
1755 // Set up data structures for entirely new set of partitions on the
1756 // specified device. Returns 1 if OK, 0 if there were problems.
1757 // Note that this function does NOT clear the protectiveMBR data
1758 // structure, since it may hold the original MBR partitions if the
1759 // program was launched on an MBR disk, and those may need to be
1760 // converted to GPT format.
1761 int GPTData::ClearGPTData(void) {
1764 // Set up the partition table....
1765 delete[] partitions;
1767 SetGPTSize(NUM_GPT_ENTRIES);
1769 // Now initialize a bunch of stuff that's static....
1770 mainHeader.signature = GPT_SIGNATURE;
1771 mainHeader.revision = 0x00010000;
1772 mainHeader.headerSize = HEADER_SIZE;
1773 mainHeader.reserved = 0;
1774 mainHeader.currentLBA = UINT64_C(1);
1775 mainHeader.partitionEntriesLBA = (uint64_t) 2;
1776 mainHeader.sizeOfPartitionEntries = GPT_SIZE;
1777 for (i = 0; i < GPT_RESERVED; i++) {
1778 mainHeader.reserved2[i] = '\0';
1781 sectorAlignment = DEFAULT_ALIGNMENT * SECTOR_SIZE / blockSize;
1783 sectorAlignment = DEFAULT_ALIGNMENT;
1785 // Now some semi-static items (computed based on end of disk)
1786 mainHeader.backupLBA = diskSize - UINT64_C(1);
1787 mainHeader.lastUsableLBA = diskSize - mainHeader.firstUsableLBA;
1789 // Set a unique GUID for the disk, based on random numbers
1790 mainHeader.diskGUID.Randomize();
1792 // Copy main header to backup header
1793 RebuildSecondHeader();
1795 // Blank out the partitions array....
1798 // Flag all CRCs as being OK....
1802 secondPartsCrcOk = 1;
1805 } // GPTData::ClearGPTData()
1807 // Set the location of the second GPT header data to the end of the disk.
1808 // If the disk size has actually changed, this also adjusts the protective
1809 // entry in the MBR, since it's probably no longer correct.
1810 // Used internally and called by the 'e' option on the recovery &
1811 // transformation menu, to help users of RAID arrays who add disk space
1812 // to their arrays or to adjust data structures in restore operations
1813 // involving unequal-sized disks.
1814 void GPTData::MoveSecondHeaderToEnd() {
1815 mainHeader.backupLBA = secondHeader.currentLBA = diskSize - UINT64_C(1);
1816 if (mainHeader.lastUsableLBA != diskSize - mainHeader.firstUsableLBA) {
1817 if (protectiveMBR.GetValidity() == hybrid) {
1818 protectiveMBR.OptimizeEESize();
1821 if (protectiveMBR.GetValidity() == gpt)
1822 MakeProtectiveMBR();
1824 mainHeader.lastUsableLBA = secondHeader.lastUsableLBA = diskSize - mainHeader.firstUsableLBA;
1825 secondHeader.partitionEntriesLBA = secondHeader.lastUsableLBA + UINT64_C(1);
1826 } // GPTData::FixSecondHeaderLocation()
1828 // Sets the partition's name to the specified UnicodeString without
1829 // user interaction.
1830 // Returns 1 on success, 0 on failure (invalid partition number).
1831 int GPTData::SetName(uint32_t partNum, const UnicodeString & theName) {
1834 if (IsUsedPartNum(partNum))
1835 partitions[partNum].SetName(theName);
1840 } // GPTData::SetName
1842 // Set the disk GUID to the specified value. Note that the header CRCs must
1843 // be recomputed after calling this function.
1844 void GPTData::SetDiskGUID(GUIDData newGUID) {
1845 mainHeader.diskGUID = newGUID;
1846 secondHeader.diskGUID = newGUID;
1849 // Set the unique GUID of the specified partition. Returns 1 on
1850 // successful completion, 0 if there were problems (invalid
1851 // partition number).
1852 int GPTData::SetPartitionGUID(uint32_t pn, GUIDData theGUID) {
1855 if (pn < numParts) {
1856 if (partitions[pn].IsUsed()) {
1857 partitions[pn].SetUniqueGUID(theGUID);
1862 } // GPTData::SetPartitionGUID()
1864 // Set new random GUIDs for the disk and all partitions. Intended to be used
1865 // after disk cloning or similar operations that don't randomize the GUIDs.
1866 void GPTData::RandomizeGUIDs(void) {
1869 mainHeader.diskGUID.Randomize();
1870 secondHeader.diskGUID = mainHeader.diskGUID;
1871 for (i = 0; i < numParts; i++)
1872 if (partitions[i].IsUsed())
1873 partitions[i].RandomizeUniqueGUID();
1874 } // GPTData::RandomizeGUIDs()
1876 // Change partition type code non-interactively. Returns 1 if
1877 // successful, 0 if not....
1878 int GPTData::ChangePartType(uint32_t partNum, PartType theGUID) {
1881 if (!IsFreePartNum(partNum)) {
1882 partitions[partNum].SetType(theGUID);
1885 } // GPTData::ChangePartType()
1887 // Recompute the CHS values of all the MBR partitions. Used to reset
1888 // CHS values that some BIOSes require, despite the fact that the
1889 // resulting CHS values violate the GPT standard.
1890 void GPTData::RecomputeCHS(void) {
1893 for (i = 0; i < 4; i++)
1894 protectiveMBR.RecomputeCHS(i);
1895 } // GPTData::RecomputeCHS()
1897 // Adjust sector number so that it falls on a sector boundary that's a
1898 // multiple of sectorAlignment. This is done to improve the performance
1899 // of Western Digital Advanced Format disks and disks with similar
1900 // technology from other companies, which use 4096-byte sectors
1901 // internally although they translate to 512-byte sectors for the
1902 // benefit of the OS. If partitions aren't properly aligned on these
1903 // disks, some filesystem data structures can span multiple physical
1904 // sectors, degrading performance. This function should be called
1905 // only on the FIRST sector of the partition, not the last!
1906 // This function returns 1 if the alignment was altered, 0 if it
1908 int GPTData::Align(uint64_t* sector) {
1909 int retval = 0, sectorOK = 0;
1910 uint64_t earlier, later, testSector;
1912 if ((*sector % sectorAlignment) != 0) {
1913 earlier = (*sector / sectorAlignment) * sectorAlignment;
1914 later = earlier + (uint64_t) sectorAlignment;
1916 // Check to see that every sector between the earlier one and the
1917 // requested one is clear, and that it's not too early....
1918 if (earlier >= mainHeader.firstUsableLBA) {
1920 testSector = earlier;
1922 sectorOK = IsFree(testSector++);
1923 } while ((sectorOK == 1) && (testSector < *sector));
1924 if (sectorOK == 1) {
1928 } // if firstUsableLBA check
1930 // If couldn't move the sector earlier, try to move it later instead....
1931 if ((sectorOK != 1) && (later <= mainHeader.lastUsableLBA)) {
1935 sectorOK = IsFree(testSector--);
1936 } while ((sectorOK == 1) && (testSector > *sector));
1937 if (sectorOK == 1) {
1944 } // GPTData::Align()
1946 /********************************************************
1948 * Functions that return data about GPT data structures *
1949 * (most of these are inline in gpt.h) *
1951 ********************************************************/
1953 // Find the low and high used partition numbers (numbered from 0).
1954 // Return value is the number of partitions found. Note that the
1955 // *low and *high values are both set to 0 when no partitions
1956 // are found, as well as when a single partition in the first
1957 // position exists. Thus, the return value is the only way to
1958 // tell when no partitions exist.
1959 int GPTData::GetPartRange(uint32_t *low, uint32_t *high) {
1963 *low = numParts + 1; // code for "not found"
1965 for (i = 0; i < numParts; i++) {
1966 if (partitions[i].IsUsed()) { // it exists
1967 *high = i; // since we're counting up, set the high value
1968 // Set the low value only if it's not yet found...
1969 if (*low == (numParts + 1)) *low = i;
1974 // Above will leave *low pointing to its "not found" value if no partitions
1975 // are defined, so reset to 0 if this is the case....
1976 if (*low == (numParts + 1))
1979 } // GPTData::GetPartRange()
1981 // Returns the value of the first free partition, or -1 if none is
1983 int GPTData::FindFirstFreePart(void) {
1986 if (partitions != NULL) {
1987 while ((i < (int) numParts) && (partitions[i].IsUsed()))
1989 if (i >= (int) numParts)
1993 } // GPTData::FindFirstFreePart()
1995 // Returns the number of defined partitions.
1996 uint32_t GPTData::CountParts(void) {
1997 uint32_t i, counted = 0;
1999 for (i = 0; i < numParts; i++) {
2000 if (partitions[i].IsUsed())
2004 } // GPTData::CountParts()
2006 /****************************************************
2008 * Functions that return data about disk free space *
2010 ****************************************************/
2012 // Find the first available block after the starting point; returns 0 if
2013 // there are no available blocks left
2014 uint64_t GPTData::FindFirstAvailable(uint64_t start) {
2019 // Begin from the specified starting point or from the first usable
2020 // LBA, whichever is greater...
2021 if (start < mainHeader.firstUsableLBA)
2022 first = mainHeader.firstUsableLBA;
2026 // ...now search through all partitions; if first is within an
2027 // existing partition, move it to the next sector after that
2028 // partition and repeat. If first was moved, set firstMoved
2029 // flag; repeat until firstMoved is not set, so as to catch
2030 // cases where partitions are out of sequential order....
2033 for (i = 0; i < numParts; i++) {
2034 if ((partitions[i].IsUsed()) && (first >= partitions[i].GetFirstLBA()) &&
2035 (first <= partitions[i].GetLastLBA())) { // in existing part.
2036 first = partitions[i].GetLastLBA() + 1;
2040 } while (firstMoved == 1);
2041 if (first > mainHeader.lastUsableLBA)
2044 } // GPTData::FindFirstAvailable()
2046 // Finds the first available sector in the largest block of unallocated
2047 // space on the disk. Returns 0 if there are no available blocks left
2048 uint64_t GPTData::FindFirstInLargest(void) {
2049 uint64_t start, firstBlock, lastBlock, segmentSize, selectedSize = 0, selectedSegment = 0;
2053 firstBlock = FindFirstAvailable(start);
2054 if (firstBlock != UINT32_C(0)) { // something's free...
2055 lastBlock = FindLastInFree(firstBlock);
2056 segmentSize = lastBlock - firstBlock + UINT32_C(1);
2057 if (segmentSize > selectedSize) {
2058 selectedSize = segmentSize;
2059 selectedSegment = firstBlock;
2061 start = lastBlock + 1;
2063 } while (firstBlock != 0);
2064 return selectedSegment;
2065 } // GPTData::FindFirstInLargest()
2067 // Find the last available block on the disk.
2068 // Returns 0 if there are no available partitions
2069 uint64_t GPTData::FindLastAvailable(void) {
2074 // Start by assuming the last usable LBA is available....
2075 last = mainHeader.lastUsableLBA;
2077 // ...now, similar to algorithm in FindFirstAvailable(), search
2078 // through all partitions, moving last when it's in an existing
2079 // partition. Set the lastMoved flag so we repeat to catch cases
2080 // where partitions are out of logical order.
2083 for (i = 0; i < numParts; i++) {
2084 if ((last >= partitions[i].GetFirstLBA()) &&
2085 (last <= partitions[i].GetLastLBA())) { // in existing part.
2086 last = partitions[i].GetFirstLBA() - 1;
2090 } while (lastMoved == 1);
2091 if (last < mainHeader.firstUsableLBA)
2094 } // GPTData::FindLastAvailable()
2096 // Find the last available block in the free space pointed to by start.
2097 uint64_t GPTData::FindLastInFree(uint64_t start) {
2098 uint64_t nearestStart;
2101 nearestStart = mainHeader.lastUsableLBA;
2102 for (i = 0; i < numParts; i++) {
2103 if ((nearestStart > partitions[i].GetFirstLBA()) &&
2104 (partitions[i].GetFirstLBA() > start)) {
2105 nearestStart = partitions[i].GetFirstLBA() - 1;
2108 return (nearestStart);
2109 } // GPTData::FindLastInFree()
2111 // Finds the total number of free blocks, the number of segments in which
2112 // they reside, and the size of the largest of those segments
2113 uint64_t GPTData::FindFreeBlocks(uint32_t *numSegments, uint64_t *largestSegment) {
2114 uint64_t start = UINT64_C(0); // starting point for each search
2115 uint64_t totalFound = UINT64_C(0); // running total
2116 uint64_t firstBlock; // first block in a segment
2117 uint64_t lastBlock; // last block in a segment
2118 uint64_t segmentSize; // size of segment in blocks
2121 *largestSegment = UINT64_C(0);
2124 firstBlock = FindFirstAvailable(start);
2125 if (firstBlock != UINT64_C(0)) { // something's free...
2126 lastBlock = FindLastInFree(firstBlock);
2127 segmentSize = lastBlock - firstBlock + UINT64_C(1);
2128 if (segmentSize > *largestSegment) {
2129 *largestSegment = segmentSize;
2131 totalFound += segmentSize;
2133 start = lastBlock + 1;
2135 } while (firstBlock != 0);
2139 } // GPTData::FindFreeBlocks()
2141 // Returns 1 if sector is unallocated, 0 if it's allocated to a partition.
2142 // If it's allocated, return the partition number to which it's allocated
2143 // in partNum, if that variable is non-NULL. (A value of UINT32_MAX is
2144 // returned in partNum if the sector is in use by basic GPT data structures.)
2145 int GPTData::IsFree(uint64_t sector, uint32_t *partNum) {
2149 for (i = 0; i < numParts; i++) {
2150 if ((sector >= partitions[i].GetFirstLBA()) &&
2151 (sector <= partitions[i].GetLastLBA())) {
2153 if (partNum != NULL)
2157 if ((sector < mainHeader.firstUsableLBA) ||
2158 (sector > mainHeader.lastUsableLBA)) {
2160 if (partNum != NULL)
2161 *partNum = UINT32_MAX;
2164 } // GPTData::IsFree()
2166 // Returns 1 if partNum is unused AND if it's a legal value.
2167 int GPTData::IsFreePartNum(uint32_t partNum) {
2168 return ((partNum < numParts) && (partitions != NULL) &&
2169 (!partitions[partNum].IsUsed()));
2170 } // GPTData::IsFreePartNum()
2172 // Returns 1 if partNum is in use.
2173 int GPTData::IsUsedPartNum(uint32_t partNum) {
2174 return ((partNum < numParts) && (partitions != NULL) &&
2175 (partitions[partNum].IsUsed()));
2176 } // GPTData::IsUsedPartNum()
2178 /***********************************************************
2180 * Change how functions work or return information on them *
2182 ***********************************************************/
2184 // Set partition alignment value; partitions will begin on multiples of
2185 // the specified value
2186 void GPTData::SetAlignment(uint32_t n) {
2188 sectorAlignment = n;
2190 cerr << "Attempt to set partition alignment to 0!\n";
2191 } // GPTData::SetAlignment()
2193 // Compute sector alignment based on the current partitions (if any). Each
2194 // partition's starting LBA is examined, and if it's divisible by a power-of-2
2195 // value less than or equal to the DEFAULT_ALIGNMENT value (adjusted for the
2196 // sector size), but not by the previously-located alignment value, then the
2197 // alignment value is adjusted down. If the computed alignment is less than 8
2198 // and the disk is bigger than SMALLEST_ADVANCED_FORMAT, resets it to 8. This
2199 // is a safety measure for WD Advanced Format and similar drives. If no partitions
2200 // are defined, the alignment value is set to DEFAULT_ALIGNMENT (2048) (or an
2201 // adjustment of that based on the current sector size). The result is that new
2202 // drives are aligned to 2048-sector multiples but the program won't complain
2203 // about other alignments on existing disks unless a smaller-than-8 alignment
2204 // is used on big disks (as safety for WD Advanced Format drives).
2205 // Returns the computed alignment value.
2206 uint32_t GPTData::ComputeAlignment(void) {
2207 uint32_t i = 0, found, exponent = 31;
2208 uint32_t align = DEFAULT_ALIGNMENT;
2211 align = DEFAULT_ALIGNMENT * SECTOR_SIZE / blockSize;
2212 exponent = (uint32_t) log2(align);
2213 for (i = 0; i < numParts; i++) {
2214 if (partitions[i].IsUsed()) {
2217 align = UINT64_C(1) << exponent;
2218 if ((partitions[i].GetFirstLBA() % align) == 0) {
2226 if ((align < MIN_AF_ALIGNMENT) && (diskSize >= SMALLEST_ADVANCED_FORMAT))
2227 align = MIN_AF_ALIGNMENT;
2228 sectorAlignment = align;
2230 } // GPTData::ComputeAlignment()
2232 /********************************
2234 * Endianness support functions *
2236 ********************************/
2238 void GPTData::ReverseHeaderBytes(struct GPTHeader* header) {
2239 ReverseBytes(&header->signature, 8);
2240 ReverseBytes(&header->revision, 4);
2241 ReverseBytes(&header->headerSize, 4);
2242 ReverseBytes(&header->headerCRC, 4);
2243 ReverseBytes(&header->reserved, 4);
2244 ReverseBytes(&header->currentLBA, 8);
2245 ReverseBytes(&header->backupLBA, 8);
2246 ReverseBytes(&header->firstUsableLBA, 8);
2247 ReverseBytes(&header->lastUsableLBA, 8);
2248 ReverseBytes(&header->partitionEntriesLBA, 8);
2249 ReverseBytes(&header->numParts, 4);
2250 ReverseBytes(&header->sizeOfPartitionEntries, 4);
2251 ReverseBytes(&header->partitionEntriesCRC, 4);
2252 ReverseBytes(header->reserved2, GPT_RESERVED);
2253 } // GPTData::ReverseHeaderBytes()
2255 // Reverse byte order for all partitions.
2256 void GPTData::ReversePartitionBytes() {
2259 for (i = 0; i < numParts; i++) {
2260 partitions[i].ReversePartBytes();
2262 } // GPTData::ReversePartitionBytes()
2264 // Validate partition number
2265 bool GPTData::ValidPartNum (const uint32_t partNum) {
2266 if (partNum >= numParts) {
2267 cerr << "Partition number out of range: " << partNum << "\n";
2271 } // GPTData::ValidPartNum
2273 // Return a single partition for inspection (not modification!) by other
2275 const GPTPart & GPTData::operator[](uint32_t partNum) const {
2276 if (partNum >= numParts) {
2277 cerr << "Partition number out of range (" << partNum << " requested, but only "
2278 << numParts << " available)\n";
2281 if (partitions == NULL) {
2282 cerr << "No partitions defined in GPTData::operator[]; fatal error!\n";
2285 return partitions[partNum];
2288 // Return (not for modification!) the disk's GUID value
2289 const GUIDData & GPTData::GetDiskGUID(void) const {
2290 return mainHeader.diskGUID;
2291 } // GPTData::GetDiskGUID()
2293 // Manage attributes for a partition, based on commands passed to this function.
2294 // (Function is non-interactive.)
2295 // Returns 1 if a modification command succeeded, 0 if the command should not have
2296 // modified data, and -1 if a modification command failed.
2297 int GPTData::ManageAttributes(int partNum, const string & command, const string & bits) {
2301 if (command == "show") {
2302 ShowAttributes(partNum);
2303 } else if (command == "get") {
2304 GetAttribute(partNum, bits);
2306 theAttr = partitions[partNum].GetAttributes();
2307 if (theAttr.OperateOnAttributes(partNum, command, bits)) {
2308 partitions[partNum].SetAttributes(theAttr.GetAttributes());
2316 } // GPTData::ManageAttributes()
2318 // Show all attributes for a specified partition....
2319 void GPTData::ShowAttributes(const uint32_t partNum) {
2320 if (partitions[partNum].IsUsed())
2321 partitions[partNum].ShowAttributes(partNum);
2322 } // GPTData::ShowAttributes
2324 // Show whether a single attribute bit is set (terse output)...
2325 void GPTData::GetAttribute(const uint32_t partNum, const string& attributeBits) {
2326 partitions[partNum].GetAttributes().OperateOnAttributes(partNum, "get", attributeBits);
2327 } // GPTData::GetAttribute
2330 /******************************************
2332 * Additional non-class support functions *
2334 ******************************************/
2336 // Check to be sure that data type sizes are correct. The basic types (uint*_t) should
2337 // never fail these tests, but the struct types may fail depending on compile options.
2338 // Specifically, the -fpack-struct option to gcc may be required to ensure proper structure
2343 if (sizeof(uint8_t) != 1) {
2344 cerr << "uint8_t is " << sizeof(uint8_t) << " bytes, should be 1 byte; aborting!\n";
2347 if (sizeof(uint16_t) != 2) {
2348 cerr << "uint16_t is " << sizeof(uint16_t) << " bytes, should be 2 bytes; aborting!\n";
2351 if (sizeof(uint32_t) != 4) {
2352 cerr << "uint32_t is " << sizeof(uint32_t) << " bytes, should be 4 bytes; aborting!\n";
2355 if (sizeof(uint64_t) != 8) {
2356 cerr << "uint64_t is " << sizeof(uint64_t) << " bytes, should be 8 bytes; aborting!\n";
2359 if (sizeof(struct MBRRecord) != 16) {
2360 cerr << "MBRRecord is " << sizeof(MBRRecord) << " bytes, should be 16 bytes; aborting!\n";
2363 if (sizeof(struct TempMBR) != 512) {
2364 cerr << "TempMBR is " << sizeof(TempMBR) << " bytes, should be 512 bytes; aborting!\n";
2367 if (sizeof(struct GPTHeader) != 512) {
2368 cerr << "GPTHeader is " << sizeof(GPTHeader) << " bytes, should be 512 bytes; aborting!\n";
2371 if (sizeof(GPTPart) != 128) {
2372 cerr << "GPTPart is " << sizeof(GPTPart) << " bytes, should be 128 bytes; aborting!\n";
2375 if (sizeof(GUIDData) != 16) {
2376 cerr << "GUIDData is " << sizeof(GUIDData) << " bytes, should be 16 bytes; aborting!\n";
2379 if (sizeof(PartType) != 16) {
2380 cerr << "PartType is " << sizeof(GUIDData) << " bytes, should be 16 bytes; aborting!\n";