gpt.cc revision 1d1448a82d62ad32a8d597ed9ade46b4f37d8eb5
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 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 <unistd.h> 14#include <stdlib.h> 15#include <stdint.h> 16#include <fcntl.h> 17#include <string.h> 18#include <time.h> 19#include <sys/stat.h> 20#include <errno.h> 21#include "crc32.h" 22#include "gpt.h" 23#include "bsd.h" 24#include "support.h" 25#include "parttypes.h" 26#include "attributes.h" 27 28using namespace std; 29 30/**************************************** 31 * * 32 * GPTData class and related structures * 33 * * 34 ****************************************/ 35 36// Default constructor 37GPTData::GPTData(void) { 38 blockSize = SECTOR_SIZE; // set a default 39 diskSize = 0; 40 partitions = NULL; 41 state = gpt_valid; 42 strcpy(device, ""); 43 mainCrcOk = 0; 44 secondCrcOk = 0; 45 mainPartsCrcOk = 0; 46 secondPartsCrcOk = 0; 47 apmFound = 0; 48 bsdFound = 0; 49 sectorAlignment = 8; // Align partitions on 4096-byte boundaries by default 50 srand((unsigned int) time(NULL)); 51 SetGPTSize(NUM_GPT_ENTRIES); 52} // GPTData default constructor 53 54// The following constructor loads GPT data from a device file 55GPTData::GPTData(char* filename) { 56 blockSize = SECTOR_SIZE; // set a default 57 diskSize = 0; 58 partitions = NULL; 59 state = gpt_invalid; 60 strcpy(device, ""); 61 mainCrcOk = 0; 62 secondCrcOk = 0; 63 mainPartsCrcOk = 0; 64 secondPartsCrcOk = 0; 65 apmFound = 0; 66 bsdFound = 0; 67 sectorAlignment = 8; // Align partitions on 4096-byte boundaries by default 68 srand((unsigned int) time(NULL)); 69 LoadPartitions(filename); 70} // GPTData(char* filename) constructor 71 72// Destructor 73GPTData::~GPTData(void) { 74 free(partitions); 75} // GPTData destructor 76 77/********************************************************************* 78 * * 79 * Begin functions that verify data, or that adjust the verification * 80 * information (compute CRCs, rebuild headers) * 81 * * 82 *********************************************************************/ 83 84// Perform detailed verification, reporting on any problems found, but 85// do *NOT* recover from these problems. Returns the total number of 86// problems identified. 87int GPTData::Verify(void) { 88 int problems = 0, numSegments, i; 89 uint64_t totalFree, largestSegment, firstSector; 90 char tempStr[255], siTotal[255], siLargest[255]; 91 92 // First, check for CRC errors in the GPT data.... 93 if (!mainCrcOk) { 94 problems++; 95 printf("\nProblem: The CRC for the main GPT header is invalid. The main GPT header may\n" 96 "be corrupt. Consider loading the backup GPT header to rebuild the main GPT\n" 97 "header\n"); 98 } // if 99 if (!mainPartsCrcOk) { 100 problems++; 101 printf("\nProblem: The CRC for the main partition table is invalid. This table may be\n" 102 "corrupt. Consider loading the backup partition table.\n"); 103 } // if 104 if (!secondCrcOk) { 105 problems++; 106 printf("\nProblem: The CRC for the backup GPT header is invalid. The backup GPT header\n" 107 "may be corrupt. Consider using the main GPT header to rebuild the backup GPT\n" 108 "header.\n"); 109 } // if 110 if (!secondPartsCrcOk) { 111 problems++; 112 printf("\nCaution: The CRC for the backup partition table is invalid. This table may\n" 113 "be corrupt. This program will automatically create a new backup partition\n" 114 "table when you save your partitions.\n"); 115 } // if 116 117 // Now check that the main and backup headers both point to themselves.... 118 if (mainHeader.currentLBA != 1) { 119 problems++; 120 printf("\nProblem: The main header's self-pointer doesn't point to itself. This problem\n" 121 "is being automatically corrected, but it may be a symptom of more serious\n" 122 "problems. Think carefully before saving changes with 'w' or using this disk.\n"); 123 mainHeader.currentLBA = 1; 124 } // if 125 if (secondHeader.currentLBA != (diskSize - UINT64_C(1))) { 126 problems++; 127 printf("\nProblem: The secondary header's self-pointer indicates that it doesn't reside\n" 128 "at the end of the disk. If you've added a disk to a RAID array, use the 'e'\n" 129 "option on the experts' menu to adjust the secondary header's and partition\n" 130 "table's locations.\n"); 131 } // if 132 133 // Now check that critical main and backup GPT entries match each other 134 if (mainHeader.currentLBA != secondHeader.backupLBA) { 135 problems++; 136 printf("\nProblem: main GPT header's current LBA pointer (%llu) doesn't\n" 137 "match the backup GPT header's LBA pointer(%llu)\n", 138 (unsigned long long) mainHeader.currentLBA, 139 (unsigned long long) secondHeader.backupLBA); 140 } // if 141 if (mainHeader.backupLBA != secondHeader.currentLBA) { 142 problems++; 143 printf("\nProblem: main GPT header's backup LBA pointer (%llu) doesn't\n" 144 "match the backup GPT header's current LBA pointer (%llu)\n", 145 (unsigned long long) mainHeader.backupLBA, 146 (unsigned long long) secondHeader.currentLBA); 147 } // if 148 if (mainHeader.firstUsableLBA != secondHeader.firstUsableLBA) { 149 problems++; 150 printf("\nProblem: main GPT header's first usable LBA pointer (%llu) doesn't\n" 151 "match the backup GPT header's first usable LBA pointer (%llu)\n", 152 (unsigned long long) mainHeader.firstUsableLBA, 153 (unsigned long long) secondHeader.firstUsableLBA); 154 } // if 155 if (mainHeader.lastUsableLBA != secondHeader.lastUsableLBA) { 156 problems++; 157 printf("\nProblem: main GPT header's last usable LBA pointer (%llu) doesn't\n" 158 "match the backup GPT header's last usable LBA pointer (%llu)\n", 159 (unsigned long long) mainHeader.lastUsableLBA, 160 (unsigned long long) secondHeader.lastUsableLBA); 161 } // if 162 if ((mainHeader.diskGUID.data1 != secondHeader.diskGUID.data1) || 163 (mainHeader.diskGUID.data2 != secondHeader.diskGUID.data2)) { 164 problems++; 165 printf("\nProblem: main header's disk GUID (%s) doesn't\n", 166 GUIDToStr(mainHeader.diskGUID, tempStr)); 167 printf("match the backup GPT header's disk GUID (%s)\n", 168 GUIDToStr(secondHeader.diskGUID, tempStr)); 169 } // if 170 if (mainHeader.numParts != secondHeader.numParts) { 171 problems++; 172 printf("\nProblem: main GPT header's number of partitions (%lu) doesn't\n" 173 "match the backup GPT header's number of partitions (%lu)\n", 174 (unsigned long) mainHeader.numParts, 175 (unsigned long) secondHeader.numParts); 176 } // if 177 if (mainHeader.sizeOfPartitionEntries != secondHeader.sizeOfPartitionEntries) { 178 problems++; 179 printf("\nProblem: main GPT header's size of partition entries (%lu) doesn't\n" 180 "match the backup GPT header's size of partition entries (%lu)\n", 181 (unsigned long) mainHeader.sizeOfPartitionEntries, 182 (unsigned long) secondHeader.sizeOfPartitionEntries); 183 } // if 184 185 // Now check for a few other miscellaneous problems... 186 // Check that the disk size will hold the data... 187 if (mainHeader.backupLBA > diskSize) { 188 problems++; 189 printf("\nProblem: Disk is too small to hold all the data!\n"); 190 printf("(Disk size is %llu sectors, needs to be %llu sectors.)\n", 191 (unsigned long long) diskSize, 192 (unsigned long long) mainHeader.backupLBA); 193 } // if 194 195 // Check for overlapping partitions.... 196 problems += FindOverlaps(); 197 198 // Check for mismatched MBR and GPT partitions... 199 problems += FindHybridMismatches(); 200 201 // Verify that partitions don't run into GPT data areas.... 202 problems += CheckGPTSize(); 203 204 // Check that partitions are aligned on proper boundaries (for WD Advanced 205 // Format and similar disks).... 206 for (i = 0; i < mainHeader.numParts; i++) { 207 if ((partitions[i].GetFirstLBA() % sectorAlignment) != 0) { 208 printf("\nCaution: Partition %d doesn't begin on a %d-sector boundary. This may\n" 209 "result in degraded performance on some modern (2010 and later) hard disks.\n", 210 i + 1, sectorAlignment); 211 } // if 212 } // for 213 214 // Now compute available space, but only if no problems found, since 215 // problems could affect the results 216 if (problems == 0) { 217 totalFree = FindFreeBlocks(&numSegments, &largestSegment); 218 BytesToSI(totalFree * (uint64_t) blockSize, siTotal); 219 BytesToSI(largestSegment * (uint64_t) blockSize, siLargest); 220 printf("No problems found. %llu free sectors (%s) available in %u\n" 221 "segments, the largest of which is %llu sectors (%s) in size\n", 222 (unsigned long long) totalFree, 223 siTotal, numSegments, (unsigned long long) largestSegment, 224 siLargest); 225 } else { 226 printf("\nIdentified %d problems!\n", problems); 227 } // if/else 228 229 return (problems); 230} // GPTData::Verify() 231 232// Checks to see if the GPT tables overrun existing partitions; if they 233// do, issues a warning but takes no action. Returns number of problems 234// detected (0 if OK, 1 to 2 if problems). 235int GPTData::CheckGPTSize(void) { 236 uint64_t overlap, firstUsedBlock, lastUsedBlock; 237 uint32_t i; 238 int numProbs = 0; 239 240 // first, locate the first & last used blocks 241 firstUsedBlock = UINT64_MAX; 242 lastUsedBlock = 0; 243 for (i = 0; i < mainHeader.numParts; i++) { 244 if ((partitions[i].GetFirstLBA() < firstUsedBlock) && 245 (partitions[i].GetFirstLBA() != 0)) 246 firstUsedBlock = partitions[i].GetFirstLBA(); 247 if (partitions[i].GetLastLBA() > lastUsedBlock) 248 lastUsedBlock = partitions[i].GetLastLBA(); 249 } // for 250 251 // If the disk size is 0 (the default), then it means that various 252 // variables aren't yet set, so the below tests will be useless; 253 // therefore we should skip everything 254 if (diskSize != 0) { 255 if (mainHeader.firstUsableLBA > firstUsedBlock) { 256 overlap = mainHeader.firstUsableLBA - firstUsedBlock; 257 printf("Warning! Main partition table overlaps the first partition by %lu blocks!\n", 258 (unsigned long) overlap); 259 if (firstUsedBlock > 2) { 260 printf("Try reducing the partition table size by %lu entries.\n", 261 (unsigned long) (overlap * 4)); 262 printf("(Use the 's' item on the experts' menu.)\n"); 263 } else { 264 printf("You will need to delete this partition or resize it in another utility.\n"); 265 } // if/else 266 numProbs++; 267 } // Problem at start of disk 268 if (mainHeader.lastUsableLBA < lastUsedBlock) { 269 overlap = lastUsedBlock - mainHeader.lastUsableLBA; 270 printf("Warning! Secondary partition table overlaps the last partition by %lu blocks\n", 271 (unsigned long) overlap); 272 if (lastUsedBlock > (diskSize - 2)) { 273 printf("You will need to delete this partition or resize it in another utility.\n"); 274 } else { 275 printf("Try reducing the partition table size by %lu entries.\n", 276 (unsigned long) (overlap * 4)); 277 printf("(Use the 's' item on the experts' menu.)\n"); 278 } // if/else 279 numProbs++; 280 } // Problem at end of disk 281 } // if (diskSize != 0) 282 return numProbs; 283} // GPTData::CheckGPTSize() 284 285// Check the validity of the GPT header. Returns 1 if the main header 286// is valid, 2 if the backup header is valid, 3 if both are valid, and 287// 0 if neither is valid. Note that this function just checks the GPT 288// signature and revision numbers, not CRCs or other data. 289int GPTData::CheckHeaderValidity(void) { 290 int valid = 3; 291 292 if (mainHeader.signature != GPT_SIGNATURE) { 293 valid -= 1; 294// printf("Main GPT signature invalid; read 0x%016llX, should be\n0x%016llX\n", 295// (unsigned long long) mainHeader.signature, (unsigned long long) GPT_SIGNATURE); 296 } else if ((mainHeader.revision != 0x00010000) && valid) { 297 valid -= 1; 298 printf("Unsupported GPT version in main header; read 0x%08lX, should be\n0x%08lX\n", 299 (unsigned long) mainHeader.revision, UINT32_C(0x00010000)); 300 } // if/else/if 301 302 if (secondHeader.signature != GPT_SIGNATURE) { 303 valid -= 2; 304// printf("Secondary GPT signature invalid; read 0x%016llX, should be\n0x%016llX\n", 305// (unsigned long long) secondHeader.signature, (unsigned long long) GPT_SIGNATURE); 306 } else if ((secondHeader.revision != 0x00010000) && valid) { 307 valid -= 2; 308 printf("Unsupported GPT version in backup header; read 0x%08lX, should be\n0x%08lX\n", 309 (unsigned long) mainHeader.revision, UINT32_C(0x00010000)); 310 } // if/else/if 311 312 // If MBR bad, check for an Apple disk signature 313 if ((protectiveMBR.GetValidity() == invalid) && 314 (((mainHeader.signature << 32) == APM_SIGNATURE1) || 315 (mainHeader.signature << 32) == APM_SIGNATURE2)) { 316 apmFound = 1; // Will display warning message later 317 } // if 318 319 return valid; 320} // GPTData::CheckHeaderValidity() 321 322// Check the header CRC to see if it's OK... 323// Note: Must be called BEFORE byte-order reversal on big-endian 324// systems! 325int GPTData::CheckHeaderCRC(struct GPTHeader* header) { 326 uint32_t oldCRC, newCRC, hSize; 327 328 // Back up old header CRC and then blank it, since it must be 0 for 329 // computation to be valid 330 oldCRC = header->headerCRC; 331 header->headerCRC = UINT32_C(0); 332 hSize = header->headerSize; 333 334 // If big-endian system, reverse byte order 335 if (IsLittleEndian() == 0) { 336 ReverseBytes(&oldCRC, 4); 337 } // if 338 339 // Initialize CRC functions... 340 chksum_crc32gentab(); 341 342 // Compute CRC, restore original, and return result of comparison 343 newCRC = chksum_crc32((unsigned char*) header, HEADER_SIZE); 344 header->headerCRC = oldCRC; 345 return (oldCRC == newCRC); 346} // GPTData::CheckHeaderCRC() 347 348// Recompute all the CRCs. Must be called before saving (but after reversing 349// byte order on big-endian systems) if any changes have been made. 350void GPTData::RecomputeCRCs(void) { 351 uint32_t crc, hSize, trueNumParts; 352 int littleEndian = 1; 353 354 // Initialize CRC functions... 355 chksum_crc32gentab(); 356 357 hSize = mainHeader.headerSize; 358 littleEndian = IsLittleEndian(); 359 360 // Compute CRC of partition tables & store in main and secondary headers 361 trueNumParts = mainHeader.numParts; 362 if (littleEndian == 0) 363 ReverseBytes(&trueNumParts, 4); // unreverse this key piece of data.... 364 crc = chksum_crc32((unsigned char*) partitions, trueNumParts * GPT_SIZE); 365 mainHeader.partitionEntriesCRC = crc; 366 secondHeader.partitionEntriesCRC = crc; 367 if (littleEndian == 0) { 368 ReverseBytes(&mainHeader.partitionEntriesCRC, 4); 369 ReverseBytes(&secondHeader.partitionEntriesCRC, 4); 370 } // if 371 372 // Zero out GPT tables' own CRCs (required for correct computation) 373 mainHeader.headerCRC = 0; 374 secondHeader.headerCRC = 0; 375 376 // Compute & store CRCs of main & secondary headers... 377 crc = chksum_crc32((unsigned char*) &mainHeader, hSize); 378 if (littleEndian == 0) 379 ReverseBytes(&crc, 4); 380 mainHeader.headerCRC = crc; 381 crc = chksum_crc32((unsigned char*) &secondHeader, hSize); 382 if (littleEndian == 0) 383 ReverseBytes(&crc, 4); 384 secondHeader.headerCRC = crc; 385} // GPTData::RecomputeCRCs() 386 387// Rebuild the main GPT header, using the secondary header as a model. 388// Typically called when the main header has been found to be corrupt. 389void GPTData::RebuildMainHeader(void) { 390 int i; 391 392 mainHeader.signature = GPT_SIGNATURE; 393 mainHeader.revision = secondHeader.revision; 394 mainHeader.headerSize = secondHeader.headerSize; 395 mainHeader.headerCRC = UINT32_C(0); 396 mainHeader.reserved = secondHeader.reserved; 397 mainHeader.currentLBA = secondHeader.backupLBA; 398 mainHeader.backupLBA = secondHeader.currentLBA; 399 mainHeader.firstUsableLBA = secondHeader.firstUsableLBA; 400 mainHeader.lastUsableLBA = secondHeader.lastUsableLBA; 401 mainHeader.diskGUID.data1 = secondHeader.diskGUID.data1; 402 mainHeader.diskGUID.data2 = secondHeader.diskGUID.data2; 403 mainHeader.partitionEntriesLBA = UINT64_C(2); 404 mainHeader.numParts = secondHeader.numParts; 405 mainHeader.sizeOfPartitionEntries = secondHeader.sizeOfPartitionEntries; 406 mainHeader.partitionEntriesCRC = secondHeader.partitionEntriesCRC; 407 for (i = 0 ; i < GPT_RESERVED; i++) 408 mainHeader.reserved2[i] = secondHeader.reserved2[i]; 409} // GPTData::RebuildMainHeader() 410 411// Rebuild the secondary GPT header, using the main header as a model. 412void GPTData::RebuildSecondHeader(void) { 413 int i; 414 415 secondHeader.signature = GPT_SIGNATURE; 416 secondHeader.revision = mainHeader.revision; 417 secondHeader.headerSize = mainHeader.headerSize; 418 secondHeader.headerCRC = UINT32_C(0); 419 secondHeader.reserved = mainHeader.reserved; 420 secondHeader.currentLBA = mainHeader.backupLBA; 421 secondHeader.backupLBA = mainHeader.currentLBA; 422 secondHeader.firstUsableLBA = mainHeader.firstUsableLBA; 423 secondHeader.lastUsableLBA = mainHeader.lastUsableLBA; 424 secondHeader.diskGUID.data1 = mainHeader.diskGUID.data1; 425 secondHeader.diskGUID.data2 = mainHeader.diskGUID.data2; 426 secondHeader.partitionEntriesLBA = secondHeader.lastUsableLBA + UINT64_C(1); 427 secondHeader.numParts = mainHeader.numParts; 428 secondHeader.sizeOfPartitionEntries = mainHeader.sizeOfPartitionEntries; 429 secondHeader.partitionEntriesCRC = mainHeader.partitionEntriesCRC; 430 for (i = 0 ; i < GPT_RESERVED; i++) 431 secondHeader.reserved2[i] = mainHeader.reserved2[i]; 432} // GPTData::RebuildSecondHeader() 433 434// Search for hybrid MBR entries that have no corresponding GPT partition. 435// Returns number of such mismatches found 436int GPTData::FindHybridMismatches(void) { 437 int i, j, found, numFound = 0; 438 uint64_t mbrFirst, mbrLast; 439 440 for (i = 0; i < 4; i++) { 441 if ((protectiveMBR.GetType(i) != 0xEE) && (protectiveMBR.GetType(i) != 0x00)) { 442 j = 0; 443 found = 0; 444 do { 445 mbrFirst = (uint64_t) protectiveMBR.GetFirstSector(i); 446 mbrLast = mbrFirst + (uint64_t) protectiveMBR.GetLength(i) - UINT64_C(1); 447 if ((partitions[j].GetFirstLBA() == mbrFirst) && 448 (partitions[j].GetLastLBA() == mbrLast)) 449 found = 1; 450 j++; 451 } while ((!found) && (j < mainHeader.numParts)); 452 if (!found) { 453 numFound++; 454 printf("\nWarning! Mismatched GPT and MBR partitions! MBR partition " 455 "%d, of type 0x%02X,\nhas no corresponding GPT partition! " 456 "You may continue, but this condition\nmight cause data loss" 457 " in the future!\a\n", i + 1, protectiveMBR.GetType(i)); 458 } // if 459 } // if 460 } // for 461 return numFound; 462} // GPTData::FindHybridMismatches 463 464// Find overlapping partitions and warn user about them. Returns number of 465// overlapping partitions. 466int GPTData::FindOverlaps(void) { 467 int i, j, problems = 0; 468 469 for (i = 1; i < mainHeader.numParts; i++) { 470 for (j = 0; j < i; j++) { 471 if (partitions[i].DoTheyOverlap(&partitions[j])) { 472 problems++; 473 printf("\nProblem: partitions %d and %d overlap:\n", i + 1, j + 1); 474 printf(" Partition %d: %llu to %llu\n", i, 475 (unsigned long long) partitions[i].GetFirstLBA(), 476 (unsigned long long) partitions[i].GetLastLBA()); 477 printf(" Partition %d: %llu to %llu\n", j, 478 (unsigned long long) partitions[j].GetFirstLBA(), 479 (unsigned long long) partitions[j].GetLastLBA()); 480 } // if 481 } // for j... 482 } // for i... 483 return problems; 484} // GPTData::FindOverlaps() 485 486/****************************************************************** 487 * * 488 * Begin functions that load data from disk or save data to disk. * 489 * * 490 ******************************************************************/ 491 492// Scan for partition data. This function loads the MBR data (regular MBR or 493// protective MBR) and loads BSD disklabel data (which is probably invalid). 494// It also looks for APM data, forces a load of GPT data, and summarizes 495// the results. 496void GPTData::PartitionScan(int fd) { 497 BSDData bsdDisklabel; 498// int bsdFound; 499 500 printf("Partition table scan:\n"); 501 502 // Read the MBR & check for BSD disklabel 503 protectiveMBR.ReadMBRData(fd); 504 protectiveMBR.ShowState(); 505 bsdDisklabel.ReadBSDData(fd, 0, diskSize - 1); 506 bsdFound = bsdDisklabel.ShowState(); 507// bsdDisklabel.DisplayBSDData(); 508 509 // Load the GPT data, whether or not it's valid 510 ForceLoadGPTData(fd); 511 ShowAPMState(); // Show whether there's an Apple Partition Map present 512 ShowGPTState(); // Show GPT status 513 printf("\n"); 514 515 if (apmFound) { 516 printf("\n*******************************************************************\n"); 517 printf("This disk appears to contain an Apple-format (APM) partition table!\n"); 518 printf("It will be destroyed if you continue!\n"); 519 printf("*******************************************************************\n\n\a"); 520 } // if 521} // GPTData::PartitionScan() 522 523// Read GPT data from a disk. 524int GPTData::LoadPartitions(char* deviceFilename) { 525 int fd, err; 526 int allOK = 1, i; 527 uint64_t firstBlock, lastBlock; 528 BSDData bsdDisklabel; 529 530 // First, do a test to see if writing will be possible later.... 531 fd = OpenForWrite(deviceFilename); 532 if (fd == -1) 533 printf("\aNOTE: Write test failed with error number %d. It will be " 534 "impossible to save\nchanges to this disk's partition table!\n\n", 535 errno); 536 close(fd); 537 538 if ((fd = open(deviceFilename, O_RDONLY)) != -1) { 539 // store disk information.... 540 diskSize = disksize(fd, &err); 541 blockSize = (uint32_t) GetBlockSize(fd); 542 strcpy(device, deviceFilename); 543 PartitionScan(fd); // Check for partition types & print summary 544 545 switch (UseWhichPartitions()) { 546 case use_mbr: 547 XFormPartitions(); 548 break; 549 case use_bsd: 550 bsdDisklabel.ReadBSDData(fd, 0, diskSize - 1); 551// bsdDisklabel.DisplayBSDData(); 552 ClearGPTData(); 553 protectiveMBR.MakeProtectiveMBR(1); // clear boot area (option 1) 554 XFormDisklabel(&bsdDisklabel, 0); 555 break; 556 case use_gpt: 557 break; 558 case use_new: 559 ClearGPTData(); 560 protectiveMBR.MakeProtectiveMBR(); 561 break; 562 } // switch 563 564 // Now find the first and last sectors used by partitions... 565 if (allOK) { 566 firstBlock = mainHeader.backupLBA; // start high 567 lastBlock = 0; // start low 568 for (i = 0; i < mainHeader.numParts; i++) { 569 if ((partitions[i].GetFirstLBA() < firstBlock) && 570 (partitions[i].GetFirstLBA() > 0)) 571 firstBlock = partitions[i].GetFirstLBA(); 572 if (partitions[i].GetLastLBA() > lastBlock) 573 lastBlock = partitions[i].GetLastLBA(); 574 } // for 575 } // if 576 CheckGPTSize(); 577 } else { 578 allOK = 0; 579 fprintf(stderr, "Problem opening %s for reading! Error is %d\n", 580 deviceFilename, errno); 581 if (errno == EACCES) { // User is probably not running as root 582 fprintf(stderr, "You must run this program as root or use sudo!\n"); 583 } // if 584 } // if/else 585 return (allOK); 586} // GPTData::LoadPartitions() 587 588// Loads the GPT, as much as possible. Returns 1 if this seems to have 589// succeeded, 0 if there are obvious problems.... 590int GPTData::ForceLoadGPTData(int fd) { 591 int allOK = 1, validHeaders; 592 off_t seekTo; 593 char* storage; 594 uint32_t newCRC, sizeOfParts; 595 596 // Seek to and read the main GPT header 597 lseek64(fd, 512, SEEK_SET); 598 read(fd, &mainHeader, 512); // read main GPT header 599 mainCrcOk = CheckHeaderCRC(&mainHeader); 600 if (IsLittleEndian() == 0) // big-endian system; adjust header byte order.... 601 ReverseHeaderBytes(&mainHeader); 602 603 // Load backup header, check its CRC, and store the results of the 604 // check for future reference. Load backup header using pointer in main 605 // header if possible; but if main header has a CRC error, or if it 606 // points to beyond the end of the disk, load the last sector of the 607 // disk instead. 608 if (mainCrcOk) { 609 if (mainHeader.backupLBA < diskSize) { 610 seekTo = mainHeader.backupLBA * blockSize; 611 } else { 612 seekTo = (diskSize * blockSize) - UINT64_C(512); 613 printf("Warning! Disk size is smaller than the main header indicates! Loading\n" 614 "secondary header from the last sector of the disk! You should use 'v' to\n" 615 "verify disk integrity, and perhaps options on the experts' menu to repair\n" 616 "the disk.\n"); 617 } // else 618 } else { 619 seekTo = (diskSize * blockSize) - UINT64_C(512); 620 } // if/else (mainCrcOk) 621 622 if (lseek64(fd, seekTo, SEEK_SET) != (off_t) -1) { 623 read(fd, &secondHeader, 512); // read secondary GPT header 624 secondCrcOk = CheckHeaderCRC(&secondHeader); 625 if (IsLittleEndian() == 0) // big-endian system; adjust header byte order.... 626 ReverseHeaderBytes(&secondHeader); 627 } else { 628 allOK = 0; 629 state = gpt_invalid; 630 fprintf(stderr, "Unable to seek to secondary GPT header at sector %llu!\n", 631 diskSize - (UINT64_C(1))); 632 } // if/else lseek 633 634 // Return valid headers code: 0 = both headers bad; 1 = main header 635 // good, backup bad; 2 = backup header good, main header bad; 636 // 3 = both headers good. Note these codes refer to valid GPT 637 // signatures and version numbers; more subtle problems will elude 638 // this check! 639 validHeaders = CheckHeaderValidity(); 640 641 // Read partitions (from primary array) 642 if (validHeaders > 0) { // if at least one header is OK.... 643 // GPT appears to be valid.... 644 state = gpt_valid; 645 646 // We're calling the GPT valid, but there's a possibility that one 647 // of the two headers is corrupt. If so, use the one that seems to 648 // be in better shape to regenerate the bad one 649 if (validHeaders == 2) { // valid backup header, invalid main header 650 printf("Caution: invalid main GPT header, but valid backup; regenerating main header\n" 651 "from backup!\n"); 652 RebuildMainHeader(); 653 mainCrcOk = secondCrcOk; // Since copied, use CRC validity of backup 654 } else if (validHeaders == 1) { // valid main header, invalid backup 655 printf("Caution: invalid backup GPT header, but valid main header; regenerating\n" 656 "backup header from main header.\n"); 657 RebuildSecondHeader(); 658 secondCrcOk = mainCrcOk; // Since regenerated, use CRC validity of main 659 } // if/else/if 660 661 // Load the main partition table, including storing results of its 662 // CRC check 663 if (LoadMainTable() == 0) 664 allOK = 0; 665 666 // Load backup partition table into temporary storage to check 667 // its CRC and store the results, then discard this temporary 668 // storage, since we don't use it in any but recovery operations 669 seekTo = secondHeader.partitionEntriesLBA * (off_t) blockSize; 670 if ((lseek64(fd, seekTo, SEEK_SET) != (off_t) -1) && (secondCrcOk)) { 671 sizeOfParts = secondHeader.numParts * secondHeader.sizeOfPartitionEntries; 672 storage = (char*) malloc(sizeOfParts); 673 read(fd, storage, sizeOfParts); 674 newCRC = chksum_crc32((unsigned char*) storage, sizeOfParts); 675 free(storage); 676 secondPartsCrcOk = (newCRC == secondHeader.partitionEntriesCRC); 677 } // if 678 679 // Check for valid CRCs and warn if there are problems 680 if ((mainCrcOk == 0) || (secondCrcOk == 0) || (mainPartsCrcOk == 0) || 681 (secondPartsCrcOk == 0)) { 682 printf("Warning! One or more CRCs don't match. You should repair the disk!\n"); 683 state = gpt_corrupt; 684 } // if 685 } else { 686 state = gpt_invalid; 687 } // if/else 688 return allOK; 689} // GPTData::ForceLoadGPTData() 690 691// Loads the partition table pointed to by the main GPT header. The 692// main GPT header in memory MUST be valid for this call to do anything 693// sensible! 694int GPTData::LoadMainTable(void) { 695 int fd, retval = 0; 696 uint32_t newCRC, sizeOfParts; 697 698 if ((fd = open(device, O_RDONLY)) != -1) { 699 // Set internal data structures for number of partitions on the disk 700 SetGPTSize(mainHeader.numParts); 701 702 // Load main partition table, and record whether its CRC 703 // matches the stored value 704 lseek64(fd, mainHeader.partitionEntriesLBA * blockSize, SEEK_SET); 705 sizeOfParts = mainHeader.numParts * mainHeader.sizeOfPartitionEntries; 706 read(fd, partitions, sizeOfParts); 707 newCRC = chksum_crc32((unsigned char*) partitions, sizeOfParts); 708 mainPartsCrcOk = (newCRC == mainHeader.partitionEntriesCRC); 709 if (IsLittleEndian() == 0) 710 ReversePartitionBytes(); 711 retval = 1; 712 } // if 713 return retval; 714} // GPTData::LoadMainTable() 715 716// Load the second (backup) partition table as the primary partition 717// table. Used in repair functions 718void GPTData::LoadSecondTableAsMain(void) { 719 int fd; 720 off_t seekTo; 721 uint32_t sizeOfParts, newCRC; 722 723 if ((fd = open(device, O_RDONLY)) != -1) { 724 seekTo = secondHeader.partitionEntriesLBA * (off_t) blockSize; 725 if (lseek64(fd, seekTo, SEEK_SET) != (off_t) -1) { 726 SetGPTSize(secondHeader.numParts); 727 sizeOfParts = secondHeader.numParts * secondHeader.sizeOfPartitionEntries; 728 read(fd, partitions, sizeOfParts); 729 newCRC = chksum_crc32((unsigned char*) partitions, sizeOfParts); 730 secondPartsCrcOk = (newCRC == secondHeader.partitionEntriesCRC); 731 mainPartsCrcOk = secondPartsCrcOk; 732 if (IsLittleEndian() == 0) 733 ReversePartitionBytes(); 734 if (!secondPartsCrcOk) { 735 printf("Error! After loading backup partitions, the CRC still doesn't check out!\n"); 736 } // if 737 } else { 738 printf("Error! Couldn't seek to backup partition table!\n"); 739 } // if/else 740 } else { 741 printf("Error! Couldn't open device %s when recovering backup partition table!\n", device); 742 } // if/else 743} // GPTData::LoadSecondTableAsMain() 744 745// Writes GPT (and protective MBR) to disk. Returns 1 on successful 746// write, 0 if there was a problem. 747int GPTData::SaveGPTData(void) { 748 int allOK = 1; 749 char answer, line[256]; 750 int fd; 751 uint64_t secondTable; 752 uint32_t numParts; 753 off_t offset; 754 755 if (strlen(device) == 0) { 756 printf("Device not defined.\n"); 757 } // if 758 759 // First do some final sanity checks.... 760 // Is there enough space to hold the GPT headers and partition tables, 761 // given the partition sizes? 762 if (CheckGPTSize() > 0) { 763 allOK = 0; 764 } // if 765 766 // Check that disk is really big enough to handle this... 767 if (mainHeader.backupLBA > diskSize) { 768 fprintf(stderr, "Error! Disk is too small! The 'e' option on the experts' menu might fix the\n" 769 "problem (or it might not). Aborting!\n"); 770 printf("(Disk size is %llu sectors, needs to be %llu sectors.)\n", diskSize, 771 mainHeader.backupLBA); 772 allOK = 0; 773 } // if 774 // Check that second header is properly placed. Warn and ask if this should 775 // be corrected if the test fails.... 776 if (mainHeader.backupLBA < (diskSize - UINT64_C(1))) { 777 printf("Warning! Secondary header is placed too early on the disk! Do you want to\n" 778 "correct this problem? "); 779 if (GetYN() == 'Y') { 780 MoveSecondHeaderToEnd(); 781 printf("Have moved second header and partition table to correct location.\n"); 782 } else { 783 printf("Have not corrected the problem. Strange problems may occur in the future!\n"); 784 } // if correction requested 785 } // if 786 787 // Check for overlapping partitions.... 788 if (FindOverlaps() > 0) { 789 allOK = 0; 790 fprintf(stderr, "Aborting write operation!\n"); 791 } // if 792 793 // Check for mismatched MBR and GPT data, but let it pass if found 794 // (function displays warning message) 795 FindHybridMismatches(); 796 797 // Pull out some data that's needed before doing byte-order reversal on 798 // big-endian systems.... 799 numParts = mainHeader.numParts; 800 secondTable = secondHeader.partitionEntriesLBA; 801 if (IsLittleEndian() == 0) { 802 // Reverse partition bytes first, since that function requires non-reversed 803 // data from the main header.... 804 ReversePartitionBytes(); 805 ReverseHeaderBytes(&mainHeader); 806 ReverseHeaderBytes(&secondHeader); 807 } // if 808 RecomputeCRCs(); 809 810 if (allOK) { 811 printf("\nFinal checks complete. About to write GPT data. THIS WILL OVERWRITE EXISTING\n"); 812 printf("MBR PARTITIONS!! THIS PROGRAM IS BETA QUALITY AT BEST. IF YOU LOSE ALL YOUR\n"); 813 printf("DATA, YOU HAVE ONLY YOURSELF TO BLAME IF YOU ANSWER 'Y' BELOW!\n\n"); 814 printf("Do you want to proceed, possibly destroying your data? (Y/N) "); 815 fgets(line, 255, stdin); 816 sscanf(line, "%c", &answer); 817 if ((answer == 'Y') || (answer == 'y')) { 818 printf("OK; writing new GPT partition table.\n"); 819 } else { 820 allOK = 0; 821 } // if/else 822 } // if 823 824 // Do it! 825 if (allOK) { 826 fd = OpenForWrite(device); 827 if (fd != -1) { 828 // First, write the protective MBR... 829 protectiveMBR.WriteMBRData(fd); 830 831 // Now write the main GPT header... 832 if (allOK) 833 if (write(fd, &mainHeader, 512) == -1) 834 allOK = 0; 835 836 // Now write the main partition tables... 837 if (allOK) { 838 if (write(fd, partitions, GPT_SIZE * numParts) == -1) 839 allOK = 0; 840 } // if 841 842 // Now seek to near the end to write the secondary GPT.... 843 if (allOK) { 844 offset = (off_t) secondTable * (off_t) (blockSize); 845 if (lseek64(fd, offset, SEEK_SET) == (off_t) - 1) { 846 allOK = 0; 847 printf("Unable to seek to end of disk!\n"); 848 } // if 849 } // if 850 851 // Now write the secondary partition tables.... 852 if (allOK) 853 if (write(fd, partitions, GPT_SIZE * numParts) == -1) 854 allOK = 0; 855 856 // Now write the secondary GPT header... 857 if (allOK) 858 if (write(fd, &secondHeader, 512) == -1) 859 allOK = 0; 860 861 // re-read the partition table 862 if (allOK) { 863 DiskSync(fd); 864 } // if 865 866 if (allOK) { // writes completed OK 867 printf("The operation has completed successfully.\n"); 868 } else { 869 printf("Warning! An error was reported when writing the partition table! This error\n"); 870 printf("MIGHT be harmless, but you may have trashed the disk! Use parted and, if\n"); 871 printf("necessary, restore your original partition table.\n"); 872 } // if/else 873 close(fd); 874 } else { 875 fprintf(stderr, "Unable to open device %s for writing! Errno is %d! Aborting write!\n", 876 device, errno); 877 allOK = 0; 878 } // if/else 879 } else { 880 printf("Aborting write of new partition table.\n"); 881 } // if 882 883 if (IsLittleEndian() == 0) { 884 // Reverse (normalize) header bytes first, since ReversePartitionBytes() 885 // requires non-reversed data in mainHeader... 886 ReverseHeaderBytes(&mainHeader); 887 ReverseHeaderBytes(&secondHeader); 888 ReversePartitionBytes(); 889 } // if 890 891 return (allOK); 892} // GPTData::SaveGPTData() 893 894// Save GPT data to a backup file. This function does much less error 895// checking than SaveGPTData(). It can therefore preserve many types of 896// corruption for later analysis; however, it preserves only the MBR, 897// the main GPT header, the backup GPT header, and the main partition 898// table; it discards the backup partition table, since it should be 899// identical to the main partition table on healthy disks. 900int GPTData::SaveGPTBackup(char* filename) { 901 int fd, allOK = 1; 902 uint32_t numParts; 903 904 if ((fd = open(filename, O_WRONLY | O_CREAT, S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH)) != -1) { 905 // Reverse the byte order, if necessary.... 906 numParts = mainHeader.numParts; 907 if (IsLittleEndian() == 0) { 908 ReversePartitionBytes(); 909 ReverseHeaderBytes(&mainHeader); 910 ReverseHeaderBytes(&secondHeader); 911 } // if 912 913 // Recomputing the CRCs is likely to alter them, which could be bad 914 // if the intent is to save a potentially bad GPT for later analysis; 915 // but if we don't do this, we get bogus errors when we load the 916 // backup. I'm favoring misses over false alarms.... 917 RecomputeCRCs(); 918 919 // Now write the protective MBR... 920 protectiveMBR.WriteMBRData(fd); 921 922 // Now write the main GPT header... 923 if (allOK) 924 if (write(fd, &mainHeader, 512) == -1) 925 allOK = 0; 926 927 // Now write the secondary GPT header... 928 if (allOK) 929 if (write(fd, &secondHeader, 512) == -1) 930 allOK = 0; 931 932 // Now write the main partition tables... 933 if (allOK) { 934 if (write(fd, partitions, GPT_SIZE * numParts) == -1) 935 allOK = 0; 936 } // if 937 938 if (allOK) { // writes completed OK 939 printf("The operation has completed successfully.\n"); 940 } else { 941 printf("Warning! An error was reported when writing the backup file.\n"); 942 printf("It may not be usable!\n"); 943 } // if/else 944 close(fd); 945 946 // Now reverse the byte-order reversal, if necessary.... 947 if (IsLittleEndian() == 0) { 948 ReverseHeaderBytes(&mainHeader); 949 ReverseHeaderBytes(&secondHeader); 950 ReversePartitionBytes(); 951 } // if 952 } else { 953 fprintf(stderr, "Unable to open file %s for writing! Aborting!\n", filename); 954 allOK = 0; 955 } // if/else 956 return allOK; 957} // GPTData::SaveGPTBackup() 958 959// Load GPT data from a backup file created by SaveGPTBackup(). This function 960// does minimal error checking. It returns 1 if it completed successfully, 961// 0 if there was a problem. In the latter case, it creates a new empty 962// set of partitions. 963int GPTData::LoadGPTBackup(char* filename) { 964 int fd, allOK = 1, val; 965 uint32_t numParts, sizeOfEntries, sizeOfParts, newCRC; 966 int littleEndian = 1; 967 968 if ((fd = open(filename, O_RDONLY)) != -1) { 969 if (IsLittleEndian() == 0) 970 littleEndian = 0; 971 972 // Let the MBRData class load the saved MBR... 973 protectiveMBR.ReadMBRData(fd, 0); // 0 = don't check block size 974 975 // Load the main GPT header, check its vaility, and set the GPT 976 // size based on the data 977 read(fd, &mainHeader, 512); 978 mainCrcOk = CheckHeaderCRC(&mainHeader); 979 980 // Reverse byte order, if necessary 981 if (littleEndian == 0) { 982 ReverseHeaderBytes(&mainHeader); 983 } // if 984 985 // Load the backup GPT header in much the same way as the main 986 // GPT header.... 987 read(fd, &secondHeader, 512); 988 secondCrcOk = CheckHeaderCRC(&secondHeader); 989 990 // Reverse byte order, if necessary 991 if (littleEndian == 0) { 992 ReverseHeaderBytes(&secondHeader); 993 } // if 994 995 // Return valid headers code: 0 = both headers bad; 1 = main header 996 // good, backup bad; 2 = backup header good, main header bad; 997 // 3 = both headers good. Note these codes refer to valid GPT 998 // signatures and version numbers; more subtle problems will elude 999 // this check! 1000 if ((val = CheckHeaderValidity()) > 0) { 1001 if (val == 2) { // only backup header seems to be good 1002 numParts = secondHeader.numParts; 1003 sizeOfEntries = secondHeader.sizeOfPartitionEntries; 1004 } else { // main header is OK 1005 numParts = mainHeader.numParts; 1006 sizeOfEntries = mainHeader.sizeOfPartitionEntries; 1007 } // if/else 1008 1009 SetGPTSize(numParts); 1010 1011 // If current disk size doesn't match that of backup.... 1012 if (secondHeader.currentLBA != diskSize - UINT64_C(1)) { 1013 printf("Warning! Current disk size doesn't match that of the backup!\n" 1014 "Adjusting sizes to match, but subsequent problems are possible!\n"); 1015 MoveSecondHeaderToEnd(); 1016 } // if 1017 1018 // Load main partition table, and record whether its CRC 1019 // matches the stored value 1020 sizeOfParts = numParts * sizeOfEntries; 1021 read(fd, partitions, sizeOfParts); 1022 1023 newCRC = chksum_crc32((unsigned char*) partitions, sizeOfParts); 1024 mainPartsCrcOk = (newCRC == mainHeader.partitionEntriesCRC); 1025 secondPartsCrcOk = (newCRC == secondHeader.partitionEntriesCRC); 1026 // Reverse byte order, if necessary 1027 if (littleEndian == 0) { 1028 ReversePartitionBytes(); 1029 } // if 1030 1031 } else { 1032 allOK = 0; 1033 } // if/else 1034 } else { 1035 allOK = 0; 1036 fprintf(stderr, "Unable to open file %s for reading! Aborting!\n", filename); 1037 } // if/else 1038 1039 // Something went badly wrong, so blank out partitions 1040 if (allOK == 0) { 1041 ClearGPTData(); 1042 protectiveMBR.MakeProtectiveMBR(); 1043 } // if 1044 return allOK; 1045} // GPTData::LoadGPTBackup() 1046 1047// Tell user whether Apple Partition Map (APM) was discovered.... 1048void GPTData::ShowAPMState(void) { 1049 if (apmFound) 1050 printf(" APM: present\n"); 1051 else 1052 printf(" APM: not present\n"); 1053} // GPTData::ShowAPMState() 1054 1055// Tell user about the state of the GPT data.... 1056void GPTData::ShowGPTState(void) { 1057 switch (state) { 1058 case gpt_invalid: 1059 printf(" GPT: not present\n"); 1060 break; 1061 case gpt_valid: 1062 printf(" GPT: present\n"); 1063 break; 1064 case gpt_corrupt: 1065 printf(" GPT: damaged\n"); 1066 break; 1067 default: 1068 printf("\a GPT: unknown -- bug!\n"); 1069 break; 1070 } // switch 1071} // GPTData::ShowGPTState() 1072 1073// Display the basic GPT data 1074void GPTData::DisplayGPTData(void) { 1075 int i; 1076 char sizeInSI[255]; // String to hold size of disk in SI units 1077 char tempStr[255]; 1078 uint64_t temp, totalFree; 1079 1080 BytesToSI(diskSize * blockSize, sizeInSI); 1081 printf("Disk %s: %llu sectors, %s\n", device, 1082 (unsigned long long) diskSize, sizeInSI); 1083 printf("Disk identifier (GUID): %s\n", GUIDToStr(mainHeader.diskGUID, tempStr)); 1084 printf("Partition table holds up to %lu entries\n", (unsigned long) mainHeader.numParts); 1085 printf("First usable sector is %lu, last usable sector is %lu\n", 1086 (unsigned long) mainHeader.firstUsableLBA, 1087 (unsigned long) mainHeader.lastUsableLBA); 1088 totalFree = FindFreeBlocks(&i, &temp); 1089 printf("Total free space is %llu sectors (%s)\n", totalFree, 1090 BytesToSI(totalFree * (uint64_t) blockSize, sizeInSI)); 1091 printf("\nNumber Start (sector) End (sector) Size Code Name\n"); 1092 for (i = 0; i < mainHeader.numParts; i++) { 1093 partitions[i].ShowSummary(i, blockSize); 1094 } // for 1095} // GPTData::DisplayGPTData() 1096 1097// Get partition number from user and then call ShowPartDetails(partNum) 1098// to show its detailed information 1099void GPTData::ShowDetails(void) { 1100 int partNum; 1101 uint32_t low, high; 1102 1103 if (GetPartRange(&low, &high) > 0) { 1104 partNum = GetPartNum(); 1105 ShowPartDetails(partNum); 1106 } else { 1107 printf("No partitions\n"); 1108 } // if/else 1109} // GPTData::ShowDetails() 1110 1111// Show detailed information on the specified partition 1112void GPTData::ShowPartDetails(uint32_t partNum) { 1113 if (partitions[partNum].GetFirstLBA() != 0) { 1114 partitions[partNum].ShowDetails(blockSize); 1115 } else { 1116 printf("Partition #%d does not exist.", (int) (partNum + 1)); 1117 } // if 1118} // GPTData::ShowPartDetails() 1119 1120/********************************************************************* 1121 * * 1122 * Begin functions that obtain information from the users, and often * 1123 * do something with that information (call other functions) * 1124 * * 1125 *********************************************************************/ 1126 1127// Prompts user for partition number and returns the result. 1128uint32_t GPTData::GetPartNum(void) { 1129 uint32_t partNum; 1130 uint32_t low, high; 1131 char prompt[255]; 1132 1133 if (GetPartRange(&low, &high) > 0) { 1134 sprintf(prompt, "Partition number (%d-%d): ", low + 1, high + 1); 1135 partNum = GetNumber(low + 1, high + 1, low, prompt); 1136 } else partNum = 1; 1137 return (partNum - 1); 1138} // GPTData::GetPartNum() 1139 1140// What it says: Resize the partition table. (Default is 128 entries.) 1141void GPTData::ResizePartitionTable(void) { 1142 int newSize; 1143 char prompt[255]; 1144 uint32_t curLow, curHigh; 1145 1146 printf("Current partition table size is %lu.\n", 1147 (unsigned long) mainHeader.numParts); 1148 GetPartRange(&curLow, &curHigh); 1149 curHigh++; // since GetPartRange() returns numbers starting from 0... 1150 // There's no point in having fewer than four partitions.... 1151 if (curHigh < 4) 1152 curHigh = 4; 1153 sprintf(prompt, "Enter new size (%d up, default %d): ", (int) curHigh, 1154 (int) NUM_GPT_ENTRIES); 1155 newSize = GetNumber(4, 65535, 128, prompt); 1156 if (newSize < 128) { 1157 printf("Caution: The partition table size should officially be 16KB or larger,\n" 1158 "which works out to 128 entries. In practice, smaller tables seem to\n" 1159 "work with most OSes, but this practice is risky. I'm proceeding with\n" 1160 "the resize, but you may want to reconsider this action and undo it.\n\n"); 1161 } // if 1162 SetGPTSize(newSize); 1163} // GPTData::ResizePartitionTable() 1164 1165// Interactively create a partition 1166void GPTData::CreatePartition(void) { 1167 uint64_t firstBlock, firstInLargest, lastBlock, sector; 1168 char prompt[255]; 1169 int partNum, firstFreePart = 0; 1170 1171 // Find first free partition... 1172 while (partitions[firstFreePart].GetFirstLBA() != 0) { 1173 firstFreePart++; 1174 } // while 1175 1176 if (((firstBlock = FindFirstAvailable()) != 0) && 1177 (firstFreePart < mainHeader.numParts)) { 1178 lastBlock = FindLastAvailable(firstBlock); 1179 firstInLargest = FindFirstInLargest(); 1180 1181 // Get partition number.... 1182 do { 1183 sprintf(prompt, "Partition number (%d-%d, default %d): ", firstFreePart + 1, 1184 mainHeader.numParts, firstFreePart + 1); 1185 partNum = GetNumber(firstFreePart + 1, mainHeader.numParts, 1186 firstFreePart + 1, prompt) - 1; 1187 if (partitions[partNum].GetFirstLBA() != 0) 1188 printf("partition %d is in use.\n", partNum + 1); 1189 } while (partitions[partNum].GetFirstLBA() != 0); 1190 1191 // Get first block for new partition... 1192 sprintf(prompt, 1193 "First sector (%llu-%llu, default = %llu) or {+-}size{KMGT}: ", 1194 firstBlock, lastBlock, firstInLargest); 1195 do { 1196 sector = GetSectorNum(firstBlock, lastBlock, firstInLargest, prompt); 1197 } while (IsFree(sector) == 0); 1198 Align(§or); // Align sector to correct multiple 1199 firstBlock = sector; 1200 1201 // Get last block for new partitions... 1202 lastBlock = FindLastInFree(firstBlock); 1203 sprintf(prompt, 1204 "Last sector (%llu-%llu, default = %llu) or {+-}size{KMGT}: ", 1205 firstBlock, lastBlock, lastBlock); 1206 do { 1207 sector = GetSectorNum(firstBlock, lastBlock, lastBlock, prompt); 1208 } while (IsFree(sector) == 0); 1209 lastBlock = sector; 1210 1211 partitions[partNum].SetFirstLBA(firstBlock); 1212 partitions[partNum].SetLastLBA(lastBlock); 1213 1214 partitions[partNum].SetUniqueGUID(1); 1215 partitions[partNum].ChangeType(); 1216 partitions[partNum].SetName((unsigned char*) partitions[partNum].GetNameType(prompt)); 1217 } else { 1218 printf("No free sectors available\n"); 1219 } // if/else 1220} // GPTData::CreatePartition() 1221 1222// Interactively delete a partition (duh!) 1223void GPTData::DeletePartition(void) { 1224 int partNum; 1225 uint32_t low, high; 1226 uint64_t startSector, length; 1227 char prompt[255]; 1228 1229 if (GetPartRange(&low, &high) > 0) { 1230 sprintf(prompt, "Partition number (%d-%d): ", low + 1, high + 1); 1231 partNum = GetNumber(low + 1, high + 1, low, prompt); 1232 1233 // In case there's a protective MBR, look for & delete matching 1234 // MBR partition.... 1235 startSector = partitions[partNum - 1].GetFirstLBA(); 1236 length = partitions[partNum - 1].GetLengthLBA(); 1237 protectiveMBR.DeleteByLocation(startSector, length); 1238 1239 // Now delete the GPT partition 1240 partitions[partNum - 1].BlankPartition(); 1241 } else { 1242 printf("No partitions\n"); 1243 } // if/else 1244} // GPTData::DeletePartition() 1245 1246// Prompt user for a partition number, then change its type code 1247// using ChangeGPTType(struct GPTPartition*) function. 1248void GPTData::ChangePartType(void) { 1249 int partNum; 1250 uint32_t low, high; 1251 1252 if (GetPartRange(&low, &high) > 0) { 1253 partNum = GetPartNum(); 1254 partitions[partNum].ChangeType(); 1255 } else { 1256 printf("No partitions\n"); 1257 } // if/else 1258} // GPTData::ChangePartType() 1259 1260// Partition attributes seem to be rarely used, but I want a way to 1261// adjust them for completeness.... 1262void GPTData::SetAttributes(uint32_t partNum) { 1263 Attributes theAttr; 1264 1265 theAttr.SetAttributes(partitions[partNum].GetAttributes()); 1266 theAttr.DisplayAttributes(); 1267 theAttr.ChangeAttributes(); 1268 partitions[partNum].SetAttributes(theAttr.GetAttributes()); 1269} // GPTData::SetAttributes() 1270 1271// This function destroys the on-disk GPT structures. Returns 1 if the 1272// user confirms destruction, 0 if the user aborts. 1273// If prompt == 0, don't ask user about proceeding and do NOT wipe out 1274// MBR. (Set prompt == 0 when doing a GPT-to-MBR conversion.) 1275int GPTData::DestroyGPT(int prompt) { 1276 int fd, i; 1277 char blankSector[512], goOn = 'Y', blank = 'N'; 1278 1279 for (i = 0; i < 512; i++) { 1280 blankSector[i] = '\0'; 1281 } // for 1282 1283 if (((apmFound) || (bsdFound)) && prompt) { 1284 printf("WARNING: APM or BSD disklabel structures detected! This operation could\n" 1285 "damage any APM or BSD partitions on this disk!\n"); 1286 } // if APM or BSD 1287 if (prompt) { 1288 printf("\a\aAbout to wipe out GPT on %s. Proceed? ", device); 1289 goOn = GetYN(); 1290 } // if 1291 if (goOn == 'Y') { 1292 fd = open(device, O_WRONLY); 1293#ifdef __APPLE__ 1294 // MacOS X requires a shared lock under some circumstances.... 1295 if (fd < 0) { 1296 fd = open(device, O_WRONLY|O_SHLOCK); 1297 } // if 1298#endif 1299 if (fd != -1) { 1300 lseek64(fd, mainHeader.currentLBA * 512, SEEK_SET); // seek to GPT header 1301 write(fd, blankSector, 512); // blank it out 1302 lseek64(fd, mainHeader.partitionEntriesLBA * 512, SEEK_SET); // seek to partition table 1303 for (i = 0; i < GetBlocksInPartTable(); i++) 1304 write(fd, blankSector, 512); 1305 lseek64(fd, secondHeader.partitionEntriesLBA * 512, SEEK_SET); // seek to partition table 1306 for (i = 0; i < GetBlocksInPartTable(); i++) 1307 write(fd, blankSector, 512); 1308 lseek64(fd, secondHeader.currentLBA * 512, SEEK_SET); // seek to GPT header 1309 write(fd, blankSector, 512); // blank it out 1310 if (prompt) { 1311 printf("Blank out MBR? "); 1312 blank = GetYN(); 1313 }// if 1314 // Note on below: Touch the MBR only if the user wants it completely 1315 // blanked out. Version 0.4.2 deleted the 0xEE partition and re-wrote 1316 // the MBR, but this could wipe out a valid MBR that the program 1317 // had subsequently discarded (say, if it conflicted with older GPT 1318 // structures). 1319 if (blank == 'Y') { 1320 lseek64(fd, 0, SEEK_SET); 1321 write(fd, blankSector, 512); // blank it out 1322 } else { 1323 printf("MBR is unchanged. You may need to delete an EFI GPT (0xEE) partition\n" 1324 "with fdisk or another tool.\n"); 1325 } // if/else 1326 DiskSync(fd); 1327 close(fd); 1328 printf("GPT data structures destroyed! You may now partition the disk using fdisk or\n" 1329 "other utilities. Program will now terminate.\n"); 1330 } else { 1331 printf("Problem opening %s for writing! Program will now terminate.\n", device); 1332 } // if/else (fd != -1) 1333 } // if (goOn == 'Y') 1334 return (goOn == 'Y'); 1335} // GPTData::DestroyGPT() 1336 1337/************************************************************************** 1338 * * 1339 * Partition table transformation functions (MBR or BSD disklabel to GPT) * 1340 * (some of these functions may require user interaction) * 1341 * * 1342 **************************************************************************/ 1343 1344// Examines the MBR & GPT data, and perhaps asks the user questions, to 1345// determine which set of data to use: the MBR (use_mbr), the GPT (use_gpt), 1346// or create a new set of partitions (use_new) 1347WhichToUse GPTData::UseWhichPartitions(void) { 1348 WhichToUse which = use_new; 1349 MBRValidity mbrState; 1350 int answer; 1351 1352 mbrState = protectiveMBR.GetValidity(); 1353 1354 if ((state == gpt_invalid) && ((mbrState == mbr) || (mbrState == hybrid))) { 1355 printf("\n\a***************************************************************\n" 1356 "Found invalid GPT and valid MBR; converting MBR to GPT format.\n" 1357 "THIS OPERATON IS POTENTIALLY DESTRUCTIVE! Exit by typing 'q' if\n" 1358 "you don't want to convert your MBR partitions to GPT format!\n" 1359 "***************************************************************\n\n"); 1360 which = use_mbr; 1361 } // if 1362 1363 if ((state == gpt_invalid) && bsdFound) { 1364 printf("\n\a**********************************************************************\n" 1365 "Found invalid GPT and valid BSD disklabel; converting BSD disklabel\n" 1366 "to GPT format. THIS OPERATON IS POTENTIALLY DESTRUCTIVE! Your first\n" 1367 "BSD partition will likely be unusable. Exit by typing 'q' if you don't\n" 1368 "want to convert your BSD partitions to GPT format!\n" 1369 "**********************************************************************\n\n"); 1370 which = use_bsd; 1371 } // if 1372 1373 if ((state == gpt_valid) && (mbrState == gpt)) { 1374 printf("Found valid GPT with protective MBR; using GPT.\n"); 1375 which = use_gpt; 1376 } // if 1377 if ((state == gpt_valid) && (mbrState == hybrid)) { 1378 printf("Found valid GPT with hybrid MBR; using GPT.\n"); 1379 which = use_gpt; 1380 } // if 1381 if ((state == gpt_valid) && (mbrState == invalid)) { 1382 printf("\aFound valid GPT with corrupt MBR; using GPT and will create new\nprotective MBR on save.\n"); 1383 which = use_gpt; 1384 protectiveMBR.MakeProtectiveMBR(); 1385 } // if 1386 if ((state == gpt_valid) && (mbrState == mbr)) { 1387 printf("Found valid MBR and GPT. Which do you want to use?\n"); 1388 answer = GetNumber(1, 3, 2, (char*) " 1 - MBR\n 2 - GPT\n 3 - Create blank GPT\n\nYour answer: "); 1389 if (answer == 1) { 1390 which = use_mbr; 1391 } else if (answer == 2) { 1392 which = use_gpt; 1393 protectiveMBR.MakeProtectiveMBR(); 1394 printf("Using GPT and creating fresh protective MBR.\n"); 1395 } else which = use_new; 1396 } // if 1397 1398 // Nasty decisions here -- GPT is present, but corrupt (bad CRCs or other 1399 // problems) 1400 if (state == gpt_corrupt) { 1401 if ((mbrState == mbr) || (mbrState == hybrid)) { 1402 printf("Found valid MBR and corrupt GPT. Which do you want to use? (Using the\n" 1403 "GPT MAY permit recovery of GPT data.)\n"); 1404 answer = GetNumber(1, 3, 2, (char*) " 1 - MBR\n 2 - GPT\n 3 - Create blank GPT\n\nYour answer: "); 1405 if (answer == 1) { 1406 which = use_mbr; 1407// protectiveMBR.MakeProtectiveMBR(); 1408 } else if (answer == 2) { 1409 which = use_gpt; 1410 } else which = use_new; 1411 } else if (mbrState == invalid) { 1412 printf("Found invalid MBR and corrupt GPT. What do you want to do? (Using the\n" 1413 "GPT MAY permit recovery of GPT data.)\n"); 1414 answer = GetNumber(1, 2, 1, (char*) " 1 - GPT\n 2 - Create blank GPT\n\nYour answer: "); 1415 if (answer == 1) { 1416 which = use_gpt; 1417 } else which = use_new; 1418 } else { // corrupt GPT, MBR indicates it's a GPT disk.... 1419 printf("\a\a****************************************************************************\n" 1420 "Caution: Found protective or hybrid MBR and corrupt GPT. Using GPT, but disk\n" 1421 "verification and recovery are STRONGLY recommended.\n" 1422 "****************************************************************************\n"); 1423 which = use_gpt; 1424 } // if/else/else 1425 } // if (corrupt GPT) 1426 1427 if (which == use_new) 1428 printf("Creating new GPT entries.\n"); 1429 1430 return which; 1431} // UseWhichPartitions() 1432 1433// Convert MBR partition table into GPT form 1434int GPTData::XFormPartitions(void) { 1435 int i, numToConvert; 1436 uint8_t origType; 1437 struct newGUID; 1438 1439 // Clear out old data & prepare basics.... 1440 ClearGPTData(); 1441 1442 // Convert the smaller of the # of GPT or MBR partitions 1443 if (mainHeader.numParts > (MAX_MBR_PARTS)) 1444 numToConvert = MAX_MBR_PARTS; 1445 else 1446 numToConvert = mainHeader.numParts; 1447 1448 for (i = 0; i < numToConvert; i++) { 1449 origType = protectiveMBR.GetType(i); 1450 // don't waste CPU time trying to convert extended, hybrid protective, or 1451 // null (non-existent) partitions 1452 if ((origType != 0x05) && (origType != 0x0f) && (origType != 0x85) && 1453 (origType != 0x00) && (origType != 0xEE)) 1454 partitions[i] = protectiveMBR.AsGPT(i); 1455 } // for 1456 1457 // Convert MBR into protective MBR 1458 protectiveMBR.MakeProtectiveMBR(); 1459 1460 // Record that all original CRCs were OK so as not to raise flags 1461 // when doing a disk verification 1462 mainCrcOk = secondCrcOk = mainPartsCrcOk = secondPartsCrcOk = 1; 1463 1464 return (1); 1465} // GPTData::XFormPartitions() 1466 1467// Transforms BSD disklabel on the specified partition (numbered from 0). 1468// If an invalid partition number is given, the program prompts for one. 1469// Returns the number of new partitions created. 1470int GPTData::XFormDisklabel(int i) { 1471 uint32_t low, high, partNum, startPart; 1472 uint16_t hexCode; 1473 int goOn = 1, numDone = 0; 1474 BSDData disklabel; 1475 1476 if (GetPartRange(&low, &high) != 0) { 1477 if ((i < low) || (i > high)) 1478 partNum = GetPartNum(); 1479 else 1480 partNum = (uint32_t) i; 1481 1482 // Find the partition after the last used one 1483 startPart = high + 1; 1484 1485 // Now see if the specified partition has a BSD type code.... 1486 hexCode = partitions[partNum].GetHexType(); 1487 if ((hexCode != 0xa500) && (hexCode != 0xa900)) { 1488 printf("Specified partition doesn't have a disklabel partition type " 1489 "code.\nContinue anyway?"); 1490 goOn = (GetYN() == 'Y'); 1491 } // if 1492 1493 // If all is OK, read the disklabel and convert it. 1494 if (goOn) { 1495 goOn = disklabel.ReadBSDData(device, partitions[partNum].GetFirstLBA(), 1496 partitions[partNum].GetLastLBA()); 1497 if ((goOn) && (disklabel.IsDisklabel())) { 1498 numDone = XFormDisklabel(&disklabel, startPart); 1499 if (numDone == 1) 1500 printf("Converted %d BSD partition.\n", numDone); 1501 else 1502 printf("Converted %d BSD partitions.\n", numDone); 1503 } else { 1504 printf("Unable to convert partitions! Unrecognized BSD disklabel.\n"); 1505 } // if/else 1506 } // if 1507 if (numDone > 0) { // converted partitions; delete carrier 1508 partitions[partNum].BlankPartition(); 1509 } // if 1510 } else { 1511 printf("No partitions\n"); 1512 } // if/else 1513 return numDone; 1514} // GPTData::XFormDisklable(int i) 1515 1516// Transform the partitions on an already-loaded BSD disklabel... 1517int GPTData::XFormDisklabel(BSDData* disklabel, int startPart) { 1518 int i, numDone = 0; 1519 1520 if ((disklabel->IsDisklabel()) && (startPart >= 0) && 1521 (startPart < mainHeader.numParts)) { 1522 for (i = 0; i < disklabel->GetNumParts(); i++) { 1523 partitions[i + startPart] = disklabel->AsGPT(i); 1524 if (partitions[i + startPart].GetFirstLBA() != UINT64_C(0)) 1525 numDone++; 1526 } // for 1527 } // if 1528 1529 // Record that all original CRCs were OK so as not to raise flags 1530 // when doing a disk verification 1531 mainCrcOk = secondCrcOk = mainPartsCrcOk = secondPartsCrcOk = 1; 1532 1533 return numDone; 1534} // GPTData::XFormDisklabel(BSDData* disklabel) 1535 1536// Add one GPT partition to MBR. Used by XFormToMBR() and MakeHybrid() 1537// functions. Returns 1 if operation was successful. 1538int GPTData::OnePartToMBR(uint32_t gptPart, int mbrPart) { 1539 int allOK = 1, typeCode, bootable; 1540 uint64_t length; 1541 char line[255]; 1542 1543 if ((mbrPart < 0) || (mbrPart > 3)) { 1544 printf("MBR partition %d is out of range; omitting it.\n", mbrPart + 1); 1545 allOK = 0; 1546 } // if 1547 if (gptPart >= mainHeader.numParts) { 1548 printf("GPT partition %d is out of range; omitting it.\n", gptPart + 1); 1549 allOK = 0; 1550 } // if 1551 if (allOK && (partitions[gptPart].GetLastLBA() == UINT64_C(0))) { 1552 printf("GPT partition %d is undefined; omitting it.\n", gptPart + 1); 1553 allOK = 0; 1554 } // if 1555 if (allOK && (partitions[gptPart].GetFirstLBA() <= UINT32_MAX) && 1556 (partitions[gptPart].GetLengthLBA() <= UINT32_MAX)) { 1557 if (partitions[gptPart].GetLastLBA() > UINT32_MAX) { 1558 printf("Caution: Partition end point past 32-bit pointer boundary;" 1559 " some OSes may\nreact strangely.\n"); 1560 } // if partition ends past 32-bit (usually 2TiB) boundary 1561 do { 1562 printf("Enter an MBR hex code (default %02X): ", 1563 typeHelper.GUIDToID(partitions[gptPart].GetType()) / 256); 1564 fgets(line, 255, stdin); 1565 sscanf(line, "%x", &typeCode); 1566 if (line[0] == '\n') 1567 typeCode = partitions[gptPart].GetHexType() / 256; 1568 } while ((typeCode <= 0) || (typeCode > 255)); 1569 printf("Set the bootable flag? "); 1570 bootable = (GetYN() == 'Y'); 1571 length = partitions[gptPart].GetLengthLBA(); 1572 protectiveMBR.MakePart(mbrPart, (uint32_t) partitions[gptPart].GetFirstLBA(), 1573 (uint32_t) length, typeCode, bootable); 1574 } else { // partition out of range 1575 printf("Partition %d begins beyond the 32-bit pointer limit of MBR " 1576 "partitions, or is\n too big; omitting it.\n", gptPart + 1); 1577 allOK = 0; 1578 } // if/else 1579 return allOK; 1580} // GPTData::OnePartToMBR() 1581 1582// Convert the GPT to MBR form. This function is necessarily limited; it 1583// handles at most four partitions and creates layouts that ignore CHS 1584// geometries. Returns the number of converted partitions; if this value 1585// is over 0, the calling function should call DestroyGPT() to destroy 1586// the GPT data, and then exit. 1587int GPTData::XFormToMBR(void) { 1588 char line[255]; 1589 int i, j, numParts, numConverted = 0; 1590 uint32_t partNums[4]; 1591 1592 // Get the numbers of up to four partitions to add to the 1593 // hybrid MBR.... 1594 numParts = CountParts(); 1595 printf("Counted %d partitions.\n", numParts); 1596 1597 // Prepare the MBR for conversion (empty it of existing partitions). 1598 protectiveMBR.EmptyMBR(0); 1599 protectiveMBR.SetDiskSize(diskSize); 1600 1601 if (numParts > 4) { // Over four partitions; engage in triage 1602 printf("Type from one to four GPT partition numbers, separated by spaces, to be\n" 1603 "used in the MBR, in sequence: "); 1604 fgets(line, 255, stdin); 1605 numParts = sscanf(line, "%d %d %d %d", &partNums[0], &partNums[1], 1606 &partNums[2], &partNums[3]); 1607 } else { // Four or fewer partitions; convert them all 1608 i = j = 0; 1609 while ((j < numParts) && (i < mainHeader.numParts)) { 1610 if (partitions[i].GetFirstLBA() > 0) { // if GPT part. is defined 1611 partNums[j++] = ++i; // flag it for conversion 1612 } else i++; 1613 } // while 1614 } // if/else 1615 1616 for (i = 0; i < numParts; i++) { 1617 j = partNums[i] - 1; 1618 printf("\nCreating entry for partition #%d\n", j + 1); 1619 numConverted += OnePartToMBR(j, i); 1620 } // for 1621 return numConverted; 1622} // GPTData::XFormToMBR() 1623 1624// Create a hybrid MBR -- an ugly, funky thing that helps GPT work with 1625// OSes that don't understand GPT. 1626void GPTData::MakeHybrid(void) { 1627 uint32_t partNums[3]; 1628 char line[255]; 1629 int numParts, numConverted = 0, i, j, typeCode, mbrNum; 1630 char fillItUp = 'M'; // fill extra partition entries? (Yes/No/Maybe) 1631 char eeFirst = 'Y'; // Whether EFI GPT (0xEE) partition comes first in table 1632 1633 printf("\nWARNING! Hybrid MBRs are flaky and potentially dangerous! If you decide not\n" 1634 "to use one, just hit the Enter key at the below prompt and your MBR\n" 1635 "partition table will be untouched.\n\n\a"); 1636 1637 // Now get the numbers of up to three partitions to add to the 1638 // hybrid MBR.... 1639 printf("Type from one to three GPT partition numbers, separated by spaces, to be\n" 1640 "added to the hybrid MBR, in sequence: "); 1641 fgets(line, 255, stdin); 1642 numParts = sscanf(line, "%d %d %d", &partNums[0], &partNums[1], &partNums[2]); 1643 1644 if (numParts > 0) { 1645 // Blank out the protective MBR, but leave the boot loader code 1646 // alone.... 1647 protectiveMBR.EmptyMBR(0); 1648 protectiveMBR.SetDiskSize(diskSize); 1649 printf("Place EFI GPT (0xEE) partition first in MBR (good for GRUB)? "); 1650 eeFirst = GetYN(); 1651 } // if 1652 1653 for (i = 0; i < numParts; i++) { 1654 j = partNums[i] - 1; 1655 printf("\nCreating entry for partition #%d\n", j + 1); 1656 if (eeFirst == 'Y') 1657 mbrNum = i + 1; 1658 else 1659 mbrNum = i; 1660 numConverted += OnePartToMBR(j, mbrNum); 1661 } // for 1662 1663 if ((numParts > 0) && (numConverted > 0)) { // User opted to create a hybrid MBR.... 1664 // Create EFI protective partition that covers the start of the disk. 1665 // If this location (covering the main GPT data structures) is omitted, 1666 // Linux won't find any partitions on the disk. Note that this is 1667 // NUMBERED AFTER the hybrid partitions, contrary to what the 1668 // gptsync utility does. This is because Windows seems to choke on 1669 // disks with a 0xEE partition in the first slot and subsequent 1670 // additional partitions, unless it boots from the disk. 1671 if (eeFirst == 'Y') 1672 mbrNum = 0; 1673 else 1674 mbrNum = numParts; 1675 protectiveMBR.MakePart(mbrNum, 1, protectiveMBR.FindLastInFree(1), 0xEE); 1676 protectiveMBR.SetHybrid(); 1677 1678 // ... and for good measure, if there are any partition spaces left, 1679 // optionally create another protective EFI partition to cover as much 1680 // space as possible.... 1681 for (i = 0; i < 4; i++) { 1682 if (protectiveMBR.GetType(i) == 0x00) { // unused entry.... 1683 if (fillItUp == 'M') { 1684 printf("\nUnused partition space(s) found. Use one to protect more partitions? "); 1685 fillItUp = GetYN(); 1686 typeCode = 0x00; // use this to flag a need to get type code 1687 } // if 1688 if (fillItUp == 'Y') { 1689 while ((typeCode <= 0) || (typeCode > 255)) { 1690 printf("Enter an MBR hex code (EE is EFI GPT, but may confuse MacOS): "); 1691 // Comment on above: Mac OS treats disks with more than one 1692 // 0xEE MBR partition as MBR disks, not as GPT disks. 1693 fgets(line, 255, stdin); 1694 sscanf(line, "%x", &typeCode); 1695 if (line[0] == '\n') 1696 typeCode = 0; 1697 } // while 1698 protectiveMBR.MakeBiggestPart(i, typeCode); // make a partition 1699 } // if (fillItUp == 'Y') 1700 } // if unused entry 1701 } // for (i = 0; i < 4; i++) 1702 } // if (numParts > 0) 1703} // GPTData::MakeHybrid() 1704 1705/********************************************************************** 1706 * * 1707 * Functions that adjust GPT data structures WITHOUT user interaction * 1708 * (they may display information for the user's benefit, though) * 1709 * * 1710 **********************************************************************/ 1711 1712// Resizes GPT to specified number of entries. Creates a new table if 1713// necessary, copies data if it already exists. 1714int GPTData::SetGPTSize(uint32_t numEntries) { 1715 struct GPTPart* newParts; 1716 struct GPTPart* trash; 1717 uint32_t i, high, copyNum; 1718 int allOK = 1; 1719 1720 // First, adjust numEntries upward, if necessary, to get a number 1721 // that fills the allocated sectors 1722 i = blockSize / GPT_SIZE; 1723 if ((numEntries % i) != 0) { 1724 printf("Adjusting GPT size from %lu ", (unsigned long) numEntries); 1725 numEntries = ((numEntries / i) + 1) * i; 1726 printf("to %lu to fill the sector\n", (unsigned long) numEntries); 1727 } // if 1728 1729 // Do the work only if the # of partitions is changing. Along with being 1730 // efficient, this prevents mucking the with location of the secondary 1731 // partition table, which causes problems when loading data from a RAID 1732 // array that's been expanded because this function is called when loading 1733 // data. 1734 if ((numEntries != mainHeader.numParts) || (partitions == NULL)) { 1735 newParts = (GPTPart*) calloc(numEntries, sizeof (GPTPart)); 1736 if (newParts != NULL) { 1737 if (partitions != NULL) { // existing partitions; copy them over 1738 GetPartRange(&i, &high); 1739 if (numEntries < (high + 1)) { // Highest entry too high for new # 1740 printf("The highest-numbered partition is %lu, which is greater than the requested\n" 1741 "partition table size of %d; cannot resize. Perhaps sorting will help.\n", 1742 (unsigned long) (high + 1), numEntries); 1743 allOK = 0; 1744 } else { // go ahead with copy 1745 if (numEntries < mainHeader.numParts) 1746 copyNum = numEntries; 1747 else 1748 copyNum = mainHeader.numParts; 1749 for (i = 0; i < copyNum; i++) { 1750 newParts[i] = partitions[i]; 1751 } // for 1752 trash = partitions; 1753 partitions = newParts; 1754 free(trash); 1755 } // if 1756 } else { // No existing partition table; just create it 1757 partitions = newParts; 1758 } // if/else existing partitions 1759 mainHeader.numParts = numEntries; 1760 secondHeader.numParts = numEntries; 1761 mainHeader.firstUsableLBA = ((numEntries * GPT_SIZE) / blockSize) + 2 ; 1762 secondHeader.firstUsableLBA = mainHeader.firstUsableLBA; 1763 MoveSecondHeaderToEnd(); 1764 if (diskSize > 0) 1765 CheckGPTSize(); 1766 } else { // Bad memory allocation 1767 fprintf(stderr, "Error allocating memory for partition table!\n"); 1768 allOK = 0; 1769 } // if/else 1770 } // if/else 1771 return (allOK); 1772} // GPTData::SetGPTSize() 1773 1774// Blank the partition array 1775void GPTData::BlankPartitions(void) { 1776 uint32_t i; 1777 1778 for (i = 0; i < mainHeader.numParts; i++) { 1779 partitions[i].BlankPartition(); 1780 } // for 1781} // GPTData::BlankPartitions() 1782 1783// Sort the GPT entries, eliminating gaps and making for a logical 1784// ordering. Relies on QuickSortGPT() for the bulk of the work 1785void GPTData::SortGPT(void) { 1786 int i, lastPart = 0; 1787 GPTPart temp; 1788 1789 // First, find the last partition with data, so as not to 1790 // spend needless time sorting empty entries.... 1791 for (i = 0; i < mainHeader.numParts; i++) { 1792 if (partitions[i].GetFirstLBA() > 0) 1793 lastPart = i; 1794 } // for 1795 1796 // Now swap empties with the last partitions, to simplify the logic 1797 // in the Quicksort function.... 1798 i = 0; 1799 while (i < lastPart) { 1800 if (partitions[i].GetFirstLBA() == 0) { 1801 temp = partitions[i]; 1802 partitions[i] = partitions[lastPart]; 1803 partitions[lastPart] = temp; 1804 lastPart--; 1805 } // if 1806 i++; 1807 } // while 1808 1809 // Now call the recursive quick sort routine to do the real work.... 1810 QuickSortGPT(partitions, 0, lastPart); 1811} // GPTData::SortGPT() 1812 1813// Set up data structures for entirely new set of partitions on the 1814// specified device. Returns 1 if OK, 0 if there were problems. 1815// Note that this function does NOT clear the protectiveMBR data 1816// structure, since it may hold the original MBR partitions if the 1817// program was launched on an MBR disk, and those may need to be 1818// converted to GPT format. 1819int GPTData::ClearGPTData(void) { 1820 int goOn = 1, i; 1821 1822 // Set up the partition table.... 1823 free(partitions); 1824 partitions = NULL; 1825 SetGPTSize(NUM_GPT_ENTRIES); 1826 1827 // Now initialize a bunch of stuff that's static.... 1828 mainHeader.signature = GPT_SIGNATURE; 1829 mainHeader.revision = 0x00010000; 1830 mainHeader.headerSize = HEADER_SIZE; 1831 mainHeader.reserved = 0; 1832 mainHeader.currentLBA = UINT64_C(1); 1833 mainHeader.partitionEntriesLBA = (uint64_t) 2; 1834 mainHeader.sizeOfPartitionEntries = GPT_SIZE; 1835 for (i = 0; i < GPT_RESERVED; i++) { 1836 mainHeader.reserved2[i] = '\0'; 1837 } // for 1838 1839 // Now some semi-static items (computed based on end of disk) 1840 mainHeader.backupLBA = diskSize - UINT64_C(1); 1841 mainHeader.lastUsableLBA = diskSize - mainHeader.firstUsableLBA; 1842 1843 // Set a unique GUID for the disk, based on random numbers 1844 // rand() is only 32 bits, so multiply together to fill a 64-bit value 1845 mainHeader.diskGUID.data1 = (uint64_t) rand() * (uint64_t) rand(); 1846 mainHeader.diskGUID.data2 = (uint64_t) rand() * (uint64_t) rand(); 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// Used internally and called by the 'e' option on the recovery & 1865// transformation menu, to help users of RAID arrays who add disk space 1866// to their arrays. 1867void GPTData::MoveSecondHeaderToEnd() { 1868 mainHeader.backupLBA = secondHeader.currentLBA = diskSize - UINT64_C(1); 1869 mainHeader.lastUsableLBA = secondHeader.lastUsableLBA = diskSize - mainHeader.firstUsableLBA; 1870 secondHeader.partitionEntriesLBA = secondHeader.lastUsableLBA + UINT64_C(1); 1871} // GPTData::FixSecondHeaderLocation() 1872 1873void GPTData::SetName(uint32_t partNum, char* theName) { 1874 if ((partNum >= 0) && (partNum < mainHeader.numParts)) 1875 if (partitions[partNum].GetFirstLBA() > 0) 1876 partitions[partNum].SetName((unsigned char*) theName); 1877} // GPTData::SetName 1878 1879// Set the disk GUID to the specified value. Note that the header CRCs must 1880// be recomputed after calling this function. 1881void GPTData::SetDiskGUID(GUIDData newGUID) { 1882 mainHeader.diskGUID = newGUID; 1883 secondHeader.diskGUID = newGUID; 1884} // SetDiskGUID() 1885 1886// Set the unique GUID of the specified partition. Returns 1 on 1887// successful completion, 0 if there were problems (invalid 1888// partition number). 1889int GPTData::SetPartitionGUID(uint32_t pn, GUIDData theGUID) { 1890 int retval = 0; 1891 1892 if (pn < mainHeader.numParts) { 1893 if (partitions[pn].GetFirstLBA() != UINT64_C(0)) { 1894 partitions[pn].SetUniqueGUID(theGUID); 1895 retval = 1; 1896 } // if 1897 } // if 1898 return retval; 1899} // GPTData::SetPartitionGUID() 1900 1901// Adjust sector number so that it falls on a sector boundary that's a 1902// multiple of sectorAlignment. This is done to improve the performance 1903// of Western Digital Advanced Format disks and disks with similar 1904// technology from other companies, which use 4096-byte sectors 1905// internally although they translate to 512-byte sectors for the 1906// benefit of the OS. If partitions aren't properly aligned on these 1907// disks, some filesystem data structures can span multiple physical 1908// sectors, degrading performance. This function should be called 1909// only on the FIRST sector of the partition, not the last! 1910// This function returns 1 if the alignment was altered, 0 if it 1911// was unchanged. 1912int GPTData::Align(uint64_t* sector) { 1913 int retval = 0, sectorOK = 0; 1914 uint64_t earlier, later, testSector, original; 1915 1916 if ((*sector % sectorAlignment) != 0) { 1917 original = *sector; 1918 retval = 1; 1919 earlier = (*sector / sectorAlignment) * sectorAlignment; 1920 later = earlier + (uint64_t) sectorAlignment; 1921 1922 // Check to see that every sector between the earlier one and the 1923 // requested one is clear, and that it's not too early.... 1924 if (earlier >= mainHeader.firstUsableLBA) { 1925// printf("earlier is %llu, first usable is %llu\n", earlier, mainHeader.firstUsableLBA); 1926 sectorOK = 1; 1927 testSector = earlier; 1928 do { 1929 sectorOK = IsFree(testSector++); 1930 } while ((sectorOK == 1) && (testSector < *sector)); 1931 if (sectorOK == 1) { 1932 *sector = earlier; 1933// printf("Moved sector earlier.\n"); 1934 } // if 1935 } // if firstUsableLBA check 1936 1937 // If couldn't move the sector earlier, try to move it later instead.... 1938 if ((sectorOK != 1) && (later <= mainHeader.lastUsableLBA)) { 1939 sectorOK = 1; 1940 testSector = later; 1941 do { 1942 sectorOK = IsFree(testSector--); 1943 } while ((sectorOK == 1) && (testSector > *sector)); 1944 if (sectorOK == 1) { 1945 *sector = later; 1946// printf("Moved sector later\n"); 1947 } // if 1948 } // if 1949 1950 // If sector was changed successfully, inform the user of this fact. 1951 // Otherwise, notify the user that it couldn't be done.... 1952 if (sectorOK == 1) { 1953 printf("Information: Moved requested sector from %llu to %llu for\n" 1954 "alignment purposes. Use 'l' on the experts' menu to adjust alignment.\n", 1955 original, *sector); 1956 } else { 1957 printf("Information: Sector not aligned on %d-sector boundary and could not be moved.\n" 1958 "If you're using a Western Digital Advanced Format or similar disk with\n" 1959 "underlying 4096-byte sectors, performance may suffer.\n", sectorAlignment); 1960 retval = 0; 1961 } // if/else 1962 } // if 1963 return retval; 1964} // GPTData::Align() 1965 1966/******************************************************** 1967 * * 1968 * Functions that return data about GPT data structures * 1969 * (most of these are inline in gpt.h) * 1970 * * 1971 ********************************************************/ 1972 1973// Find the low and high used partition numbers (numbered from 0). 1974// Return value is the number of partitions found. Note that the 1975// *low and *high values are both set to 0 when no partitions 1976// are found, as well as when a single partition in the first 1977// position exists. Thus, the return value is the only way to 1978// tell when no partitions exist. 1979int GPTData::GetPartRange(uint32_t *low, uint32_t *high) { 1980 uint32_t i; 1981 int numFound = 0; 1982 1983 *low = mainHeader.numParts + 1; // code for "not found" 1984 *high = 0; 1985 if (mainHeader.numParts > 0) { // only try if partition table exists... 1986 for (i = 0; i < mainHeader.numParts; i++) { 1987 if (partitions[i].GetFirstLBA() != UINT64_C(0)) { // it exists 1988 *high = i; // since we're counting up, set the high value 1989 // Set the low value only if it's not yet found... 1990 if (*low == (mainHeader.numParts + 1)) *low = i; 1991 numFound++; 1992 } // if 1993 } // for 1994 } // if 1995 1996 // Above will leave *low pointing to its "not found" value if no partitions 1997 // are defined, so reset to 0 if this is the case.... 1998 if (*low == (mainHeader.numParts + 1)) 1999 *low = 0; 2000 return numFound; 2001} // GPTData::GetPartRange() 2002 2003// Returns the number of defined partitions. 2004uint32_t GPTData::CountParts(void) { 2005 int i, counted = 0; 2006 2007 for (i = 0; i < mainHeader.numParts; i++) { 2008 if (partitions[i].GetFirstLBA() > 0) 2009 counted++; 2010 } // for 2011 return counted; 2012} // GPTData::CountParts() 2013 2014/**************************************************** 2015 * * 2016 * Functions that return data about disk free space * 2017 * * 2018 ****************************************************/ 2019 2020// Find the first available block after the starting point; returns 0 if 2021// there are no available blocks left 2022uint64_t GPTData::FindFirstAvailable(uint64_t start) { 2023 uint64_t first; 2024 uint32_t i; 2025 int firstMoved = 0; 2026 2027 // Begin from the specified starting point or from the first usable 2028 // LBA, whichever is greater... 2029 if (start < mainHeader.firstUsableLBA) 2030 first = mainHeader.firstUsableLBA; 2031 else 2032 first = start; 2033 2034 // ...now search through all partitions; if first is within an 2035 // existing partition, move it to the next sector after that 2036 // partition and repeat. If first was moved, set firstMoved 2037 // flag; repeat until firstMoved is not set, so as to catch 2038 // cases where partitions are out of sequential order.... 2039 do { 2040 firstMoved = 0; 2041 for (i = 0; i < mainHeader.numParts; i++) { 2042 if ((first >= partitions[i].GetFirstLBA()) && 2043 (first <= partitions[i].GetLastLBA())) { // in existing part. 2044 first = partitions[i].GetLastLBA() + 1; 2045 firstMoved = 1; 2046 } // if 2047 } // for 2048 } while (firstMoved == 1); 2049 if (first > mainHeader.lastUsableLBA) 2050 first = 0; 2051 return (first); 2052} // GPTData::FindFirstAvailable() 2053 2054// Finds the first available sector in the largest block of unallocated 2055// space on the disk. Returns 0 if there are no available blocks left 2056uint64_t GPTData::FindFirstInLargest(void) { 2057 uint64_t start, firstBlock, lastBlock, segmentSize, selectedSize = 0, selectedSegment = 0; 2058 2059 start = 0; 2060 do { 2061 firstBlock = FindFirstAvailable(start); 2062 if (firstBlock != UINT32_C(0)) { // something's free... 2063 lastBlock = FindLastInFree(firstBlock); 2064 segmentSize = lastBlock - firstBlock + UINT32_C(1); 2065 if (segmentSize > selectedSize) { 2066 selectedSize = segmentSize; 2067 selectedSegment = firstBlock; 2068 } // if 2069 start = lastBlock + 1; 2070 } // if 2071 } while (firstBlock != 0); 2072 return selectedSegment; 2073} // GPTData::FindFirstInLargest() 2074 2075// Find the last available block on the disk at or after the start 2076// block. Returns 0 if there are no available partitions after 2077// (or including) start. 2078uint64_t GPTData::FindLastAvailable(uint64_t start) { 2079 uint64_t last; 2080 uint32_t i; 2081 int lastMoved = 0; 2082 2083 // Start by assuming the last usable LBA is available.... 2084 last = mainHeader.lastUsableLBA; 2085 2086 // ...now, similar to algorithm in FindFirstAvailable(), search 2087 // through all partitions, moving last when it's in an existing 2088 // partition. Set the lastMoved flag so we repeat to catch cases 2089 // where partitions are out of logical order. 2090 do { 2091 lastMoved = 0; 2092 for (i = 0; i < mainHeader.numParts; i++) { 2093 if ((last >= partitions[i].GetFirstLBA()) && 2094 (last <= partitions[i].GetLastLBA())) { // in existing part. 2095 last = partitions[i].GetFirstLBA() - 1; 2096 lastMoved = 1; 2097 } // if 2098 } // for 2099 } while (lastMoved == 1); 2100 if (last < mainHeader.firstUsableLBA) 2101 last = 0; 2102 return (last); 2103} // GPTData::FindLastAvailable() 2104 2105// Find the last available block in the free space pointed to by start. 2106uint64_t GPTData::FindLastInFree(uint64_t start) { 2107 uint64_t nearestStart; 2108 uint32_t i; 2109 2110 nearestStart = mainHeader.lastUsableLBA; 2111 for (i = 0; i < mainHeader.numParts; i++) { 2112 if ((nearestStart > partitions[i].GetFirstLBA()) && 2113 (partitions[i].GetFirstLBA() > start)) { 2114 nearestStart = partitions[i].GetFirstLBA() - 1; 2115 } // if 2116 } // for 2117 return (nearestStart); 2118} // GPTData::FindLastInFree() 2119 2120// Finds the total number of free blocks, the number of segments in which 2121// they reside, and the size of the largest of those segments 2122uint64_t GPTData::FindFreeBlocks(int *numSegments, uint64_t *largestSegment) { 2123 uint64_t start = UINT64_C(0); // starting point for each search 2124 uint64_t totalFound = UINT64_C(0); // running total 2125 uint64_t firstBlock; // first block in a segment 2126 uint64_t lastBlock; // last block in a segment 2127 uint64_t segmentSize; // size of segment in blocks 2128 int num = 0; 2129 2130 *largestSegment = UINT64_C(0); 2131 do { 2132 firstBlock = FindFirstAvailable(start); 2133 if (firstBlock != UINT64_C(0)) { // something's free... 2134 lastBlock = FindLastInFree(firstBlock); 2135 segmentSize = lastBlock - firstBlock + UINT64_C(1); 2136 if (segmentSize > *largestSegment) { 2137 *largestSegment = segmentSize; 2138 } // if 2139 totalFound += segmentSize; 2140 num++; 2141 start = lastBlock + 1; 2142 } // if 2143 } while (firstBlock != 0); 2144 *numSegments = num; 2145 return totalFound; 2146} // GPTData::FindFreeBlocks() 2147 2148// Returns 1 if sector is unallocated, 0 if it's allocated to a partition 2149int GPTData::IsFree(uint64_t sector) { 2150 int isFree = 1; 2151 uint32_t i; 2152 2153 for (i = 0; i < mainHeader.numParts; i++) { 2154 if ((sector >= partitions[i].GetFirstLBA()) && 2155 (sector <= partitions[i].GetLastLBA())) { 2156 isFree = 0; 2157 } // if 2158 } // for 2159 if ((sector < mainHeader.firstUsableLBA) || 2160 (sector > mainHeader.lastUsableLBA)) { 2161 isFree = 0; 2162 } // if 2163 return (isFree); 2164} // GPTData::IsFree() 2165 2166/******************************** 2167 * * 2168 * Endianness support functions * 2169 * * 2170 ********************************/ 2171 2172void GPTData::ReverseHeaderBytes(struct GPTHeader* header) { 2173 ReverseBytes(&header->signature, 8); 2174 ReverseBytes(&header->revision, 4); 2175 ReverseBytes(&header->headerSize, 4); 2176 ReverseBytes(&header->headerCRC, 4); 2177 ReverseBytes(&header->reserved, 4); 2178 ReverseBytes(&header->currentLBA, 8); 2179 ReverseBytes(&header->backupLBA, 8); 2180 ReverseBytes(&header->firstUsableLBA, 8); 2181 ReverseBytes(&header->lastUsableLBA, 8); 2182 ReverseBytes(&header->partitionEntriesLBA, 8); 2183 ReverseBytes(&header->numParts, 4); 2184 ReverseBytes(&header->sizeOfPartitionEntries, 4); 2185 ReverseBytes(&header->partitionEntriesCRC, 4); 2186 ReverseBytes(&header->reserved2, GPT_RESERVED); 2187 ReverseBytes(&header->diskGUID.data1, 8); 2188 ReverseBytes(&header->diskGUID.data2, 8); 2189} // GPTData::ReverseHeaderBytes() 2190 2191// IMPORTANT NOTE: This function requires non-reversed mainHeader 2192// structure! 2193void GPTData::ReversePartitionBytes() { 2194 uint32_t i; 2195 2196 // Check GPT signature on big-endian systems; this will mismatch 2197 // if the function is called out of order. Unfortunately, it'll also 2198 // mismatch if there's data corruption. 2199 if ((mainHeader.signature != GPT_SIGNATURE) && (IsLittleEndian() == 0)) { 2200 printf("GPT signature mismatch in GPTData::ReversePartitionBytes(). This indicates\n" 2201 "data corruption or a misplaced call to this function.\n"); 2202 } // if signature mismatch.... 2203 for (i = 0; i < mainHeader.numParts; i++) { 2204 partitions[i].ReversePartBytes(); 2205 } // for 2206} // GPTData::ReversePartitionBytes() 2207 2208/****************************************** 2209 * * 2210 * Additional non-class support functions * 2211 * * 2212 ******************************************/ 2213 2214// Check to be sure that data type sizes are correct. The basic types (uint*_t) should 2215// never fail these tests, but the struct types may fail depending on compile options. 2216// Specifically, the -fpack-struct option to gcc may be required to ensure proper structure 2217// sizes. 2218int SizesOK(void) { 2219 int allOK = 1; 2220 2221 if (sizeof(uint8_t) != 1) { 2222 fprintf(stderr, "uint8_t is %lu bytes, should be 1 byte; aborting!\n", sizeof(uint8_t)); 2223 allOK = 0; 2224 } // if 2225 if (sizeof(uint16_t) != 2) { 2226 fprintf(stderr, "uint16_t is %lu bytes, should be 2 bytes; aborting!\n", sizeof(uint16_t)); 2227 allOK = 0; 2228 } // if 2229 if (sizeof(uint32_t) != 4) { 2230 fprintf(stderr, "uint32_t is %lu bytes, should be 4 bytes; aborting!\n", sizeof(uint32_t)); 2231 allOK = 0; 2232 } // if 2233 if (sizeof(uint64_t) != 8) { 2234 fprintf(stderr, "uint64_t is %lu bytes, should be 8 bytes; aborting!\n", sizeof(uint64_t)); 2235 allOK = 0; 2236 } // if 2237 if (sizeof(struct MBRRecord) != 16) { 2238 fprintf(stderr, "MBRRecord is %lu bytes, should be 16 bytes; aborting!\n", sizeof(MBRRecord)); 2239 allOK = 0; 2240 } // if 2241 if (sizeof(struct TempMBR) != 512) { 2242 fprintf(stderr, "TempMBR is %lu bytes, should be 512 bytes; aborting!\n", sizeof(TempMBR)); 2243 allOK = 0; 2244 } // if 2245 if (sizeof(struct GPTHeader) != 512) { 2246 fprintf(stderr, "GPTHeader is %lu bytes, should be 512 bytes; aborting!\n", sizeof(GPTHeader)); 2247 allOK = 0; 2248 } // if 2249 if (sizeof(GPTPart) != 128) { 2250 fprintf(stderr, "GPTPart is %lu bytes, should be 128 bytes; aborting!\n", sizeof(GPTPart)); 2251 allOK = 0; 2252 } // if 2253// Determine endianness; set allOK = 0 if running on big-endian hardware 2254 if (IsLittleEndian() == 0) { 2255 fprintf(stderr, "\aRunning on big-endian hardware. Big-endian support is new and poorly" 2256 " tested!\nBeware!\n"); 2257 // allOK = 0; 2258 } // if 2259 return (allOK); 2260} // SizesOK() 2261 2262