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