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