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