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