blob: 4fdc5fc4964a7fea9c43db4004f08dd5c4565bac [file] [log] [blame]
srs5694e7b4ff92009-08-18 13:16:10 -04001/* gpt.cc -- Functions for loading, saving, and manipulating legacy MBR and GPT partition
2 data. */
3
4/* By Rod Smith, January to February, 2009 */
5
6#define __STDC_LIMIT_MACROS
7#define __STDC_CONSTANT_MACROS
8
9#include <stdio.h>
10#include <unistd.h>
11#include <stdlib.h>
12#include <stdint.h>
13#include <fcntl.h>
14#include <string.h>
15#include <time.h>
16#include <sys/stat.h>
17#include <errno.h>
18#include "crc32.h"
19#include "gpt.h"
20#include "support.h"
21#include "parttypes.h"
22#include "attributes.h"
23
24using namespace std;
25
26/****************************************
27 * *
28 * GPTData class and related structures *
29 * *
30 ****************************************/
31
32GPTData::GPTData(void) {
33 blockSize = SECTOR_SIZE; // set a default
34 diskSize = 0;
35 partitions = NULL;
36 state = gpt_valid;
37 strcpy(device, "");
38 mainCrcOk = 0;
39 secondCrcOk = 0;
40 mainPartsCrcOk = 0;
41 secondPartsCrcOk = 0;
42 srand((unsigned int) time(NULL));
43 SetGPTSize(NUM_GPT_ENTRIES);
44} // GPTData default constructor
45
46// The following constructor loads GPT data from a device file
47GPTData::GPTData(char* filename) {
48 blockSize = SECTOR_SIZE; // set a default
49 diskSize = 0;
50 partitions = NULL;
51 state = gpt_invalid;
52 strcpy(device, "");
53 mainCrcOk = 0;
54 secondCrcOk = 0;
55 mainPartsCrcOk = 0;
56 secondPartsCrcOk = 0;
57 srand((unsigned int) time(NULL));
58 LoadPartitions(filename);
59} // GPTData(char* filename) constructor
60
61GPTData::~GPTData(void) {
62 free(partitions);
63} // GPTData destructor
64
65// Resizes GPT to specified number of entries. Creates a new table if
66// necessary, copies data if it already exists.
67int GPTData::SetGPTSize(uint32_t numEntries) {
68 struct GPTPartition* newParts;
69 struct GPTPartition* trash;
70 uint32_t i, high, copyNum;
71 int allOK = 1;
72
73 // First, adjust numEntries upward, if necessary, to get a number
74 // that fills the allocated sectors
75 i = blockSize / GPT_SIZE;
76 if ((numEntries % i) != 0) {
77 printf("Adjusting GPT size from %lu ", (unsigned long) numEntries);
78 numEntries = ((numEntries / i) + 1) * i;
79 printf("to %lu to fill the sector\n", (unsigned long) numEntries);
80 } // if
81
82 newParts = (struct GPTPartition*) calloc(numEntries, sizeof (struct GPTPartition));
83 if (newParts != NULL) {
84 if (partitions != NULL) { // existing partitions; copy them over
85 GetPartRange(&i, &high);
86 if (numEntries < (high + 1)) { // Highest entry too high for new #
87 printf("The highest-numbered partition is %lu, which is greater than the requested\n"
88 "partition table size of %d; cannot resize. Perhaps sorting will help.\n",
89 (unsigned long) (high + 1), numEntries);
90 allOK = 0;
91 } else { // go ahead with copy
92 if (numEntries < mainHeader.numParts)
93 copyNum = numEntries;
94 else
95 copyNum = mainHeader.numParts;
96 for (i = 0; i < copyNum; i++) {
97 newParts[i] = partitions[i];
98 } // for
99 trash = partitions;
100 partitions = newParts;
101 free(trash);
102 } // if
103 } else { // No existing partition table; just create it
104 partitions = newParts;
105 } // if/else existing partitions
106 mainHeader.numParts = numEntries;
107 secondHeader.numParts = numEntries;
108 mainHeader.firstUsableLBA = ((numEntries * GPT_SIZE) / blockSize) + 2 ;
109 secondHeader.firstUsableLBA = mainHeader.firstUsableLBA;
110 mainHeader.lastUsableLBA = diskSize - mainHeader.firstUsableLBA;
111 secondHeader.lastUsableLBA = mainHeader.lastUsableLBA;
112 secondHeader.partitionEntriesLBA = secondHeader.lastUsableLBA + UINT64_C(1);
113 if (diskSize > 0)
114 CheckGPTSize();
115 } else { // Bad memory allocation
116 fprintf(stderr, "Error allocating memory for partition table!\n");
117 allOK = 0;
118 } // if/else
119 return (allOK);
120} // GPTData::SetGPTSize()
121
122// Checks to see if the GPT tables overrun existing partitions; if they
123// do, issues a warning but takes no action. Returns 1 if all is OK, 0
124// if problems were detected.
125int GPTData::CheckGPTSize(void) {
126 uint64_t overlap, firstUsedBlock, lastUsedBlock;
127 uint32_t i;
128 int allOK = 1;
129
130 // first, locate the first & last used blocks
131 firstUsedBlock = UINT64_MAX;
132 lastUsedBlock = 0;
133 for (i = 0; i < mainHeader.numParts; i++) {
134 if ((partitions[i].firstLBA < firstUsedBlock) &&
135 (partitions[i].firstLBA != 0))
136 firstUsedBlock = partitions[i].firstLBA;
137 if (partitions[i].lastLBA > lastUsedBlock)
138 lastUsedBlock = partitions[i].lastLBA;
139 } // for
140
141 // If the disk size is 0 (the default), then it means that various
142 // variables aren't yet set, so the below tests will be useless;
143 // therefore we should skip everything
144 if (diskSize != 0) {
145 if (mainHeader.firstUsableLBA > firstUsedBlock) {
146 overlap = mainHeader.firstUsableLBA - firstUsedBlock;
147 printf("Warning! Main partition table overlaps the first partition by %lu\n"
148 "blocks! Try reducing the partition table size by %lu entries.\n",
149 (unsigned long) overlap, (unsigned long) (overlap * 4));
150 printf("(Use the 's' item on the experts' menu.)\n");
151 allOK = 0;
152 } // Problem at start of disk
153 if (mainHeader.lastUsableLBA < lastUsedBlock) {
154 overlap = lastUsedBlock - mainHeader.lastUsableLBA;
155 printf("Warning! Secondary partition table overlaps the last partition by %lu\n"
156 "blocks! Try reducing the partition table size by %lu entries.\n",
157 (unsigned long) overlap, (unsigned long) (overlap * 4));
158 printf("(Use the 's' item on the experts' menu.)\n");
159 allOK = 0;
160 } // Problem at end of disk
161 } // if (diskSize != 0)
162 return allOK;
163} // GPTData::CheckGPTSize()
164
165// Read GPT data from a disk.
166int GPTData::LoadPartitions(char* deviceFilename) {
167 int fd, err;
168 int allOK = 1, i;
169 uint64_t firstBlock, lastBlock;
170
171 if ((fd = open(deviceFilename, O_RDONLY)) != -1) {
172 // store disk information....
173 diskSize = disksize(fd, &err);
174 blockSize = (uint32_t) GetBlockSize(fd);
175 strcpy(device, deviceFilename);
176
177 // Read the MBR
178 protectiveMBR.ReadMBRData(fd);
179
180 // Load the GPT data, whether or not it's valid
181 ForceLoadGPTData(fd);
182
183 switch (UseWhichPartitions()) {
184 case use_mbr:
185// printf("In LoadPartitions(), using MBR\n");
186 XFormPartitions(&protectiveMBR);
187 break;
188 case use_gpt:
189 break;
190 case use_new:
191// printf("In LoadPartitions(), making new\n");
192 ClearGPTData();
193 protectiveMBR.MakeProtectiveMBR();
194 break;
195 } // switch
196
197 // Now find the first and last sectors used by partitions...
198 if (allOK) {
199 firstBlock = mainHeader.backupLBA; // start high
200 lastBlock = 0; // start low
201 for (i = 0; i < mainHeader.numParts; i++) {
202 if ((partitions[i].firstLBA < firstBlock) &&
203 (partitions[i].firstLBA > 0))
204 firstBlock = partitions[i].firstLBA;
205 if (partitions[i].lastLBA > lastBlock)
206 lastBlock = partitions[i].lastLBA;
207 } // for
208 } // if
209 CheckGPTSize();
210 } else {
211 allOK = 0;
srs5694e19ba092009-08-24 14:10:35 -0400212 fprintf(stderr, "Problem opening %s for reading! Error is %d\n",
213 deviceFilename, errno);
214 if (errno == EACCES) { // User is probably not running as root
215 fprintf(stderr, "You must run this program as root or use sudo!\n");
216 } // if
srs5694e7b4ff92009-08-18 13:16:10 -0400217 } // if/else
218 return (allOK);
219} // GPTData::LoadPartitions()
220
221// Loads the GPT, as much as possible. Returns 1 if this seems to have
222// succeeded, 0 if there are obvious problems....
223int GPTData::ForceLoadGPTData(int fd) {
224 int allOK = 1, validHeaders;
225 off_t seekTo;
226 char* storage;
227 uint32_t newCRC, sizeOfParts;
228
229 // Seek to and read the main GPT header
230 lseek64(fd, 512, SEEK_SET);
231 read(fd, &mainHeader, 512); // read main GPT header
232 mainCrcOk = CheckHeaderCRC(&mainHeader);
233
234 // Load backup header, check its CRC, and store the results of
235 // the check for future reference
236 seekTo = (diskSize * blockSize) - UINT64_C(512);
237 if (lseek64(fd, seekTo, SEEK_SET) != (off_t) -1) {
238 read(fd, &secondHeader, 512); // read secondary GPT header
239 secondCrcOk = CheckHeaderCRC(&secondHeader);
240 } else {
241 allOK = 0;
242 state = gpt_invalid;
243 fprintf(stderr, "Unable to seek to secondary GPT at sector %llu!\n",
244 diskSize - (UINT64_C(1)));
245 } // if/else lseek
246
247 // Return valid headers code: 0 = both headers bad; 1 = main header
248 // good, backup bad; 2 = backup header good, main header bad;
249 // 3 = both headers good. Note these codes refer to valid GPT
250 // signatures and version numbers; more subtle problems will elude
251 // this check!
252 validHeaders = CheckHeaderValidity();
253
254 // Read partitions (from primary array)
255 if (validHeaders > 0) { // if at least one header is OK....
256 // GPT appears to be valid....
257 state = gpt_valid;
258
259 // We're calling the GPT valid, but there's a possibility that one
260 // of the two headers is corrupt. If so, use the one that seems to
261 // be in better shape to regenerate the bad one
262 if (validHeaders == 2) { // valid backup header, invalid main header
263 printf("Caution: invalid main GPT header, but valid backup; regenerating main header\n"
264 "from backup!\n");
265 RebuildMainHeader();
266 mainCrcOk = secondCrcOk; // Since copied, use CRC validity of backup
267 } else if (validHeaders == 1) { // valid main header, invalid backup
268 printf("Caution: invalid backup GPT header, but valid main header; regenerating\n"
269 "backup header from main header.\n");
270 RebuildSecondHeader();
271 secondCrcOk = mainCrcOk; // Since regenerated, use CRC validity of main
272 } // if/else/if
273
274 // Load the main partition table, including storing results of its
275 // CRC check
276 if (LoadMainTable() == 0)
277 allOK = 0;
278
279 // Load backup partition table into temporary storage to check
280 // its CRC and store the results, then discard this temporary
281 // storage, since we don't use it in any but recovery operations
282 seekTo = secondHeader.partitionEntriesLBA * (off_t) blockSize;
283 if ((lseek64(fd, seekTo, SEEK_SET) != (off_t) -1) && (secondCrcOk)) {
284 sizeOfParts = secondHeader.numParts * secondHeader.sizeOfPartitionEntries;
285 storage = (char*) malloc(sizeOfParts);
286 read(fd, storage, sizeOfParts);
287 newCRC = chksum_crc32((unsigned char*) storage, sizeOfParts);
288 free(storage);
289 secondPartsCrcOk = (newCRC == secondHeader.partitionEntriesCRC);
290 } // if
291
292 // Check for valid CRCs and warn if there are problems
293 if ((mainCrcOk == 0) || (secondCrcOk == 0) || (mainPartsCrcOk == 0) ||
294 (secondPartsCrcOk == 0)) {
295 printf("Warning! One or more CRCs don't match. You should repair the disk!\n");
296 state = gpt_corrupt;
297 } // if
298 } else {
299 state = gpt_invalid;
300 } // if/else
301 return allOK;
302} // GPTData::ForceLoadGPTData()
303
304// Loads the partition tables pointed to by the main GPT header. The
305// main GPT header in memory MUST be valid for this call to do anything
306// sensible!
307int GPTData::LoadMainTable(void) {
308 int fd, retval = 0;
309 uint32_t newCRC, sizeOfParts;
310
311 if ((fd = open(device, O_RDONLY)) != -1) {
312 // Set internal data structures for number of partitions on the disk
313 SetGPTSize(mainHeader.numParts);
314
315 // Load main partition table, and record whether its CRC
316 // matches the stored value
317 lseek64(fd, mainHeader.partitionEntriesLBA * blockSize, SEEK_SET);
318 sizeOfParts = mainHeader.numParts * mainHeader.sizeOfPartitionEntries;
319 read(fd, partitions, sizeOfParts);
320 newCRC = chksum_crc32((unsigned char*) partitions, sizeOfParts);
321 mainPartsCrcOk = (newCRC == mainHeader.partitionEntriesCRC);
322 retval = 1;
323 } // if
324 return retval;
325} // GPTData::LoadMainTable()
326
327// Examines the MBR & GPT data, and perhaps asks the user questions, to
328// determine which set of data to use: the MBR (use_mbr), the GPT (use_gpt),
329// or create a new set of partitions (use_new)
330WhichToUse GPTData::UseWhichPartitions(void) {
331 WhichToUse which = use_new;
332 MBRValidity mbrState;
333 int answer;
334
335 mbrState = protectiveMBR.GetValidity();
336
337 if ((state == gpt_invalid) && (mbrState == mbr)) {
338 printf("\n\a***************************************************************\n"
339 "Found invalid GPT and valid MBR; converting MBR to GPT format.\n"
340 "THIS OPERATON IS POTENTIALLY DESTRUCTIVE! Exit by typing 'q' if\n"
341 "you don't want to convert your MBR partitions to GPT format!\n"
342 "***************************************************************\n\n");
343 which = use_mbr;
344 } // if
345 if ((state == gpt_valid) && (mbrState == gpt)) {
346 printf("Found valid GPT with protective MBR; using GPT.\n");
347 which = use_gpt;
348 } // if
349 if ((state == gpt_valid) && (mbrState == invalid)) {
350 printf("\aFound valid GPT with corrupt MBR; using GPT and will create new\nprotective MBR on save.\n");
351 which = use_gpt;
352 protectiveMBR.MakeProtectiveMBR();
353 } // if
354 if ((state == gpt_valid) && (mbrState == mbr)) {
355 printf("Found valid MBR and GPT. Which do you want to use?\n");
356 answer = GetNumber(1, 3, 2, (char*) " 1 - MBR\n 2 - GPT\n 3 - Create blank GPT\n\nYour answer: ");
357 if (answer == 1) {
358 which = use_mbr;
359 } else if (answer == 2) {
360 which = use_gpt;
361 protectiveMBR.MakeProtectiveMBR();
362 printf("Using GPT and creating fresh protective MBR.\n");
363 } else which = use_new;
364 } // if
365
366 // Nasty decisions here -- GPT is present, but corrupt (bad CRCs or other
367 // problems)
368 if (state == gpt_corrupt) {
369 if (mbrState == mbr) {
370 printf("Found valid MBR and corrupt GPT. Which do you want to use? (Using the\n"
371 "GPT MAY permit recovery of GPT data.)\n");
372 answer = GetNumber(1, 3, 2, (char*) " 1 - MBR\n 2 - GPT\n 3 - Create blank GPT\n\nYour answer: ");
373 if (answer == 1) {
374 which = use_mbr;
375// protectiveMBR.MakeProtectiveMBR();
376 } else if (answer == 2) {
377 which = use_gpt;
378 } else which = use_new;
379 } else if (mbrState == invalid) {
380 printf("Found invalid MBR and corrupt GPT. What do you want to do? (Using the\n"
381 "GPT MAY permit recovery of GPT data.)\n");
382 answer = GetNumber(1, 2, 1, (char*) " 1 - GPT\n 2 - Create blank GPT\n\nYour answer: ");
383 if (answer == 1) {
384 which = use_gpt;
385 } else which = use_new;
386 } else {
387 printf("\a\a****************************************************************************\n"
388 "Caution: Found protective or hybrid MBR and corrupt GPT. Using GPT, but disk\n"
389 "verification and recovery are STRONGLY recommended.\n"
390 "****************************************************************************\n");
391 } // if
392 } // if
393
394 if (which == use_new)
395 printf("Creating new GPT entries.\n");
396
397 return which;
398} // UseWhichPartitions()
399
400void GPTData::ResizePartitionTable(void) {
401 int newSize;
402 char prompt[255];
403 uint32_t curLow, curHigh;
404
405 printf("Current partition table size is %lu.\n",
406 (unsigned long) mainHeader.numParts);
407 GetPartRange(&curLow, &curHigh);
408 curHigh++; // since GetPartRange() returns numbers starting from 0...
409 // There's no point in having fewer than four partitions....
410 if (curHigh < 4)
411 curHigh = 4;
412 sprintf(prompt, "Enter new size (%d up, default %d): ", (int) curHigh,
413 (int) NUM_GPT_ENTRIES);
414 newSize = GetNumber(4, 65535, 128, prompt);
415 if (newSize < 128) {
416 printf("Caution: The partition table size should officially be 16KB or larger,\n"
417 "which works out to 128 entries. In practice, smaller tables seem to\n"
418 "work with most OSes, but this practice is risky. I'm proceeding with\n"
419 "the resize, but you may want to reconsider this action and undo it.\n\n");
420 } // if
421 SetGPTSize(newSize);
422} // GPTData::ResizePartitionTable()
423
424// Find the low and high used partition numbers (numbered from 0).
425// Return value is the number of partitions found. Note that the
426// *low and *high values are both set to 0 when no partitions
427// are found, as well as when a single partition in the first
428// position exists. Thus, the return value is the only way to
429// tell when no partitions exist.
430int GPTData::GetPartRange(uint32_t *low, uint32_t *high) {
431 uint32_t i;
432 int numFound = 0;
433
434 *low = mainHeader.numParts + 1; // code for "not found"
435 *high = 0;
436 if (mainHeader.numParts > 0) { // only try if partition table exists...
437 for (i = 0; i < mainHeader.numParts; i++) {
438 if (partitions[i].firstLBA != UINT64_C(0)) { // it exists
439 *high = i; // since we're counting up, set the high value
440 // Set the low value only if it's not yet found...
441 if (*low == (mainHeader.numParts + 1)) *low = i;
442 numFound++;
443 } // if
444 } // for
445 } // if
446
447 // Above will leave *low pointing to its "not found" value if no partitions
448 // are defined, so reset to 0 if this is the case....
449 if (*low == (mainHeader.numParts + 1))
450 *low = 0;
451 return numFound;
452} // GPTData::GetPartRange()
453
454// Display the basic GPT data
455void GPTData::DisplayGPTData(void) {
456 int i, j;
457 char sizeInSI[255]; // String to hold size of disk in SI units
458 char tempStr[255];
459 uint64_t temp, totalFree;
460
461 BytesToSI(diskSize * blockSize, sizeInSI);
462 printf("Disk %s: %lu sectors, %s\n", device,
463 (unsigned long) diskSize, sizeInSI);
464 printf("Disk identifier (GUID): %s\n", GUIDToStr(mainHeader.diskGUID, tempStr));
465 printf("Partition table holds up to %lu entries\n", (unsigned long) mainHeader.numParts);
466 printf("First usable sector is %lu, last usable sector is %lu\n",
467 (unsigned long) mainHeader.firstUsableLBA,
468 (unsigned long) mainHeader.lastUsableLBA);
469 totalFree = FindFreeBlocks(&i, &temp);
470 printf("Total free space is %llu sectors (%s)\n", totalFree,
471 BytesToSI(totalFree * (uint64_t) blockSize, sizeInSI));
srs5694e19ba092009-08-24 14:10:35 -0400472 printf("\nNumber Start (sector) End (sector) Size Code Name\n");
srs5694e7b4ff92009-08-18 13:16:10 -0400473 for (i = 0; i < mainHeader.numParts; i++) {
474 if (partitions[i].firstLBA != 0) {
475 BytesToSI(blockSize * (partitions[i].lastLBA - partitions[i].firstLBA + 1),
476 sizeInSI);
srs5694e19ba092009-08-24 14:10:35 -0400477 printf("%4d %14lu %14lu", i + 1, (unsigned long) partitions[i].firstLBA,
srs5694e7b4ff92009-08-18 13:16:10 -0400478 (unsigned long) partitions[i].lastLBA);
srs5694e19ba092009-08-24 14:10:35 -0400479 printf(" %-10s %04X ", sizeInSI,
srs5694e7b4ff92009-08-18 13:16:10 -0400480 typeHelper.GUIDToID(partitions[i].partitionType));
481 j = 0;
482 while ((partitions[i].name[j] != '\0') && (j < 44)) {
483 printf("%c", partitions[i].name[j]);
484 j += 2;
485 } // while
486 printf("\n");
487 } // if
488 } // for
489} // GPTData::DisplayGPTData()
490
491// Get partition number from user and then call ShowPartDetails(partNum)
492// to show its detailed information
493void GPTData::ShowDetails(void) {
494 int partNum;
495 uint32_t low, high;
496
497 if (GetPartRange(&low, &high) > 0) {
498 partNum = GetPartNum();
499 ShowPartDetails(partNum);
500 } else {
501 printf("No partitions\n");
502 } // if/else
503} // GPTData::ShowDetails()
504
505// Show detailed information on the specified partition
506void GPTData::ShowPartDetails(uint32_t partNum) {
507 char temp[255];
508 int i;
509 uint64_t size;
510
511 if (partitions[partNum].firstLBA != 0) {
512 printf("Partition GUID code: %s ", GUIDToStr(partitions[partNum].partitionType, temp));
513 printf("(%s)\n", typeHelper.GUIDToName(partitions[partNum].partitionType, temp));
514 printf("Partition unique GUID: %s\n", GUIDToStr(partitions[partNum].uniqueGUID, temp));
515
516 printf("First sector: %llu (at %s)\n", (unsigned long long)
517 partitions[partNum].firstLBA,
518 BytesToSI(partitions[partNum].firstLBA * blockSize, temp));
519 printf("Last sector: %llu (at %s)\n", (unsigned long long)
520 partitions[partNum].lastLBA,
521 BytesToSI(partitions[partNum].lastLBA * blockSize, temp));
522 size = (partitions[partNum].lastLBA - partitions[partNum].firstLBA + 1);
srs5694e19ba092009-08-24 14:10:35 -0400523 printf("Partition size: %llu sectors (%s)\n", (unsigned long long)
srs5694e7b4ff92009-08-18 13:16:10 -0400524 size, BytesToSI(size * ((uint64_t) blockSize), temp));
525 printf("Attribute flags: %016llx\n", (unsigned long long)
526 partitions[partNum].attributes);
527 printf("Partition name: ");
528 i = 0;
529 while ((partitions[partNum].name[i] != '\0') && (i < NAME_SIZE)) {
530 printf("%c", partitions[partNum].name[i]);
531 i += 2;
532 } // while
533 printf("\n");
534 } else {
535 printf("Partition #%d does not exist.", (int) (partNum + 1));
536 } // if
537} // GPTData::ShowPartDetails()
538
539// Interactively create a partition
540void GPTData::CreatePartition(void) {
541 uint64_t firstBlock, lastBlock, sector;
542 char prompt[255];
543 int partNum, firstFreePart = 0;
544
545 // Find first free partition...
546 while (partitions[firstFreePart].firstLBA != 0) {
547 firstFreePart++;
548 } // while
549
550 if (((firstBlock = FindFirstAvailable()) != 0) &&
551 (firstFreePart < mainHeader.numParts)) {
552 lastBlock = FindLastAvailable(firstBlock);
553
554 // Get partition number....
555 do {
556 sprintf(prompt, "Partition number (%d-%d, default %d): ", firstFreePart + 1,
557 mainHeader.numParts, firstFreePart + 1);
558 partNum = GetNumber(firstFreePart + 1, mainHeader.numParts,
559 firstFreePart + 1, prompt) - 1;
560 if (partitions[partNum].firstLBA != 0)
561 printf("partition %d is in use.\n", partNum + 1);
562 } while (partitions[partNum].firstLBA != 0);
563
564 // Get first block for new partition...
565 sprintf(prompt, "First sector (%llu-%llu, default = %llu): ", firstBlock,
566 lastBlock, firstBlock);
567 do {
568 sector = GetNumber(firstBlock, lastBlock, firstBlock, prompt);
569 } while (IsFree(sector) == 0);
570 firstBlock = sector;
571
572 // Get last block for new partitions...
573 lastBlock = FindLastInFree(firstBlock);
574 sprintf(prompt, "Last sector or +size or +sizeM or +sizeK (%llu-%llu, default = %d): ",
575 firstBlock, lastBlock, lastBlock);
576 do {
577 sector = GetLastSector(firstBlock, lastBlock, prompt);
578 } while (IsFree(sector) == 0);
579 lastBlock = sector;
580
581 partitions[partNum].firstLBA = firstBlock;
582 partitions[partNum].lastLBA = lastBlock;
583
584 // rand() is only 32 bits on 32-bit systems, so multiply together to
585 // fill a 64-bit value.
586 partitions[partNum].uniqueGUID.data1 = (uint64_t) rand() * (uint64_t) rand();
587 partitions[partNum].uniqueGUID.data2 = (uint64_t) rand() * (uint64_t) rand();
588 ChangeGPTType(&partitions[partNum]);
589 } else {
590 printf("No free sectors available\n");
591 } // if/else
592} // GPTData::CreatePartition()
593
594// Interactively delete a partition (duh!)
595void GPTData::DeletePartition(void) {
596 int partNum;
597 uint32_t low, high;
598 char prompt[255];
599
600 if (GetPartRange(&low, &high) > 0) {
601 sprintf(prompt, "Partition number (%d-%d): ", low + 1, high + 1);
602 partNum = GetNumber(low + 1, high + 1, low, prompt);
603 BlankPartition(&partitions[partNum - 1]);
604 } else {
605 printf("No partitions\n");
606 } // if/else
607} // GPTData::DeletePartition
608
609// Find the first available block after the starting point; returns 0 if
610// there are no available blocks left
611uint64_t GPTData::FindFirstAvailable(uint64_t start) {
612 uint64_t first;
613 uint32_t i;
614 int firstMoved = 0;
615
616 // Begin from the specified starting point or from the first usable
617 // LBA, whichever is greater...
618 if (start < mainHeader.firstUsableLBA)
619 first = mainHeader.firstUsableLBA;
620 else
621 first = start;
622
623 // ...now search through all partitions; if first is within an
624 // existing partition, move it to the next sector after that
625 // partition and repeat. If first was moved, set firstMoved
626 // flag; repeat until firstMoved is not set, so as to catch
627 // cases where partitions are out of sequential order....
628 do {
629 firstMoved = 0;
630 for (i = 0; i < mainHeader.numParts; i++) {
631 if ((first >= partitions[i].firstLBA) &&
632 (first <= partitions[i].lastLBA)) { // in existing part.
633 first = partitions[i].lastLBA + 1;
634 firstMoved = 1;
635 } // if
636 } // for
637 } while (firstMoved == 1);
638 if (first > mainHeader.lastUsableLBA)
639 first = 0;
640 return (first);
641} // GPTData::FindFirstAvailable()
642
643// Find the last available block on the disk at or after the start
644// block. Returns 0 if there are no available partitions after
645// (or including) start.
646uint64_t GPTData::FindLastAvailable(uint64_t start) {
647 uint64_t last;
648 uint32_t i;
649 int lastMoved = 0;
650
651 // Start by assuming the last usable LBA is available....
652 last = mainHeader.lastUsableLBA;
653
654 // ...now, similar to algorithm in FindFirstAvailable(), search
655 // through all partitions, moving last when it's in an existing
656 // partition. Set the lastMoved flag so we repeat to catch cases
657 // where partitions are out of logical order.
658 do {
659 lastMoved = 0;
660 for (i = 0; i < mainHeader.numParts; i++) {
661 if ((last >= partitions[i].firstLBA) &&
662 (last <= partitions[i].lastLBA)) { // in existing part.
663 last = partitions[i].firstLBA - 1;
664 lastMoved = 1;
665 } // if
666 } // for
667 } while (lastMoved == 1);
668 if (last < mainHeader.firstUsableLBA)
669 last = 0;
670 return (last);
671} // GPTData::FindLastAvailable()
672
673// Find the last available block in the free space pointed to by start.
674uint64_t GPTData::FindLastInFree(uint64_t start) {
675 uint64_t nearestStart;
676 uint32_t i;
677
678 nearestStart = mainHeader.lastUsableLBA;
679 for (i = 0; i < mainHeader.numParts; i++) {
680 if ((nearestStart > partitions[i].firstLBA) &&
681 (partitions[i].firstLBA > start)) {
682 nearestStart = partitions[i].firstLBA - 1;
683 } // if
684 } // for
685 return (nearestStart);
686} // GPTData::FindLastInFree()
687
688// Returns 1 if sector is unallocated, 0 if it's allocated to a partition
689int GPTData::IsFree(uint64_t sector) {
690 int isFree = 1;
691 uint32_t i;
692
693 for (i = 0; i < mainHeader.numParts; i++) {
694 if ((sector >= partitions[i].firstLBA) &&
695 (sector <= partitions[i].lastLBA)) {
696 isFree = 0;
697 } // if
698 } // for
699 if ((sector < mainHeader.firstUsableLBA) ||
700 (sector > mainHeader.lastUsableLBA)) {
701 isFree = 0;
702 } // if
703 return (isFree);
704} // GPTData::IsFree()
705
706int GPTData::XFormPartitions(MBRData* origParts) {
707 int i, j;
708 int numToConvert;
709 uint8_t origType;
710
711 // Clear out old data & prepare basics....
712 ClearGPTData();
713
714 // Convert the smaller of the # of GPT or MBR partitions
715 if (mainHeader.numParts > (NUM_LOGICALS + 4))
716 numToConvert = NUM_LOGICALS + 4;
717 else
718 numToConvert = mainHeader.numParts;
719
720// printf("In XFormPartitions(), numToConvert = %d\n", numToConvert);
721
722 for (i = 0; i < numToConvert; i++) {
723 origType = origParts->GetType(i);
724// printf("Converting partition of type 0x%02X\n", (int) origType);
725
726 // don't convert extended partitions or null (non-existent) partitions
727 if ((origType != 0x05) && (origType != 0x0f) && (origType != 0x00)) {
728 partitions[i].firstLBA = (uint64_t) origParts->GetFirstSector(i);
729 partitions[i].lastLBA = partitions[i].firstLBA + (uint64_t)
730 origParts->GetLength(i) - 1;
731 partitions[i].partitionType = typeHelper.IDToGUID(((uint16_t) origType) * 0x0100);
732
733 // Create random unique GUIDs for the partitions
734 // rand() is only 32 bits, so multiply together to fill a 64-bit value
735 partitions[i].uniqueGUID.data1 = (uint64_t) rand() * (uint64_t) rand();
736 partitions[i].uniqueGUID.data2 = (uint64_t) rand() * (uint64_t) rand();
737 partitions[i].attributes = 0;
738 for (j = 0; j < NAME_SIZE; j++)
739 partitions[i].name[j] = '\0';
740 } // if
741 } // for
742
743 // Convert MBR into protective MBR
744 protectiveMBR.MakeProtectiveMBR();
745
746 // Record that all original CRCs were OK so as not to raise flags
747 // when doing a disk verification
748 mainCrcOk = secondCrcOk = mainPartsCrcOk = secondPartsCrcOk = 1;
749
750 return (1);
751} // XFormPartitions()
752
753// Sort the GPT entries, eliminating gaps and making for a logical
754// ordering. Relies on QuickSortGPT() for the bulk of the work
755void GPTData::SortGPT(void) {
756 int i, lastPart = 0;
757 struct GPTPartition temp;
758
759 // First, find the last partition with data, so as not to
760 // spend needless time sorting empty entries....
761 for (i = 0; i < GPT_SIZE; i++) {
762 if (partitions[i].firstLBA > 0)
763 lastPart = i;
764 } // for
765
766 // Now swap empties with the last partitions, to simplify the logic
767 // in the Quicksort function....
768 i = 0;
769 while (i < lastPart) {
770 if (partitions[i].firstLBA == 0) {
771 temp = partitions[i];
772 partitions[i] = partitions[lastPart];
773 partitions[lastPart] = temp;
774 lastPart--;
775 } // if
776 i++;
777 } // while
778
779 // Now call the recursive quick sort routine to do the real work....
780 QuickSortGPT(partitions, 0, lastPart);
781} // GPTData::SortGPT()
782
783// Recursive quick sort algorithm for GPT partitions. Note that if there
784// are any empties in the specified range, they'll be sorted to the
785// start, resulting in a sorted set of partitions that begins with
786// partition 2, 3, or higher.
787void QuickSortGPT(struct GPTPartition* partitions, int start, int finish) {
788 uint64_t starterValue; // starting location of median partition
789 int left, right;
790 struct GPTPartition temp;
791
792 left = start;
793 right = finish;
794 starterValue = partitions[(start + finish) / 2].firstLBA;
795 do {
796 while (partitions[left].firstLBA < starterValue)
797 left++;
798 while (partitions[right].firstLBA > starterValue)
799 right--;
800 if (left <= right) {
801 temp = partitions[left];
802 partitions[left] = partitions[right];
803 partitions[right] = temp;
804 left++;
805 right--;
806 } // if
807 } while (left <= right);
808 if (start < right) QuickSortGPT(partitions, start, right);
809 if (finish > left) QuickSortGPT(partitions, left, finish);
810} // QuickSortGPT()
811
812// Blank (delete) a single partition
813void BlankPartition(struct GPTPartition* partition) {
814 int j;
815
816 partition->uniqueGUID.data1 = 0;
817 partition->uniqueGUID.data2 = 0;
818 partition->partitionType.data1 = 0;
819 partition->partitionType.data2 = 0;
820 partition->firstLBA = 0;
821 partition->lastLBA = 0;
822 partition->attributes = 0;
823 for (j = 0; j < NAME_SIZE; j++)
824 partition->name[j] = '\0';
825} // BlankPartition
826
827// Blank the partition array
828void GPTData::BlankPartitions(void) {
829 uint32_t i;
830
831 for (i = 0; i < mainHeader.numParts; i++) {
832 BlankPartition(&partitions[i]);
833 } // for
834} // GPTData::BlankPartitions()
835
836// Set up data structures for entirely new set of partitions on the
837// specified device. Returns 1 if OK, 0 if there were problems.
838int GPTData::ClearGPTData(void) {
839 int goOn, i;
840
841 // Set up the partition table....
842 free(partitions);
843 partitions = NULL;
844 SetGPTSize(NUM_GPT_ENTRIES);
845
846 // Now initialize a bunch of stuff that's static....
847 mainHeader.signature = GPT_SIGNATURE;
848 mainHeader.revision = 0x00010000;
849 mainHeader.headerSize = (uint32_t) HEADER_SIZE;
850 mainHeader.reserved = 0;
851 mainHeader.currentLBA = UINT64_C(1);
852 mainHeader.partitionEntriesLBA = (uint64_t) 2;
853 mainHeader.sizeOfPartitionEntries = GPT_SIZE;
854 for (i = 0; i < GPT_RESERVED; i++) {
855 mainHeader.reserved2[i] = '\0';
856 } // for
857
858 // Now some semi-static items (computed based on end of disk)
859 mainHeader.backupLBA = diskSize - UINT64_C(1);
860 mainHeader.lastUsableLBA = diskSize - mainHeader.firstUsableLBA;
861
862 // Set a unique GUID for the disk, based on random numbers
863 // rand() is only 32 bits, so multiply together to fill a 64-bit value
864 mainHeader.diskGUID.data1 = (uint64_t) rand() * (uint64_t) rand();
865 mainHeader.diskGUID.data2 = (uint64_t) rand() * (uint64_t) rand();
866
867 // Copy main header to backup header
868 RebuildSecondHeader();
869
870 // Blank out the partitions array....
871 BlankPartitions();
872 return (goOn);
873} // GPTData::ClearGPTData()
874
875// Returns 1 if the two partitions overlap, 0 if they don't
876int TheyOverlap(struct GPTPartition* first, struct GPTPartition* second) {
877 int theyDo = 0;
878
879 // Don't bother checking unless these are defined (both start and end points
880 // are 0 for undefined partitions, so just check the start points)
881 if ((first->firstLBA != 0) && (second->firstLBA != 0)) {
882 if ((first->firstLBA < second->lastLBA) && (first->lastLBA >= second->firstLBA))
883 theyDo = 1;
884 if ((second->firstLBA < first->lastLBA) && (second->lastLBA >= first->firstLBA))
885 theyDo = 1;
886 } // if
887 return (theyDo);
888} // Overlap()
889
890// Change the type code on the specified partition.
891// Note: The GPT CRCs must be recomputed after calling this function!
892void ChangeGPTType(struct GPTPartition* part) {
893 char typeName[255], line[255];
894 uint16_t typeNum = 0xFFFF;
895 PartTypes typeHelper;
896 GUIDData newType;
897
898 printf("Current type is '%s'\n", typeHelper.GUIDToName(part->partitionType, typeName));
899 while ((!typeHelper.Valid(typeNum)) && (typeNum != 0)) {
900 printf("Hex code (L to show codes, 0 to enter raw code): ");
901 fgets(line, 255, stdin);
902 sscanf(line, "%x", &typeNum);
903 if (line[0] == 'L')
904 typeHelper.ShowTypes();
905 } // while
906 if (typeNum != 0) // user entered a code, so convert it
907 newType = typeHelper.IDToGUID(typeNum);
908 else // user wants to enter the GUID directly, so do that
909 newType = GetGUID();
910 part->partitionType = newType;
911 printf("Changed system type of partition to '%s'\n",
912 typeHelper.GUIDToName(part->partitionType, typeName));
913} // ChangeGPTType()
914
915// Prompt user for a partition number, then change its type code
916// using ChangeGPTType(struct GPTPartition*) function.
917void GPTData::ChangePartType(void) {
918 int partNum;
919 uint32_t low, high;
920
921 if (GetPartRange(&low, &high) > 0) {
922 partNum = GetPartNum();
923 ChangeGPTType(&partitions[partNum]);
924 } else {
925 printf("No partitions\n");
926 } // if/else
927} // GPTData::ChangePartType()
928
929// Prompts user for partition number and returns the result.
930uint32_t GPTData::GetPartNum(void) {
931 uint32_t partNum;
932 uint32_t low, high;
933 char prompt[255];
934
935 if (GetPartRange(&low, &high) > 0) {
936 sprintf(prompt, "Partition number (%d-%d): ", low + 1, high + 1);
937 partNum = GetNumber(low + 1, high + 1, low, prompt);
938 } else partNum = 1;
939 return (partNum - 1);
940} // GPTData::GetPartNum()
941
942// Prompt user for attributes to change on the specified partition
943// and change them.
944void GPTData::SetAttributes(uint32_t partNum) {
945 Attributes theAttr;
946
947 theAttr.SetAttributes(partitions[partNum].attributes);
948 theAttr.DisplayAttributes();
949 theAttr.ChangeAttributes();
950 partitions[partNum].attributes = theAttr.GetAttributes();
951} // GPTData::SetAttributes()
952
953// Set the name for a partition to theName, or prompt for a name if
954// theName is a NULL pointer. Note that theName is a standard C-style
955// string, although the GUID partition definition requires a UTF-16LE
956// string. This function creates a simple-minded copy for this.
957void GPTData::SetName(uint32_t partNum, char* theName) {
958 char newName[NAME_SIZE]; // New name
959 int i;
960
961 // Blank out new name string, just to be on the safe side....
962 for (i = 0; i < NAME_SIZE; i++)
963 newName[i] = '\0';
964
965 if (theName == NULL) { // No name specified, so get one from the user
966 printf("Enter name: ");
967 fgets(newName, NAME_SIZE / 2, stdin);
968
969 // Input is likely to include a newline, so remove it....
970 i = strlen(newName);
971 if (newName[i - 1] == '\n')
972 newName[i - 1] = '\0';
973 } else {
974 strcpy(newName, theName);
975 } // if
976
977 // Copy the C-style ASCII string from newName into a form that the GPT
978 // table will accept....
979 for (i = 0; i < NAME_SIZE; i++) {
980 if ((i % 2) == 0) {
981 partitions[partNum].name[i] = newName[(i / 2)];
982 } else {
983 partitions[partNum].name[i] = '\0';
984 } // if/else
985 } // for
986} // GPTData::SetName()
987
988// Set the disk GUID to the specified value. Note that the header CRCs must
989// be recomputed after calling this function.
990void GPTData::SetDiskGUID(GUIDData newGUID) {
991 mainHeader.diskGUID = newGUID;
992 secondHeader.diskGUID = newGUID;
993} // SetDiskGUID()
994
995// Set the unique GUID of the specified partition. Returns 1 on
996// successful completion, 0 if there were problems (invalid
997// partition number).
998int GPTData::SetPartitionGUID(uint32_t pn, GUIDData theGUID) {
999 int retval = 0;
1000
1001 if (pn < mainHeader.numParts) {
1002 if (partitions[pn].firstLBA != UINT64_C(0)) {
1003 partitions[pn].uniqueGUID = theGUID;
1004 retval = 1;
1005 } // if
1006 } // if
1007 return retval;
1008} // GPTData::SetPartitionGUID()
1009
1010// Check the validity of the GPT header. Returns 1 if the main header
1011// is valid, 2 if the backup header is valid, 3 if both are valid, and
1012// 0 if neither is valid. Note that this function just checks the GPT
1013// signature and revision numbers, not CRCs or other data.
1014int GPTData::CheckHeaderValidity(void) {
1015 int valid = 3;
1016
1017 if (mainHeader.signature != GPT_SIGNATURE) {
1018 valid -= 1;
1019 printf("Main GPT signature invalid; read 0x%016llX, should be\n0x%016llX\n",
1020 (unsigned long long) mainHeader.signature, (unsigned long long) GPT_SIGNATURE);
1021 } else if ((mainHeader.revision != 0x00010000) && valid) {
1022 valid -= 1;
1023 printf("Unsupported GPT version in main header; read 0x%08lX, should be\n0x%08lX\n",
1024 (unsigned long) mainHeader.revision, UINT32_C(0x00010000));
1025 } // if/else/if
1026
1027 if (secondHeader.signature != GPT_SIGNATURE) {
1028 valid -= 2;
1029 printf("Secondary GPT signature invalid; read 0x%016llX, should be\n0x%016llX\n",
1030 (unsigned long long) secondHeader.signature, (unsigned long long) GPT_SIGNATURE);
1031 } else if ((secondHeader.revision != 0x00010000) && valid) {
1032 valid -= 2;
1033 printf("Unsupported GPT version in backup header; read 0x%08lX, should be\n0x%08lX\n",
1034 (unsigned long) mainHeader.revision, UINT32_C(0x00010000));
1035 } // if/else/if
1036
1037 return valid;
1038} // GPTData::CheckHeaderValidity()
1039
1040// Check the header CRC to see if it's OK...
1041int GPTData::CheckHeaderCRC(struct GPTHeader* header) {
1042 uint32_t oldCRC, newCRC;
1043
1044 // Back up old header and then blank it, since it must be 0 for
1045 // computation to be valid
1046 oldCRC = header->headerCRC;
1047 header->headerCRC = UINT32_C(0);
1048
1049 // Initialize CRC functions...
1050 chksum_crc32gentab();
1051
1052 // Compute CRC, restore original, and return result of comparison
1053 newCRC = chksum_crc32((unsigned char*) header, HEADER_SIZE);
1054 mainHeader.headerCRC = oldCRC;
1055 return (oldCRC == newCRC);
1056} // GPTData::CheckHeaderCRC()
1057
1058// Recompute all the CRCs. Must be called before saving if any changes
1059// have been made.
1060void GPTData::RecomputeCRCs(void) {
1061 uint32_t crc;
1062
1063 // Initialize CRC functions...
1064 chksum_crc32gentab();
1065
1066 // Compute CRC of partition tables & store in main and secondary headers
1067 crc = chksum_crc32((unsigned char*) partitions, mainHeader.numParts * GPT_SIZE);
1068 mainHeader.partitionEntriesCRC = crc;
1069 secondHeader.partitionEntriesCRC = crc;
1070
1071 // Zero out GPT tables' own CRCs (required for correct computation)
1072 mainHeader.headerCRC = 0;
1073 secondHeader.headerCRC = 0;
1074
1075 // Compute & store CRCs of main & secondary headers...
1076 crc = chksum_crc32((unsigned char*) &mainHeader, HEADER_SIZE);
1077 mainHeader.headerCRC = crc;
1078 crc = chksum_crc32((unsigned char*) &secondHeader, HEADER_SIZE);
1079 secondHeader.headerCRC = crc;
1080} // GPTData::RecomputeCRCs()
1081
1082// Perform detailed verification, reporting on any problems found, but
1083// do *NOT* recover from these problems. Returns the total number of
1084// problems identified.
1085int GPTData::Verify(void) {
1086 int problems = 0, numSegments, i, j;
1087 uint64_t totalFree, largestSegment;
1088 char tempStr[255], siTotal[255], siLargest[255];
1089
1090 // First, check for CRC errors in the GPT data....
1091 if (!mainCrcOk) {
1092 problems++;
1093 printf("\nProblem: The CRC for the main GPT header is invalid. The main GPT header may\n"
1094 "be corrupt. Consider loading the backup GPT header to rebuild the main GPT\n"
1095 "header\n");
1096 } // if
1097 if (!mainPartsCrcOk) {
1098 problems++;
1099 printf("\nProblem: The CRC for the main partition table is invalid. This table may be\n"
1100 "corrupt. Consider loading the backup partition table.\n");
1101 } // if
1102 if (!secondCrcOk) {
1103 problems++;
1104 printf("\nProblem: The CRC for the backup GPT header is invalid. The backup GPT header\n"
1105 "may be corrupt. Consider using the main GPT header to rebuild the backup GPT\n"
1106 "header.\n");
1107 } // if
1108 if (!secondPartsCrcOk) {
1109 problems++;
1110 printf("\nCaution: The CRC for the backup partition table is invalid. This table may\n"
1111 "be corrupt. This program will automatically create a new backup partition\n"
1112 "table when you save your partitions.\n");
1113 } // if
1114
1115 // Now check that critical main and backup GPT entries match
1116 if (mainHeader.currentLBA != secondHeader.backupLBA) {
1117 problems++;
1118 printf("\nProblem: main GPT header's current LBA pointer (%llu) doesn't\n"
1119 "match the backup GPT header's LBA pointer(%llu)\n",
1120 (unsigned long long) mainHeader.currentLBA,
1121 (unsigned long long) secondHeader.backupLBA);
1122 } // if
1123 if (mainHeader.backupLBA != secondHeader.currentLBA) {
1124 problems++;
1125 printf("\nProblem: main GPT header's backup LBA pointer (%llu) doesn't\n"
1126 "match the backup GPT header's current LBA pointer (%llu)\n",
1127 (unsigned long long) mainHeader.backupLBA,
1128 (unsigned long long) secondHeader.currentLBA);
1129 } // if
1130 if (mainHeader.firstUsableLBA != secondHeader.firstUsableLBA) {
1131 problems++;
1132 printf("\nProblem: main GPT header's first usable LBA pointer (%llu) doesn't\n"
1133 "match the backup GPT header's first usable LBA pointer (%llu)\n",
1134 (unsigned long long) mainHeader.firstUsableLBA,
1135 (unsigned long long) secondHeader.firstUsableLBA);
1136 } // if
1137 if (mainHeader.lastUsableLBA != secondHeader.lastUsableLBA) {
1138 problems++;
1139 printf("\nProblem: main GPT header's last usable LBA pointer (%llu) doesn't\n"
1140 "match the backup GPT header's last usable LBA pointer (%llu)\n",
1141 (unsigned long long) mainHeader.lastUsableLBA,
1142 (unsigned long long) secondHeader.lastUsableLBA);
1143 } // if
1144 if ((mainHeader.diskGUID.data1 != secondHeader.diskGUID.data1) ||
1145 (mainHeader.diskGUID.data2 != secondHeader.diskGUID.data2)) {
1146 problems++;
1147 printf("\nProblem: main header's disk GUID (%s) doesn't\n",
1148 GUIDToStr(mainHeader.diskGUID, tempStr));
1149 printf("match the backup GPT header's disk GUID (%s)\n",
1150 GUIDToStr(secondHeader.diskGUID, tempStr));
1151 } // if
1152 if (mainHeader.numParts != secondHeader.numParts) {
1153 problems++;
1154 printf("\nProblem: main GPT header's number of partitions (%lu) doesn't\n"
1155 "match the backup GPT header's number of partitions (%lu)\n",
1156 (unsigned long) mainHeader.numParts,
1157 (unsigned long) secondHeader.numParts);
1158 } // if
1159 if (mainHeader.sizeOfPartitionEntries != secondHeader.sizeOfPartitionEntries) {
1160 problems++;
1161 printf("\nProblem: main GPT header's size of partition entries (%lu) doesn't\n"
1162 "match the backup GPT header's size of partition entries (%lu)\n",
1163 (unsigned long) mainHeader.sizeOfPartitionEntries,
1164 (unsigned long) secondHeader.sizeOfPartitionEntries);
1165 } // if
1166
1167 // Now check for a few other miscellaneous problems...
1168 // Check that the disk size will hold the data...
1169 if (mainHeader.backupLBA > diskSize) {
1170 problems++;
1171 printf("\nProblem: Disk is too small to hold all the data!\n");
1172 printf("(Disk size is %llu sectors, needs to be %llu sectors.)\n",
1173 (unsigned long long) diskSize,
1174 (unsigned long long) mainHeader.backupLBA);
1175 } // if
1176
1177 // Check for overlapping partitions....
1178 for (i = 1; i < mainHeader.numParts; i++) {
1179 for (j = 0; j < i; j++) {
1180 if (TheyOverlap(&partitions[i], &partitions[j])) {
1181 problems++;
1182 printf("\nProblem: partitions %d and %d overlap:\n", i + 1, j + 1);
1183 printf(" Partition %d: %llu to %llu\n", i,
1184 (unsigned long long) partitions[i].firstLBA,
1185 (unsigned long long) partitions[i].lastLBA);
1186 printf(" Partition %d: %llu to %llu\n", j,
1187 (unsigned long long) partitions[j].firstLBA,
1188 (unsigned long long) partitions[j].lastLBA);
1189 } // if
1190 } // for j...
1191 } // for i...
1192
1193 // Now compute available space, but only if no problems found, since
1194 // problems could affect the results
1195 if (problems == 0) {
1196 totalFree = FindFreeBlocks(&numSegments, &largestSegment);
1197 BytesToSI(totalFree * (uint64_t) blockSize, siTotal);
1198 BytesToSI(largestSegment * (uint64_t) blockSize, siLargest);
1199 printf("No problems found. %llu free sectors (%s) available in %u\n"
1200 "segments, the largest of which is %llu sectors (%s) in size\n",
1201 (unsigned long long) totalFree,
1202 siTotal, numSegments, (unsigned long long) largestSegment,
1203 siLargest);
1204 } else {
1205 printf("\nIdentified %d problems!\n", problems);
1206 } // if/else
1207
1208 return (problems);
1209} // GPTData::Verify()
1210
1211// Rebuild the main GPT header, using the secondary header as a model.
1212// Typically called when the main header has been found to be corrupt.
1213void GPTData::RebuildMainHeader(void) {
1214 int i;
1215
1216 mainHeader.signature = GPT_SIGNATURE;
1217 mainHeader.revision = secondHeader.revision;
1218 mainHeader.headerSize = HEADER_SIZE;
1219 mainHeader.headerCRC = UINT32_C(0);
1220 mainHeader.reserved = secondHeader.reserved;
1221 mainHeader.currentLBA = secondHeader.backupLBA;
1222 mainHeader.backupLBA = secondHeader.currentLBA;
1223 mainHeader.firstUsableLBA = secondHeader.firstUsableLBA;
1224 mainHeader.lastUsableLBA = secondHeader.lastUsableLBA;
1225 mainHeader.diskGUID.data1 = secondHeader.diskGUID.data1;
1226 mainHeader.diskGUID.data2 = secondHeader.diskGUID.data2;
1227 mainHeader.partitionEntriesLBA = UINT64_C(2);
1228 mainHeader.numParts = secondHeader.numParts;
1229 mainHeader.sizeOfPartitionEntries = secondHeader.sizeOfPartitionEntries;
1230 mainHeader.partitionEntriesCRC = secondHeader.partitionEntriesCRC;
1231 for (i = 0 ; i < GPT_RESERVED; i++)
1232 mainHeader.reserved2[i] = secondHeader.reserved2[i];
1233} // GPTData::RebuildMainHeader()
1234
1235// Rebuild the secondary GPT header, using the main header as a model.
1236void GPTData::RebuildSecondHeader(void) {
1237 int i;
1238
1239 secondHeader.signature = GPT_SIGNATURE;
1240 secondHeader.revision = mainHeader.revision;
1241 secondHeader.headerSize = HEADER_SIZE;
1242 secondHeader.headerCRC = UINT32_C(0);
1243 secondHeader.reserved = mainHeader.reserved;
1244 secondHeader.currentLBA = mainHeader.backupLBA;
1245 secondHeader.backupLBA = mainHeader.currentLBA;
1246 secondHeader.firstUsableLBA = mainHeader.firstUsableLBA;
1247 secondHeader.lastUsableLBA = mainHeader.lastUsableLBA;
1248 secondHeader.diskGUID.data1 = mainHeader.diskGUID.data1;
1249 secondHeader.diskGUID.data2 = mainHeader.diskGUID.data2;
1250 secondHeader.partitionEntriesLBA = secondHeader.lastUsableLBA + UINT64_C(1);
1251 secondHeader.numParts = mainHeader.numParts;
1252 secondHeader.sizeOfPartitionEntries = mainHeader.sizeOfPartitionEntries;
1253 secondHeader.partitionEntriesCRC = mainHeader.partitionEntriesCRC;
1254 for (i = 0 ; i < GPT_RESERVED; i++)
1255 secondHeader.reserved2[i] = mainHeader.reserved2[i];
1256} // RebuildSecondHeader()
1257
1258// Load the second (backup) partition table as the primary partition
1259// table. Used in repair functions
1260void GPTData::LoadSecondTableAsMain(void) {
1261 int fd;
1262 off_t seekTo;
1263 uint32_t sizeOfParts, newCRC;
1264
1265 if ((fd = open(device, O_RDONLY)) != -1) {
1266 seekTo = secondHeader.partitionEntriesLBA * (off_t) blockSize;
1267 if (lseek64(fd, seekTo, SEEK_SET) != (off_t) -1) {
1268 SetGPTSize(secondHeader.numParts);
1269 sizeOfParts = secondHeader.numParts * secondHeader.sizeOfPartitionEntries;
1270 read(fd, partitions, sizeOfParts);
1271 newCRC = chksum_crc32((unsigned char*) partitions, sizeOfParts);
1272 secondPartsCrcOk = (newCRC == secondHeader.partitionEntriesCRC);
1273 mainPartsCrcOk = secondPartsCrcOk;
1274 if (!secondPartsCrcOk) {
1275 printf("Error! After loading backup partitions, the CRC still doesn't check out!\n");
1276 } // if
1277 } else {
1278 printf("Error! Couldn't seek to backup partition table!\n");
1279 } // if/else
1280 } else {
1281 printf("Error! Couldn't open device %s when recovering backup partition table!\n");
1282 } // if/else
1283} // GPTData::LoadSecondTableAsMain()
1284
1285// Finds the total number of free blocks, the number of segments in which
1286// they reside, and the size of the largest of those segments
1287uint64_t GPTData::FindFreeBlocks(int *numSegments, uint64_t *largestSegment) {
1288 uint64_t start = UINT64_C(0); // starting point for each search
1289 uint64_t totalFound = UINT64_C(0); // running total
1290 uint64_t firstBlock; // first block in a segment
1291 uint64_t lastBlock; // last block in a segment
1292 uint64_t segmentSize; // size of segment in blocks
1293 int num = 0;
1294
1295 *largestSegment = UINT64_C(0);
1296 do {
1297 firstBlock = FindFirstAvailable(start);
1298 if (firstBlock != UINT64_C(0)) { // something's free...
1299 lastBlock = FindLastInFree(firstBlock);
1300 segmentSize = lastBlock - firstBlock + UINT64_C(1);
1301 if (segmentSize > *largestSegment) {
1302 *largestSegment = segmentSize;
1303 } // if
1304 totalFound += segmentSize;
1305 num++;
1306 start = lastBlock + 1;
1307 } // if
1308 } while (firstBlock != 0);
1309 *numSegments = num;
1310 return totalFound;
1311} // GPTData::FindFreeBlocks()
1312
srs5694e7b4ff92009-08-18 13:16:10 -04001313// Create a hybrid MBR -- an ugly, funky thing that helps GPT work with
1314// OSes that don't understand GPT.
1315void GPTData::MakeHybrid(void) {
1316 uint32_t partNums[3];
1317 char line[255];
srs5694e19ba092009-08-24 14:10:35 -04001318 int numParts, i, j, typeCode, bootable, mbrNum;
srs5694e7b4ff92009-08-18 13:16:10 -04001319 uint64_t length;
srs5694c0ca8f82009-08-20 21:35:25 -04001320 char fillItUp = 'M'; // fill extra partition entries? (Yes/No/Maybe)
srs5694e19ba092009-08-24 14:10:35 -04001321 char eeFirst; // Whether EFI GPT (0xEE) partition comes first in table
srs5694e7b4ff92009-08-18 13:16:10 -04001322
srs5694c0ca8f82009-08-20 21:35:25 -04001323 printf("\nWARNING! Hybrid MBRs are flaky and potentially dangerous! If you decide not\n"
1324 "to use one, just hit the Enter key at the below prompt and your MBR\n"
1325 "partition table will be untouched.\n\n\a");
srs5694e7b4ff92009-08-18 13:16:10 -04001326
1327 // Now get the numbers of up to three partitions to add to the
1328 // hybrid MBR....
srs5694c0ca8f82009-08-20 21:35:25 -04001329 printf("Type from one to three GPT partition numbers, separated by spaces, to be\n"
1330 "added to the hybrid MBR, in sequence: ");
srs5694e7b4ff92009-08-18 13:16:10 -04001331 fgets(line, 255, stdin);
1332 numParts = sscanf(line, "%d %d %d", &partNums[0], &partNums[1], &partNums[2]);
srs5694c0ca8f82009-08-20 21:35:25 -04001333
1334 if (numParts > 0) {
1335 // Blank out the protective MBR, but leave the boot loader code
1336 // alone....
1337 protectiveMBR.EmptyMBR(0);
1338 protectiveMBR.SetDiskSize(diskSize);
srs5694e19ba092009-08-24 14:10:35 -04001339 printf("Place EFI GPT (0xEE) partition first in MBR (good for GRUB)? ");
1340 eeFirst = GetYN();
srs5694c0ca8f82009-08-20 21:35:25 -04001341 } // if
1342
srs5694e7b4ff92009-08-18 13:16:10 -04001343 for (i = 0; i < numParts; i++) {
1344 j = partNums[i] - 1;
srs5694e19ba092009-08-24 14:10:35 -04001345 printf("\nCreating entry for partition #%d\n", j + 1);
srs5694e7b4ff92009-08-18 13:16:10 -04001346 if ((j >= 0) && (j < mainHeader.numParts)) {
1347 if (partitions[j].lastLBA < UINT32_MAX) {
srs5694e19ba092009-08-24 14:10:35 -04001348 do {
1349 printf("Enter an MBR hex code (default %02X): ",
1350 typeHelper.GUIDToID(partitions[j].partitionType) / 256);
1351 fgets(line, 255, stdin);
1352 sscanf(line, "%x", &typeCode);
1353 if (line[0] == '\n')
1354 typeCode = typeHelper.GUIDToID(partitions[j].partitionType) / 256;
1355 } while ((typeCode <= 0) || (typeCode > 255));
srs5694e7b4ff92009-08-18 13:16:10 -04001356 printf("Set the bootable flag? ");
1357 bootable = (GetYN() == 'Y');
1358 length = partitions[j].lastLBA - partitions[j].firstLBA + UINT64_C(1);
srs5694e19ba092009-08-24 14:10:35 -04001359 if (eeFirst == 'Y')
1360 mbrNum = i + 1;
1361 else
1362 mbrNum = i;
1363 protectiveMBR.MakePart(mbrNum, (uint32_t) partitions[j].firstLBA,
srs5694e7b4ff92009-08-18 13:16:10 -04001364 (uint32_t) length, typeCode, bootable);
1365 } else { // partition out of range
1366 printf("Partition %d ends beyond the 2TiB limit of MBR partitions; omitting it.\n",
1367 j + 1);
1368 } // if/else
1369 } else {
1370 printf("Partition %d is out of range; omitting it.\n", j + 1);
1371 } // if/else
1372 } // for
srs5694c0ca8f82009-08-20 21:35:25 -04001373
1374 if (numParts > 0) { // User opted to create a hybrid MBR....
1375 // Create EFI protective partition that covers the start of the disk.
1376 // If this location (covering the main GPT data structures) is omitted,
1377 // Linux won't find any partitions on the disk. Note that this is
1378 // NUMBERED AFTER the hybrid partitions, contrary to what the
1379 // gptsync utility does. This is because Windows seems to choke on
1380 // disks with a 0xEE partition in the first slot and subsequent
1381 // additional partitions, unless it boots from the disk.
srs5694e19ba092009-08-24 14:10:35 -04001382 if (eeFirst == 'Y')
1383 mbrNum = 0;
1384 else
1385 mbrNum = numParts;
1386 protectiveMBR.MakePart(mbrNum, 1, protectiveMBR.FindLastInFree(1), 0xEE);
srs5694c0ca8f82009-08-20 21:35:25 -04001387
1388 // ... and for good measure, if there are any partition spaces left,
1389 // optionally create more protective EFI partitions to cover as much
1390 // space as possible....
1391 for (i = 0; i < 4; i++) {
1392 if (protectiveMBR.GetType(i) == 0x00) { // unused entry....
1393 if (fillItUp == 'M') {
srs5694e19ba092009-08-24 14:10:35 -04001394 printf("\nUnused partition space(s) found. Use one to protect more partitions? ");
srs5694c0ca8f82009-08-20 21:35:25 -04001395 fillItUp = GetYN();
1396 typeCode = 0x00; // use this to flag a need to get type code
1397 } // if
1398 if (fillItUp == 'Y') {
srs5694e19ba092009-08-24 14:10:35 -04001399 while ((typeCode <= 0) || (typeCode > 255)) {
1400 printf("Enter an MBR hex code (EE is EFI GPT, but may confuse MacOS): ");
srs5694c0ca8f82009-08-20 21:35:25 -04001401 // Comment on above: Mac OS treats disks with more than one
srs5694e19ba092009-08-24 14:10:35 -04001402 // 0xEE MBR partition as MBR disks, not as GPT disks.
srs5694c0ca8f82009-08-20 21:35:25 -04001403 fgets(line, 255, stdin);
1404 sscanf(line, "%x", &typeCode);
srs5694e19ba092009-08-24 14:10:35 -04001405 if (line[0] == '\n')
1406 typeCode = 0;
1407 } // while
srs5694c0ca8f82009-08-20 21:35:25 -04001408 protectiveMBR.MakeBiggestPart(i, typeCode); // make a partition
1409 } // if (fillItUp == 'Y')
1410 } // if unused entry
1411 } // for (i = 0; i < 4; i++)
1412 } // if (numParts > 0)
srs5694e7b4ff92009-08-18 13:16:10 -04001413} // GPTData::MakeHybrid()
srs5694c0ca8f82009-08-20 21:35:25 -04001414
1415// Create a fresh protective MBR.
1416void GPTData::MakeProtectiveMBR(void) {
1417 protectiveMBR.MakeProtectiveMBR();
1418} // GPTData::MakeProtectiveMBR(void)
srs5694e7b4ff92009-08-18 13:16:10 -04001419
1420// Writes GPT (and protective MBR) to disk. Returns 1 on successful
1421// write, 0 if there was a problem.
1422int GPTData::SaveGPTData(void) {
1423 int allOK = 1, i, j;
1424 char answer, line[256];
1425 int fd;
1426 uint64_t secondTable;
1427 off_t offset;
1428
1429 if (strlen(device) == 0) {
1430 printf("Device not defined.\n");
1431 } // if
1432
1433 // First do some final sanity checks....
1434 // Is there enough space to hold the GPT headers and partition tables,
1435 // given the partition sizes?
1436 if (CheckGPTSize() == 0) {
1437 allOK = 0;
1438 } // if
1439
1440 // Check that disk is really big enough to handle this...
1441 if (mainHeader.backupLBA > diskSize) {
1442 fprintf(stderr, "Error! Disk is too small -- either the original MBR is corrupt or you're\n");
1443 fprintf(stderr, "working from an MBR copied to a file! Aborting!\n");
1444 printf("(Disk size is %ld sectors, needs to be %ld sectors.)\n", diskSize,
1445 mainHeader.backupLBA);
1446 allOK = 0;
1447 } // if
1448
1449 // Check for overlapping partitions....
1450 for (i = 1; i < mainHeader.numParts; i++) {
1451 for (j = 0; j < i; j++) {
1452 if (TheyOverlap(&partitions[i], &partitions[j])) {
1453 fprintf(stderr, "\Error: partitions %d and %d overlap:\n", i + 1, j + 1);
1454 fprintf(stderr, " Partition %d: %llu to %llu\n", i,
1455 (unsigned long long) partitions[i].firstLBA,
1456 (unsigned long long) partitions[i].lastLBA);
1457 fprintf(stderr, " Partition %d: %llu to %llu\n", j,
1458 (unsigned long long) partitions[j].firstLBA,
1459 (unsigned long long) partitions[j].lastLBA);
1460 fprintf(stderr, "Aborting write operation!\n");
1461 allOK = 0;
1462 } // if
1463 } // for j...
1464 } // for i...
1465
1466 RecomputeCRCs();
1467
1468 if (allOK) {
1469 printf("\nFinal checks complete. About to write GPT data. THIS WILL OVERWRITE EXISTING\n");
1470 printf("MBR PARTITIONS!! THIS PROGRAM IS BETA QUALITY AT BEST. IF YOU LOSE ALL YOUR\n");
1471 printf("DATA, YOU HAVE ONLY YOURSELF TO BLAME IF YOU ANSWER 'Y' BELOW!\n\n");
1472 printf("Do you want to proceed, possibly destroying your data? (Y/N) ");
1473 fgets(line, 255, stdin);
1474 sscanf(line, "%c", &answer);
1475 if ((answer == 'Y') || (answer == 'y')) {
1476 printf("OK; writing new GPT partition table.\n");
1477 } else {
1478 allOK = 0;
1479 } // if/else
1480 } // if
1481
1482 // Do it!
1483 if (allOK) {
1484 fd = open(device, O_WRONLY); // try to open the device; may fail....
1485#ifdef __APPLE__
1486 // MacOS X requires a shared lock under some circumstances....
1487 if (fd < 0) {
1488 fd = open(device, O_WRONLY|O_SHLOCK);
1489 } // if
1490#endif
1491 if (fd != -1) {
1492 // First, write the protective MBR...
1493 protectiveMBR.WriteMBRData(fd);
1494
1495 // Now write the main GPT header...
1496 if (allOK)
1497 if (write(fd, &mainHeader, 512) == -1)
1498 allOK = 0;
1499
1500 // Now write the main partition tables...
1501 if (allOK) {
1502 if (write(fd, partitions, GPT_SIZE * mainHeader.numParts) == -1)
1503 allOK = 0;
1504 } // if
1505
1506 // Now seek to near the end to write the secondary GPT....
1507 if (allOK) {
1508 secondTable = secondHeader.partitionEntriesLBA;
1509 offset = (off_t) secondTable * (off_t) (blockSize);
1510 if (lseek64(fd, offset, SEEK_SET) == (off_t) - 1) {
1511 allOK = 0;
1512 printf("Unable to seek to end of disk!\n");
1513 } // if
1514 } // if
1515
1516 // Now write the secondary partition tables....
1517 if (allOK)
1518 if (write(fd, partitions, GPT_SIZE * mainHeader.numParts) == -1)
1519 allOK = 0;
1520
1521 // Now write the secondary GPT header...
1522 if (allOK)
1523 if (write(fd, &secondHeader, 512) == -1)
1524 allOK = 0;
1525
1526 // re-read the partition table
1527 if (allOK) {
1528 sync();
1529#ifdef __APPLE__
1530 printf("Warning: The kernel may continue to use old or deleted partitions.\n"
1531 "You should reboot or remove the drive.\n");
1532 /* don't know if this helps
1533 * it definitely will get things on disk though:
1534 * http://topiks.org/mac-os-x/0321278542/ch12lev1sec8.html */
1535 i = ioctl(fd, DKIOCSYNCHRONIZECACHE);
1536#else
1537 sleep(2);
1538 i = ioctl(fd, BLKRRPART);
1539 if (i)
1540 printf("Warning: The kernel is still using the old partition table.\n"
1541 "The new table will be used at the next reboot.\n");
1542#endif
1543 } // if
1544
1545 if (allOK) { // writes completed OK
1546 printf("The operation has completed successfully.\n");
1547 } else {
1548 printf("Warning! An error was reported when writing the partition table! This error\n");
1549 printf("MIGHT be harmless, but you may have trashed the disk! Use parted and, if\n");
1550 printf("necessary, restore your original partition table.\n");
1551 } // if/else
1552 close(fd);
1553 } else {
1554 fprintf(stderr, "Unable to open device %s for writing! Errno is %d! Aborting!\n", device, errno);
1555 allOK = 0;
1556 } // if/else
1557 } else {
1558 printf("Aborting write of new partition table.\n");
1559 } // if
1560
1561 return (allOK);
1562} // GPTData::SaveGPTData()
1563
1564// Save GPT data to a backup file. This function does much less error
1565// checking than SaveGPTData(). It can therefore preserve many types of
1566// corruption for later analysis; however, it preserves only the MBR,
1567// the main GPT header, the backup GPT header, and the main partition
1568// table; it discards the backup partition table, since it should be
1569// identical to the main partition table on healthy disks.
1570int GPTData::SaveGPTBackup(char* filename) {
1571 int fd, allOK = 1;;
1572
1573 if ((fd = open(filename, O_WRONLY | O_CREAT, S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH)) != -1) {
1574 // First, write the protective MBR...
1575 protectiveMBR.WriteMBRData(fd);
1576
1577 // Now write the main GPT header...
1578 if (allOK)
1579 if (write(fd, &mainHeader, 512) == -1)
1580 allOK = 0;
1581
1582 // Now write the secondary GPT header...
1583 if (allOK)
1584 if (write(fd, &secondHeader, 512) == -1)
1585 allOK = 0;
1586
1587 // Now write the main partition tables...
1588 if (allOK) {
1589 if (write(fd, partitions, GPT_SIZE * mainHeader.numParts) == -1)
1590 allOK = 0;
1591 } // if
1592
1593 if (allOK) { // writes completed OK
1594 printf("The operation has completed successfully.\n");
1595 } else {
1596 printf("Warning! An error was reported when writing the backup file.\n");
1597 printf("It may not be useable!\n");
1598 } // if/else
1599 close(fd);
1600 } else {
1601 fprintf(stderr, "Unable to open file %s for writing! Aborting!\n", filename);
1602 allOK = 0;
1603 } // if/else
1604 return allOK;
1605} // GPTData::SaveGPTBackup()
1606
1607// Load GPT data from a backup file created by SaveGPTBackup(). This function
1608// does minimal error checking. It returns 1 if it completed successfully,
1609// 0 if there was a problem. In the latter case, it creates a new empty
1610// set of partitions.
1611int GPTData::LoadGPTBackup(char* filename) {
1612 int fd, allOK = 1, val;
1613 uint32_t numParts, sizeOfEntries, sizeOfParts, newCRC;
1614
1615 if ((fd = open(filename, O_RDONLY)) != -1) {
1616 // Let the MBRData class load the saved MBR...
1617 protectiveMBR.ReadMBRData(fd);
1618
1619 // Load the main GPT header, check its vaility, and set the GPT
1620 // size based on the data
1621 read(fd, &mainHeader, 512);
1622 mainCrcOk = CheckHeaderCRC(&mainHeader);
1623
1624 // Load the backup GPT header in much the same way as the main
1625 // GPT header....
1626 read(fd, &secondHeader, 512);
1627 secondCrcOk = CheckHeaderCRC(&secondHeader);
1628
1629 // Return valid headers code: 0 = both headers bad; 1 = main header
1630 // good, backup bad; 2 = backup header good, main header bad;
1631 // 3 = both headers good. Note these codes refer to valid GPT
1632 // signatures and version numbers; more subtle problems will elude
1633 // this check!
1634 if ((val = CheckHeaderValidity()) > 0) {
1635 if (val == 2) { // only backup header seems to be good
1636 numParts = secondHeader.numParts;
1637 sizeOfEntries = secondHeader.sizeOfPartitionEntries;
1638 } else { // main header is OK
1639 numParts = mainHeader.numParts;
1640 sizeOfEntries = mainHeader.sizeOfPartitionEntries;
1641 } // if/else
1642
1643 SetGPTSize(numParts);
1644
1645 // If current disk size doesn't match that of backup....
1646 if (secondHeader.currentLBA != diskSize - UINT64_C(1)) {
1647 printf("Warning! Current disk size doesn't match that of the backup!\n"
1648 "Adjusting sizes to match, but subsequent problems are possible!\n");
1649 secondHeader.currentLBA = mainHeader.backupLBA = diskSize - UINT64_C(1);
1650 mainHeader.lastUsableLBA = diskSize - mainHeader.firstUsableLBA;
1651 secondHeader.lastUsableLBA = mainHeader.lastUsableLBA;
1652 secondHeader.partitionEntriesLBA = secondHeader.lastUsableLBA + UINT64_C(1);
1653 } // if
1654
1655 // Load main partition table, and record whether its CRC
1656 // matches the stored value
1657 sizeOfParts = numParts * sizeOfEntries;
1658 read(fd, partitions, sizeOfParts);
1659
1660 newCRC = chksum_crc32((unsigned char*) partitions, sizeOfParts);
1661 mainPartsCrcOk = (newCRC == mainHeader.partitionEntriesCRC);
1662 secondPartsCrcOk = (newCRC == secondHeader.partitionEntriesCRC);
1663 } else {
1664 allOK = 0;
1665 } // if/else
1666 } else {
1667 allOK = 0;
1668 fprintf(stderr, "Unable to open file %s for reading! Aborting!\n", filename);
1669 } // if/else
1670
1671 // Something went badly wrong, so blank out partitions
1672 if (allOK == 0) {
1673 ClearGPTData();
1674 protectiveMBR.MakeProtectiveMBR();
1675 } // if
1676 return allOK;
1677} // GPTData::LoadGPTBackup()
1678
srs5694c0ca8f82009-08-20 21:35:25 -04001679// This function destroys the on-disk GPT structures. Returns 1 if the
1680// user confirms destruction, 0 if the user aborts.
1681int GPTData::DestroyGPT(void) {
1682 int fd, i, doMore;
1683 char blankSector[512], goOn;
1684
1685 for (i = 0; i < 512; i++) {
1686 blankSector[i] = '\0';
1687 } // for
1688
1689 printf("\a\aAbout to wipe out GPT on %s. Proceed? ", device);
1690 goOn = GetYN();
1691 if (goOn == 'Y') {
1692 fd = open(device, O_WRONLY);
1693#ifdef __APPLE__
1694 // MacOS X requires a shared lock under some circumstances....
1695 if (fd < 0) {
1696 fd = open(device, O_WRONLY|O_SHLOCK);
1697 } // if
1698#endif
1699 if (fd != -1) {
1700 lseek64(fd, mainHeader.currentLBA * 512, SEEK_SET); // seek to GPT header
1701 write(fd, blankSector, 512); // blank it out
1702 lseek64(fd, mainHeader.partitionEntriesLBA * 512, SEEK_SET); // seek to partition table
1703 for (i = 0; i < GetBlocksInPartTable(); i++)
1704 write(fd, blankSector, 512);
1705 lseek64(fd, secondHeader.partitionEntriesLBA * 512, SEEK_SET); // seek to partition table
1706 for (i = 0; i < GetBlocksInPartTable(); i++)
1707 write(fd, blankSector, 512);
1708 lseek64(fd, secondHeader.currentLBA * 512, SEEK_SET); // seek to GPT header
1709 write(fd, blankSector, 512); // blank it out
1710 printf("Blank out MBR? ");
1711 if (GetYN() == 'Y') {
1712 lseek64(fd, 0, SEEK_SET);
1713 write(fd, blankSector, 512); // blank it out
1714 } // if blank MBR
1715 close(fd);
1716 printf("GPT data structures destroyed! You may now partition the disk using fdisk or\n"
1717 "other utilities. Program will now terminate.\n");
1718 } else {
1719 printf("Problem opening %s for writing! Program will now terminate.\n");
1720 } // if/else (fd != -1)
1721 } // if (goOn == 'Y')
1722 return (goOn == 'Y');
1723} // GPTData::DestroyGPT()
1724
srs5694e7b4ff92009-08-18 13:16:10 -04001725// Check to be sure that data type sizes are correct. The basic types (uint*_t) should
1726// never fail these tests, but the struct types may fail depending on compile options.
1727// Specifically, the -fpack-struct option to gcc may be required to ensure proper structure
1728// sizes.
1729int SizesOK(void) {
1730 int allOK = 1;
1731 union {
1732 uint32_t num;
1733 unsigned char uc[sizeof(uint32_t)];
1734 } endian;
1735
1736 if (sizeof(uint8_t) != 1) {
1737 fprintf(stderr, "uint8_t is %d bytes, should be 1 byte; aborting!\n", sizeof(uint8_t));
1738 allOK = 0;
1739 } // if
1740 if (sizeof(uint16_t) != 2) {
1741 fprintf(stderr, "uint16_t is %d bytes, should be 2 bytes; aborting!\n", sizeof(uint16_t));
1742 allOK = 0;
1743 } // if
1744 if (sizeof(uint32_t) != 4) {
1745 fprintf(stderr, "uint32_t is %d bytes, should be 4 bytes; aborting!\n", sizeof(uint32_t));
1746 allOK = 0;
1747 } // if
1748 if (sizeof(uint64_t) != 8) {
1749 fprintf(stderr, "uint64_t is %d bytes, should be 8 bytes; aborting!\n", sizeof(uint64_t));
1750 allOK = 0;
1751 } // if
1752 if (sizeof(struct MBRRecord) != 16) {
1753 fprintf(stderr, "MBRRecord is %d bytes, should be 16 bytes; aborting!\n", sizeof(uint32_t));
1754 allOK = 0;
1755 } // if
1756 if (sizeof(struct EBRRecord) != 512) {
1757 fprintf(stderr, "EBRRecord is %d bytes, should be 512 bytes; aborting!\n", sizeof(uint32_t));
1758 allOK = 0;
1759 } // if
1760 if (sizeof(struct GPTHeader) != 512) {
1761 fprintf(stderr, "GPTHeader is %d bytes, should be 512 bytes; aborting!\n", sizeof(uint32_t));
1762 allOK = 0;
1763 } // if
1764 // Determine endianness; set allOK = 0 if running on big-endian hardware
1765 endian.num = 1;
1766 if (endian.uc[0] != (unsigned char) 1) {
1767 fprintf(stderr, "Running on big-endian hardware, but this program only works on little-endian\n"
1768 "systems; aborting!\n");
1769 allOK = 0;
1770 } // if
1771 return (allOK);
1772} // SizesOK()
1773