blob: 19246fef7dc446b22d08651ae4ba68697d5c20cb [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
734 if ((origType != 0x05) && (origType != 0x0f) && (origType != 0x00) && (origType != 0xEE)) {
srs5694e7b4ff92009-08-18 13:16:10 -0400735 partitions[i].firstLBA = (uint64_t) origParts->GetFirstSector(i);
736 partitions[i].lastLBA = partitions[i].firstLBA + (uint64_t)
737 origParts->GetLength(i) - 1;
738 partitions[i].partitionType = typeHelper.IDToGUID(((uint16_t) origType) * 0x0100);
739
740 // Create random unique GUIDs for the partitions
741 // rand() is only 32 bits, so multiply together to fill a 64-bit value
742 partitions[i].uniqueGUID.data1 = (uint64_t) rand() * (uint64_t) rand();
743 partitions[i].uniqueGUID.data2 = (uint64_t) rand() * (uint64_t) rand();
744 partitions[i].attributes = 0;
745 for (j = 0; j < NAME_SIZE; j++)
746 partitions[i].name[j] = '\0';
747 } // if
748 } // for
749
750 // Convert MBR into protective MBR
751 protectiveMBR.MakeProtectiveMBR();
752
753 // Record that all original CRCs were OK so as not to raise flags
754 // when doing a disk verification
755 mainCrcOk = secondCrcOk = mainPartsCrcOk = secondPartsCrcOk = 1;
756
757 return (1);
758} // XFormPartitions()
759
760// Sort the GPT entries, eliminating gaps and making for a logical
761// ordering. Relies on QuickSortGPT() for the bulk of the work
762void GPTData::SortGPT(void) {
763 int i, lastPart = 0;
764 struct GPTPartition temp;
765
766 // First, find the last partition with data, so as not to
767 // spend needless time sorting empty entries....
768 for (i = 0; i < GPT_SIZE; i++) {
769 if (partitions[i].firstLBA > 0)
770 lastPart = i;
771 } // for
772
773 // Now swap empties with the last partitions, to simplify the logic
774 // in the Quicksort function....
775 i = 0;
776 while (i < lastPart) {
777 if (partitions[i].firstLBA == 0) {
778 temp = partitions[i];
779 partitions[i] = partitions[lastPart];
780 partitions[lastPart] = temp;
781 lastPart--;
782 } // if
783 i++;
784 } // while
785
786 // Now call the recursive quick sort routine to do the real work....
787 QuickSortGPT(partitions, 0, lastPart);
788} // GPTData::SortGPT()
789
790// Recursive quick sort algorithm for GPT partitions. Note that if there
791// are any empties in the specified range, they'll be sorted to the
792// start, resulting in a sorted set of partitions that begins with
793// partition 2, 3, or higher.
794void QuickSortGPT(struct GPTPartition* partitions, int start, int finish) {
795 uint64_t starterValue; // starting location of median partition
796 int left, right;
797 struct GPTPartition temp;
798
799 left = start;
800 right = finish;
801 starterValue = partitions[(start + finish) / 2].firstLBA;
802 do {
803 while (partitions[left].firstLBA < starterValue)
804 left++;
805 while (partitions[right].firstLBA > starterValue)
806 right--;
807 if (left <= right) {
808 temp = partitions[left];
809 partitions[left] = partitions[right];
810 partitions[right] = temp;
811 left++;
812 right--;
813 } // if
814 } while (left <= right);
815 if (start < right) QuickSortGPT(partitions, start, right);
816 if (finish > left) QuickSortGPT(partitions, left, finish);
817} // QuickSortGPT()
818
819// Blank (delete) a single partition
820void BlankPartition(struct GPTPartition* partition) {
821 int j;
822
823 partition->uniqueGUID.data1 = 0;
824 partition->uniqueGUID.data2 = 0;
825 partition->partitionType.data1 = 0;
826 partition->partitionType.data2 = 0;
827 partition->firstLBA = 0;
828 partition->lastLBA = 0;
829 partition->attributes = 0;
830 for (j = 0; j < NAME_SIZE; j++)
831 partition->name[j] = '\0';
832} // BlankPartition
833
834// Blank the partition array
835void GPTData::BlankPartitions(void) {
836 uint32_t i;
837
838 for (i = 0; i < mainHeader.numParts; i++) {
839 BlankPartition(&partitions[i]);
840 } // for
841} // GPTData::BlankPartitions()
842
843// Set up data structures for entirely new set of partitions on the
844// specified device. Returns 1 if OK, 0 if there were problems.
845int GPTData::ClearGPTData(void) {
846 int goOn, i;
847
848 // Set up the partition table....
849 free(partitions);
850 partitions = NULL;
851 SetGPTSize(NUM_GPT_ENTRIES);
852
853 // Now initialize a bunch of stuff that's static....
854 mainHeader.signature = GPT_SIGNATURE;
855 mainHeader.revision = 0x00010000;
856 mainHeader.headerSize = (uint32_t) HEADER_SIZE;
857 mainHeader.reserved = 0;
858 mainHeader.currentLBA = UINT64_C(1);
859 mainHeader.partitionEntriesLBA = (uint64_t) 2;
860 mainHeader.sizeOfPartitionEntries = GPT_SIZE;
861 for (i = 0; i < GPT_RESERVED; i++) {
862 mainHeader.reserved2[i] = '\0';
863 } // for
864
865 // Now some semi-static items (computed based on end of disk)
866 mainHeader.backupLBA = diskSize - UINT64_C(1);
867 mainHeader.lastUsableLBA = diskSize - mainHeader.firstUsableLBA;
868
869 // Set a unique GUID for the disk, based on random numbers
870 // rand() is only 32 bits, so multiply together to fill a 64-bit value
871 mainHeader.diskGUID.data1 = (uint64_t) rand() * (uint64_t) rand();
872 mainHeader.diskGUID.data2 = (uint64_t) rand() * (uint64_t) rand();
873
874 // Copy main header to backup header
875 RebuildSecondHeader();
876
877 // Blank out the partitions array....
878 BlankPartitions();
879 return (goOn);
880} // GPTData::ClearGPTData()
881
882// Returns 1 if the two partitions overlap, 0 if they don't
883int TheyOverlap(struct GPTPartition* first, struct GPTPartition* second) {
884 int theyDo = 0;
885
886 // Don't bother checking unless these are defined (both start and end points
887 // are 0 for undefined partitions, so just check the start points)
888 if ((first->firstLBA != 0) && (second->firstLBA != 0)) {
889 if ((first->firstLBA < second->lastLBA) && (first->lastLBA >= second->firstLBA))
890 theyDo = 1;
891 if ((second->firstLBA < first->lastLBA) && (second->lastLBA >= first->firstLBA))
892 theyDo = 1;
893 } // if
894 return (theyDo);
895} // Overlap()
896
897// Change the type code on the specified partition.
898// Note: The GPT CRCs must be recomputed after calling this function!
899void ChangeGPTType(struct GPTPartition* part) {
900 char typeName[255], line[255];
901 uint16_t typeNum = 0xFFFF;
902 PartTypes typeHelper;
903 GUIDData newType;
904
905 printf("Current type is '%s'\n", typeHelper.GUIDToName(part->partitionType, typeName));
906 while ((!typeHelper.Valid(typeNum)) && (typeNum != 0)) {
907 printf("Hex code (L to show codes, 0 to enter raw code): ");
908 fgets(line, 255, stdin);
909 sscanf(line, "%x", &typeNum);
910 if (line[0] == 'L')
911 typeHelper.ShowTypes();
912 } // while
913 if (typeNum != 0) // user entered a code, so convert it
914 newType = typeHelper.IDToGUID(typeNum);
915 else // user wants to enter the GUID directly, so do that
916 newType = GetGUID();
917 part->partitionType = newType;
918 printf("Changed system type of partition to '%s'\n",
919 typeHelper.GUIDToName(part->partitionType, typeName));
920} // ChangeGPTType()
921
922// Prompt user for a partition number, then change its type code
923// using ChangeGPTType(struct GPTPartition*) function.
924void GPTData::ChangePartType(void) {
925 int partNum;
926 uint32_t low, high;
927
928 if (GetPartRange(&low, &high) > 0) {
929 partNum = GetPartNum();
930 ChangeGPTType(&partitions[partNum]);
931 } else {
932 printf("No partitions\n");
933 } // if/else
934} // GPTData::ChangePartType()
935
936// Prompts user for partition number and returns the result.
937uint32_t GPTData::GetPartNum(void) {
938 uint32_t partNum;
939 uint32_t low, high;
940 char prompt[255];
941
942 if (GetPartRange(&low, &high) > 0) {
943 sprintf(prompt, "Partition number (%d-%d): ", low + 1, high + 1);
944 partNum = GetNumber(low + 1, high + 1, low, prompt);
945 } else partNum = 1;
946 return (partNum - 1);
947} // GPTData::GetPartNum()
948
949// Prompt user for attributes to change on the specified partition
950// and change them.
951void GPTData::SetAttributes(uint32_t partNum) {
952 Attributes theAttr;
953
954 theAttr.SetAttributes(partitions[partNum].attributes);
955 theAttr.DisplayAttributes();
956 theAttr.ChangeAttributes();
957 partitions[partNum].attributes = theAttr.GetAttributes();
958} // GPTData::SetAttributes()
959
960// Set the name for a partition to theName, or prompt for a name if
961// theName is a NULL pointer. Note that theName is a standard C-style
962// string, although the GUID partition definition requires a UTF-16LE
963// string. This function creates a simple-minded copy for this.
964void GPTData::SetName(uint32_t partNum, char* theName) {
965 char newName[NAME_SIZE]; // New name
966 int i;
967
968 // Blank out new name string, just to be on the safe side....
969 for (i = 0; i < NAME_SIZE; i++)
970 newName[i] = '\0';
971
972 if (theName == NULL) { // No name specified, so get one from the user
973 printf("Enter name: ");
974 fgets(newName, NAME_SIZE / 2, stdin);
975
976 // Input is likely to include a newline, so remove it....
977 i = strlen(newName);
978 if (newName[i - 1] == '\n')
979 newName[i - 1] = '\0';
980 } else {
981 strcpy(newName, theName);
982 } // if
983
984 // Copy the C-style ASCII string from newName into a form that the GPT
985 // table will accept....
986 for (i = 0; i < NAME_SIZE; i++) {
987 if ((i % 2) == 0) {
988 partitions[partNum].name[i] = newName[(i / 2)];
989 } else {
990 partitions[partNum].name[i] = '\0';
991 } // if/else
992 } // for
993} // GPTData::SetName()
994
995// Set the disk GUID to the specified value. Note that the header CRCs must
996// be recomputed after calling this function.
997void GPTData::SetDiskGUID(GUIDData newGUID) {
998 mainHeader.diskGUID = newGUID;
999 secondHeader.diskGUID = newGUID;
1000} // SetDiskGUID()
1001
1002// Set the unique GUID of the specified partition. Returns 1 on
1003// successful completion, 0 if there were problems (invalid
1004// partition number).
1005int GPTData::SetPartitionGUID(uint32_t pn, GUIDData theGUID) {
1006 int retval = 0;
1007
1008 if (pn < mainHeader.numParts) {
1009 if (partitions[pn].firstLBA != UINT64_C(0)) {
1010 partitions[pn].uniqueGUID = theGUID;
1011 retval = 1;
1012 } // if
1013 } // if
1014 return retval;
1015} // GPTData::SetPartitionGUID()
1016
1017// Check the validity of the GPT header. Returns 1 if the main header
1018// is valid, 2 if the backup header is valid, 3 if both are valid, and
1019// 0 if neither is valid. Note that this function just checks the GPT
1020// signature and revision numbers, not CRCs or other data.
1021int GPTData::CheckHeaderValidity(void) {
1022 int valid = 3;
1023
1024 if (mainHeader.signature != GPT_SIGNATURE) {
1025 valid -= 1;
1026 printf("Main GPT signature invalid; read 0x%016llX, should be\n0x%016llX\n",
1027 (unsigned long long) mainHeader.signature, (unsigned long long) GPT_SIGNATURE);
1028 } else if ((mainHeader.revision != 0x00010000) && valid) {
1029 valid -= 1;
1030 printf("Unsupported GPT version in main header; read 0x%08lX, should be\n0x%08lX\n",
1031 (unsigned long) mainHeader.revision, UINT32_C(0x00010000));
1032 } // if/else/if
1033
1034 if (secondHeader.signature != GPT_SIGNATURE) {
1035 valid -= 2;
1036 printf("Secondary GPT signature invalid; read 0x%016llX, should be\n0x%016llX\n",
1037 (unsigned long long) secondHeader.signature, (unsigned long long) GPT_SIGNATURE);
1038 } else if ((secondHeader.revision != 0x00010000) && valid) {
1039 valid -= 2;
1040 printf("Unsupported GPT version in backup header; read 0x%08lX, should be\n0x%08lX\n",
1041 (unsigned long) mainHeader.revision, UINT32_C(0x00010000));
1042 } // if/else/if
1043
srs56942a9f5da2009-08-26 00:48:01 -04001044 // If MBR bad, check for an Apple disk signature
1045 if ((protectiveMBR.GetValidity() == invalid) &&
1046 (((mainHeader.signature << 32) == APM_SIGNATURE1) ||
1047 (mainHeader.signature << 32) == APM_SIGNATURE2)) {
1048 printf("\n*******************************************************************\n");
1049 printf("This disk appears to contain an Apple-format (APM) partition table!\n");
1050 printf("*******************************************************************\n\n\a");
1051 } // if
1052
srs5694e7b4ff92009-08-18 13:16:10 -04001053 return valid;
1054} // GPTData::CheckHeaderValidity()
1055
1056// Check the header CRC to see if it's OK...
srs56942a9f5da2009-08-26 00:48:01 -04001057// Note: Must be called BEFORE byte-order reversal on big-endian
1058// systems!
srs5694e7b4ff92009-08-18 13:16:10 -04001059int GPTData::CheckHeaderCRC(struct GPTHeader* header) {
1060 uint32_t oldCRC, newCRC;
1061
srs56942a9f5da2009-08-26 00:48:01 -04001062 // Back up old header CRC and then blank it, since it must be 0 for
srs5694e7b4ff92009-08-18 13:16:10 -04001063 // computation to be valid
1064 oldCRC = header->headerCRC;
srs56942a9f5da2009-08-26 00:48:01 -04001065 if (IsLittleEndian() == 0)
1066 ReverseBytes((char*) &oldCRC, 4);
srs5694e7b4ff92009-08-18 13:16:10 -04001067 header->headerCRC = UINT32_C(0);
1068
1069 // Initialize CRC functions...
1070 chksum_crc32gentab();
1071
1072 // Compute CRC, restore original, and return result of comparison
1073 newCRC = chksum_crc32((unsigned char*) header, HEADER_SIZE);
1074 mainHeader.headerCRC = oldCRC;
1075 return (oldCRC == newCRC);
1076} // GPTData::CheckHeaderCRC()
1077
srs56942a9f5da2009-08-26 00:48:01 -04001078// Recompute all the CRCs. Must be called before saving (but after reversing
1079// byte order on big-endian systems) if any changes have been made.
srs5694e7b4ff92009-08-18 13:16:10 -04001080void GPTData::RecomputeCRCs(void) {
1081 uint32_t crc;
srs56942a9f5da2009-08-26 00:48:01 -04001082 uint32_t trueNumParts, crcTemp;
1083 int littleEndian = 1;
srs5694e7b4ff92009-08-18 13:16:10 -04001084
1085 // Initialize CRC functions...
1086 chksum_crc32gentab();
1087
srs56942a9f5da2009-08-26 00:48:01 -04001088 littleEndian = IsLittleEndian();
1089
srs5694e7b4ff92009-08-18 13:16:10 -04001090 // Compute CRC of partition tables & store in main and secondary headers
srs56942a9f5da2009-08-26 00:48:01 -04001091 trueNumParts = mainHeader.numParts;
1092 if (littleEndian == 0)
1093 ReverseBytes((char*) &trueNumParts, 4); // unreverse this key piece of data....
1094 crc = chksum_crc32((unsigned char*) partitions, trueNumParts * GPT_SIZE);
srs5694e7b4ff92009-08-18 13:16:10 -04001095 mainHeader.partitionEntriesCRC = crc;
1096 secondHeader.partitionEntriesCRC = crc;
srs56942a9f5da2009-08-26 00:48:01 -04001097 if (littleEndian == 0) {
1098 ReverseBytes((char*) &mainHeader.partitionEntriesCRC, 4);
1099 ReverseBytes((char*) &secondHeader.partitionEntriesCRC, 4);
1100 } // if
srs5694e7b4ff92009-08-18 13:16:10 -04001101
1102 // Zero out GPT tables' own CRCs (required for correct computation)
1103 mainHeader.headerCRC = 0;
1104 secondHeader.headerCRC = 0;
1105
1106 // Compute & store CRCs of main & secondary headers...
1107 crc = chksum_crc32((unsigned char*) &mainHeader, HEADER_SIZE);
srs56942a9f5da2009-08-26 00:48:01 -04001108 if (littleEndian == 0)
1109 ReverseBytes((char*) &crc, 4);
srs5694e7b4ff92009-08-18 13:16:10 -04001110 mainHeader.headerCRC = crc;
1111 crc = chksum_crc32((unsigned char*) &secondHeader, HEADER_SIZE);
srs56942a9f5da2009-08-26 00:48:01 -04001112 if (littleEndian == 0)
1113 ReverseBytes((char*) &crc, 4);
srs5694e7b4ff92009-08-18 13:16:10 -04001114 secondHeader.headerCRC = crc;
1115} // GPTData::RecomputeCRCs()
1116
1117// Perform detailed verification, reporting on any problems found, but
1118// do *NOT* recover from these problems. Returns the total number of
1119// problems identified.
1120int GPTData::Verify(void) {
1121 int problems = 0, numSegments, i, j;
1122 uint64_t totalFree, largestSegment;
1123 char tempStr[255], siTotal[255], siLargest[255];
1124
1125 // First, check for CRC errors in the GPT data....
1126 if (!mainCrcOk) {
1127 problems++;
1128 printf("\nProblem: The CRC for the main GPT header is invalid. The main GPT header may\n"
1129 "be corrupt. Consider loading the backup GPT header to rebuild the main GPT\n"
1130 "header\n");
1131 } // if
1132 if (!mainPartsCrcOk) {
1133 problems++;
1134 printf("\nProblem: The CRC for the main partition table is invalid. This table may be\n"
1135 "corrupt. Consider loading the backup partition table.\n");
1136 } // if
1137 if (!secondCrcOk) {
1138 problems++;
1139 printf("\nProblem: The CRC for the backup GPT header is invalid. The backup GPT header\n"
1140 "may be corrupt. Consider using the main GPT header to rebuild the backup GPT\n"
1141 "header.\n");
1142 } // if
1143 if (!secondPartsCrcOk) {
1144 problems++;
1145 printf("\nCaution: The CRC for the backup partition table is invalid. This table may\n"
1146 "be corrupt. This program will automatically create a new backup partition\n"
1147 "table when you save your partitions.\n");
1148 } // if
1149
1150 // Now check that critical main and backup GPT entries match
1151 if (mainHeader.currentLBA != secondHeader.backupLBA) {
1152 problems++;
1153 printf("\nProblem: main GPT header's current LBA pointer (%llu) doesn't\n"
1154 "match the backup GPT header's LBA pointer(%llu)\n",
1155 (unsigned long long) mainHeader.currentLBA,
1156 (unsigned long long) secondHeader.backupLBA);
1157 } // if
1158 if (mainHeader.backupLBA != secondHeader.currentLBA) {
1159 problems++;
1160 printf("\nProblem: main GPT header's backup LBA pointer (%llu) doesn't\n"
1161 "match the backup GPT header's current LBA pointer (%llu)\n",
1162 (unsigned long long) mainHeader.backupLBA,
1163 (unsigned long long) secondHeader.currentLBA);
1164 } // if
1165 if (mainHeader.firstUsableLBA != secondHeader.firstUsableLBA) {
1166 problems++;
1167 printf("\nProblem: main GPT header's first usable LBA pointer (%llu) doesn't\n"
1168 "match the backup GPT header's first usable LBA pointer (%llu)\n",
1169 (unsigned long long) mainHeader.firstUsableLBA,
1170 (unsigned long long) secondHeader.firstUsableLBA);
1171 } // if
1172 if (mainHeader.lastUsableLBA != secondHeader.lastUsableLBA) {
1173 problems++;
1174 printf("\nProblem: main GPT header's last usable LBA pointer (%llu) doesn't\n"
1175 "match the backup GPT header's last usable LBA pointer (%llu)\n",
1176 (unsigned long long) mainHeader.lastUsableLBA,
1177 (unsigned long long) secondHeader.lastUsableLBA);
1178 } // if
1179 if ((mainHeader.diskGUID.data1 != secondHeader.diskGUID.data1) ||
1180 (mainHeader.diskGUID.data2 != secondHeader.diskGUID.data2)) {
1181 problems++;
1182 printf("\nProblem: main header's disk GUID (%s) doesn't\n",
1183 GUIDToStr(mainHeader.diskGUID, tempStr));
1184 printf("match the backup GPT header's disk GUID (%s)\n",
1185 GUIDToStr(secondHeader.diskGUID, tempStr));
1186 } // if
1187 if (mainHeader.numParts != secondHeader.numParts) {
1188 problems++;
1189 printf("\nProblem: main GPT header's number of partitions (%lu) doesn't\n"
1190 "match the backup GPT header's number of partitions (%lu)\n",
1191 (unsigned long) mainHeader.numParts,
1192 (unsigned long) secondHeader.numParts);
1193 } // if
1194 if (mainHeader.sizeOfPartitionEntries != secondHeader.sizeOfPartitionEntries) {
1195 problems++;
1196 printf("\nProblem: main GPT header's size of partition entries (%lu) doesn't\n"
1197 "match the backup GPT header's size of partition entries (%lu)\n",
1198 (unsigned long) mainHeader.sizeOfPartitionEntries,
1199 (unsigned long) secondHeader.sizeOfPartitionEntries);
1200 } // if
1201
1202 // Now check for a few other miscellaneous problems...
1203 // Check that the disk size will hold the data...
1204 if (mainHeader.backupLBA > diskSize) {
1205 problems++;
1206 printf("\nProblem: Disk is too small to hold all the data!\n");
1207 printf("(Disk size is %llu sectors, needs to be %llu sectors.)\n",
1208 (unsigned long long) diskSize,
1209 (unsigned long long) mainHeader.backupLBA);
1210 } // if
1211
1212 // Check for overlapping partitions....
1213 for (i = 1; i < mainHeader.numParts; i++) {
1214 for (j = 0; j < i; j++) {
1215 if (TheyOverlap(&partitions[i], &partitions[j])) {
1216 problems++;
1217 printf("\nProblem: partitions %d and %d overlap:\n", i + 1, j + 1);
1218 printf(" Partition %d: %llu to %llu\n", i,
1219 (unsigned long long) partitions[i].firstLBA,
1220 (unsigned long long) partitions[i].lastLBA);
1221 printf(" Partition %d: %llu to %llu\n", j,
1222 (unsigned long long) partitions[j].firstLBA,
1223 (unsigned long long) partitions[j].lastLBA);
1224 } // if
1225 } // for j...
1226 } // for i...
1227
1228 // Now compute available space, but only if no problems found, since
1229 // problems could affect the results
1230 if (problems == 0) {
1231 totalFree = FindFreeBlocks(&numSegments, &largestSegment);
1232 BytesToSI(totalFree * (uint64_t) blockSize, siTotal);
1233 BytesToSI(largestSegment * (uint64_t) blockSize, siLargest);
1234 printf("No problems found. %llu free sectors (%s) available in %u\n"
1235 "segments, the largest of which is %llu sectors (%s) in size\n",
1236 (unsigned long long) totalFree,
1237 siTotal, numSegments, (unsigned long long) largestSegment,
1238 siLargest);
1239 } else {
1240 printf("\nIdentified %d problems!\n", problems);
1241 } // if/else
1242
1243 return (problems);
1244} // GPTData::Verify()
1245
1246// Rebuild the main GPT header, using the secondary header as a model.
1247// Typically called when the main header has been found to be corrupt.
1248void GPTData::RebuildMainHeader(void) {
1249 int i;
1250
1251 mainHeader.signature = GPT_SIGNATURE;
1252 mainHeader.revision = secondHeader.revision;
1253 mainHeader.headerSize = HEADER_SIZE;
1254 mainHeader.headerCRC = UINT32_C(0);
1255 mainHeader.reserved = secondHeader.reserved;
1256 mainHeader.currentLBA = secondHeader.backupLBA;
1257 mainHeader.backupLBA = secondHeader.currentLBA;
1258 mainHeader.firstUsableLBA = secondHeader.firstUsableLBA;
1259 mainHeader.lastUsableLBA = secondHeader.lastUsableLBA;
1260 mainHeader.diskGUID.data1 = secondHeader.diskGUID.data1;
1261 mainHeader.diskGUID.data2 = secondHeader.diskGUID.data2;
1262 mainHeader.partitionEntriesLBA = UINT64_C(2);
1263 mainHeader.numParts = secondHeader.numParts;
1264 mainHeader.sizeOfPartitionEntries = secondHeader.sizeOfPartitionEntries;
1265 mainHeader.partitionEntriesCRC = secondHeader.partitionEntriesCRC;
1266 for (i = 0 ; i < GPT_RESERVED; i++)
1267 mainHeader.reserved2[i] = secondHeader.reserved2[i];
1268} // GPTData::RebuildMainHeader()
1269
1270// Rebuild the secondary GPT header, using the main header as a model.
1271void GPTData::RebuildSecondHeader(void) {
1272 int i;
1273
1274 secondHeader.signature = GPT_SIGNATURE;
1275 secondHeader.revision = mainHeader.revision;
1276 secondHeader.headerSize = HEADER_SIZE;
1277 secondHeader.headerCRC = UINT32_C(0);
1278 secondHeader.reserved = mainHeader.reserved;
1279 secondHeader.currentLBA = mainHeader.backupLBA;
1280 secondHeader.backupLBA = mainHeader.currentLBA;
1281 secondHeader.firstUsableLBA = mainHeader.firstUsableLBA;
1282 secondHeader.lastUsableLBA = mainHeader.lastUsableLBA;
1283 secondHeader.diskGUID.data1 = mainHeader.diskGUID.data1;
1284 secondHeader.diskGUID.data2 = mainHeader.diskGUID.data2;
1285 secondHeader.partitionEntriesLBA = secondHeader.lastUsableLBA + UINT64_C(1);
1286 secondHeader.numParts = mainHeader.numParts;
1287 secondHeader.sizeOfPartitionEntries = mainHeader.sizeOfPartitionEntries;
1288 secondHeader.partitionEntriesCRC = mainHeader.partitionEntriesCRC;
1289 for (i = 0 ; i < GPT_RESERVED; i++)
1290 secondHeader.reserved2[i] = mainHeader.reserved2[i];
1291} // RebuildSecondHeader()
1292
1293// Load the second (backup) partition table as the primary partition
1294// table. Used in repair functions
1295void GPTData::LoadSecondTableAsMain(void) {
1296 int fd;
1297 off_t seekTo;
1298 uint32_t sizeOfParts, newCRC;
1299
1300 if ((fd = open(device, O_RDONLY)) != -1) {
1301 seekTo = secondHeader.partitionEntriesLBA * (off_t) blockSize;
1302 if (lseek64(fd, seekTo, SEEK_SET) != (off_t) -1) {
1303 SetGPTSize(secondHeader.numParts);
1304 sizeOfParts = secondHeader.numParts * secondHeader.sizeOfPartitionEntries;
1305 read(fd, partitions, sizeOfParts);
1306 newCRC = chksum_crc32((unsigned char*) partitions, sizeOfParts);
1307 secondPartsCrcOk = (newCRC == secondHeader.partitionEntriesCRC);
1308 mainPartsCrcOk = secondPartsCrcOk;
srs56942a9f5da2009-08-26 00:48:01 -04001309 if (IsLittleEndian() == 0)
1310 ReversePartitionBytes();
srs5694e7b4ff92009-08-18 13:16:10 -04001311 if (!secondPartsCrcOk) {
1312 printf("Error! After loading backup partitions, the CRC still doesn't check out!\n");
1313 } // if
1314 } else {
1315 printf("Error! Couldn't seek to backup partition table!\n");
1316 } // if/else
1317 } else {
1318 printf("Error! Couldn't open device %s when recovering backup partition table!\n");
1319 } // if/else
1320} // GPTData::LoadSecondTableAsMain()
1321
1322// Finds the total number of free blocks, the number of segments in which
1323// they reside, and the size of the largest of those segments
1324uint64_t GPTData::FindFreeBlocks(int *numSegments, uint64_t *largestSegment) {
1325 uint64_t start = UINT64_C(0); // starting point for each search
1326 uint64_t totalFound = UINT64_C(0); // running total
1327 uint64_t firstBlock; // first block in a segment
1328 uint64_t lastBlock; // last block in a segment
1329 uint64_t segmentSize; // size of segment in blocks
1330 int num = 0;
1331
1332 *largestSegment = UINT64_C(0);
1333 do {
1334 firstBlock = FindFirstAvailable(start);
1335 if (firstBlock != UINT64_C(0)) { // something's free...
1336 lastBlock = FindLastInFree(firstBlock);
1337 segmentSize = lastBlock - firstBlock + UINT64_C(1);
1338 if (segmentSize > *largestSegment) {
1339 *largestSegment = segmentSize;
1340 } // if
1341 totalFound += segmentSize;
1342 num++;
1343 start = lastBlock + 1;
1344 } // if
1345 } while (firstBlock != 0);
1346 *numSegments = num;
1347 return totalFound;
1348} // GPTData::FindFreeBlocks()
1349
srs5694e7b4ff92009-08-18 13:16:10 -04001350// Create a hybrid MBR -- an ugly, funky thing that helps GPT work with
1351// OSes that don't understand GPT.
1352void GPTData::MakeHybrid(void) {
1353 uint32_t partNums[3];
1354 char line[255];
srs5694e19ba092009-08-24 14:10:35 -04001355 int numParts, i, j, typeCode, bootable, mbrNum;
srs5694e7b4ff92009-08-18 13:16:10 -04001356 uint64_t length;
srs5694c0ca8f82009-08-20 21:35:25 -04001357 char fillItUp = 'M'; // fill extra partition entries? (Yes/No/Maybe)
srs5694e19ba092009-08-24 14:10:35 -04001358 char eeFirst; // Whether EFI GPT (0xEE) partition comes first in table
srs5694e7b4ff92009-08-18 13:16:10 -04001359
srs5694c0ca8f82009-08-20 21:35:25 -04001360 printf("\nWARNING! Hybrid MBRs are flaky and potentially dangerous! If you decide not\n"
1361 "to use one, just hit the Enter key at the below prompt and your MBR\n"
1362 "partition table will be untouched.\n\n\a");
srs5694e7b4ff92009-08-18 13:16:10 -04001363
1364 // Now get the numbers of up to three partitions to add to the
1365 // hybrid MBR....
srs5694c0ca8f82009-08-20 21:35:25 -04001366 printf("Type from one to three GPT partition numbers, separated by spaces, to be\n"
1367 "added to the hybrid MBR, in sequence: ");
srs5694e7b4ff92009-08-18 13:16:10 -04001368 fgets(line, 255, stdin);
1369 numParts = sscanf(line, "%d %d %d", &partNums[0], &partNums[1], &partNums[2]);
srs5694c0ca8f82009-08-20 21:35:25 -04001370
1371 if (numParts > 0) {
1372 // Blank out the protective MBR, but leave the boot loader code
1373 // alone....
1374 protectiveMBR.EmptyMBR(0);
1375 protectiveMBR.SetDiskSize(diskSize);
srs5694e19ba092009-08-24 14:10:35 -04001376 printf("Place EFI GPT (0xEE) partition first in MBR (good for GRUB)? ");
1377 eeFirst = GetYN();
srs5694c0ca8f82009-08-20 21:35:25 -04001378 } // if
1379
srs5694e7b4ff92009-08-18 13:16:10 -04001380 for (i = 0; i < numParts; i++) {
1381 j = partNums[i] - 1;
srs5694e19ba092009-08-24 14:10:35 -04001382 printf("\nCreating entry for partition #%d\n", j + 1);
srs5694e7b4ff92009-08-18 13:16:10 -04001383 if ((j >= 0) && (j < mainHeader.numParts)) {
1384 if (partitions[j].lastLBA < UINT32_MAX) {
srs5694e19ba092009-08-24 14:10:35 -04001385 do {
1386 printf("Enter an MBR hex code (default %02X): ",
1387 typeHelper.GUIDToID(partitions[j].partitionType) / 256);
1388 fgets(line, 255, stdin);
1389 sscanf(line, "%x", &typeCode);
1390 if (line[0] == '\n')
1391 typeCode = typeHelper.GUIDToID(partitions[j].partitionType) / 256;
1392 } while ((typeCode <= 0) || (typeCode > 255));
srs5694e7b4ff92009-08-18 13:16:10 -04001393 printf("Set the bootable flag? ");
1394 bootable = (GetYN() == 'Y');
1395 length = partitions[j].lastLBA - partitions[j].firstLBA + UINT64_C(1);
srs5694e19ba092009-08-24 14:10:35 -04001396 if (eeFirst == 'Y')
1397 mbrNum = i + 1;
1398 else
1399 mbrNum = i;
1400 protectiveMBR.MakePart(mbrNum, (uint32_t) partitions[j].firstLBA,
srs5694e7b4ff92009-08-18 13:16:10 -04001401 (uint32_t) length, typeCode, bootable);
1402 } else { // partition out of range
1403 printf("Partition %d ends beyond the 2TiB limit of MBR partitions; omitting it.\n",
1404 j + 1);
1405 } // if/else
1406 } else {
1407 printf("Partition %d is out of range; omitting it.\n", j + 1);
1408 } // if/else
1409 } // for
srs5694c0ca8f82009-08-20 21:35:25 -04001410
1411 if (numParts > 0) { // User opted to create a hybrid MBR....
1412 // Create EFI protective partition that covers the start of the disk.
1413 // If this location (covering the main GPT data structures) is omitted,
1414 // Linux won't find any partitions on the disk. Note that this is
1415 // NUMBERED AFTER the hybrid partitions, contrary to what the
1416 // gptsync utility does. This is because Windows seems to choke on
1417 // disks with a 0xEE partition in the first slot and subsequent
1418 // additional partitions, unless it boots from the disk.
srs5694e19ba092009-08-24 14:10:35 -04001419 if (eeFirst == 'Y')
1420 mbrNum = 0;
1421 else
1422 mbrNum = numParts;
1423 protectiveMBR.MakePart(mbrNum, 1, protectiveMBR.FindLastInFree(1), 0xEE);
srs5694c0ca8f82009-08-20 21:35:25 -04001424
1425 // ... and for good measure, if there are any partition spaces left,
1426 // optionally create more protective EFI partitions to cover as much
1427 // space as possible....
1428 for (i = 0; i < 4; i++) {
1429 if (protectiveMBR.GetType(i) == 0x00) { // unused entry....
1430 if (fillItUp == 'M') {
srs5694e19ba092009-08-24 14:10:35 -04001431 printf("\nUnused partition space(s) found. Use one to protect more partitions? ");
srs5694c0ca8f82009-08-20 21:35:25 -04001432 fillItUp = GetYN();
1433 typeCode = 0x00; // use this to flag a need to get type code
1434 } // if
1435 if (fillItUp == 'Y') {
srs5694e19ba092009-08-24 14:10:35 -04001436 while ((typeCode <= 0) || (typeCode > 255)) {
1437 printf("Enter an MBR hex code (EE is EFI GPT, but may confuse MacOS): ");
srs5694c0ca8f82009-08-20 21:35:25 -04001438 // Comment on above: Mac OS treats disks with more than one
srs5694e19ba092009-08-24 14:10:35 -04001439 // 0xEE MBR partition as MBR disks, not as GPT disks.
srs5694c0ca8f82009-08-20 21:35:25 -04001440 fgets(line, 255, stdin);
1441 sscanf(line, "%x", &typeCode);
srs5694e19ba092009-08-24 14:10:35 -04001442 if (line[0] == '\n')
1443 typeCode = 0;
1444 } // while
srs5694c0ca8f82009-08-20 21:35:25 -04001445 protectiveMBR.MakeBiggestPart(i, typeCode); // make a partition
1446 } // if (fillItUp == 'Y')
1447 } // if unused entry
1448 } // for (i = 0; i < 4; i++)
1449 } // if (numParts > 0)
srs5694e7b4ff92009-08-18 13:16:10 -04001450} // GPTData::MakeHybrid()
srs5694c0ca8f82009-08-20 21:35:25 -04001451
1452// Create a fresh protective MBR.
1453void GPTData::MakeProtectiveMBR(void) {
1454 protectiveMBR.MakeProtectiveMBR();
1455} // GPTData::MakeProtectiveMBR(void)
srs5694e7b4ff92009-08-18 13:16:10 -04001456
1457// Writes GPT (and protective MBR) to disk. Returns 1 on successful
1458// write, 0 if there was a problem.
1459int GPTData::SaveGPTData(void) {
1460 int allOK = 1, i, j;
1461 char answer, line[256];
1462 int fd;
1463 uint64_t secondTable;
srs56942a9f5da2009-08-26 00:48:01 -04001464 uint32_t numParts;
srs5694e7b4ff92009-08-18 13:16:10 -04001465 off_t offset;
1466
1467 if (strlen(device) == 0) {
1468 printf("Device not defined.\n");
1469 } // if
1470
1471 // First do some final sanity checks....
1472 // Is there enough space to hold the GPT headers and partition tables,
1473 // given the partition sizes?
1474 if (CheckGPTSize() == 0) {
1475 allOK = 0;
1476 } // if
1477
1478 // Check that disk is really big enough to handle this...
1479 if (mainHeader.backupLBA > diskSize) {
1480 fprintf(stderr, "Error! Disk is too small -- either the original MBR is corrupt or you're\n");
1481 fprintf(stderr, "working from an MBR copied to a file! Aborting!\n");
1482 printf("(Disk size is %ld sectors, needs to be %ld sectors.)\n", diskSize,
1483 mainHeader.backupLBA);
1484 allOK = 0;
1485 } // if
1486
1487 // Check for overlapping partitions....
1488 for (i = 1; i < mainHeader.numParts; i++) {
1489 for (j = 0; j < i; j++) {
1490 if (TheyOverlap(&partitions[i], &partitions[j])) {
1491 fprintf(stderr, "\Error: partitions %d and %d overlap:\n", i + 1, j + 1);
1492 fprintf(stderr, " Partition %d: %llu to %llu\n", i,
1493 (unsigned long long) partitions[i].firstLBA,
1494 (unsigned long long) partitions[i].lastLBA);
1495 fprintf(stderr, " Partition %d: %llu to %llu\n", j,
1496 (unsigned long long) partitions[j].firstLBA,
1497 (unsigned long long) partitions[j].lastLBA);
1498 fprintf(stderr, "Aborting write operation!\n");
1499 allOK = 0;
1500 } // if
1501 } // for j...
1502 } // for i...
1503
srs56942a9f5da2009-08-26 00:48:01 -04001504 // Pull out some data that's needed before doing byte-order reversal on
1505 // big-endian systems....
1506 numParts = mainHeader.numParts;
1507 secondTable = secondHeader.partitionEntriesLBA;
1508 if (IsLittleEndian() == 0) {
1509 // Reverse partition bytes first, since that function requires non-reversed
1510 // data from the main header....
1511 ReversePartitionBytes();
1512 ReverseHeaderBytes(&mainHeader);
1513 ReverseHeaderBytes(&secondHeader);
1514 } // if
srs5694e7b4ff92009-08-18 13:16:10 -04001515 RecomputeCRCs();
1516
1517 if (allOK) {
1518 printf("\nFinal checks complete. About to write GPT data. THIS WILL OVERWRITE EXISTING\n");
1519 printf("MBR PARTITIONS!! THIS PROGRAM IS BETA QUALITY AT BEST. IF YOU LOSE ALL YOUR\n");
1520 printf("DATA, YOU HAVE ONLY YOURSELF TO BLAME IF YOU ANSWER 'Y' BELOW!\n\n");
1521 printf("Do you want to proceed, possibly destroying your data? (Y/N) ");
1522 fgets(line, 255, stdin);
1523 sscanf(line, "%c", &answer);
1524 if ((answer == 'Y') || (answer == 'y')) {
1525 printf("OK; writing new GPT partition table.\n");
1526 } else {
1527 allOK = 0;
1528 } // if/else
1529 } // if
1530
1531 // Do it!
1532 if (allOK) {
1533 fd = open(device, O_WRONLY); // try to open the device; may fail....
1534#ifdef __APPLE__
1535 // MacOS X requires a shared lock under some circumstances....
1536 if (fd < 0) {
1537 fd = open(device, O_WRONLY|O_SHLOCK);
1538 } // if
1539#endif
1540 if (fd != -1) {
1541 // First, write the protective MBR...
1542 protectiveMBR.WriteMBRData(fd);
1543
1544 // Now write the main GPT header...
1545 if (allOK)
1546 if (write(fd, &mainHeader, 512) == -1)
1547 allOK = 0;
1548
1549 // Now write the main partition tables...
1550 if (allOK) {
srs56942a9f5da2009-08-26 00:48:01 -04001551 if (write(fd, partitions, GPT_SIZE * numParts) == -1)
srs5694e7b4ff92009-08-18 13:16:10 -04001552 allOK = 0;
1553 } // if
1554
1555 // Now seek to near the end to write the secondary GPT....
1556 if (allOK) {
srs5694e7b4ff92009-08-18 13:16:10 -04001557 offset = (off_t) secondTable * (off_t) (blockSize);
1558 if (lseek64(fd, offset, SEEK_SET) == (off_t) - 1) {
1559 allOK = 0;
1560 printf("Unable to seek to end of disk!\n");
1561 } // if
1562 } // if
1563
1564 // Now write the secondary partition tables....
1565 if (allOK)
srs56942a9f5da2009-08-26 00:48:01 -04001566 if (write(fd, partitions, GPT_SIZE * numParts) == -1)
srs5694e7b4ff92009-08-18 13:16:10 -04001567 allOK = 0;
1568
1569 // Now write the secondary GPT header...
1570 if (allOK)
1571 if (write(fd, &secondHeader, 512) == -1)
1572 allOK = 0;
1573
1574 // re-read the partition table
1575 if (allOK) {
1576 sync();
1577#ifdef __APPLE__
1578 printf("Warning: The kernel may continue to use old or deleted partitions.\n"
1579 "You should reboot or remove the drive.\n");
1580 /* don't know if this helps
1581 * it definitely will get things on disk though:
1582 * http://topiks.org/mac-os-x/0321278542/ch12lev1sec8.html */
1583 i = ioctl(fd, DKIOCSYNCHRONIZECACHE);
1584#else
1585 sleep(2);
1586 i = ioctl(fd, BLKRRPART);
1587 if (i)
1588 printf("Warning: The kernel is still using the old partition table.\n"
1589 "The new table will be used at the next reboot.\n");
1590#endif
1591 } // if
1592
1593 if (allOK) { // writes completed OK
1594 printf("The operation has completed successfully.\n");
1595 } else {
1596 printf("Warning! An error was reported when writing the partition table! This error\n");
1597 printf("MIGHT be harmless, but you may have trashed the disk! Use parted and, if\n");
1598 printf("necessary, restore your original partition table.\n");
1599 } // if/else
1600 close(fd);
1601 } else {
1602 fprintf(stderr, "Unable to open device %s for writing! Errno is %d! Aborting!\n", device, errno);
1603 allOK = 0;
1604 } // if/else
1605 } else {
1606 printf("Aborting write of new partition table.\n");
1607 } // if
1608
srs56942a9f5da2009-08-26 00:48:01 -04001609 if (IsLittleEndian() == 0) {
1610 // Reverse (normalize) header bytes first, since ReversePartitionBytes()
1611 // requires non-reversed data in mainHeader...
1612 ReverseHeaderBytes(&mainHeader);
1613 ReverseHeaderBytes(&secondHeader);
1614 ReversePartitionBytes();
1615 } // if
1616
srs5694e7b4ff92009-08-18 13:16:10 -04001617 return (allOK);
1618} // GPTData::SaveGPTData()
1619
1620// Save GPT data to a backup file. This function does much less error
1621// checking than SaveGPTData(). It can therefore preserve many types of
1622// corruption for later analysis; however, it preserves only the MBR,
1623// the main GPT header, the backup GPT header, and the main partition
1624// table; it discards the backup partition table, since it should be
1625// identical to the main partition table on healthy disks.
1626int GPTData::SaveGPTBackup(char* filename) {
srs56942a9f5da2009-08-26 00:48:01 -04001627 int fd, allOK = 1;
1628 uint32_t numParts;
srs5694e7b4ff92009-08-18 13:16:10 -04001629
1630 if ((fd = open(filename, O_WRONLY | O_CREAT, S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH)) != -1) {
srs56942a9f5da2009-08-26 00:48:01 -04001631 // Reverse the byte order, if necessary....
1632 numParts = mainHeader.numParts;
1633 if (IsLittleEndian() == 0) {
1634 ReversePartitionBytes();
1635 ReverseHeaderBytes(&mainHeader);
1636 ReverseHeaderBytes(&secondHeader);
1637 } // if
1638
1639 // Now write the protective MBR...
srs5694e7b4ff92009-08-18 13:16:10 -04001640 protectiveMBR.WriteMBRData(fd);
1641
1642 // Now write the main GPT header...
1643 if (allOK)
1644 if (write(fd, &mainHeader, 512) == -1)
1645 allOK = 0;
1646
1647 // Now write the secondary GPT header...
1648 if (allOK)
1649 if (write(fd, &secondHeader, 512) == -1)
1650 allOK = 0;
1651
1652 // Now write the main partition tables...
1653 if (allOK) {
srs56942a9f5da2009-08-26 00:48:01 -04001654 if (write(fd, partitions, GPT_SIZE * numParts) == -1)
srs5694e7b4ff92009-08-18 13:16:10 -04001655 allOK = 0;
1656 } // if
1657
1658 if (allOK) { // writes completed OK
1659 printf("The operation has completed successfully.\n");
1660 } else {
1661 printf("Warning! An error was reported when writing the backup file.\n");
1662 printf("It may not be useable!\n");
1663 } // if/else
1664 close(fd);
srs56942a9f5da2009-08-26 00:48:01 -04001665
1666 // Now reverse the byte-order reversal, if necessary....
1667 if (IsLittleEndian() == 0) {
1668 ReverseHeaderBytes(&mainHeader);
1669 ReverseHeaderBytes(&secondHeader);
1670 ReversePartitionBytes();
1671 } // if
srs5694e7b4ff92009-08-18 13:16:10 -04001672 } else {
1673 fprintf(stderr, "Unable to open file %s for writing! Aborting!\n", filename);
1674 allOK = 0;
1675 } // if/else
1676 return allOK;
1677} // GPTData::SaveGPTBackup()
1678
1679// Load GPT data from a backup file created by SaveGPTBackup(). This function
1680// does minimal error checking. It returns 1 if it completed successfully,
1681// 0 if there was a problem. In the latter case, it creates a new empty
1682// set of partitions.
1683int GPTData::LoadGPTBackup(char* filename) {
1684 int fd, allOK = 1, val;
1685 uint32_t numParts, sizeOfEntries, sizeOfParts, newCRC;
srs56942a9f5da2009-08-26 00:48:01 -04001686 int littleEndian = 1;
srs5694e7b4ff92009-08-18 13:16:10 -04001687
1688 if ((fd = open(filename, O_RDONLY)) != -1) {
srs56942a9f5da2009-08-26 00:48:01 -04001689 if (IsLittleEndian() == 0)
1690 littleEndian = 0;
1691
srs5694e7b4ff92009-08-18 13:16:10 -04001692 // Let the MBRData class load the saved MBR...
1693 protectiveMBR.ReadMBRData(fd);
1694
1695 // Load the main GPT header, check its vaility, and set the GPT
1696 // size based on the data
1697 read(fd, &mainHeader, 512);
1698 mainCrcOk = CheckHeaderCRC(&mainHeader);
1699
srs56942a9f5da2009-08-26 00:48:01 -04001700 // Reverse byte order, if necessary
1701 if (littleEndian == 0) {
1702 ReverseHeaderBytes(&mainHeader);
1703 } // if
1704
srs5694e7b4ff92009-08-18 13:16:10 -04001705 // Load the backup GPT header in much the same way as the main
1706 // GPT header....
1707 read(fd, &secondHeader, 512);
1708 secondCrcOk = CheckHeaderCRC(&secondHeader);
1709
srs56942a9f5da2009-08-26 00:48:01 -04001710 // Reverse byte order, if necessary
1711 if (littleEndian == 0) {
1712 ReverseHeaderBytes(&secondHeader);
1713 } // if
1714
srs5694e7b4ff92009-08-18 13:16:10 -04001715 // Return valid headers code: 0 = both headers bad; 1 = main header
1716 // good, backup bad; 2 = backup header good, main header bad;
1717 // 3 = both headers good. Note these codes refer to valid GPT
1718 // signatures and version numbers; more subtle problems will elude
1719 // this check!
1720 if ((val = CheckHeaderValidity()) > 0) {
1721 if (val == 2) { // only backup header seems to be good
1722 numParts = secondHeader.numParts;
1723 sizeOfEntries = secondHeader.sizeOfPartitionEntries;
1724 } else { // main header is OK
1725 numParts = mainHeader.numParts;
1726 sizeOfEntries = mainHeader.sizeOfPartitionEntries;
1727 } // if/else
1728
1729 SetGPTSize(numParts);
1730
1731 // If current disk size doesn't match that of backup....
1732 if (secondHeader.currentLBA != diskSize - UINT64_C(1)) {
1733 printf("Warning! Current disk size doesn't match that of the backup!\n"
1734 "Adjusting sizes to match, but subsequent problems are possible!\n");
1735 secondHeader.currentLBA = mainHeader.backupLBA = diskSize - UINT64_C(1);
1736 mainHeader.lastUsableLBA = diskSize - mainHeader.firstUsableLBA;
1737 secondHeader.lastUsableLBA = mainHeader.lastUsableLBA;
1738 secondHeader.partitionEntriesLBA = secondHeader.lastUsableLBA + UINT64_C(1);
1739 } // if
1740
1741 // Load main partition table, and record whether its CRC
1742 // matches the stored value
1743 sizeOfParts = numParts * sizeOfEntries;
1744 read(fd, partitions, sizeOfParts);
1745
1746 newCRC = chksum_crc32((unsigned char*) partitions, sizeOfParts);
1747 mainPartsCrcOk = (newCRC == mainHeader.partitionEntriesCRC);
1748 secondPartsCrcOk = (newCRC == secondHeader.partitionEntriesCRC);
srs56942a9f5da2009-08-26 00:48:01 -04001749 // Reverse byte order, if necessary
1750 if (littleEndian == 0) {
1751 ReversePartitionBytes();
1752 } // if
1753
srs5694e7b4ff92009-08-18 13:16:10 -04001754 } else {
1755 allOK = 0;
1756 } // if/else
1757 } else {
1758 allOK = 0;
1759 fprintf(stderr, "Unable to open file %s for reading! Aborting!\n", filename);
1760 } // if/else
1761
1762 // Something went badly wrong, so blank out partitions
1763 if (allOK == 0) {
1764 ClearGPTData();
1765 protectiveMBR.MakeProtectiveMBR();
1766 } // if
1767 return allOK;
1768} // GPTData::LoadGPTBackup()
1769
srs5694c0ca8f82009-08-20 21:35:25 -04001770// This function destroys the on-disk GPT structures. Returns 1 if the
1771// user confirms destruction, 0 if the user aborts.
1772int GPTData::DestroyGPT(void) {
1773 int fd, i, doMore;
1774 char blankSector[512], goOn;
1775
1776 for (i = 0; i < 512; i++) {
1777 blankSector[i] = '\0';
1778 } // for
1779
1780 printf("\a\aAbout to wipe out GPT on %s. Proceed? ", device);
1781 goOn = GetYN();
1782 if (goOn == 'Y') {
1783 fd = open(device, O_WRONLY);
1784#ifdef __APPLE__
1785 // MacOS X requires a shared lock under some circumstances....
1786 if (fd < 0) {
1787 fd = open(device, O_WRONLY|O_SHLOCK);
1788 } // if
1789#endif
1790 if (fd != -1) {
1791 lseek64(fd, mainHeader.currentLBA * 512, SEEK_SET); // seek to GPT header
1792 write(fd, blankSector, 512); // blank it out
1793 lseek64(fd, mainHeader.partitionEntriesLBA * 512, SEEK_SET); // seek to partition table
1794 for (i = 0; i < GetBlocksInPartTable(); i++)
1795 write(fd, blankSector, 512);
1796 lseek64(fd, secondHeader.partitionEntriesLBA * 512, SEEK_SET); // seek to partition table
1797 for (i = 0; i < GetBlocksInPartTable(); i++)
1798 write(fd, blankSector, 512);
1799 lseek64(fd, secondHeader.currentLBA * 512, SEEK_SET); // seek to GPT header
1800 write(fd, blankSector, 512); // blank it out
1801 printf("Blank out MBR? ");
1802 if (GetYN() == 'Y') {
1803 lseek64(fd, 0, SEEK_SET);
1804 write(fd, blankSector, 512); // blank it out
1805 } // if blank MBR
1806 close(fd);
1807 printf("GPT data structures destroyed! You may now partition the disk using fdisk or\n"
1808 "other utilities. Program will now terminate.\n");
1809 } else {
1810 printf("Problem opening %s for writing! Program will now terminate.\n");
1811 } // if/else (fd != -1)
1812 } // if (goOn == 'Y')
1813 return (goOn == 'Y');
1814} // GPTData::DestroyGPT()
1815
srs56942a9f5da2009-08-26 00:48:01 -04001816void GPTData::ReverseHeaderBytes(struct GPTHeader* header) {
1817 ReverseBytes((char*) &header->signature, 8);
1818 ReverseBytes((char*) &header->revision, 4);
1819 ReverseBytes((char*) &header->headerSize, 4);
1820 ReverseBytes((char*) &header->headerCRC, 4);
1821 ReverseBytes((char*) &header->reserved, 4);
1822 ReverseBytes((char*) &header->currentLBA, 8);
1823 ReverseBytes((char*) &header->backupLBA, 8);
1824 ReverseBytes((char*) &header->firstUsableLBA, 8);
1825 ReverseBytes((char*) &header->lastUsableLBA, 8);
1826 ReverseBytes((char*) &header->partitionEntriesLBA, 8);
1827 ReverseBytes((char*) &header->numParts, 4);
1828 ReverseBytes((char*) &header->sizeOfPartitionEntries, 4);
1829 ReverseBytes((char*) &header->partitionEntriesCRC, 4);
1830 ReverseBytes((char*) header->reserved2, GPT_RESERVED);
1831 ReverseBytes((char*) &header->diskGUID.data1, 8);
1832 ReverseBytes((char*) &header->diskGUID.data2, 8);
1833} // GPTData::ReverseHeaderBytes()
1834
1835// IMPORTANT NOTE: This function requires non-reversed mainHeader
1836// structure!
1837void GPTData::ReversePartitionBytes() {
1838 uint32_t i;
1839
1840 // Check GPT signature on big-endian systems; this will mismatch
1841 // if the function is called out of order. Unfortunately, it'll also
1842 // mismatch if there's data corruption.
1843 if ((mainHeader.signature != GPT_SIGNATURE) && (IsLittleEndian() == 0)) {
1844 printf("GPT signature mismatch in GPTData::ReversePartitionBytes(). This indicates\n"
1845 "data corruption or a misplaced call to this function.\n");
1846 } // if signature mismatch....
1847 for (i = 0; i < mainHeader.numParts; i++) {
1848 ReverseBytes((char*) &partitions[i].partitionType.data1, 8);
1849 ReverseBytes((char*) &partitions[i].partitionType.data2, 8);
1850 ReverseBytes((char*) &partitions[i].uniqueGUID.data1, 8);
1851 ReverseBytes((char*) &partitions[i].uniqueGUID.data2, 8);
1852 ReverseBytes((char*) &partitions[i].firstLBA, 8);
1853 ReverseBytes((char*) &partitions[i].lastLBA, 8);
1854 ReverseBytes((char*) &partitions[i].attributes, 8);
1855 } // for
1856} // GPTData::ReversePartitionBytes()
1857
1858/******************************************
1859 * *
1860 * Additional non-class support functions *
1861 * *
1862 ******************************************/
1863
srs5694e7b4ff92009-08-18 13:16:10 -04001864// Check to be sure that data type sizes are correct. The basic types (uint*_t) should
1865// never fail these tests, but the struct types may fail depending on compile options.
1866// Specifically, the -fpack-struct option to gcc may be required to ensure proper structure
1867// sizes.
1868int SizesOK(void) {
1869 int allOK = 1;
srs5694e7b4ff92009-08-18 13:16:10 -04001870
1871 if (sizeof(uint8_t) != 1) {
1872 fprintf(stderr, "uint8_t is %d bytes, should be 1 byte; aborting!\n", sizeof(uint8_t));
1873 allOK = 0;
1874 } // if
1875 if (sizeof(uint16_t) != 2) {
1876 fprintf(stderr, "uint16_t is %d bytes, should be 2 bytes; aborting!\n", sizeof(uint16_t));
1877 allOK = 0;
1878 } // if
1879 if (sizeof(uint32_t) != 4) {
1880 fprintf(stderr, "uint32_t is %d bytes, should be 4 bytes; aborting!\n", sizeof(uint32_t));
1881 allOK = 0;
1882 } // if
1883 if (sizeof(uint64_t) != 8) {
1884 fprintf(stderr, "uint64_t is %d bytes, should be 8 bytes; aborting!\n", sizeof(uint64_t));
1885 allOK = 0;
1886 } // if
1887 if (sizeof(struct MBRRecord) != 16) {
1888 fprintf(stderr, "MBRRecord is %d bytes, should be 16 bytes; aborting!\n", sizeof(uint32_t));
1889 allOK = 0;
1890 } // if
1891 if (sizeof(struct EBRRecord) != 512) {
1892 fprintf(stderr, "EBRRecord is %d bytes, should be 512 bytes; aborting!\n", sizeof(uint32_t));
1893 allOK = 0;
1894 } // if
1895 if (sizeof(struct GPTHeader) != 512) {
1896 fprintf(stderr, "GPTHeader is %d bytes, should be 512 bytes; aborting!\n", sizeof(uint32_t));
1897 allOK = 0;
1898 } // if
1899 // Determine endianness; set allOK = 0 if running on big-endian hardware
srs56942a9f5da2009-08-26 00:48:01 -04001900 if (IsLittleEndian() == 0) {
1901 fprintf(stderr, "\aRunning on big-endian hardware. Big-endian support is new and poorly"
1902 " tested!\nBeware!\n");
1903 // allOK = 0;
srs5694e7b4ff92009-08-18 13:16:10 -04001904 } // if
1905 return (allOK);
1906} // SizesOK()
1907