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