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