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