1
/* gpt.cc -- Functions for loading, saving, and manipulating legacy MBR and GPT partition
4
/* By Rod Smith, initial coding January to February, 2009 */
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. */
9
#define __STDC_LIMIT_MACROS
10
#define __STDC_CONSTANT_MACROS
25
#include "parttypes.h"
26
#include "attributes.h"
30
/****************************************
32
* GPTData class and related structures *
34
****************************************/
36
// Default constructor
37
GPTData::GPTData(void) {
38
blockSize = SECTOR_SIZE; // set a default
49
srand((unsigned int) time(NULL));
50
SetGPTSize(NUM_GPT_ENTRIES);
51
} // GPTData default constructor
53
// The following constructor loads GPT data from a device file
54
GPTData::GPTData(char* filename) {
55
blockSize = SECTOR_SIZE; // set a default
66
srand((unsigned int) time(NULL));
67
LoadPartitions(filename);
68
} // GPTData(char* filename) constructor
71
GPTData::~GPTData(void) {
73
} // GPTData destructor
75
/*********************************************************************
77
* Begin functions that verify data, or that adjust the verification *
78
* information (compute CRCs, rebuild headers) *
80
*********************************************************************/
82
// Perform detailed verification, reporting on any problems found, but
83
// do *NOT* recover from these problems. Returns the total number of
84
// problems identified.
85
int GPTData::Verify(void) {
86
int problems = 0, numSegments;
87
uint64_t totalFree, largestSegment;
88
char tempStr[255], siTotal[255], siLargest[255];
90
// First, check for CRC errors in the GPT data....
93
printf("\nProblem: The CRC for the main GPT header is invalid. The main GPT header may\n"
94
"be corrupt. Consider loading the backup GPT header to rebuild the main GPT\n"
97
if (!mainPartsCrcOk) {
99
printf("\nProblem: The CRC for the main partition table is invalid. This table may be\n"
100
"corrupt. Consider loading the backup partition table.\n");
104
printf("\nProblem: The CRC for the backup GPT header is invalid. The backup GPT header\n"
105
"may be corrupt. Consider using the main GPT header to rebuild the backup GPT\n"
108
if (!secondPartsCrcOk) {
110
printf("\nCaution: The CRC for the backup partition table is invalid. This table may\n"
111
"be corrupt. This program will automatically create a new backup partition\n"
112
"table when you save your partitions.\n");
115
// Now check that the main and backup headers both point to themselves....
116
if (mainHeader.currentLBA != 1) {
118
printf("\nProblem: The main header's self-pointer doesn't point to itself. This problem\n"
119
"is being automatically corrected, but it may be a symptom of more serious\n"
120
"problems. Think carefully before saving changes with 'w' or using this disk.\n");
121
mainHeader.currentLBA = 1;
123
if (secondHeader.currentLBA != (diskSize - UINT64_C(1))) {
125
printf("\nProblem: The secondary header's self-pointer indicates that it doesn't reside\n"
126
"at the end of the disk. If you've added a disk to a RAID array, use the 'e'\n"
127
"option on the experts' menu to adjust the secondary header's and partition\n"
128
"table's locations.\n");
131
// Now check that critical main and backup GPT entries match each other
132
if (mainHeader.currentLBA != secondHeader.backupLBA) {
134
printf("\nProblem: main GPT header's current LBA pointer (%llu) doesn't\n"
135
"match the backup GPT header's LBA pointer(%llu)\n",
136
(unsigned long long) mainHeader.currentLBA,
137
(unsigned long long) secondHeader.backupLBA);
139
if (mainHeader.backupLBA != secondHeader.currentLBA) {
141
printf("\nProblem: main GPT header's backup LBA pointer (%llu) doesn't\n"
142
"match the backup GPT header's current LBA pointer (%llu)\n",
143
(unsigned long long) mainHeader.backupLBA,
144
(unsigned long long) secondHeader.currentLBA);
146
if (mainHeader.firstUsableLBA != secondHeader.firstUsableLBA) {
148
printf("\nProblem: main GPT header's first usable LBA pointer (%llu) doesn't\n"
149
"match the backup GPT header's first usable LBA pointer (%llu)\n",
150
(unsigned long long) mainHeader.firstUsableLBA,
151
(unsigned long long) secondHeader.firstUsableLBA);
153
if (mainHeader.lastUsableLBA != secondHeader.lastUsableLBA) {
155
printf("\nProblem: main GPT header's last usable LBA pointer (%llu) doesn't\n"
156
"match the backup GPT header's last usable LBA pointer (%llu)\n",
157
(unsigned long long) mainHeader.lastUsableLBA,
158
(unsigned long long) secondHeader.lastUsableLBA);
160
if ((mainHeader.diskGUID.data1 != secondHeader.diskGUID.data1) ||
161
(mainHeader.diskGUID.data2 != secondHeader.diskGUID.data2)) {
163
printf("\nProblem: main header's disk GUID (%s) doesn't\n",
164
GUIDToStr(mainHeader.diskGUID, tempStr));
165
printf("match the backup GPT header's disk GUID (%s)\n",
166
GUIDToStr(secondHeader.diskGUID, tempStr));
168
if (mainHeader.numParts != secondHeader.numParts) {
170
printf("\nProblem: main GPT header's number of partitions (%lu) doesn't\n"
171
"match the backup GPT header's number of partitions (%lu)\n",
172
(unsigned long) mainHeader.numParts,
173
(unsigned long) secondHeader.numParts);
175
if (mainHeader.sizeOfPartitionEntries != secondHeader.sizeOfPartitionEntries) {
177
printf("\nProblem: main GPT header's size of partition entries (%lu) doesn't\n"
178
"match the backup GPT header's size of partition entries (%lu)\n",
179
(unsigned long) mainHeader.sizeOfPartitionEntries,
180
(unsigned long) secondHeader.sizeOfPartitionEntries);
183
// Now check for a few other miscellaneous problems...
184
// Check that the disk size will hold the data...
185
if (mainHeader.backupLBA > diskSize) {
187
printf("\nProblem: Disk is too small to hold all the data!\n");
188
printf("(Disk size is %llu sectors, needs to be %llu sectors.)\n",
189
(unsigned long long) diskSize,
190
(unsigned long long) mainHeader.backupLBA);
193
// Check for overlapping partitions....
194
problems += FindOverlaps();
196
// Check for mismatched MBR and GPT partitions...
197
problems += FindHybridMismatches();
199
// Verify that partitions don't run into GPT data areas....
200
problems += CheckGPTSize();
202
// Now compute available space, but only if no problems found, since
203
// problems could affect the results
205
totalFree = FindFreeBlocks(&numSegments, &largestSegment);
206
BytesToSI(totalFree * (uint64_t) blockSize, siTotal);
207
BytesToSI(largestSegment * (uint64_t) blockSize, siLargest);
208
printf("No problems found. %llu free sectors (%s) available in %u\n"
209
"segments, the largest of which is %llu sectors (%s) in size\n",
210
(unsigned long long) totalFree,
211
siTotal, numSegments, (unsigned long long) largestSegment,
214
printf("\nIdentified %d problems!\n", problems);
218
} // GPTData::Verify()
220
// Checks to see if the GPT tables overrun existing partitions; if they
221
// do, issues a warning but takes no action. Returns number of problems
222
// detected (0 if OK, 1 to 2 if problems).
223
int GPTData::CheckGPTSize(void) {
224
uint64_t overlap, firstUsedBlock, lastUsedBlock;
228
// first, locate the first & last used blocks
229
firstUsedBlock = UINT64_MAX;
231
for (i = 0; i < mainHeader.numParts; i++) {
232
if ((partitions[i].GetFirstLBA() < firstUsedBlock) &&
233
(partitions[i].GetFirstLBA() != 0))
234
firstUsedBlock = partitions[i].GetFirstLBA();
235
if (partitions[i].GetLastLBA() > lastUsedBlock)
236
lastUsedBlock = partitions[i].GetLastLBA();
239
// If the disk size is 0 (the default), then it means that various
240
// variables aren't yet set, so the below tests will be useless;
241
// therefore we should skip everything
243
if (mainHeader.firstUsableLBA > firstUsedBlock) {
244
overlap = mainHeader.firstUsableLBA - firstUsedBlock;
245
printf("Warning! Main partition table overlaps the first partition by %lu blocks!\n",
246
(unsigned long) overlap);
247
if (firstUsedBlock > 2) {
248
printf("Try reducing the partition table size by %lu entries.\n",
249
(unsigned long) (overlap * 4));
250
printf("(Use the 's' item on the experts' menu.)\n");
252
printf("You will need to delete this partition or resize it in another utility.\n");
255
} // Problem at start of disk
256
if (mainHeader.lastUsableLBA < lastUsedBlock) {
257
overlap = lastUsedBlock - mainHeader.lastUsableLBA;
258
printf("Warning! Secondary partition table overlaps the last partition by %lu blocks\n",
259
(unsigned long) overlap);
260
if (lastUsedBlock > (diskSize - 2)) {
261
printf("You will need to delete this partition or resize it in another utility.\n");
263
printf("Try reducing the partition table size by %lu entries.\n",
264
(unsigned long) (overlap * 4));
265
printf("(Use the 's' item on the experts' menu.)\n");
268
} // Problem at end of disk
269
} // if (diskSize != 0)
271
} // GPTData::CheckGPTSize()
273
// Check the validity of the GPT header. Returns 1 if the main header
274
// is valid, 2 if the backup header is valid, 3 if both are valid, and
275
// 0 if neither is valid. Note that this function just checks the GPT
276
// signature and revision numbers, not CRCs or other data.
277
int GPTData::CheckHeaderValidity(void) {
280
if (mainHeader.signature != GPT_SIGNATURE) {
282
// printf("Main GPT signature invalid; read 0x%016llX, should be\n0x%016llX\n",
283
// (unsigned long long) mainHeader.signature, (unsigned long long) GPT_SIGNATURE);
284
} else if ((mainHeader.revision != 0x00010000) && valid) {
286
printf("Unsupported GPT version in main header; read 0x%08lX, should be\n0x%08lX\n",
287
(unsigned long) mainHeader.revision, UINT32_C(0x00010000));
290
if (secondHeader.signature != GPT_SIGNATURE) {
292
// printf("Secondary GPT signature invalid; read 0x%016llX, should be\n0x%016llX\n",
293
// (unsigned long long) secondHeader.signature, (unsigned long long) GPT_SIGNATURE);
294
} else if ((secondHeader.revision != 0x00010000) && valid) {
296
printf("Unsupported GPT version in backup header; read 0x%08lX, should be\n0x%08lX\n",
297
(unsigned long) mainHeader.revision, UINT32_C(0x00010000));
300
// If MBR bad, check for an Apple disk signature
301
if ((protectiveMBR.GetValidity() == invalid) &&
302
(((mainHeader.signature << 32) == APM_SIGNATURE1) ||
303
(mainHeader.signature << 32) == APM_SIGNATURE2)) {
304
apmFound = 1; // Will display warning message later
308
} // GPTData::CheckHeaderValidity()
310
// Check the header CRC to see if it's OK...
311
// Note: Must be called BEFORE byte-order reversal on big-endian
313
int GPTData::CheckHeaderCRC(struct GPTHeader* header) {
314
uint32_t oldCRC, newCRC, hSize;
316
// Back up old header CRC and then blank it, since it must be 0 for
317
// computation to be valid
318
oldCRC = header->headerCRC;
319
header->headerCRC = UINT32_C(0);
320
hSize = header->headerSize;
322
// If big-endian system, reverse byte order
323
if (IsLittleEndian() == 0) {
324
ReverseBytes(&oldCRC, 4);
327
// Initialize CRC functions...
328
chksum_crc32gentab();
330
// Compute CRC, restore original, and return result of comparison
331
newCRC = chksum_crc32((unsigned char*) header, HEADER_SIZE);
332
header->headerCRC = oldCRC;
333
return (oldCRC == newCRC);
334
} // GPTData::CheckHeaderCRC()
336
// Recompute all the CRCs. Must be called before saving (but after reversing
337
// byte order on big-endian systems) if any changes have been made.
338
void GPTData::RecomputeCRCs(void) {
339
uint32_t crc, hSize, trueNumParts;
340
int littleEndian = 1;
342
// Initialize CRC functions...
343
chksum_crc32gentab();
345
hSize = mainHeader.headerSize;
346
littleEndian = IsLittleEndian();
348
// Compute CRC of partition tables & store in main and secondary headers
349
trueNumParts = mainHeader.numParts;
350
if (littleEndian == 0)
351
ReverseBytes(&trueNumParts, 4); // unreverse this key piece of data....
352
crc = chksum_crc32((unsigned char*) partitions, trueNumParts * GPT_SIZE);
353
mainHeader.partitionEntriesCRC = crc;
354
secondHeader.partitionEntriesCRC = crc;
355
if (littleEndian == 0) {
356
ReverseBytes(&mainHeader.partitionEntriesCRC, 4);
357
ReverseBytes(&secondHeader.partitionEntriesCRC, 4);
360
// Zero out GPT tables' own CRCs (required for correct computation)
361
mainHeader.headerCRC = 0;
362
secondHeader.headerCRC = 0;
364
// Compute & store CRCs of main & secondary headers...
365
crc = chksum_crc32((unsigned char*) &mainHeader, hSize);
366
if (littleEndian == 0)
367
ReverseBytes(&crc, 4);
368
mainHeader.headerCRC = crc;
369
crc = chksum_crc32((unsigned char*) &secondHeader, hSize);
370
if (littleEndian == 0)
371
ReverseBytes(&crc, 4);
372
secondHeader.headerCRC = crc;
373
} // GPTData::RecomputeCRCs()
375
// Rebuild the main GPT header, using the secondary header as a model.
376
// Typically called when the main header has been found to be corrupt.
377
void GPTData::RebuildMainHeader(void) {
380
mainHeader.signature = GPT_SIGNATURE;
381
mainHeader.revision = secondHeader.revision;
382
mainHeader.headerSize = secondHeader.headerSize;
383
mainHeader.headerCRC = UINT32_C(0);
384
mainHeader.reserved = secondHeader.reserved;
385
mainHeader.currentLBA = secondHeader.backupLBA;
386
mainHeader.backupLBA = secondHeader.currentLBA;
387
mainHeader.firstUsableLBA = secondHeader.firstUsableLBA;
388
mainHeader.lastUsableLBA = secondHeader.lastUsableLBA;
389
mainHeader.diskGUID.data1 = secondHeader.diskGUID.data1;
390
mainHeader.diskGUID.data2 = secondHeader.diskGUID.data2;
391
mainHeader.partitionEntriesLBA = UINT64_C(2);
392
mainHeader.numParts = secondHeader.numParts;
393
mainHeader.sizeOfPartitionEntries = secondHeader.sizeOfPartitionEntries;
394
mainHeader.partitionEntriesCRC = secondHeader.partitionEntriesCRC;
395
for (i = 0 ; i < GPT_RESERVED; i++)
396
mainHeader.reserved2[i] = secondHeader.reserved2[i];
397
} // GPTData::RebuildMainHeader()
399
// Rebuild the secondary GPT header, using the main header as a model.
400
void GPTData::RebuildSecondHeader(void) {
403
secondHeader.signature = GPT_SIGNATURE;
404
secondHeader.revision = mainHeader.revision;
405
secondHeader.headerSize = mainHeader.headerSize;
406
secondHeader.headerCRC = UINT32_C(0);
407
secondHeader.reserved = mainHeader.reserved;
408
secondHeader.currentLBA = mainHeader.backupLBA;
409
secondHeader.backupLBA = mainHeader.currentLBA;
410
secondHeader.firstUsableLBA = mainHeader.firstUsableLBA;
411
secondHeader.lastUsableLBA = mainHeader.lastUsableLBA;
412
secondHeader.diskGUID.data1 = mainHeader.diskGUID.data1;
413
secondHeader.diskGUID.data2 = mainHeader.diskGUID.data2;
414
secondHeader.partitionEntriesLBA = secondHeader.lastUsableLBA + UINT64_C(1);
415
secondHeader.numParts = mainHeader.numParts;
416
secondHeader.sizeOfPartitionEntries = mainHeader.sizeOfPartitionEntries;
417
secondHeader.partitionEntriesCRC = mainHeader.partitionEntriesCRC;
418
for (i = 0 ; i < GPT_RESERVED; i++)
419
secondHeader.reserved2[i] = mainHeader.reserved2[i];
420
} // GPTData::RebuildSecondHeader()
422
// Search for hybrid MBR entries that have no corresponding GPT partition.
423
// Returns number of such mismatches found
424
int GPTData::FindHybridMismatches(void) {
425
int i, j, found, numFound = 0;
426
uint64_t mbrFirst, mbrLast;
428
for (i = 0; i < 4; i++) {
429
if ((protectiveMBR.GetType(i) != 0xEE) && (protectiveMBR.GetType(i) != 0x00)) {
433
mbrFirst = (uint64_t) protectiveMBR.GetFirstSector(i);
434
mbrLast = mbrFirst + (uint64_t) protectiveMBR.GetLength(i) - UINT64_C(1);
435
if ((partitions[j].GetFirstLBA() == mbrFirst) &&
436
(partitions[j].GetLastLBA() == mbrLast))
439
} while ((!found) && (j < mainHeader.numParts));
442
printf("\nWarning! Mismatched GPT and MBR partitions! MBR partition "
443
"%d, of type 0x%02X,\nhas no corresponding GPT partition! "
444
"You may continue, but this condition\nmight cause data loss"
445
" in the future!\a\n", i + 1, protectiveMBR.GetType(i));
450
} // GPTData::FindHybridMismatches
452
// Find overlapping partitions and warn user about them. Returns number of
453
// overlapping partitions.
454
int GPTData::FindOverlaps(void) {
455
int i, j, problems = 0;
457
for (i = 1; i < mainHeader.numParts; i++) {
458
for (j = 0; j < i; j++) {
459
if (partitions[i].DoTheyOverlap(&partitions[j])) {
461
printf("\nProblem: partitions %d and %d overlap:\n", i + 1, j + 1);
462
printf(" Partition %d: %llu to %llu\n", i,
463
(unsigned long long) partitions[i].GetFirstLBA(),
464
(unsigned long long) partitions[i].GetLastLBA());
465
printf(" Partition %d: %llu to %llu\n", j,
466
(unsigned long long) partitions[j].GetFirstLBA(),
467
(unsigned long long) partitions[j].GetLastLBA());
472
} // GPTData::FindOverlaps()
474
/******************************************************************
476
* Begin functions that load data from disk or save data to disk. *
478
******************************************************************/
480
// Scan for partition data. This function loads the MBR data (regular MBR or
481
// protective MBR) and loads BSD disklabel data (which is probably invalid).
482
// It also looks for APM data, forces a load of GPT data, and summarizes
484
void GPTData::PartitionScan(int fd) {
485
BSDData bsdDisklabel;
488
printf("Partition table scan:\n");
490
// Read the MBR & check for BSD disklabel
491
protectiveMBR.ReadMBRData(fd);
492
protectiveMBR.ShowState();
493
bsdDisklabel.ReadBSDData(fd, 0, diskSize - 1);
494
bsdFound = bsdDisklabel.ShowState();
495
// bsdDisklabel.DisplayBSDData();
497
// Load the GPT data, whether or not it's valid
498
ForceLoadGPTData(fd);
499
ShowAPMState(); // Show whether there's an Apple Partition Map present
500
ShowGPTState(); // Show GPT status
504
printf("\n*******************************************************************\n");
505
printf("This disk appears to contain an Apple-format (APM) partition table!\n");
506
printf("It will be destroyed if you continue!\n");
507
printf("*******************************************************************\n\n\a");
509
} // GPTData::PartitionScan()
511
// Read GPT data from a disk.
512
int GPTData::LoadPartitions(char* deviceFilename) {
515
uint64_t firstBlock, lastBlock;
516
BSDData bsdDisklabel;
518
// First, do a test to see if writing will be possible later....
519
fd = OpenForWrite(deviceFilename);
521
printf("\aNOTE: Write test failed with error number %d. It will be "
522
"impossible to save\nchanges to this disk's partition table!\n\n",
526
if ((fd = open(deviceFilename, O_RDONLY)) != -1) {
527
// store disk information....
528
diskSize = disksize(fd, &err);
529
blockSize = (uint32_t) GetBlockSize(fd);
530
strcpy(device, deviceFilename);
531
PartitionScan(fd); // Check for partition types & print summary
533
switch (UseWhichPartitions()) {
538
bsdDisklabel.ReadBSDData(fd, 0, diskSize - 1);
539
// bsdDisklabel.DisplayBSDData();
541
protectiveMBR.MakeProtectiveMBR(1); // clear boot area (option 1)
542
XFormDisklabel(&bsdDisklabel, 0);
548
protectiveMBR.MakeProtectiveMBR();
552
// Now find the first and last sectors used by partitions...
554
firstBlock = mainHeader.backupLBA; // start high
555
lastBlock = 0; // start low
556
for (i = 0; i < mainHeader.numParts; i++) {
557
if ((partitions[i].GetFirstLBA() < firstBlock) &&
558
(partitions[i].GetFirstLBA() > 0))
559
firstBlock = partitions[i].GetFirstLBA();
560
if (partitions[i].GetLastLBA() > lastBlock)
561
lastBlock = partitions[i].GetLastLBA();
567
fprintf(stderr, "Problem opening %s for reading! Error is %d\n",
568
deviceFilename, errno);
569
if (errno == EACCES) { // User is probably not running as root
570
fprintf(stderr, "You must run this program as root or use sudo!\n");
574
} // GPTData::LoadPartitions()
576
// Loads the GPT, as much as possible. Returns 1 if this seems to have
577
// succeeded, 0 if there are obvious problems....
578
int GPTData::ForceLoadGPTData(int fd) {
579
int allOK = 1, validHeaders;
582
uint32_t newCRC, sizeOfParts;
584
// Seek to and read the main GPT header
585
lseek64(fd, 512, SEEK_SET);
586
read(fd, &mainHeader, 512); // read main GPT header
587
mainCrcOk = CheckHeaderCRC(&mainHeader);
588
if (IsLittleEndian() == 0) // big-endian system; adjust header byte order....
589
ReverseHeaderBytes(&mainHeader);
591
// Load backup header, check its CRC, and store the results of the
592
// check for future reference. Load backup header using pointer in main
593
// header if possible; but if main header has a CRC error, or if it
594
// points to beyond the end of the disk, load the last sector of the
597
if (mainHeader.backupLBA < diskSize) {
598
seekTo = mainHeader.backupLBA * blockSize;
600
seekTo = (diskSize * blockSize) - UINT64_C(512);
601
printf("Warning! Disk size is smaller than the main header indicates! Loading\n"
602
"secondary header from the last sector of the disk! You should use 'v' to\n"
603
"verify disk integrity, and perhaps options on the experts' menu to repair\n"
607
seekTo = (diskSize * blockSize) - UINT64_C(512);
608
} // if/else (mainCrcOk)
610
if (lseek64(fd, seekTo, SEEK_SET) != (off_t) -1) {
611
read(fd, &secondHeader, 512); // read secondary GPT header
612
secondCrcOk = CheckHeaderCRC(&secondHeader);
613
if (IsLittleEndian() == 0) // big-endian system; adjust header byte order....
614
ReverseHeaderBytes(&secondHeader);
618
fprintf(stderr, "Unable to seek to secondary GPT header at sector %llu!\n",
619
diskSize - (UINT64_C(1)));
622
// Return valid headers code: 0 = both headers bad; 1 = main header
623
// good, backup bad; 2 = backup header good, main header bad;
624
// 3 = both headers good. Note these codes refer to valid GPT
625
// signatures and version numbers; more subtle problems will elude
627
validHeaders = CheckHeaderValidity();
629
// Read partitions (from primary array)
630
if (validHeaders > 0) { // if at least one header is OK....
631
// GPT appears to be valid....
634
// We're calling the GPT valid, but there's a possibility that one
635
// of the two headers is corrupt. If so, use the one that seems to
636
// be in better shape to regenerate the bad one
637
if (validHeaders == 2) { // valid backup header, invalid main header
638
printf("Caution: invalid main GPT header, but valid backup; regenerating main header\n"
641
mainCrcOk = secondCrcOk; // Since copied, use CRC validity of backup
642
} else if (validHeaders == 1) { // valid main header, invalid backup
643
printf("Caution: invalid backup GPT header, but valid main header; regenerating\n"
644
"backup header from main header.\n");
645
RebuildSecondHeader();
646
secondCrcOk = mainCrcOk; // Since regenerated, use CRC validity of main
649
// Load the main partition table, including storing results of its
651
if (LoadMainTable() == 0)
654
// Load backup partition table into temporary storage to check
655
// its CRC and store the results, then discard this temporary
656
// storage, since we don't use it in any but recovery operations
657
seekTo = secondHeader.partitionEntriesLBA * (off_t) blockSize;
658
if ((lseek64(fd, seekTo, SEEK_SET) != (off_t) -1) && (secondCrcOk)) {
659
sizeOfParts = secondHeader.numParts * secondHeader.sizeOfPartitionEntries;
660
storage = (char*) malloc(sizeOfParts);
661
read(fd, storage, sizeOfParts);
662
newCRC = chksum_crc32((unsigned char*) storage, sizeOfParts);
664
secondPartsCrcOk = (newCRC == secondHeader.partitionEntriesCRC);
667
// Check for valid CRCs and warn if there are problems
668
if ((mainCrcOk == 0) || (secondCrcOk == 0) || (mainPartsCrcOk == 0) ||
669
(secondPartsCrcOk == 0)) {
670
printf("Warning! One or more CRCs don't match. You should repair the disk!\n");
677
} // GPTData::ForceLoadGPTData()
679
// Loads the partition table pointed to by the main GPT header. The
680
// main GPT header in memory MUST be valid for this call to do anything
682
int GPTData::LoadMainTable(void) {
684
uint32_t newCRC, sizeOfParts;
686
if ((fd = open(device, O_RDONLY)) != -1) {
687
// Set internal data structures for number of partitions on the disk
688
SetGPTSize(mainHeader.numParts);
690
// Load main partition table, and record whether its CRC
691
// matches the stored value
692
lseek64(fd, mainHeader.partitionEntriesLBA * blockSize, SEEK_SET);
693
sizeOfParts = mainHeader.numParts * mainHeader.sizeOfPartitionEntries;
694
read(fd, partitions, sizeOfParts);
695
newCRC = chksum_crc32((unsigned char*) partitions, sizeOfParts);
696
mainPartsCrcOk = (newCRC == mainHeader.partitionEntriesCRC);
697
if (IsLittleEndian() == 0)
698
ReversePartitionBytes();
702
} // GPTData::LoadMainTable()
704
// Load the second (backup) partition table as the primary partition
705
// table. Used in repair functions
706
void GPTData::LoadSecondTableAsMain(void) {
709
uint32_t sizeOfParts, newCRC;
711
if ((fd = open(device, O_RDONLY)) != -1) {
712
seekTo = secondHeader.partitionEntriesLBA * (off_t) blockSize;
713
if (lseek64(fd, seekTo, SEEK_SET) != (off_t) -1) {
714
SetGPTSize(secondHeader.numParts);
715
sizeOfParts = secondHeader.numParts * secondHeader.sizeOfPartitionEntries;
716
read(fd, partitions, sizeOfParts);
717
newCRC = chksum_crc32((unsigned char*) partitions, sizeOfParts);
718
secondPartsCrcOk = (newCRC == secondHeader.partitionEntriesCRC);
719
mainPartsCrcOk = secondPartsCrcOk;
720
if (IsLittleEndian() == 0)
721
ReversePartitionBytes();
722
if (!secondPartsCrcOk) {
723
printf("Error! After loading backup partitions, the CRC still doesn't check out!\n");
726
printf("Error! Couldn't seek to backup partition table!\n");
729
printf("Error! Couldn't open device %s when recovering backup partition table!\n");
731
} // GPTData::LoadSecondTableAsMain()
733
// Writes GPT (and protective MBR) to disk. Returns 1 on successful
734
// write, 0 if there was a problem.
735
int GPTData::SaveGPTData(void) {
737
char answer, line[256];
739
uint64_t secondTable;
743
if (strlen(device) == 0) {
744
printf("Device not defined.\n");
747
// First do some final sanity checks....
748
// Is there enough space to hold the GPT headers and partition tables,
749
// given the partition sizes?
750
if (CheckGPTSize() > 0) {
754
// Check that disk is really big enough to handle this...
755
if (mainHeader.backupLBA > diskSize) {
756
fprintf(stderr, "Error! Disk is too small! The 'e' option on the experts' menu might fix the\n"
757
"problem (or it might not). Aborting!\n");
758
printf("(Disk size is %ld sectors, needs to be %ld sectors.)\n", diskSize,
759
mainHeader.backupLBA);
762
// Check that second header is properly placed. Warn and ask if this should
763
// be corrected if the test fails....
764
if (mainHeader.backupLBA < (diskSize - UINT64_C(1))) {
765
printf("Warning! Secondary header is placed too early on the disk! Do you want to\n"
766
"correct this problem? ");
767
if (GetYN() == 'Y') {
768
MoveSecondHeaderToEnd();
769
printf("Have moved second header and partition table to correct location.\n");
771
printf("Have not corrected the problem. Strange problems may occur in the future!\n");
772
} // if correction requested
775
// Check for overlapping partitions....
776
if (FindOverlaps() > 0) {
778
fprintf(stderr, "Aborting write operation!\n");
781
// Check for mismatched MBR and GPT data, but let it pass if found
782
// (function displays warning message)
783
FindHybridMismatches();
785
// Pull out some data that's needed before doing byte-order reversal on
786
// big-endian systems....
787
numParts = mainHeader.numParts;
788
secondTable = secondHeader.partitionEntriesLBA;
789
if (IsLittleEndian() == 0) {
790
// Reverse partition bytes first, since that function requires non-reversed
791
// data from the main header....
792
ReversePartitionBytes();
793
ReverseHeaderBytes(&mainHeader);
794
ReverseHeaderBytes(&secondHeader);
799
printf("\nFinal checks complete. About to write GPT data. THIS WILL OVERWRITE EXISTING\n");
800
printf("MBR PARTITIONS!! THIS PROGRAM IS BETA QUALITY AT BEST. IF YOU LOSE ALL YOUR\n");
801
printf("DATA, YOU HAVE ONLY YOURSELF TO BLAME IF YOU ANSWER 'Y' BELOW!\n\n");
802
printf("Do you want to proceed, possibly destroying your data? (Y/N) ");
803
fgets(line, 255, stdin);
804
sscanf(line, "%c", &answer);
805
if ((answer == 'Y') || (answer == 'y')) {
806
printf("OK; writing new GPT partition table.\n");
814
fd = OpenForWrite(device);
816
// First, write the protective MBR...
817
protectiveMBR.WriteMBRData(fd);
819
// Now write the main GPT header...
821
if (write(fd, &mainHeader, 512) == -1)
824
// Now write the main partition tables...
826
if (write(fd, partitions, GPT_SIZE * numParts) == -1)
830
// Now seek to near the end to write the secondary GPT....
832
offset = (off_t) secondTable * (off_t) (blockSize);
833
if (lseek64(fd, offset, SEEK_SET) == (off_t) - 1) {
835
printf("Unable to seek to end of disk!\n");
839
// Now write the secondary partition tables....
841
if (write(fd, partitions, GPT_SIZE * numParts) == -1)
844
// Now write the secondary GPT header...
846
if (write(fd, &secondHeader, 512) == -1)
849
// re-read the partition table
854
if (allOK) { // writes completed OK
855
printf("The operation has completed successfully.\n");
857
printf("Warning! An error was reported when writing the partition table! This error\n");
858
printf("MIGHT be harmless, but you may have trashed the disk! Use parted and, if\n");
859
printf("necessary, restore your original partition table.\n");
863
fprintf(stderr, "Unable to open device %s for writing! Errno is %d! Aborting write!\n",
868
printf("Aborting write of new partition table.\n");
871
if (IsLittleEndian() == 0) {
872
// Reverse (normalize) header bytes first, since ReversePartitionBytes()
873
// requires non-reversed data in mainHeader...
874
ReverseHeaderBytes(&mainHeader);
875
ReverseHeaderBytes(&secondHeader);
876
ReversePartitionBytes();
880
} // GPTData::SaveGPTData()
882
// Save GPT data to a backup file. This function does much less error
883
// checking than SaveGPTData(). It can therefore preserve many types of
884
// corruption for later analysis; however, it preserves only the MBR,
885
// the main GPT header, the backup GPT header, and the main partition
886
// table; it discards the backup partition table, since it should be
887
// identical to the main partition table on healthy disks.
888
int GPTData::SaveGPTBackup(char* filename) {
892
if ((fd = open(filename, O_WRONLY | O_CREAT, S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH)) != -1) {
893
// Reverse the byte order, if necessary....
894
numParts = mainHeader.numParts;
895
if (IsLittleEndian() == 0) {
896
ReversePartitionBytes();
897
ReverseHeaderBytes(&mainHeader);
898
ReverseHeaderBytes(&secondHeader);
901
// Recomputing the CRCs is likely to alter them, which could be bad
902
// if the intent is to save a potentially bad GPT for later analysis;
903
// but if we don't do this, we get bogus errors when we load the
904
// backup. I'm favoring misses over false alarms....
907
// Now write the protective MBR...
908
protectiveMBR.WriteMBRData(fd);
910
// Now write the main GPT header...
912
if (write(fd, &mainHeader, 512) == -1)
915
// Now write the secondary GPT header...
917
if (write(fd, &secondHeader, 512) == -1)
920
// Now write the main partition tables...
922
if (write(fd, partitions, GPT_SIZE * numParts) == -1)
926
if (allOK) { // writes completed OK
927
printf("The operation has completed successfully.\n");
929
printf("Warning! An error was reported when writing the backup file.\n");
930
printf("It may not be usable!\n");
934
// Now reverse the byte-order reversal, if necessary....
935
if (IsLittleEndian() == 0) {
936
ReverseHeaderBytes(&mainHeader);
937
ReverseHeaderBytes(&secondHeader);
938
ReversePartitionBytes();
941
fprintf(stderr, "Unable to open file %s for writing! Aborting!\n", filename);
945
} // GPTData::SaveGPTBackup()
947
// Load GPT data from a backup file created by SaveGPTBackup(). This function
948
// does minimal error checking. It returns 1 if it completed successfully,
949
// 0 if there was a problem. In the latter case, it creates a new empty
950
// set of partitions.
951
int GPTData::LoadGPTBackup(char* filename) {
952
int fd, allOK = 1, val;
953
uint32_t numParts, sizeOfEntries, sizeOfParts, newCRC;
954
int littleEndian = 1;
956
if ((fd = open(filename, O_RDONLY)) != -1) {
957
if (IsLittleEndian() == 0)
960
// Let the MBRData class load the saved MBR...
961
protectiveMBR.ReadMBRData(fd, 0); // 0 = don't check block size
963
// Load the main GPT header, check its vaility, and set the GPT
964
// size based on the data
965
read(fd, &mainHeader, 512);
966
mainCrcOk = CheckHeaderCRC(&mainHeader);
968
// Reverse byte order, if necessary
969
if (littleEndian == 0) {
970
ReverseHeaderBytes(&mainHeader);
973
// Load the backup GPT header in much the same way as the main
975
read(fd, &secondHeader, 512);
976
secondCrcOk = CheckHeaderCRC(&secondHeader);
978
// Reverse byte order, if necessary
979
if (littleEndian == 0) {
980
ReverseHeaderBytes(&secondHeader);
983
// Return valid headers code: 0 = both headers bad; 1 = main header
984
// good, backup bad; 2 = backup header good, main header bad;
985
// 3 = both headers good. Note these codes refer to valid GPT
986
// signatures and version numbers; more subtle problems will elude
988
if ((val = CheckHeaderValidity()) > 0) {
989
if (val == 2) { // only backup header seems to be good
990
numParts = secondHeader.numParts;
991
sizeOfEntries = secondHeader.sizeOfPartitionEntries;
992
} else { // main header is OK
993
numParts = mainHeader.numParts;
994
sizeOfEntries = mainHeader.sizeOfPartitionEntries;
997
SetGPTSize(numParts);
999
// If current disk size doesn't match that of backup....
1000
if (secondHeader.currentLBA != diskSize - UINT64_C(1)) {
1001
printf("Warning! Current disk size doesn't match that of the backup!\n"
1002
"Adjusting sizes to match, but subsequent problems are possible!\n");
1003
MoveSecondHeaderToEnd();
1006
// Load main partition table, and record whether its CRC
1007
// matches the stored value
1008
sizeOfParts = numParts * sizeOfEntries;
1009
read(fd, partitions, sizeOfParts);
1011
newCRC = chksum_crc32((unsigned char*) partitions, sizeOfParts);
1012
mainPartsCrcOk = (newCRC == mainHeader.partitionEntriesCRC);
1013
secondPartsCrcOk = (newCRC == secondHeader.partitionEntriesCRC);
1014
// Reverse byte order, if necessary
1015
if (littleEndian == 0) {
1016
ReversePartitionBytes();
1024
fprintf(stderr, "Unable to open file %s for reading! Aborting!\n", filename);
1027
// Something went badly wrong, so blank out partitions
1030
protectiveMBR.MakeProtectiveMBR();
1033
} // GPTData::LoadGPTBackup()
1035
// Tell user whether Apple Partition Map (APM) was discovered....
1036
void GPTData::ShowAPMState(void) {
1038
printf(" APM: present\n");
1040
printf(" APM: not present\n");
1041
} // GPTData::ShowAPMState()
1043
// Tell user about the state of the GPT data....
1044
void GPTData::ShowGPTState(void) {
1047
printf(" GPT: not present\n");
1050
printf(" GPT: present\n");
1053
printf(" GPT: damaged\n");
1056
printf("\a GPT: unknown -- bug!\n");
1059
} // GPTData::ShowGPTState()
1061
// Display the basic GPT data
1062
void GPTData::DisplayGPTData(void) {
1064
char sizeInSI[255]; // String to hold size of disk in SI units
1066
uint64_t temp, totalFree;
1068
BytesToSI(diskSize * blockSize, sizeInSI);
1069
printf("Disk %s: %llu sectors, %s\n", device,
1070
(unsigned long long) diskSize, sizeInSI);
1071
printf("Disk identifier (GUID): %s\n", GUIDToStr(mainHeader.diskGUID, tempStr));
1072
printf("Partition table holds up to %lu entries\n", (unsigned long) mainHeader.numParts);
1073
printf("First usable sector is %lu, last usable sector is %lu\n",
1074
(unsigned long) mainHeader.firstUsableLBA,
1075
(unsigned long) mainHeader.lastUsableLBA);
1076
totalFree = FindFreeBlocks(&i, &temp);
1077
printf("Total free space is %llu sectors (%s)\n", totalFree,
1078
BytesToSI(totalFree * (uint64_t) blockSize, sizeInSI));
1079
printf("\nNumber Start (sector) End (sector) Size Code Name\n");
1080
for (i = 0; i < mainHeader.numParts; i++) {
1081
partitions[i].ShowSummary(i, blockSize);
1083
} // GPTData::DisplayGPTData()
1085
// Get partition number from user and then call ShowPartDetails(partNum)
1086
// to show its detailed information
1087
void GPTData::ShowDetails(void) {
1091
if (GetPartRange(&low, &high) > 0) {
1092
partNum = GetPartNum();
1093
ShowPartDetails(partNum);
1095
printf("No partitions\n");
1097
} // GPTData::ShowDetails()
1099
// Show detailed information on the specified partition
1100
void GPTData::ShowPartDetails(uint32_t partNum) {
1101
if (partitions[partNum].GetFirstLBA() != 0) {
1102
partitions[partNum].ShowDetails(blockSize);
1104
printf("Partition #%d does not exist.", (int) (partNum + 1));
1106
} // GPTData::ShowPartDetails()
1108
/*********************************************************************
1110
* Begin functions that obtain information from the users, and often *
1111
* do something with that information (call other functions) *
1113
*********************************************************************/
1115
// Prompts user for partition number and returns the result.
1116
uint32_t GPTData::GetPartNum(void) {
1121
if (GetPartRange(&low, &high) > 0) {
1122
sprintf(prompt, "Partition number (%d-%d): ", low + 1, high + 1);
1123
partNum = GetNumber(low + 1, high + 1, low, prompt);
1125
return (partNum - 1);
1126
} // GPTData::GetPartNum()
1128
// What it says: Resize the partition table. (Default is 128 entries.)
1129
void GPTData::ResizePartitionTable(void) {
1132
uint32_t curLow, curHigh;
1134
printf("Current partition table size is %lu.\n",
1135
(unsigned long) mainHeader.numParts);
1136
GetPartRange(&curLow, &curHigh);
1137
curHigh++; // since GetPartRange() returns numbers starting from 0...
1138
// There's no point in having fewer than four partitions....
1141
sprintf(prompt, "Enter new size (%d up, default %d): ", (int) curHigh,
1142
(int) NUM_GPT_ENTRIES);
1143
newSize = GetNumber(4, 65535, 128, prompt);
1144
if (newSize < 128) {
1145
printf("Caution: The partition table size should officially be 16KB or larger,\n"
1146
"which works out to 128 entries. In practice, smaller tables seem to\n"
1147
"work with most OSes, but this practice is risky. I'm proceeding with\n"
1148
"the resize, but you may want to reconsider this action and undo it.\n\n");
1150
SetGPTSize(newSize);
1151
} // GPTData::ResizePartitionTable()
1153
// Interactively create a partition
1154
void GPTData::CreatePartition(void) {
1155
uint64_t firstBlock, firstInLargest, lastBlock, sector;
1157
int partNum, firstFreePart = 0;
1159
// Find first free partition...
1160
while (partitions[firstFreePart].GetFirstLBA() != 0) {
1164
if (((firstBlock = FindFirstAvailable()) != 0) &&
1165
(firstFreePart < mainHeader.numParts)) {
1166
lastBlock = FindLastAvailable(firstBlock);
1167
firstInLargest = FindFirstInLargest();
1169
// Get partition number....
1171
sprintf(prompt, "Partition number (%d-%d, default %d): ", firstFreePart + 1,
1172
mainHeader.numParts, firstFreePart + 1);
1173
partNum = GetNumber(firstFreePart + 1, mainHeader.numParts,
1174
firstFreePart + 1, prompt) - 1;
1175
if (partitions[partNum].GetFirstLBA() != 0)
1176
printf("partition %d is in use.\n", partNum + 1);
1177
} while (partitions[partNum].GetFirstLBA() != 0);
1179
// Get first block for new partition...
1181
"First sector (%llu-%llu, default = %llu) or {+-}size{KMGT}: ",
1182
firstBlock, lastBlock, firstInLargest);
1184
sector = GetSectorNum(firstBlock, lastBlock, firstInLargest, prompt);
1185
} while (IsFree(sector) == 0);
1186
firstBlock = sector;
1188
// Get last block for new partitions...
1189
lastBlock = FindLastInFree(firstBlock);
1191
"Last sector (%llu-%llu, default = %llu) or {+-}size{KMGT}: ",
1192
firstBlock, lastBlock, lastBlock);
1194
sector = GetSectorNum(firstBlock, lastBlock, lastBlock, prompt);
1195
} while (IsFree(sector) == 0);
1198
partitions[partNum].SetFirstLBA(firstBlock);
1199
partitions[partNum].SetLastLBA(lastBlock);
1201
partitions[partNum].SetUniqueGUID(1);
1202
partitions[partNum].ChangeType();
1203
partitions[partNum].SetName((unsigned char*) partitions[partNum].GetNameType(prompt));
1205
printf("No free sectors available\n");
1207
} // GPTData::CreatePartition()
1209
// Interactively delete a partition (duh!)
1210
void GPTData::DeletePartition(void) {
1213
uint64_t startSector, length;
1216
if (GetPartRange(&low, &high) > 0) {
1217
sprintf(prompt, "Partition number (%d-%d): ", low + 1, high + 1);
1218
partNum = GetNumber(low + 1, high + 1, low, prompt);
1220
// In case there's a protective MBR, look for & delete matching
1221
// MBR partition....
1222
startSector = partitions[partNum - 1].GetFirstLBA();
1223
length = partitions[partNum - 1].GetLengthLBA();
1224
protectiveMBR.DeleteByLocation(startSector, length);
1226
// Now delete the GPT partition
1227
partitions[partNum - 1].BlankPartition();
1229
printf("No partitions\n");
1231
} // GPTData::DeletePartition()
1233
// Prompt user for a partition number, then change its type code
1234
// using ChangeGPTType(struct GPTPartition*) function.
1235
void GPTData::ChangePartType(void) {
1239
if (GetPartRange(&low, &high) > 0) {
1240
partNum = GetPartNum();
1241
partitions[partNum].ChangeType();
1243
printf("No partitions\n");
1245
} // GPTData::ChangePartType()
1247
// Partition attributes seem to be rarely used, but I want a way to
1248
// adjust them for completeness....
1249
void GPTData::SetAttributes(uint32_t partNum) {
1252
theAttr.SetAttributes(partitions[partNum].GetAttributes());
1253
theAttr.DisplayAttributes();
1254
theAttr.ChangeAttributes();
1255
partitions[partNum].SetAttributes(theAttr.GetAttributes());
1256
} // GPTData::SetAttributes()
1258
// This function destroys the on-disk GPT structures. Returns 1 if the
1259
// user confirms destruction, 0 if the user aborts.
1260
// If prompt == 0, don't ask user about proceeding and do NOT wipe out
1261
// MBR. (Set prompt == 0 when doing a GPT-to-MBR conversion.)
1262
int GPTData::DestroyGPT(int prompt) {
1264
char blankSector[512], goOn = 'Y', blank = 'N';
1266
for (i = 0; i < 512; i++) {
1267
blankSector[i] = '\0';
1270
if (((apmFound) || (bsdFound)) && prompt) {
1271
printf("WARNING: APM or BSD disklabel structures detected! This operation could\n"
1272
"damage any APM or BSD partitions on this disk!\n");
1275
printf("\a\aAbout to wipe out GPT on %s. Proceed? ", device);
1279
fd = open(device, O_WRONLY);
1281
// MacOS X requires a shared lock under some circumstances....
1283
fd = open(device, O_WRONLY|O_SHLOCK);
1287
lseek64(fd, mainHeader.currentLBA * 512, SEEK_SET); // seek to GPT header
1288
write(fd, blankSector, 512); // blank it out
1289
lseek64(fd, mainHeader.partitionEntriesLBA * 512, SEEK_SET); // seek to partition table
1290
for (i = 0; i < GetBlocksInPartTable(); i++)
1291
write(fd, blankSector, 512);
1292
lseek64(fd, secondHeader.partitionEntriesLBA * 512, SEEK_SET); // seek to partition table
1293
for (i = 0; i < GetBlocksInPartTable(); i++)
1294
write(fd, blankSector, 512);
1295
lseek64(fd, secondHeader.currentLBA * 512, SEEK_SET); // seek to GPT header
1296
write(fd, blankSector, 512); // blank it out
1298
printf("Blank out MBR? ");
1301
// Note on below: Touch the MBR only if the user wants it completely
1302
// blanked out. Version 0.4.2 deleted the 0xEE partition and re-wrote
1303
// the MBR, but this could wipe out a valid MBR that the program
1304
// had subsequently discarded (say, if it conflicted with older GPT
1307
lseek64(fd, 0, SEEK_SET);
1308
write(fd, blankSector, 512); // blank it out
1310
printf("MBR is unchanged. You may need to delete an EFI GPT (0xEE) partition\n"
1311
"with fdisk or another tool.\n");
1315
printf("GPT data structures destroyed! You may now partition the disk using fdisk or\n"
1316
"other utilities. Program will now terminate.\n");
1318
printf("Problem opening %s for writing! Program will now terminate.\n");
1319
} // if/else (fd != -1)
1320
} // if (goOn == 'Y')
1321
return (goOn == 'Y');
1322
} // GPTData::DestroyGPT()
1324
/**************************************************************************
1326
* Partition table transformation functions (MBR or BSD disklabel to GPT) *
1327
* (some of these functions may require user interaction) *
1329
**************************************************************************/
1331
// Examines the MBR & GPT data, and perhaps asks the user questions, to
1332
// determine which set of data to use: the MBR (use_mbr), the GPT (use_gpt),
1333
// or create a new set of partitions (use_new)
1334
WhichToUse GPTData::UseWhichPartitions(void) {
1335
WhichToUse which = use_new;
1336
MBRValidity mbrState;
1339
mbrState = protectiveMBR.GetValidity();
1341
if ((state == gpt_invalid) && ((mbrState == mbr) || (mbrState == hybrid))) {
1342
printf("\n\a***************************************************************\n"
1343
"Found invalid GPT and valid MBR; converting MBR to GPT format.\n"
1344
"THIS OPERATON IS POTENTIALLY DESTRUCTIVE! Exit by typing 'q' if\n"
1345
"you don't want to convert your MBR partitions to GPT format!\n"
1346
"***************************************************************\n\n");
1350
if ((state == gpt_invalid) && bsdFound) {
1351
printf("\n\a**********************************************************************\n"
1352
"Found invalid GPT and valid BSD disklabel; converting BSD disklabel\n"
1353
"to GPT format. THIS OPERATON IS POTENTIALLY DESTRUCTIVE! Your first\n"
1354
"BSD partition will likely be unusable. Exit by typing 'q' if you don't\n"
1355
"want to convert your BSD partitions to GPT format!\n"
1356
"**********************************************************************\n\n");
1360
if ((state == gpt_valid) && (mbrState == gpt)) {
1361
printf("Found valid GPT with protective MBR; using GPT.\n");
1364
if ((state == gpt_valid) && (mbrState == hybrid)) {
1365
printf("Found valid GPT with hybrid MBR; using GPT.\n");
1368
if ((state == gpt_valid) && (mbrState == invalid)) {
1369
printf("\aFound valid GPT with corrupt MBR; using GPT and will create new\nprotective MBR on save.\n");
1371
protectiveMBR.MakeProtectiveMBR();
1373
if ((state == gpt_valid) && (mbrState == mbr)) {
1374
printf("Found valid MBR and GPT. Which do you want to use?\n");
1375
answer = GetNumber(1, 3, 2, (char*) " 1 - MBR\n 2 - GPT\n 3 - Create blank GPT\n\nYour answer: ");
1378
} else if (answer == 2) {
1380
protectiveMBR.MakeProtectiveMBR();
1381
printf("Using GPT and creating fresh protective MBR.\n");
1382
} else which = use_new;
1385
// Nasty decisions here -- GPT is present, but corrupt (bad CRCs or other
1387
if (state == gpt_corrupt) {
1388
if ((mbrState == mbr) || (mbrState == hybrid)) {
1389
printf("Found valid MBR and corrupt GPT. Which do you want to use? (Using the\n"
1390
"GPT MAY permit recovery of GPT data.)\n");
1391
answer = GetNumber(1, 3, 2, (char*) " 1 - MBR\n 2 - GPT\n 3 - Create blank GPT\n\nYour answer: ");
1394
// protectiveMBR.MakeProtectiveMBR();
1395
} else if (answer == 2) {
1397
} else which = use_new;
1398
} else if (mbrState == invalid) {
1399
printf("Found invalid MBR and corrupt GPT. What do you want to do? (Using the\n"
1400
"GPT MAY permit recovery of GPT data.)\n");
1401
answer = GetNumber(1, 2, 1, (char*) " 1 - GPT\n 2 - Create blank GPT\n\nYour answer: ");
1404
} else which = use_new;
1405
} else { // corrupt GPT, MBR indicates it's a GPT disk....
1406
printf("\a\a****************************************************************************\n"
1407
"Caution: Found protective or hybrid MBR and corrupt GPT. Using GPT, but disk\n"
1408
"verification and recovery are STRONGLY recommended.\n"
1409
"****************************************************************************\n");
1412
} // if (corrupt GPT)
1414
if (which == use_new)
1415
printf("Creating new GPT entries.\n");
1418
} // UseWhichPartitions()
1420
// Convert MBR partition table into GPT form
1421
int GPTData::XFormPartitions(void) {
1422
int i, numToConvert;
1426
// Clear out old data & prepare basics....
1429
// Convert the smaller of the # of GPT or MBR partitions
1430
if (mainHeader.numParts > (MAX_MBR_PARTS))
1431
numToConvert = MAX_MBR_PARTS;
1433
numToConvert = mainHeader.numParts;
1435
for (i = 0; i < numToConvert; i++) {
1436
origType = protectiveMBR.GetType(i);
1437
// don't waste CPU time trying to convert extended, hybrid protective, or
1438
// null (non-existent) partitions
1439
if ((origType != 0x05) && (origType != 0x0f) && (origType != 0x85) &&
1440
(origType != 0x00) && (origType != 0xEE))
1441
partitions[i] = protectiveMBR.AsGPT(i);
1444
// Convert MBR into protective MBR
1445
protectiveMBR.MakeProtectiveMBR();
1447
// Record that all original CRCs were OK so as not to raise flags
1448
// when doing a disk verification
1449
mainCrcOk = secondCrcOk = mainPartsCrcOk = secondPartsCrcOk = 1;
1452
} // GPTData::XFormPartitions()
1454
// Transforms BSD disklabel on the specified partition (numbered from 0).
1455
// If an invalid partition number is given, the program prompts for one.
1456
// Returns the number of new partitions created.
1457
int GPTData::XFormDisklabel(int i) {
1458
uint32_t low, high, partNum, startPart;
1460
int goOn = 1, numDone = 0;
1463
if (GetPartRange(&low, &high) != 0) {
1464
if ((i < low) || (i > high))
1465
partNum = GetPartNum();
1467
partNum = (uint32_t) i;
1469
// Find the partition after the last used one
1470
startPart = high + 1;
1472
// Now see if the specified partition has a BSD type code....
1473
hexCode = partitions[partNum].GetHexType();
1474
if ((hexCode != 0xa500) && (hexCode != 0xa900)) {
1475
printf("Specified partition doesn't have a disklabel partition type "
1476
"code.\nContinue anyway?");
1477
goOn = (GetYN() == 'Y');
1480
// If all is OK, read the disklabel and convert it.
1482
goOn = disklabel.ReadBSDData(device, partitions[partNum].GetFirstLBA(),
1483
partitions[partNum].GetLastLBA());
1484
if ((goOn) && (disklabel.IsDisklabel())) {
1485
numDone = XFormDisklabel(&disklabel, startPart);
1487
printf("Converted %d BSD partition.\n", numDone);
1489
printf("Converted %d BSD partitions.\n", numDone);
1491
printf("Unable to convert partitions! Unrecognized BSD disklabel.\n");
1494
if (numDone > 0) { // converted partitions; delete carrier
1495
partitions[partNum].BlankPartition();
1498
printf("No partitions\n");
1501
} // GPTData::XFormDisklable(int i)
1503
// Transform the partitions on an already-loaded BSD disklabel...
1504
int GPTData::XFormDisklabel(BSDData* disklabel, int startPart) {
1507
if ((disklabel->IsDisklabel()) && (startPart >= 0) &&
1508
(startPart < mainHeader.numParts)) {
1509
for (i = 0; i < disklabel->GetNumParts(); i++) {
1510
partitions[i + startPart] = disklabel->AsGPT(i);
1511
if (partitions[i + startPart].GetFirstLBA() != UINT64_C(0))
1516
// Record that all original CRCs were OK so as not to raise flags
1517
// when doing a disk verification
1518
mainCrcOk = secondCrcOk = mainPartsCrcOk = secondPartsCrcOk = 1;
1521
} // GPTData::XFormDisklabel(BSDData* disklabel)
1523
// Add one GPT partition to MBR. Used by XFormToMBR() and MakeHybrid()
1524
// functions. Returns 1 if operation was successful.
1525
int GPTData::OnePartToMBR(uint32_t gptPart, int mbrPart) {
1526
int allOK = 1, typeCode, bootable;
1530
if ((mbrPart < 0) || (mbrPart > 3)) {
1531
printf("MBR partition %d is out of range; omitting it.\n", mbrPart + 1);
1534
if (gptPart >= mainHeader.numParts) {
1535
printf("GPT partition %d is out of range; omitting it.\n", gptPart + 1);
1538
if (allOK && (partitions[gptPart].GetLastLBA() == UINT64_C(0))) {
1539
printf("GPT partition %d is undefined; omitting it.\n", gptPart + 1);
1542
if (allOK && (partitions[gptPart].GetFirstLBA() <= UINT32_MAX) &&
1543
(partitions[gptPart].GetLengthLBA() <= UINT32_MAX)) {
1544
if (partitions[gptPart].GetLastLBA() > UINT32_MAX) {
1545
printf("Caution: Partition end point past 32-bit pointer boundary;"
1546
" some OSes may\nreact strangely.\n");
1547
} // if partition ends past 32-bit (usually 2TiB) boundary
1549
printf("Enter an MBR hex code (default %02X): ",
1550
typeHelper.GUIDToID(partitions[gptPart].GetType()) / 256);
1551
fgets(line, 255, stdin);
1552
sscanf(line, "%x", &typeCode);
1553
if (line[0] == '\n')
1554
typeCode = partitions[gptPart].GetHexType() / 256;
1555
} while ((typeCode <= 0) || (typeCode > 255));
1556
printf("Set the bootable flag? ");
1557
bootable = (GetYN() == 'Y');
1558
length = partitions[gptPart].GetLengthLBA();
1559
protectiveMBR.MakePart(mbrPart, (uint32_t) partitions[gptPart].GetFirstLBA(),
1560
(uint32_t) length, typeCode, bootable);
1561
} else { // partition out of range
1562
printf("Partition %d begins beyond the 32-bit pointer limit of MBR "
1563
"partitions, or is\n too big; omitting it.\n", gptPart + 1);
1567
} // GPTData::OnePartToMBR()
1569
// Convert the GPT to MBR form. This function is necessarily limited; it
1570
// handles at most four partitions and creates layouts that ignore CHS
1571
// geometries. Returns the number of converted partitions; if this value
1572
// is over 0, the calling function should call DestroyGPT() to destroy
1573
// the GPT data, and then exit.
1574
int GPTData::XFormToMBR(void) {
1576
int i, j, numParts, numConverted = 0;
1577
uint32_t partNums[4];
1579
// Get the numbers of up to four partitions to add to the
1581
numParts = CountParts();
1582
printf("Counted %d partitions.\n", numParts);
1584
// Prepare the MBR for conversion (empty it of existing partitions).
1585
protectiveMBR.EmptyMBR(0);
1586
protectiveMBR.SetDiskSize(diskSize);
1588
if (numParts > 4) { // Over four partitions; engage in triage
1589
printf("Type from one to four GPT partition numbers, separated by spaces, to be\n"
1590
"used in the MBR, in sequence: ");
1591
fgets(line, 255, stdin);
1592
numParts = sscanf(line, "%d %d %d %d", &partNums[0], &partNums[1],
1593
&partNums[2], &partNums[3]);
1594
} else { // Four or fewer partitions; convert them all
1596
while ((j < numParts) && (i < mainHeader.numParts)) {
1597
if (partitions[i].GetFirstLBA() > 0) { // if GPT part. is defined
1598
partNums[j++] = ++i; // flag it for conversion
1603
for (i = 0; i < numParts; i++) {
1604
j = partNums[i] - 1;
1605
printf("\nCreating entry for partition #%d\n", j + 1);
1606
numConverted += OnePartToMBR(j, i);
1608
return numConverted;
1609
} // GPTData::XFormToMBR()
1611
// Create a hybrid MBR -- an ugly, funky thing that helps GPT work with
1612
// OSes that don't understand GPT.
1613
void GPTData::MakeHybrid(void) {
1614
uint32_t partNums[3];
1616
int numParts, numConverted = 0, i, j, typeCode, mbrNum;
1617
char fillItUp = 'M'; // fill extra partition entries? (Yes/No/Maybe)
1618
char eeFirst = 'Y'; // Whether EFI GPT (0xEE) partition comes first in table
1620
printf("\nWARNING! Hybrid MBRs are flaky and potentially dangerous! If you decide not\n"
1621
"to use one, just hit the Enter key at the below prompt and your MBR\n"
1622
"partition table will be untouched.\n\n\a");
1624
// Now get the numbers of up to three partitions to add to the
1626
printf("Type from one to three GPT partition numbers, separated by spaces, to be\n"
1627
"added to the hybrid MBR, in sequence: ");
1628
fgets(line, 255, stdin);
1629
numParts = sscanf(line, "%d %d %d", &partNums[0], &partNums[1], &partNums[2]);
1632
// Blank out the protective MBR, but leave the boot loader code
1634
protectiveMBR.EmptyMBR(0);
1635
protectiveMBR.SetDiskSize(diskSize);
1636
printf("Place EFI GPT (0xEE) partition first in MBR (good for GRUB)? ");
1640
for (i = 0; i < numParts; i++) {
1641
j = partNums[i] - 1;
1642
printf("\nCreating entry for partition #%d\n", j + 1);
1647
numConverted += OnePartToMBR(j, mbrNum);
1650
if ((numParts > 0) && (numConverted > 0)) { // User opted to create a hybrid MBR....
1651
// Create EFI protective partition that covers the start of the disk.
1652
// If this location (covering the main GPT data structures) is omitted,
1653
// Linux won't find any partitions on the disk. Note that this is
1654
// NUMBERED AFTER the hybrid partitions, contrary to what the
1655
// gptsync utility does. This is because Windows seems to choke on
1656
// disks with a 0xEE partition in the first slot and subsequent
1657
// additional partitions, unless it boots from the disk.
1662
protectiveMBR.MakePart(mbrNum, 1, protectiveMBR.FindLastInFree(1), 0xEE);
1663
protectiveMBR.SetHybrid();
1665
// ... and for good measure, if there are any partition spaces left,
1666
// optionally create another protective EFI partition to cover as much
1667
// space as possible....
1668
for (i = 0; i < 4; i++) {
1669
if (protectiveMBR.GetType(i) == 0x00) { // unused entry....
1670
if (fillItUp == 'M') {
1671
printf("\nUnused partition space(s) found. Use one to protect more partitions? ");
1673
typeCode = 0x00; // use this to flag a need to get type code
1675
if (fillItUp == 'Y') {
1676
while ((typeCode <= 0) || (typeCode > 255)) {
1677
printf("Enter an MBR hex code (EE is EFI GPT, but may confuse MacOS): ");
1678
// Comment on above: Mac OS treats disks with more than one
1679
// 0xEE MBR partition as MBR disks, not as GPT disks.
1680
fgets(line, 255, stdin);
1681
sscanf(line, "%x", &typeCode);
1682
if (line[0] == '\n')
1685
protectiveMBR.MakeBiggestPart(i, typeCode); // make a partition
1686
} // if (fillItUp == 'Y')
1687
} // if unused entry
1688
} // for (i = 0; i < 4; i++)
1689
} // if (numParts > 0)
1690
} // GPTData::MakeHybrid()
1692
/**********************************************************************
1694
* Functions that adjust GPT data structures WITHOUT user interaction *
1695
* (they may display information for the user's benefit, though) *
1697
**********************************************************************/
1699
// Resizes GPT to specified number of entries. Creates a new table if
1700
// necessary, copies data if it already exists.
1701
int GPTData::SetGPTSize(uint32_t numEntries) {
1702
struct GPTPart* newParts;
1703
struct GPTPart* trash;
1704
uint32_t i, high, copyNum;
1707
// First, adjust numEntries upward, if necessary, to get a number
1708
// that fills the allocated sectors
1709
i = blockSize / GPT_SIZE;
1710
if ((numEntries % i) != 0) {
1711
printf("Adjusting GPT size from %lu ", (unsigned long) numEntries);
1712
numEntries = ((numEntries / i) + 1) * i;
1713
printf("to %lu to fill the sector\n", (unsigned long) numEntries);
1716
// Do the work only if the # of partitions is changing. Along with being
1717
// efficient, this prevents mucking the with location of the secondary
1718
// partition table, which causes problems when loading data from a RAID
1719
// array that's been expanded because this function is called when loading
1721
if ((numEntries != mainHeader.numParts) || (partitions == NULL)) {
1722
newParts = (GPTPart*) calloc(numEntries, sizeof (GPTPart));
1723
if (newParts != NULL) {
1724
if (partitions != NULL) { // existing partitions; copy them over
1725
GetPartRange(&i, &high);
1726
if (numEntries < (high + 1)) { // Highest entry too high for new #
1727
printf("The highest-numbered partition is %lu, which is greater than the requested\n"
1728
"partition table size of %d; cannot resize. Perhaps sorting will help.\n",
1729
(unsigned long) (high + 1), numEntries);
1731
} else { // go ahead with copy
1732
if (numEntries < mainHeader.numParts)
1733
copyNum = numEntries;
1735
copyNum = mainHeader.numParts;
1736
for (i = 0; i < copyNum; i++) {
1737
newParts[i] = partitions[i];
1740
partitions = newParts;
1743
} else { // No existing partition table; just create it
1744
partitions = newParts;
1745
} // if/else existing partitions
1746
mainHeader.numParts = numEntries;
1747
secondHeader.numParts = numEntries;
1748
mainHeader.firstUsableLBA = ((numEntries * GPT_SIZE) / blockSize) + 2 ;
1749
secondHeader.firstUsableLBA = mainHeader.firstUsableLBA;
1750
MoveSecondHeaderToEnd();
1753
} else { // Bad memory allocation
1754
fprintf(stderr, "Error allocating memory for partition table!\n");
1759
} // GPTData::SetGPTSize()
1761
// Blank the partition array
1762
void GPTData::BlankPartitions(void) {
1765
for (i = 0; i < mainHeader.numParts; i++) {
1766
partitions[i].BlankPartition();
1768
} // GPTData::BlankPartitions()
1770
// Sort the GPT entries, eliminating gaps and making for a logical
1771
// ordering. Relies on QuickSortGPT() for the bulk of the work
1772
void GPTData::SortGPT(void) {
1773
int i, lastPart = 0;
1776
// First, find the last partition with data, so as not to
1777
// spend needless time sorting empty entries....
1778
for (i = 0; i < mainHeader.numParts; i++) {
1779
if (partitions[i].GetFirstLBA() > 0)
1783
// Now swap empties with the last partitions, to simplify the logic
1784
// in the Quicksort function....
1786
while (i < lastPart) {
1787
if (partitions[i].GetFirstLBA() == 0) {
1788
temp = partitions[i];
1789
partitions[i] = partitions[lastPart];
1790
partitions[lastPart] = temp;
1796
// Now call the recursive quick sort routine to do the real work....
1797
QuickSortGPT(partitions, 0, lastPart);
1798
} // GPTData::SortGPT()
1800
// Set up data structures for entirely new set of partitions on the
1801
// specified device. Returns 1 if OK, 0 if there were problems.
1802
// Note that this function does NOT clear the protectiveMBR data
1803
// structure, since it may hold the original MBR partitions if the
1804
// program was launched on an MBR disk, and those may need to be
1805
// converted to GPT format.
1806
int GPTData::ClearGPTData(void) {
1809
// Set up the partition table....
1812
SetGPTSize(NUM_GPT_ENTRIES);
1814
// Now initialize a bunch of stuff that's static....
1815
mainHeader.signature = GPT_SIGNATURE;
1816
mainHeader.revision = 0x00010000;
1817
mainHeader.headerSize = HEADER_SIZE;
1818
mainHeader.reserved = 0;
1819
mainHeader.currentLBA = UINT64_C(1);
1820
mainHeader.partitionEntriesLBA = (uint64_t) 2;
1821
mainHeader.sizeOfPartitionEntries = GPT_SIZE;
1822
for (i = 0; i < GPT_RESERVED; i++) {
1823
mainHeader.reserved2[i] = '\0';
1826
// Now some semi-static items (computed based on end of disk)
1827
mainHeader.backupLBA = diskSize - UINT64_C(1);
1828
mainHeader.lastUsableLBA = diskSize - mainHeader.firstUsableLBA;
1830
// Set a unique GUID for the disk, based on random numbers
1831
// rand() is only 32 bits, so multiply together to fill a 64-bit value
1832
mainHeader.diskGUID.data1 = (uint64_t) rand() * (uint64_t) rand();
1833
mainHeader.diskGUID.data2 = (uint64_t) rand() * (uint64_t) rand();
1835
// Copy main header to backup header
1836
RebuildSecondHeader();
1838
// Blank out the partitions array....
1841
// Flag all CRCs as being OK....
1845
secondPartsCrcOk = 1;
1848
} // GPTData::ClearGPTData()
1850
// Set the location of the second GPT header data to the end of the disk.
1851
// Used internally and called by the 'e' option on the recovery &
1852
// transformation menu, to help users of RAID arrays who add disk space
1854
void GPTData::MoveSecondHeaderToEnd() {
1855
mainHeader.backupLBA = secondHeader.currentLBA = diskSize - UINT64_C(1);
1856
mainHeader.lastUsableLBA = secondHeader.lastUsableLBA = diskSize - mainHeader.firstUsableLBA;
1857
secondHeader.partitionEntriesLBA = secondHeader.lastUsableLBA + UINT64_C(1);
1858
} // GPTData::FixSecondHeaderLocation()
1860
void GPTData::SetName(uint32_t partNum, char* theName) {
1861
if ((partNum >= 0) && (partNum < mainHeader.numParts))
1862
if (partitions[partNum].GetFirstLBA() > 0)
1863
partitions[partNum].SetName((unsigned char*) theName);
1864
} // GPTData::SetName
1866
// Set the disk GUID to the specified value. Note that the header CRCs must
1867
// be recomputed after calling this function.
1868
void GPTData::SetDiskGUID(GUIDData newGUID) {
1869
mainHeader.diskGUID = newGUID;
1870
secondHeader.diskGUID = newGUID;
1873
// Set the unique GUID of the specified partition. Returns 1 on
1874
// successful completion, 0 if there were problems (invalid
1875
// partition number).
1876
int GPTData::SetPartitionGUID(uint32_t pn, GUIDData theGUID) {
1879
if (pn < mainHeader.numParts) {
1880
if (partitions[pn].GetFirstLBA() != UINT64_C(0)) {
1881
partitions[pn].SetUniqueGUID(theGUID);
1886
} // GPTData::SetPartitionGUID()
1888
/********************************************************
1890
* Functions that return data about GPT data structures *
1891
* (most of these are inline in gpt.h) *
1893
********************************************************/
1895
// Find the low and high used partition numbers (numbered from 0).
1896
// Return value is the number of partitions found. Note that the
1897
// *low and *high values are both set to 0 when no partitions
1898
// are found, as well as when a single partition in the first
1899
// position exists. Thus, the return value is the only way to
1900
// tell when no partitions exist.
1901
int GPTData::GetPartRange(uint32_t *low, uint32_t *high) {
1905
*low = mainHeader.numParts + 1; // code for "not found"
1907
if (mainHeader.numParts > 0) { // only try if partition table exists...
1908
for (i = 0; i < mainHeader.numParts; i++) {
1909
if (partitions[i].GetFirstLBA() != UINT64_C(0)) { // it exists
1910
*high = i; // since we're counting up, set the high value
1911
// Set the low value only if it's not yet found...
1912
if (*low == (mainHeader.numParts + 1)) *low = i;
1918
// Above will leave *low pointing to its "not found" value if no partitions
1919
// are defined, so reset to 0 if this is the case....
1920
if (*low == (mainHeader.numParts + 1))
1923
} // GPTData::GetPartRange()
1925
// Returns the number of defined partitions.
1926
uint32_t GPTData::CountParts(void) {
1929
for (i = 0; i < mainHeader.numParts; i++) {
1930
if (partitions[i].GetFirstLBA() > 0)
1934
} // GPTData::CountParts()
1936
/****************************************************
1938
* Functions that return data about disk free space *
1940
****************************************************/
1942
// Find the first available block after the starting point; returns 0 if
1943
// there are no available blocks left
1944
uint64_t GPTData::FindFirstAvailable(uint64_t start) {
1949
// Begin from the specified starting point or from the first usable
1950
// LBA, whichever is greater...
1951
if (start < mainHeader.firstUsableLBA)
1952
first = mainHeader.firstUsableLBA;
1956
// ...now search through all partitions; if first is within an
1957
// existing partition, move it to the next sector after that
1958
// partition and repeat. If first was moved, set firstMoved
1959
// flag; repeat until firstMoved is not set, so as to catch
1960
// cases where partitions are out of sequential order....
1963
for (i = 0; i < mainHeader.numParts; i++) {
1964
if ((first >= partitions[i].GetFirstLBA()) &&
1965
(first <= partitions[i].GetLastLBA())) { // in existing part.
1966
first = partitions[i].GetLastLBA() + 1;
1970
} while (firstMoved == 1);
1971
if (first > mainHeader.lastUsableLBA)
1974
} // GPTData::FindFirstAvailable()
1976
// Finds the first available sector in the largest block of unallocated
1977
// space on the disk. Returns 0 if there are no available blocks left
1978
uint64_t GPTData::FindFirstInLargest(void) {
1979
uint64_t start, firstBlock, lastBlock, segmentSize, selectedSize = 0, selectedSegment = 0;
1983
firstBlock = FindFirstAvailable(start);
1984
if (firstBlock != UINT32_C(0)) { // something's free...
1985
lastBlock = FindLastInFree(firstBlock);
1986
segmentSize = lastBlock - firstBlock + UINT32_C(1);
1987
if (segmentSize > selectedSize) {
1988
selectedSize = segmentSize;
1989
selectedSegment = firstBlock;
1991
start = lastBlock + 1;
1993
} while (firstBlock != 0);
1994
return selectedSegment;
1995
} // GPTData::FindFirstInLargest()
1997
// Find the last available block on the disk at or after the start
1998
// block. Returns 0 if there are no available partitions after
1999
// (or including) start.
2000
uint64_t GPTData::FindLastAvailable(uint64_t start) {
2005
// Start by assuming the last usable LBA is available....
2006
last = mainHeader.lastUsableLBA;
2008
// ...now, similar to algorithm in FindFirstAvailable(), search
2009
// through all partitions, moving last when it's in an existing
2010
// partition. Set the lastMoved flag so we repeat to catch cases
2011
// where partitions are out of logical order.
2014
for (i = 0; i < mainHeader.numParts; i++) {
2015
if ((last >= partitions[i].GetFirstLBA()) &&
2016
(last <= partitions[i].GetLastLBA())) { // in existing part.
2017
last = partitions[i].GetFirstLBA() - 1;
2021
} while (lastMoved == 1);
2022
if (last < mainHeader.firstUsableLBA)
2025
} // GPTData::FindLastAvailable()
2027
// Find the last available block in the free space pointed to by start.
2028
uint64_t GPTData::FindLastInFree(uint64_t start) {
2029
uint64_t nearestStart;
2032
nearestStart = mainHeader.lastUsableLBA;
2033
for (i = 0; i < mainHeader.numParts; i++) {
2034
if ((nearestStart > partitions[i].GetFirstLBA()) &&
2035
(partitions[i].GetFirstLBA() > start)) {
2036
nearestStart = partitions[i].GetFirstLBA() - 1;
2039
return (nearestStart);
2040
} // GPTData::FindLastInFree()
2042
// Finds the total number of free blocks, the number of segments in which
2043
// they reside, and the size of the largest of those segments
2044
uint64_t GPTData::FindFreeBlocks(int *numSegments, uint64_t *largestSegment) {
2045
uint64_t start = UINT64_C(0); // starting point for each search
2046
uint64_t totalFound = UINT64_C(0); // running total
2047
uint64_t firstBlock; // first block in a segment
2048
uint64_t lastBlock; // last block in a segment
2049
uint64_t segmentSize; // size of segment in blocks
2052
*largestSegment = UINT64_C(0);
2054
firstBlock = FindFirstAvailable(start);
2055
if (firstBlock != UINT64_C(0)) { // something's free...
2056
lastBlock = FindLastInFree(firstBlock);
2057
segmentSize = lastBlock - firstBlock + UINT64_C(1);
2058
if (segmentSize > *largestSegment) {
2059
*largestSegment = segmentSize;
2061
totalFound += segmentSize;
2063
start = lastBlock + 1;
2065
} while (firstBlock != 0);
2068
} // GPTData::FindFreeBlocks()
2070
// Returns 1 if sector is unallocated, 0 if it's allocated to a partition
2071
int GPTData::IsFree(uint64_t sector) {
2075
for (i = 0; i < mainHeader.numParts; i++) {
2076
if ((sector >= partitions[i].GetFirstLBA()) &&
2077
(sector <= partitions[i].GetLastLBA())) {
2081
if ((sector < mainHeader.firstUsableLBA) ||
2082
(sector > mainHeader.lastUsableLBA)) {
2086
} // GPTData::IsFree()
2088
/********************************
2090
* Endianness support functions *
2092
********************************/
2094
void GPTData::ReverseHeaderBytes(struct GPTHeader* header) {
2095
ReverseBytes(&header->signature, 8);
2096
ReverseBytes(&header->revision, 4);
2097
ReverseBytes(&header->headerSize, 4);
2098
ReverseBytes(&header->headerCRC, 4);
2099
ReverseBytes(&header->reserved, 4);
2100
ReverseBytes(&header->currentLBA, 8);
2101
ReverseBytes(&header->backupLBA, 8);
2102
ReverseBytes(&header->firstUsableLBA, 8);
2103
ReverseBytes(&header->lastUsableLBA, 8);
2104
ReverseBytes(&header->partitionEntriesLBA, 8);
2105
ReverseBytes(&header->numParts, 4);
2106
ReverseBytes(&header->sizeOfPartitionEntries, 4);
2107
ReverseBytes(&header->partitionEntriesCRC, 4);
2108
ReverseBytes(&header->reserved2, GPT_RESERVED);
2109
ReverseBytes(&header->diskGUID.data1, 8);
2110
ReverseBytes(&header->diskGUID.data2, 8);
2111
} // GPTData::ReverseHeaderBytes()
2113
// IMPORTANT NOTE: This function requires non-reversed mainHeader
2115
void GPTData::ReversePartitionBytes() {
2118
// Check GPT signature on big-endian systems; this will mismatch
2119
// if the function is called out of order. Unfortunately, it'll also
2120
// mismatch if there's data corruption.
2121
if ((mainHeader.signature != GPT_SIGNATURE) && (IsLittleEndian() == 0)) {
2122
printf("GPT signature mismatch in GPTData::ReversePartitionBytes(). This indicates\n"
2123
"data corruption or a misplaced call to this function.\n");
2124
} // if signature mismatch....
2125
for (i = 0; i < mainHeader.numParts; i++) {
2126
partitions[i].ReversePartBytes();
2128
} // GPTData::ReversePartitionBytes()
2130
/******************************************
2132
* Additional non-class support functions *
2134
******************************************/
2136
// Check to be sure that data type sizes are correct. The basic types (uint*_t) should
2137
// never fail these tests, but the struct types may fail depending on compile options.
2138
// Specifically, the -fpack-struct option to gcc may be required to ensure proper structure
2143
if (sizeof(uint8_t) != 1) {
2144
fprintf(stderr, "uint8_t is %d bytes, should be 1 byte; aborting!\n", sizeof(uint8_t));
2147
if (sizeof(uint16_t) != 2) {
2148
fprintf(stderr, "uint16_t is %d bytes, should be 2 bytes; aborting!\n", sizeof(uint16_t));
2151
if (sizeof(uint32_t) != 4) {
2152
fprintf(stderr, "uint32_t is %d bytes, should be 4 bytes; aborting!\n", sizeof(uint32_t));
2155
if (sizeof(uint64_t) != 8) {
2156
fprintf(stderr, "uint64_t is %d bytes, should be 8 bytes; aborting!\n", sizeof(uint64_t));
2159
if (sizeof(struct MBRRecord) != 16) {
2160
fprintf(stderr, "MBRRecord is %d bytes, should be 16 bytes; aborting!\n", sizeof(MBRRecord));
2163
if (sizeof(struct TempMBR) != 512) {
2164
fprintf(stderr, "TempMBR is %d bytes, should be 512 bytes; aborting!\n", sizeof(TempMBR));
2167
if (sizeof(struct GPTHeader) != 512) {
2168
fprintf(stderr, "GPTHeader is %d bytes, should be 512 bytes; aborting!\n", sizeof(GPTHeader));
2171
if (sizeof(GPTPart) != 128) {
2172
fprintf(stderr, "GPTPart is %d bytes, should be 128 bytes; aborting!\n", sizeof(GPTPart));
2175
// Determine endianness; set allOK = 0 if running on big-endian hardware
2176
if (IsLittleEndian() == 0) {
2177
fprintf(stderr, "\aRunning on big-endian hardware. Big-endian support is new and poorly"
2178
" tested!\nBeware!\n");