blob: af3da2ad894f7c884a6215462903b2a51f668c72 [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
srs5694221e0872009-08-29 15:00:31 -04006/* This program is copyright (c) 2009 by Roderick W. Smith. It is distributed
7 under the terms of the GNU GPL version 2, as detailed in the COPYING file. */
8
srs5694e7b4ff92009-08-18 13:16:10 -04009#define __STDC_LIMIT_MACROS
10#define __STDC_CONSTANT_MACROS
11
12#include <stdio.h>
13#include <unistd.h>
14#include <stdlib.h>
15#include <stdint.h>
16#include <fcntl.h>
17#include <string.h>
18#include <time.h>
19#include <sys/stat.h>
20#include <errno.h>
21#include "crc32.h"
22#include "gpt.h"
srs5694221e0872009-08-29 15:00:31 -040023#include "bsd.h"
srs5694e7b4ff92009-08-18 13:16:10 -040024#include "support.h"
25#include "parttypes.h"
26#include "attributes.h"
27
28using namespace std;
29
30/****************************************
31 * *
32 * GPTData class and related structures *
33 * *
34 ****************************************/
35
36GPTData::GPTData(void) {
37 blockSize = SECTOR_SIZE; // set a default
38 diskSize = 0;
39 partitions = NULL;
40 state = gpt_valid;
41 strcpy(device, "");
42 mainCrcOk = 0;
43 secondCrcOk = 0;
44 mainPartsCrcOk = 0;
45 secondPartsCrcOk = 0;
srs5694221e0872009-08-29 15:00:31 -040046 apmFound = 0;
47 bsdFound = 0;
srs5694e7b4ff92009-08-18 13:16:10 -040048 srand((unsigned int) time(NULL));
49 SetGPTSize(NUM_GPT_ENTRIES);
50} // GPTData default constructor
51
52// The following constructor loads GPT data from a device file
53GPTData::GPTData(char* filename) {
54 blockSize = SECTOR_SIZE; // set a default
55 diskSize = 0;
56 partitions = NULL;
57 state = gpt_invalid;
58 strcpy(device, "");
59 mainCrcOk = 0;
60 secondCrcOk = 0;
61 mainPartsCrcOk = 0;
62 secondPartsCrcOk = 0;
srs5694221e0872009-08-29 15:00:31 -040063 apmFound = 0;
64 bsdFound = 0;
srs5694e7b4ff92009-08-18 13:16:10 -040065 srand((unsigned int) time(NULL));
66 LoadPartitions(filename);
67} // GPTData(char* filename) constructor
68
69GPTData::~GPTData(void) {
70 free(partitions);
71} // GPTData destructor
72
73// Resizes GPT to specified number of entries. Creates a new table if
74// necessary, copies data if it already exists.
75int GPTData::SetGPTSize(uint32_t numEntries) {
srs5694221e0872009-08-29 15:00:31 -040076 struct GPTPart* newParts;
77 struct GPTPart* trash;
srs5694e7b4ff92009-08-18 13:16:10 -040078 uint32_t i, high, copyNum;
79 int allOK = 1;
80
81 // First, adjust numEntries upward, if necessary, to get a number
82 // that fills the allocated sectors
83 i = blockSize / GPT_SIZE;
84 if ((numEntries % i) != 0) {
85 printf("Adjusting GPT size from %lu ", (unsigned long) numEntries);
86 numEntries = ((numEntries / i) + 1) * i;
87 printf("to %lu to fill the sector\n", (unsigned long) numEntries);
88 } // if
89
srs5694221e0872009-08-29 15:00:31 -040090 newParts = (GPTPart*) calloc(numEntries, sizeof (GPTPart));
srs5694e7b4ff92009-08-18 13:16:10 -040091 if (newParts != NULL) {
92 if (partitions != NULL) { // existing partitions; copy them over
93 GetPartRange(&i, &high);
94 if (numEntries < (high + 1)) { // Highest entry too high for new #
95 printf("The highest-numbered partition is %lu, which is greater than the requested\n"
96 "partition table size of %d; cannot resize. Perhaps sorting will help.\n",
97 (unsigned long) (high + 1), numEntries);
98 allOK = 0;
99 } else { // go ahead with copy
100 if (numEntries < mainHeader.numParts)
101 copyNum = numEntries;
102 else
103 copyNum = mainHeader.numParts;
104 for (i = 0; i < copyNum; i++) {
105 newParts[i] = partitions[i];
106 } // for
107 trash = partitions;
108 partitions = newParts;
109 free(trash);
110 } // if
111 } else { // No existing partition table; just create it
112 partitions = newParts;
113 } // if/else existing partitions
114 mainHeader.numParts = numEntries;
115 secondHeader.numParts = numEntries;
116 mainHeader.firstUsableLBA = ((numEntries * GPT_SIZE) / blockSize) + 2 ;
117 secondHeader.firstUsableLBA = mainHeader.firstUsableLBA;
118 mainHeader.lastUsableLBA = diskSize - mainHeader.firstUsableLBA;
119 secondHeader.lastUsableLBA = mainHeader.lastUsableLBA;
120 secondHeader.partitionEntriesLBA = secondHeader.lastUsableLBA + UINT64_C(1);
121 if (diskSize > 0)
122 CheckGPTSize();
123 } else { // Bad memory allocation
124 fprintf(stderr, "Error allocating memory for partition table!\n");
125 allOK = 0;
126 } // if/else
127 return (allOK);
128} // GPTData::SetGPTSize()
129
130// Checks to see if the GPT tables overrun existing partitions; if they
srs5694221e0872009-08-29 15:00:31 -0400131// do, issues a warning but takes no action. Returns number of problems
132// detected (0 if OK, 1 to 2 if problems).
srs5694e7b4ff92009-08-18 13:16:10 -0400133int GPTData::CheckGPTSize(void) {
134 uint64_t overlap, firstUsedBlock, lastUsedBlock;
135 uint32_t i;
srs5694221e0872009-08-29 15:00:31 -0400136 int numProbs = 0;
srs5694e7b4ff92009-08-18 13:16:10 -0400137
138 // first, locate the first & last used blocks
139 firstUsedBlock = UINT64_MAX;
140 lastUsedBlock = 0;
141 for (i = 0; i < mainHeader.numParts; i++) {
srs5694221e0872009-08-29 15:00:31 -0400142 if ((partitions[i].GetFirstLBA() < firstUsedBlock) &&
143 (partitions[i].GetFirstLBA() != 0))
144 firstUsedBlock = partitions[i].GetFirstLBA();
145 if (partitions[i].GetLastLBA() > lastUsedBlock)
146 lastUsedBlock = partitions[i].GetLastLBA();
srs5694e7b4ff92009-08-18 13:16:10 -0400147 } // for
148
149 // If the disk size is 0 (the default), then it means that various
150 // variables aren't yet set, so the below tests will be useless;
151 // therefore we should skip everything
152 if (diskSize != 0) {
153 if (mainHeader.firstUsableLBA > firstUsedBlock) {
154 overlap = mainHeader.firstUsableLBA - firstUsedBlock;
srs5694221e0872009-08-29 15:00:31 -0400155 printf("Warning! Main partition table overlaps the first partition by %lu blocks!\n",
156 (unsigned long) overlap);
157 if (firstUsedBlock > 2) {
158 printf("Try reducing the partition table size by %lu entries.\n",
159 (unsigned long) (overlap * 4));
160 printf("(Use the 's' item on the experts' menu.)\n");
161 } else {
162 printf("You will need to delete this partition or resize it in another utility.\n");
163 } // if/else
164 numProbs++;
srs5694e7b4ff92009-08-18 13:16:10 -0400165 } // Problem at start of disk
166 if (mainHeader.lastUsableLBA < lastUsedBlock) {
167 overlap = lastUsedBlock - mainHeader.lastUsableLBA;
srs5694221e0872009-08-29 15:00:31 -0400168 printf("Warning! Secondary partition table overlaps the last partition by %lu blocks\n",
169 (unsigned long) overlap);
170 if (lastUsedBlock > (diskSize - 2)) {
171 printf("You will need to delete this partition or resize it in another utility.\n");
172 } else {
173 printf("Try reducing the partition table size by %lu entries.\n",
174 (unsigned long) (overlap * 4));
175 printf("(Use the 's' item on the experts' menu.)\n");
176 } // if/else
177 numProbs++;
srs5694e7b4ff92009-08-18 13:16:10 -0400178 } // Problem at end of disk
179 } // if (diskSize != 0)
srs5694221e0872009-08-29 15:00:31 -0400180 return numProbs;
srs5694e7b4ff92009-08-18 13:16:10 -0400181} // GPTData::CheckGPTSize()
182
srs5694221e0872009-08-29 15:00:31 -0400183// Tell user whether Apple Partition Map (APM) was discovered....
184void GPTData::ShowAPMState(void) {
185 if (apmFound)
186 printf(" APM: present\n");
187 else
188 printf(" APM: not present\n");
189} // GPTData::ShowAPMState()
190
191// Tell user about the state of the GPT data....
192void GPTData::ShowGPTState(void) {
193 switch (state) {
194 case gpt_invalid:
195 printf(" GPT: not present\n");
196 break;
197 case gpt_valid:
198 printf(" GPT: present\n");
199 break;
200 case gpt_corrupt:
201 printf(" GPT: damaged\n");
202 break;
203 default:
204 printf("\a GPT: unknown -- bug!\n");
205 break;
206 } // switch
207} // GPTData::ShowGPTState()
208
209// Scan for partition data. This function loads the MBR data (regular MBR or
210// protective MBR) and loads BSD disklabel data (which is probably invalid).
211// It also looks for APM data, forces a load of GPT data, and summarizes
212// the results.
213void GPTData::PartitionScan(int fd) {
214 BSDData bsdDisklabel;
215// int bsdFound;
216
217 printf("Partition table scan:\n");
218
219 // Read the MBR & check for BSD disklabel
220 protectiveMBR.ReadMBRData(fd);
221 protectiveMBR.ShowState();
222 bsdDisklabel.ReadBSDData(fd, 0, diskSize - 1);
223 bsdFound = bsdDisklabel.ShowState();
224// bsdDisklabel.DisplayBSDData();
225
226 // Load the GPT data, whether or not it's valid
227 ForceLoadGPTData(fd);
228 ShowAPMState(); // Show whether there's an Apple Partition Map present
229 ShowGPTState(); // Show GPT status
230 printf("\n");
231
232 if (apmFound) {
233 printf("\n*******************************************************************\n");
234 printf("This disk appears to contain an Apple-format (APM) partition table!\n");
235 printf("It will be destroyed if you continue!\n");
236 printf("*******************************************************************\n\n\a");
237 } // if
238/* if (bsdFound) {
239 printf("\n*************************************************************************\n");
240 printf("This disk appears to contain a BSD disklabel! It will be destroyed if you\n"
241 "continue!\n");
242 printf("*************************************************************************\n\n\a");
243 } // if */
244} // GPTData::PartitionScan()
245
srs5694e7b4ff92009-08-18 13:16:10 -0400246// Read GPT data from a disk.
247int GPTData::LoadPartitions(char* deviceFilename) {
248 int fd, err;
249 int allOK = 1, i;
250 uint64_t firstBlock, lastBlock;
srs5694221e0872009-08-29 15:00:31 -0400251 BSDData bsdDisklabel;
srs5694e7b4ff92009-08-18 13:16:10 -0400252
253 if ((fd = open(deviceFilename, O_RDONLY)) != -1) {
254 // store disk information....
255 diskSize = disksize(fd, &err);
256 blockSize = (uint32_t) GetBlockSize(fd);
257 strcpy(device, deviceFilename);
srs5694221e0872009-08-29 15:00:31 -0400258 PartitionScan(fd); // Check for partition types & print summary
srs5694e7b4ff92009-08-18 13:16:10 -0400259
260 switch (UseWhichPartitions()) {
261 case use_mbr:
srs5694221e0872009-08-29 15:00:31 -0400262 XFormPartitions();
263 break;
264 case use_bsd:
265 bsdDisklabel.ReadBSDData(fd, 0, diskSize - 1);
266// bsdDisklabel.DisplayBSDData();
267 ClearGPTData();
268 protectiveMBR.MakeProtectiveMBR(1); // clear boot area (option 1)
269 XFormDisklabel(&bsdDisklabel, 0);
srs5694e7b4ff92009-08-18 13:16:10 -0400270 break;
271 case use_gpt:
272 break;
273 case use_new:
srs5694e7b4ff92009-08-18 13:16:10 -0400274 ClearGPTData();
275 protectiveMBR.MakeProtectiveMBR();
276 break;
277 } // switch
278
279 // Now find the first and last sectors used by partitions...
280 if (allOK) {
281 firstBlock = mainHeader.backupLBA; // start high
282 lastBlock = 0; // start low
283 for (i = 0; i < mainHeader.numParts; i++) {
srs5694221e0872009-08-29 15:00:31 -0400284 if ((partitions[i].GetFirstLBA() < firstBlock) &&
285 (partitions[i].GetFirstLBA() > 0))
286 firstBlock = partitions[i].GetFirstLBA();
287 if (partitions[i].GetLastLBA() > lastBlock)
288 lastBlock = partitions[i].GetLastLBA();
srs5694e7b4ff92009-08-18 13:16:10 -0400289 } // for
290 } // if
291 CheckGPTSize();
292 } else {
293 allOK = 0;
srs5694e19ba092009-08-24 14:10:35 -0400294 fprintf(stderr, "Problem opening %s for reading! Error is %d\n",
295 deviceFilename, errno);
296 if (errno == EACCES) { // User is probably not running as root
297 fprintf(stderr, "You must run this program as root or use sudo!\n");
298 } // if
srs5694e7b4ff92009-08-18 13:16:10 -0400299 } // if/else
300 return (allOK);
301} // GPTData::LoadPartitions()
302
303// Loads the GPT, as much as possible. Returns 1 if this seems to have
304// succeeded, 0 if there are obvious problems....
305int GPTData::ForceLoadGPTData(int fd) {
306 int allOK = 1, validHeaders;
307 off_t seekTo;
308 char* storage;
309 uint32_t newCRC, sizeOfParts;
310
311 // Seek to and read the main GPT header
312 lseek64(fd, 512, SEEK_SET);
313 read(fd, &mainHeader, 512); // read main GPT header
314 mainCrcOk = CheckHeaderCRC(&mainHeader);
srs56942a9f5da2009-08-26 00:48:01 -0400315 if (IsLittleEndian() == 0) // big-endian system; adjust header byte order....
316 ReverseHeaderBytes(&mainHeader);
srs5694e7b4ff92009-08-18 13:16:10 -0400317
318 // Load backup header, check its CRC, and store the results of
319 // the check for future reference
320 seekTo = (diskSize * blockSize) - UINT64_C(512);
321 if (lseek64(fd, seekTo, SEEK_SET) != (off_t) -1) {
322 read(fd, &secondHeader, 512); // read secondary GPT header
323 secondCrcOk = CheckHeaderCRC(&secondHeader);
srs56942a9f5da2009-08-26 00:48:01 -0400324 if (IsLittleEndian() == 0) // big-endian system; adjust header byte order....
325 ReverseHeaderBytes(&secondHeader);
srs5694e7b4ff92009-08-18 13:16:10 -0400326 } else {
327 allOK = 0;
328 state = gpt_invalid;
329 fprintf(stderr, "Unable to seek to secondary GPT at sector %llu!\n",
330 diskSize - (UINT64_C(1)));
331 } // if/else lseek
332
333 // Return valid headers code: 0 = both headers bad; 1 = main header
334 // good, backup bad; 2 = backup header good, main header bad;
335 // 3 = both headers good. Note these codes refer to valid GPT
336 // signatures and version numbers; more subtle problems will elude
337 // this check!
338 validHeaders = CheckHeaderValidity();
339
340 // Read partitions (from primary array)
341 if (validHeaders > 0) { // if at least one header is OK....
342 // GPT appears to be valid....
343 state = gpt_valid;
344
345 // We're calling the GPT valid, but there's a possibility that one
346 // of the two headers is corrupt. If so, use the one that seems to
347 // be in better shape to regenerate the bad one
348 if (validHeaders == 2) { // valid backup header, invalid main header
349 printf("Caution: invalid main GPT header, but valid backup; regenerating main header\n"
350 "from backup!\n");
351 RebuildMainHeader();
352 mainCrcOk = secondCrcOk; // Since copied, use CRC validity of backup
353 } else if (validHeaders == 1) { // valid main header, invalid backup
354 printf("Caution: invalid backup GPT header, but valid main header; regenerating\n"
355 "backup header from main header.\n");
356 RebuildSecondHeader();
357 secondCrcOk = mainCrcOk; // Since regenerated, use CRC validity of main
358 } // if/else/if
359
360 // Load the main partition table, including storing results of its
361 // CRC check
362 if (LoadMainTable() == 0)
363 allOK = 0;
364
365 // Load backup partition table into temporary storage to check
366 // its CRC and store the results, then discard this temporary
367 // storage, since we don't use it in any but recovery operations
368 seekTo = secondHeader.partitionEntriesLBA * (off_t) blockSize;
369 if ((lseek64(fd, seekTo, SEEK_SET) != (off_t) -1) && (secondCrcOk)) {
370 sizeOfParts = secondHeader.numParts * secondHeader.sizeOfPartitionEntries;
371 storage = (char*) malloc(sizeOfParts);
372 read(fd, storage, sizeOfParts);
373 newCRC = chksum_crc32((unsigned char*) storage, sizeOfParts);
374 free(storage);
375 secondPartsCrcOk = (newCRC == secondHeader.partitionEntriesCRC);
376 } // if
377
378 // Check for valid CRCs and warn if there are problems
379 if ((mainCrcOk == 0) || (secondCrcOk == 0) || (mainPartsCrcOk == 0) ||
380 (secondPartsCrcOk == 0)) {
381 printf("Warning! One or more CRCs don't match. You should repair the disk!\n");
382 state = gpt_corrupt;
383 } // if
384 } else {
385 state = gpt_invalid;
386 } // if/else
387 return allOK;
388} // GPTData::ForceLoadGPTData()
389
390// Loads the partition tables pointed to by the main GPT header. The
391// main GPT header in memory MUST be valid for this call to do anything
392// sensible!
393int GPTData::LoadMainTable(void) {
394 int fd, retval = 0;
395 uint32_t newCRC, sizeOfParts;
396
397 if ((fd = open(device, O_RDONLY)) != -1) {
398 // Set internal data structures for number of partitions on the disk
399 SetGPTSize(mainHeader.numParts);
400
401 // Load main partition table, and record whether its CRC
402 // matches the stored value
403 lseek64(fd, mainHeader.partitionEntriesLBA * blockSize, SEEK_SET);
404 sizeOfParts = mainHeader.numParts * mainHeader.sizeOfPartitionEntries;
405 read(fd, partitions, sizeOfParts);
406 newCRC = chksum_crc32((unsigned char*) partitions, sizeOfParts);
407 mainPartsCrcOk = (newCRC == mainHeader.partitionEntriesCRC);
srs56942a9f5da2009-08-26 00:48:01 -0400408 if (IsLittleEndian() == 0)
409 ReversePartitionBytes();
srs5694e7b4ff92009-08-18 13:16:10 -0400410 retval = 1;
411 } // if
412 return retval;
413} // GPTData::LoadMainTable()
414
415// Examines the MBR & GPT data, and perhaps asks the user questions, to
416// determine which set of data to use: the MBR (use_mbr), the GPT (use_gpt),
417// or create a new set of partitions (use_new)
418WhichToUse GPTData::UseWhichPartitions(void) {
419 WhichToUse which = use_new;
420 MBRValidity mbrState;
421 int answer;
422
423 mbrState = protectiveMBR.GetValidity();
424
srs56942a9f5da2009-08-26 00:48:01 -0400425 if ((state == gpt_invalid) && ((mbrState == mbr) || (mbrState == hybrid))) {
srs5694e7b4ff92009-08-18 13:16:10 -0400426 printf("\n\a***************************************************************\n"
srs5694221e0872009-08-29 15:00:31 -0400427 "Found invalid GPT and valid MBR; converting MBR to GPT format.\n"
srs5694e7b4ff92009-08-18 13:16:10 -0400428 "THIS OPERATON IS POTENTIALLY DESTRUCTIVE! Exit by typing 'q' if\n"
429 "you don't want to convert your MBR partitions to GPT format!\n"
430 "***************************************************************\n\n");
431 which = use_mbr;
432 } // if
srs56942a9f5da2009-08-26 00:48:01 -0400433
srs5694221e0872009-08-29 15:00:31 -0400434 if ((state == gpt_invalid) && bsdFound) {
435 printf("\n\a**********************************************************************\n"
436 "Found invalid GPT and valid BSD disklabel; converting BSD disklabel\n"
437 "to GPT format. THIS OPERATON IS POTENTIALLY DESTRUCTIVE! Your first\n"
438 "BSD partition will likely be unusable. Exit by typing 'q' if you don't\n"
439 "want to convert your BSD partitions to GPT format!\n"
440 "**********************************************************************\n\n");
441 which = use_bsd;
442 } // if
443
srs5694e7b4ff92009-08-18 13:16:10 -0400444 if ((state == gpt_valid) && (mbrState == gpt)) {
445 printf("Found valid GPT with protective MBR; using GPT.\n");
446 which = use_gpt;
447 } // if
srs56942a9f5da2009-08-26 00:48:01 -0400448 if ((state == gpt_valid) && (mbrState == hybrid)) {
449 printf("Found valid GPT with hybrid MBR; using GPT.\n");
srs5694221e0872009-08-29 15:00:31 -0400450 printf("\aIf you change GPT partitions, you may need to re-create the hybrid MBR!\n");
srs56942a9f5da2009-08-26 00:48:01 -0400451 which = use_gpt;
452 } // if
srs5694e7b4ff92009-08-18 13:16:10 -0400453 if ((state == gpt_valid) && (mbrState == invalid)) {
454 printf("\aFound valid GPT with corrupt MBR; using GPT and will create new\nprotective MBR on save.\n");
455 which = use_gpt;
456 protectiveMBR.MakeProtectiveMBR();
457 } // if
458 if ((state == gpt_valid) && (mbrState == mbr)) {
459 printf("Found valid MBR and GPT. Which do you want to use?\n");
460 answer = GetNumber(1, 3, 2, (char*) " 1 - MBR\n 2 - GPT\n 3 - Create blank GPT\n\nYour answer: ");
461 if (answer == 1) {
462 which = use_mbr;
463 } else if (answer == 2) {
464 which = use_gpt;
465 protectiveMBR.MakeProtectiveMBR();
466 printf("Using GPT and creating fresh protective MBR.\n");
467 } else which = use_new;
468 } // if
469
470 // Nasty decisions here -- GPT is present, but corrupt (bad CRCs or other
471 // problems)
472 if (state == gpt_corrupt) {
srs56942a9f5da2009-08-26 00:48:01 -0400473 if ((mbrState == mbr) || (mbrState == hybrid)) {
srs5694e7b4ff92009-08-18 13:16:10 -0400474 printf("Found valid MBR and corrupt GPT. Which do you want to use? (Using the\n"
475 "GPT MAY permit recovery of GPT data.)\n");
476 answer = GetNumber(1, 3, 2, (char*) " 1 - MBR\n 2 - GPT\n 3 - Create blank GPT\n\nYour answer: ");
477 if (answer == 1) {
478 which = use_mbr;
479// protectiveMBR.MakeProtectiveMBR();
480 } else if (answer == 2) {
481 which = use_gpt;
482 } else which = use_new;
483 } else if (mbrState == invalid) {
484 printf("Found invalid MBR and corrupt GPT. What do you want to do? (Using the\n"
485 "GPT MAY permit recovery of GPT data.)\n");
486 answer = GetNumber(1, 2, 1, (char*) " 1 - GPT\n 2 - Create blank GPT\n\nYour answer: ");
487 if (answer == 1) {
488 which = use_gpt;
489 } else which = use_new;
srs56942a9f5da2009-08-26 00:48:01 -0400490 } else { // corrupt GPT, MBR indicates it's a GPT disk....
srs5694e7b4ff92009-08-18 13:16:10 -0400491 printf("\a\a****************************************************************************\n"
492 "Caution: Found protective or hybrid MBR and corrupt GPT. Using GPT, but disk\n"
493 "verification and recovery are STRONGLY recommended.\n"
494 "****************************************************************************\n");
srs56942a9f5da2009-08-26 00:48:01 -0400495 } // if/else/else
496 } // if (corrupt GPT)
srs5694e7b4ff92009-08-18 13:16:10 -0400497
498 if (which == use_new)
499 printf("Creating new GPT entries.\n");
500
501 return which;
502} // UseWhichPartitions()
503
504void GPTData::ResizePartitionTable(void) {
505 int newSize;
506 char prompt[255];
507 uint32_t curLow, curHigh;
508
509 printf("Current partition table size is %lu.\n",
510 (unsigned long) mainHeader.numParts);
511 GetPartRange(&curLow, &curHigh);
512 curHigh++; // since GetPartRange() returns numbers starting from 0...
513 // There's no point in having fewer than four partitions....
514 if (curHigh < 4)
515 curHigh = 4;
516 sprintf(prompt, "Enter new size (%d up, default %d): ", (int) curHigh,
517 (int) NUM_GPT_ENTRIES);
518 newSize = GetNumber(4, 65535, 128, prompt);
519 if (newSize < 128) {
520 printf("Caution: The partition table size should officially be 16KB or larger,\n"
521 "which works out to 128 entries. In practice, smaller tables seem to\n"
522 "work with most OSes, but this practice is risky. I'm proceeding with\n"
523 "the resize, but you may want to reconsider this action and undo it.\n\n");
524 } // if
525 SetGPTSize(newSize);
526} // GPTData::ResizePartitionTable()
527
528// Find the low and high used partition numbers (numbered from 0).
529// Return value is the number of partitions found. Note that the
530// *low and *high values are both set to 0 when no partitions
531// are found, as well as when a single partition in the first
532// position exists. Thus, the return value is the only way to
533// tell when no partitions exist.
534int GPTData::GetPartRange(uint32_t *low, uint32_t *high) {
535 uint32_t i;
536 int numFound = 0;
537
538 *low = mainHeader.numParts + 1; // code for "not found"
539 *high = 0;
540 if (mainHeader.numParts > 0) { // only try if partition table exists...
541 for (i = 0; i < mainHeader.numParts; i++) {
srs5694221e0872009-08-29 15:00:31 -0400542 if (partitions[i].GetFirstLBA() != UINT64_C(0)) { // it exists
srs5694e7b4ff92009-08-18 13:16:10 -0400543 *high = i; // since we're counting up, set the high value
544 // Set the low value only if it's not yet found...
545 if (*low == (mainHeader.numParts + 1)) *low = i;
546 numFound++;
547 } // if
548 } // for
549 } // if
550
551 // Above will leave *low pointing to its "not found" value if no partitions
552 // are defined, so reset to 0 if this is the case....
553 if (*low == (mainHeader.numParts + 1))
554 *low = 0;
555 return numFound;
556} // GPTData::GetPartRange()
557
558// Display the basic GPT data
559void GPTData::DisplayGPTData(void) {
560 int i, j;
561 char sizeInSI[255]; // String to hold size of disk in SI units
562 char tempStr[255];
563 uint64_t temp, totalFree;
564
565 BytesToSI(diskSize * blockSize, sizeInSI);
566 printf("Disk %s: %lu sectors, %s\n", device,
567 (unsigned long) diskSize, sizeInSI);
568 printf("Disk identifier (GUID): %s\n", GUIDToStr(mainHeader.diskGUID, tempStr));
569 printf("Partition table holds up to %lu entries\n", (unsigned long) mainHeader.numParts);
570 printf("First usable sector is %lu, last usable sector is %lu\n",
571 (unsigned long) mainHeader.firstUsableLBA,
572 (unsigned long) mainHeader.lastUsableLBA);
573 totalFree = FindFreeBlocks(&i, &temp);
574 printf("Total free space is %llu sectors (%s)\n", totalFree,
575 BytesToSI(totalFree * (uint64_t) blockSize, sizeInSI));
srs5694e19ba092009-08-24 14:10:35 -0400576 printf("\nNumber Start (sector) End (sector) Size Code Name\n");
srs5694e7b4ff92009-08-18 13:16:10 -0400577 for (i = 0; i < mainHeader.numParts; i++) {
srs5694221e0872009-08-29 15:00:31 -0400578 partitions[i].ShowSummary(i, blockSize, sizeInSI);
srs5694e7b4ff92009-08-18 13:16:10 -0400579 } // for
580} // GPTData::DisplayGPTData()
581
582// Get partition number from user and then call ShowPartDetails(partNum)
583// to show its detailed information
584void GPTData::ShowDetails(void) {
585 int partNum;
586 uint32_t low, high;
587
588 if (GetPartRange(&low, &high) > 0) {
589 partNum = GetPartNum();
590 ShowPartDetails(partNum);
591 } else {
592 printf("No partitions\n");
593 } // if/else
594} // GPTData::ShowDetails()
595
596// Show detailed information on the specified partition
597void GPTData::ShowPartDetails(uint32_t partNum) {
srs5694221e0872009-08-29 15:00:31 -0400598 if (partitions[partNum].GetFirstLBA() != 0) {
599 partitions[partNum].ShowDetails(blockSize);
srs5694e7b4ff92009-08-18 13:16:10 -0400600 } else {
601 printf("Partition #%d does not exist.", (int) (partNum + 1));
602 } // if
603} // GPTData::ShowPartDetails()
604
605// Interactively create a partition
606void GPTData::CreatePartition(void) {
607 uint64_t firstBlock, lastBlock, sector;
608 char prompt[255];
609 int partNum, firstFreePart = 0;
610
611 // Find first free partition...
srs5694221e0872009-08-29 15:00:31 -0400612 while (partitions[firstFreePart].GetFirstLBA() != 0) {
srs5694e7b4ff92009-08-18 13:16:10 -0400613 firstFreePart++;
614 } // while
615
616 if (((firstBlock = FindFirstAvailable()) != 0) &&
617 (firstFreePart < mainHeader.numParts)) {
618 lastBlock = FindLastAvailable(firstBlock);
619
620 // Get partition number....
621 do {
622 sprintf(prompt, "Partition number (%d-%d, default %d): ", firstFreePart + 1,
623 mainHeader.numParts, firstFreePart + 1);
624 partNum = GetNumber(firstFreePart + 1, mainHeader.numParts,
625 firstFreePart + 1, prompt) - 1;
srs5694221e0872009-08-29 15:00:31 -0400626 if (partitions[partNum].GetFirstLBA() != 0)
srs5694e7b4ff92009-08-18 13:16:10 -0400627 printf("partition %d is in use.\n", partNum + 1);
srs5694221e0872009-08-29 15:00:31 -0400628 } while (partitions[partNum].GetFirstLBA() != 0);
srs5694e7b4ff92009-08-18 13:16:10 -0400629
630 // Get first block for new partition...
631 sprintf(prompt, "First sector (%llu-%llu, default = %llu): ", firstBlock,
632 lastBlock, firstBlock);
633 do {
634 sector = GetNumber(firstBlock, lastBlock, firstBlock, prompt);
635 } while (IsFree(sector) == 0);
636 firstBlock = sector;
637
638 // Get last block for new partitions...
639 lastBlock = FindLastInFree(firstBlock);
640 sprintf(prompt, "Last sector or +size or +sizeM or +sizeK (%llu-%llu, default = %d): ",
641 firstBlock, lastBlock, lastBlock);
642 do {
643 sector = GetLastSector(firstBlock, lastBlock, prompt);
644 } while (IsFree(sector) == 0);
645 lastBlock = sector;
646
srs5694221e0872009-08-29 15:00:31 -0400647 partitions[partNum].SetFirstLBA(firstBlock);
648 partitions[partNum].SetLastLBA(lastBlock);
srs5694e7b4ff92009-08-18 13:16:10 -0400649
srs5694221e0872009-08-29 15:00:31 -0400650 partitions[partNum].SetUniqueGUID(1);
651 partitions[partNum].ChangeType();
652 partitions[partNum].SetName((unsigned char*) partitions[partNum].GetNameType(prompt));
srs5694e7b4ff92009-08-18 13:16:10 -0400653 } else {
654 printf("No free sectors available\n");
655 } // if/else
656} // GPTData::CreatePartition()
657
658// Interactively delete a partition (duh!)
659void GPTData::DeletePartition(void) {
660 int partNum;
661 uint32_t low, high;
662 char prompt[255];
663
664 if (GetPartRange(&low, &high) > 0) {
665 sprintf(prompt, "Partition number (%d-%d): ", low + 1, high + 1);
666 partNum = GetNumber(low + 1, high + 1, low, prompt);
srs5694221e0872009-08-29 15:00:31 -0400667 partitions[partNum - 1].BlankPartition();
srs5694e7b4ff92009-08-18 13:16:10 -0400668 } else {
669 printf("No partitions\n");
670 } // if/else
srs5694221e0872009-08-29 15:00:31 -0400671} // GPTData::DeletePartition()
srs5694e7b4ff92009-08-18 13:16:10 -0400672
673// Find the first available block after the starting point; returns 0 if
674// there are no available blocks left
675uint64_t GPTData::FindFirstAvailable(uint64_t start) {
676 uint64_t first;
677 uint32_t i;
678 int firstMoved = 0;
679
680 // Begin from the specified starting point or from the first usable
681 // LBA, whichever is greater...
682 if (start < mainHeader.firstUsableLBA)
683 first = mainHeader.firstUsableLBA;
684 else
685 first = start;
686
687 // ...now search through all partitions; if first is within an
688 // existing partition, move it to the next sector after that
689 // partition and repeat. If first was moved, set firstMoved
690 // flag; repeat until firstMoved is not set, so as to catch
691 // cases where partitions are out of sequential order....
692 do {
693 firstMoved = 0;
694 for (i = 0; i < mainHeader.numParts; i++) {
srs5694221e0872009-08-29 15:00:31 -0400695 if ((first >= partitions[i].GetFirstLBA()) &&
696 (first <= partitions[i].GetLastLBA())) { // in existing part.
697 first = partitions[i].GetLastLBA() + 1;
srs5694e7b4ff92009-08-18 13:16:10 -0400698 firstMoved = 1;
699 } // if
700 } // for
701 } while (firstMoved == 1);
702 if (first > mainHeader.lastUsableLBA)
703 first = 0;
704 return (first);
705} // GPTData::FindFirstAvailable()
706
707// Find the last available block on the disk at or after the start
708// block. Returns 0 if there are no available partitions after
709// (or including) start.
710uint64_t GPTData::FindLastAvailable(uint64_t start) {
711 uint64_t last;
712 uint32_t i;
713 int lastMoved = 0;
714
715 // Start by assuming the last usable LBA is available....
716 last = mainHeader.lastUsableLBA;
717
718 // ...now, similar to algorithm in FindFirstAvailable(), search
719 // through all partitions, moving last when it's in an existing
720 // partition. Set the lastMoved flag so we repeat to catch cases
721 // where partitions are out of logical order.
722 do {
723 lastMoved = 0;
724 for (i = 0; i < mainHeader.numParts; i++) {
srs5694221e0872009-08-29 15:00:31 -0400725 if ((last >= partitions[i].GetFirstLBA()) &&
726 (last <= partitions[i].GetLastLBA())) { // in existing part.
727 last = partitions[i].GetFirstLBA() - 1;
srs5694e7b4ff92009-08-18 13:16:10 -0400728 lastMoved = 1;
729 } // if
730 } // for
731 } while (lastMoved == 1);
732 if (last < mainHeader.firstUsableLBA)
733 last = 0;
734 return (last);
735} // GPTData::FindLastAvailable()
736
737// Find the last available block in the free space pointed to by start.
738uint64_t GPTData::FindLastInFree(uint64_t start) {
739 uint64_t nearestStart;
740 uint32_t i;
741
742 nearestStart = mainHeader.lastUsableLBA;
743 for (i = 0; i < mainHeader.numParts; i++) {
srs5694221e0872009-08-29 15:00:31 -0400744 if ((nearestStart > partitions[i].GetFirstLBA()) &&
745 (partitions[i].GetFirstLBA() > start)) {
746 nearestStart = partitions[i].GetFirstLBA() - 1;
srs5694e7b4ff92009-08-18 13:16:10 -0400747 } // if
748 } // for
749 return (nearestStart);
750} // GPTData::FindLastInFree()
751
752// Returns 1 if sector is unallocated, 0 if it's allocated to a partition
753int GPTData::IsFree(uint64_t sector) {
754 int isFree = 1;
755 uint32_t i;
756
757 for (i = 0; i < mainHeader.numParts; i++) {
srs5694221e0872009-08-29 15:00:31 -0400758 if ((sector >= partitions[i].GetFirstLBA()) &&
759 (sector <= partitions[i].GetLastLBA())) {
srs5694e7b4ff92009-08-18 13:16:10 -0400760 isFree = 0;
761 } // if
762 } // for
763 if ((sector < mainHeader.firstUsableLBA) ||
764 (sector > mainHeader.lastUsableLBA)) {
765 isFree = 0;
766 } // if
767 return (isFree);
768} // GPTData::IsFree()
769
srs5694221e0872009-08-29 15:00:31 -0400770int GPTData::XFormPartitions(void) {
771 int i, numToConvert;
srs5694e7b4ff92009-08-18 13:16:10 -0400772 uint8_t origType;
srs5694221e0872009-08-29 15:00:31 -0400773 struct newGUID;
774 char name[NAME_SIZE];
srs5694e7b4ff92009-08-18 13:16:10 -0400775
776 // Clear out old data & prepare basics....
777 ClearGPTData();
778
779 // Convert the smaller of the # of GPT or MBR partitions
780 if (mainHeader.numParts > (NUM_LOGICALS + 4))
781 numToConvert = NUM_LOGICALS + 4;
782 else
783 numToConvert = mainHeader.numParts;
784
srs5694e7b4ff92009-08-18 13:16:10 -0400785 for (i = 0; i < numToConvert; i++) {
srs5694221e0872009-08-29 15:00:31 -0400786 origType = protectiveMBR.GetType(i);
787 // don't waste CPU time trying to convert extended, hybrid protective, or
788 // null (non-existent) partitions
srs569486dd7842009-08-26 14:39:40 -0400789 if ((origType != 0x05) && (origType != 0x0f) && (origType != 0x85) &&
srs5694221e0872009-08-29 15:00:31 -0400790 (origType != 0x00) && (origType != 0xEE))
791 partitions[i] = protectiveMBR.AsGPT(i);
srs5694e7b4ff92009-08-18 13:16:10 -0400792 } // for
793
794 // Convert MBR into protective MBR
795 protectiveMBR.MakeProtectiveMBR();
796
797 // Record that all original CRCs were OK so as not to raise flags
798 // when doing a disk verification
799 mainCrcOk = secondCrcOk = mainPartsCrcOk = secondPartsCrcOk = 1;
800
801 return (1);
srs5694221e0872009-08-29 15:00:31 -0400802} // GPTData::XFormPartitions()
803
804// Transforms BSD disklable on the specified partition (numbered from 0).
805// If an invalid partition number is given, the program prompts for one.
806// Returns the number of new partitions created.
807int GPTData::XFormDisklabel(int i) {
808 uint32_t low, high, partNum, startPart;
809 uint16_t hexCode;
810 int goOn = 1, numDone = 0;
811 BSDData disklabel;
812
813 if (GetPartRange(&low, &high) != 0) {
814 if ((i < low) || (i > high))
815 partNum = GetPartNum();
816 else
817 partNum = (uint32_t) i;
818
819 // Find the partition after the last used one
820 startPart = high + 1;
821
822 // Now see if the specified partition has a BSD type code....
823 hexCode = partitions[partNum].GetHexType();
824 if ((hexCode != 0xa500) && (hexCode != 0xa900)) {
825 printf("Specified partition doesn't have a disklabel partition type "
826 "code.\nContinue anyway?");
827 goOn = (GetYN() == 'Y');
828 } // if
829
830 // If all is OK, read the disklabel and convert it.
831 if (goOn) {
832 goOn = disklabel.ReadBSDData(device, partitions[partNum].GetFirstLBA(),
833 partitions[partNum].GetLastLBA());
834 if ((goOn) && (disklabel.IsDisklabel())) {
835 numDone = XFormDisklabel(&disklabel, startPart);
836 if (numDone == 1)
837 printf("Converted %d BSD partition.\n", numDone);
838 else
839 printf("Converted %d BSD partitions.\n", numDone);
840 } else {
841 printf("Unable to convert partitions! Unrecognized BSD disklabel.\n");
842 } // if/else
843 } // if
844 if (numDone > 0) { // converted partitions; delete carrier
845 partitions[partNum].BlankPartition();
846 } // if
847 } else {
848 printf("No partitions\n");
849 } // if/else
850 return numDone;
851} // GPTData::XFormDisklable(int i)
852
853// Transform the partitions on an already-loaded BSD disklabel...
854int GPTData::XFormDisklabel(BSDData* disklabel, int startPart) {
855 int i, numDone = 0;
856
857 if ((disklabel->IsDisklabel()) && (startPart >= 0) &&
858 (startPart < mainHeader.numParts)) {
859 for (i = 0; i < disklabel->GetNumParts(); i++) {
860 partitions[i + startPart] = disklabel->AsGPT(i);
861 if (partitions[i + startPart].GetFirstLBA() != UINT64_C(0))
862 numDone++;
863 } // for
864 } // if
865
866 // Record that all original CRCs were OK so as not to raise flags
867 // when doing a disk verification
868 mainCrcOk = secondCrcOk = mainPartsCrcOk = secondPartsCrcOk = 1;
869
870 return numDone;
871} // GPTData::XFormDisklabel(BSDData* disklabel)
srs5694e7b4ff92009-08-18 13:16:10 -0400872
873// Sort the GPT entries, eliminating gaps and making for a logical
874// ordering. Relies on QuickSortGPT() for the bulk of the work
875void GPTData::SortGPT(void) {
876 int i, lastPart = 0;
srs5694221e0872009-08-29 15:00:31 -0400877 GPTPart temp;
srs5694e7b4ff92009-08-18 13:16:10 -0400878
879 // First, find the last partition with data, so as not to
880 // spend needless time sorting empty entries....
srs5694221e0872009-08-29 15:00:31 -0400881 for (i = 0; i < mainHeader.numParts; i++) {
882 if (partitions[i].GetFirstLBA() > 0)
srs5694e7b4ff92009-08-18 13:16:10 -0400883 lastPart = i;
884 } // for
885
886 // Now swap empties with the last partitions, to simplify the logic
887 // in the Quicksort function....
888 i = 0;
889 while (i < lastPart) {
srs5694221e0872009-08-29 15:00:31 -0400890 if (partitions[i].GetFirstLBA() == 0) {
srs5694e7b4ff92009-08-18 13:16:10 -0400891 temp = partitions[i];
892 partitions[i] = partitions[lastPart];
893 partitions[lastPart] = temp;
894 lastPart--;
895 } // if
896 i++;
897 } // while
898
899 // Now call the recursive quick sort routine to do the real work....
900 QuickSortGPT(partitions, 0, lastPart);
901} // GPTData::SortGPT()
902
srs5694e7b4ff92009-08-18 13:16:10 -0400903// Blank the partition array
904void GPTData::BlankPartitions(void) {
905 uint32_t i;
906
907 for (i = 0; i < mainHeader.numParts; i++) {
srs5694221e0872009-08-29 15:00:31 -0400908 partitions[i].BlankPartition();
srs5694e7b4ff92009-08-18 13:16:10 -0400909 } // for
910} // GPTData::BlankPartitions()
911
912// Set up data structures for entirely new set of partitions on the
913// specified device. Returns 1 if OK, 0 if there were problems.
914int GPTData::ClearGPTData(void) {
915 int goOn, i;
916
917 // Set up the partition table....
918 free(partitions);
919 partitions = NULL;
920 SetGPTSize(NUM_GPT_ENTRIES);
921
922 // Now initialize a bunch of stuff that's static....
923 mainHeader.signature = GPT_SIGNATURE;
924 mainHeader.revision = 0x00010000;
925 mainHeader.headerSize = (uint32_t) HEADER_SIZE;
926 mainHeader.reserved = 0;
927 mainHeader.currentLBA = UINT64_C(1);
928 mainHeader.partitionEntriesLBA = (uint64_t) 2;
929 mainHeader.sizeOfPartitionEntries = GPT_SIZE;
930 for (i = 0; i < GPT_RESERVED; i++) {
931 mainHeader.reserved2[i] = '\0';
932 } // for
933
934 // Now some semi-static items (computed based on end of disk)
935 mainHeader.backupLBA = diskSize - UINT64_C(1);
936 mainHeader.lastUsableLBA = diskSize - mainHeader.firstUsableLBA;
937
938 // Set a unique GUID for the disk, based on random numbers
939 // rand() is only 32 bits, so multiply together to fill a 64-bit value
940 mainHeader.diskGUID.data1 = (uint64_t) rand() * (uint64_t) rand();
941 mainHeader.diskGUID.data2 = (uint64_t) rand() * (uint64_t) rand();
942
943 // Copy main header to backup header
944 RebuildSecondHeader();
945
946 // Blank out the partitions array....
947 BlankPartitions();
srs5694221e0872009-08-29 15:00:31 -0400948
949 // Flag all CRCs as being OK....
950 mainCrcOk = 1;
951 secondCrcOk = 1;
952 mainPartsCrcOk = 1;
953 secondPartsCrcOk = 1;
954
srs5694e7b4ff92009-08-18 13:16:10 -0400955 return (goOn);
956} // GPTData::ClearGPTData()
957
srs5694e7b4ff92009-08-18 13:16:10 -0400958// Prompt user for a partition number, then change its type code
959// using ChangeGPTType(struct GPTPartition*) function.
960void GPTData::ChangePartType(void) {
961 int partNum;
962 uint32_t low, high;
963
964 if (GetPartRange(&low, &high) > 0) {
965 partNum = GetPartNum();
srs5694221e0872009-08-29 15:00:31 -0400966 partitions[partNum].ChangeType();
srs5694e7b4ff92009-08-18 13:16:10 -0400967 } else {
968 printf("No partitions\n");
969 } // if/else
970} // GPTData::ChangePartType()
971
972// Prompts user for partition number and returns the result.
973uint32_t GPTData::GetPartNum(void) {
974 uint32_t partNum;
975 uint32_t low, high;
976 char prompt[255];
977
978 if (GetPartRange(&low, &high) > 0) {
979 sprintf(prompt, "Partition number (%d-%d): ", low + 1, high + 1);
980 partNum = GetNumber(low + 1, high + 1, low, prompt);
981 } else partNum = 1;
982 return (partNum - 1);
983} // GPTData::GetPartNum()
984
srs5694e7b4ff92009-08-18 13:16:10 -0400985void GPTData::SetAttributes(uint32_t partNum) {
986 Attributes theAttr;
987
srs5694221e0872009-08-29 15:00:31 -0400988 theAttr.SetAttributes(partitions[partNum].GetAttributes());
srs5694e7b4ff92009-08-18 13:16:10 -0400989 theAttr.DisplayAttributes();
990 theAttr.ChangeAttributes();
srs5694221e0872009-08-29 15:00:31 -0400991 partitions[partNum].SetAttributes(theAttr.GetAttributes());
srs5694e7b4ff92009-08-18 13:16:10 -0400992} // GPTData::SetAttributes()
993
srs5694e7b4ff92009-08-18 13:16:10 -0400994void GPTData::SetName(uint32_t partNum, char* theName) {
srs5694221e0872009-08-29 15:00:31 -0400995 if ((partNum >= 0) && (partNum < mainHeader.numParts))
996 if (partitions[partNum].GetFirstLBA() > 0)
997 partitions[partNum].SetName((unsigned char*) theName);
998} // GPTData::SetName
srs5694e7b4ff92009-08-18 13:16:10 -0400999
1000// Set the disk GUID to the specified value. Note that the header CRCs must
1001// be recomputed after calling this function.
1002void GPTData::SetDiskGUID(GUIDData newGUID) {
1003 mainHeader.diskGUID = newGUID;
1004 secondHeader.diskGUID = newGUID;
1005} // SetDiskGUID()
1006
1007// Set the unique GUID of the specified partition. Returns 1 on
1008// successful completion, 0 if there were problems (invalid
1009// partition number).
1010int GPTData::SetPartitionGUID(uint32_t pn, GUIDData theGUID) {
1011 int retval = 0;
1012
1013 if (pn < mainHeader.numParts) {
srs5694221e0872009-08-29 15:00:31 -04001014 if (partitions[pn].GetFirstLBA() != UINT64_C(0)) {
1015 partitions[pn].SetUniqueGUID(theGUID);
srs5694e7b4ff92009-08-18 13:16:10 -04001016 retval = 1;
1017 } // if
1018 } // if
1019 return retval;
1020} // GPTData::SetPartitionGUID()
1021
1022// Check the validity of the GPT header. Returns 1 if the main header
1023// is valid, 2 if the backup header is valid, 3 if both are valid, and
1024// 0 if neither is valid. Note that this function just checks the GPT
1025// signature and revision numbers, not CRCs or other data.
1026int GPTData::CheckHeaderValidity(void) {
1027 int valid = 3;
1028
1029 if (mainHeader.signature != GPT_SIGNATURE) {
1030 valid -= 1;
srs5694221e0872009-08-29 15:00:31 -04001031// printf("Main GPT signature invalid; read 0x%016llX, should be\n0x%016llX\n",
1032// (unsigned long long) mainHeader.signature, (unsigned long long) GPT_SIGNATURE);
srs5694e7b4ff92009-08-18 13:16:10 -04001033 } else if ((mainHeader.revision != 0x00010000) && valid) {
1034 valid -= 1;
1035 printf("Unsupported GPT version in main header; read 0x%08lX, should be\n0x%08lX\n",
1036 (unsigned long) mainHeader.revision, UINT32_C(0x00010000));
1037 } // if/else/if
1038
1039 if (secondHeader.signature != GPT_SIGNATURE) {
1040 valid -= 2;
srs5694221e0872009-08-29 15:00:31 -04001041// printf("Secondary GPT signature invalid; read 0x%016llX, should be\n0x%016llX\n",
1042// (unsigned long long) secondHeader.signature, (unsigned long long) GPT_SIGNATURE);
srs5694e7b4ff92009-08-18 13:16:10 -04001043 } else if ((secondHeader.revision != 0x00010000) && valid) {
1044 valid -= 2;
1045 printf("Unsupported GPT version in backup header; read 0x%08lX, should be\n0x%08lX\n",
1046 (unsigned long) mainHeader.revision, UINT32_C(0x00010000));
1047 } // if/else/if
1048
srs56942a9f5da2009-08-26 00:48:01 -04001049 // If MBR bad, check for an Apple disk signature
1050 if ((protectiveMBR.GetValidity() == invalid) &&
1051 (((mainHeader.signature << 32) == APM_SIGNATURE1) ||
1052 (mainHeader.signature << 32) == APM_SIGNATURE2)) {
srs5694221e0872009-08-29 15:00:31 -04001053 apmFound = 1; // Will display warning message later
srs56942a9f5da2009-08-26 00:48:01 -04001054 } // if
1055
srs5694e7b4ff92009-08-18 13:16:10 -04001056 return valid;
1057} // GPTData::CheckHeaderValidity()
1058
1059// Check the header CRC to see if it's OK...
srs56942a9f5da2009-08-26 00:48:01 -04001060// Note: Must be called BEFORE byte-order reversal on big-endian
1061// systems!
srs5694e7b4ff92009-08-18 13:16:10 -04001062int GPTData::CheckHeaderCRC(struct GPTHeader* header) {
1063 uint32_t oldCRC, newCRC;
1064
srs56942a9f5da2009-08-26 00:48:01 -04001065 // Back up old header CRC and then blank it, since it must be 0 for
srs5694e7b4ff92009-08-18 13:16:10 -04001066 // computation to be valid
1067 oldCRC = header->headerCRC;
srs56942a9f5da2009-08-26 00:48:01 -04001068 if (IsLittleEndian() == 0)
srs5694221e0872009-08-29 15:00:31 -04001069 ReverseBytes(&oldCRC, 4);
srs5694e7b4ff92009-08-18 13:16:10 -04001070 header->headerCRC = UINT32_C(0);
1071
1072 // Initialize CRC functions...
1073 chksum_crc32gentab();
1074
1075 // Compute CRC, restore original, and return result of comparison
1076 newCRC = chksum_crc32((unsigned char*) header, HEADER_SIZE);
1077 mainHeader.headerCRC = oldCRC;
1078 return (oldCRC == newCRC);
1079} // GPTData::CheckHeaderCRC()
1080
srs56942a9f5da2009-08-26 00:48:01 -04001081// Recompute all the CRCs. Must be called before saving (but after reversing
1082// byte order on big-endian systems) if any changes have been made.
srs5694e7b4ff92009-08-18 13:16:10 -04001083void GPTData::RecomputeCRCs(void) {
1084 uint32_t crc;
srs56942a9f5da2009-08-26 00:48:01 -04001085 uint32_t trueNumParts, crcTemp;
1086 int littleEndian = 1;
srs5694e7b4ff92009-08-18 13:16:10 -04001087
1088 // Initialize CRC functions...
1089 chksum_crc32gentab();
1090
srs56942a9f5da2009-08-26 00:48:01 -04001091 littleEndian = IsLittleEndian();
1092
srs5694e7b4ff92009-08-18 13:16:10 -04001093 // Compute CRC of partition tables & store in main and secondary headers
srs56942a9f5da2009-08-26 00:48:01 -04001094 trueNumParts = mainHeader.numParts;
1095 if (littleEndian == 0)
srs5694221e0872009-08-29 15:00:31 -04001096 ReverseBytes(&trueNumParts, 4); // unreverse this key piece of data....
srs56942a9f5da2009-08-26 00:48:01 -04001097 crc = chksum_crc32((unsigned char*) partitions, trueNumParts * GPT_SIZE);
srs5694e7b4ff92009-08-18 13:16:10 -04001098 mainHeader.partitionEntriesCRC = crc;
1099 secondHeader.partitionEntriesCRC = crc;
srs56942a9f5da2009-08-26 00:48:01 -04001100 if (littleEndian == 0) {
srs5694221e0872009-08-29 15:00:31 -04001101 ReverseBytes(&mainHeader.partitionEntriesCRC, 4);
1102 ReverseBytes(&secondHeader.partitionEntriesCRC, 4);
srs56942a9f5da2009-08-26 00:48:01 -04001103 } // if
srs5694e7b4ff92009-08-18 13:16:10 -04001104
1105 // Zero out GPT tables' own CRCs (required for correct computation)
1106 mainHeader.headerCRC = 0;
1107 secondHeader.headerCRC = 0;
1108
1109 // Compute & store CRCs of main & secondary headers...
1110 crc = chksum_crc32((unsigned char*) &mainHeader, HEADER_SIZE);
srs56942a9f5da2009-08-26 00:48:01 -04001111 if (littleEndian == 0)
srs5694221e0872009-08-29 15:00:31 -04001112 ReverseBytes(&crc, 4);
srs5694e7b4ff92009-08-18 13:16:10 -04001113 mainHeader.headerCRC = crc;
1114 crc = chksum_crc32((unsigned char*) &secondHeader, HEADER_SIZE);
srs56942a9f5da2009-08-26 00:48:01 -04001115 if (littleEndian == 0)
srs5694221e0872009-08-29 15:00:31 -04001116 ReverseBytes(&crc, 4);
srs5694e7b4ff92009-08-18 13:16:10 -04001117 secondHeader.headerCRC = crc;
1118} // GPTData::RecomputeCRCs()
1119
1120// Perform detailed verification, reporting on any problems found, but
1121// do *NOT* recover from these problems. Returns the total number of
1122// problems identified.
1123int GPTData::Verify(void) {
1124 int problems = 0, numSegments, i, j;
1125 uint64_t totalFree, largestSegment;
1126 char tempStr[255], siTotal[255], siLargest[255];
1127
1128 // First, check for CRC errors in the GPT data....
1129 if (!mainCrcOk) {
1130 problems++;
1131 printf("\nProblem: The CRC for the main GPT header is invalid. The main GPT header may\n"
1132 "be corrupt. Consider loading the backup GPT header to rebuild the main GPT\n"
1133 "header\n");
1134 } // if
1135 if (!mainPartsCrcOk) {
1136 problems++;
1137 printf("\nProblem: The CRC for the main partition table is invalid. This table may be\n"
1138 "corrupt. Consider loading the backup partition table.\n");
1139 } // if
1140 if (!secondCrcOk) {
1141 problems++;
1142 printf("\nProblem: The CRC for the backup GPT header is invalid. The backup GPT header\n"
1143 "may be corrupt. Consider using the main GPT header to rebuild the backup GPT\n"
1144 "header.\n");
1145 } // if
1146 if (!secondPartsCrcOk) {
1147 problems++;
1148 printf("\nCaution: The CRC for the backup partition table is invalid. This table may\n"
1149 "be corrupt. This program will automatically create a new backup partition\n"
1150 "table when you save your partitions.\n");
1151 } // if
1152
1153 // Now check that critical main and backup GPT entries match
1154 if (mainHeader.currentLBA != secondHeader.backupLBA) {
1155 problems++;
1156 printf("\nProblem: main GPT header's current LBA pointer (%llu) doesn't\n"
1157 "match the backup GPT header's LBA pointer(%llu)\n",
1158 (unsigned long long) mainHeader.currentLBA,
1159 (unsigned long long) secondHeader.backupLBA);
1160 } // if
1161 if (mainHeader.backupLBA != secondHeader.currentLBA) {
1162 problems++;
1163 printf("\nProblem: main GPT header's backup LBA pointer (%llu) doesn't\n"
1164 "match the backup GPT header's current LBA pointer (%llu)\n",
1165 (unsigned long long) mainHeader.backupLBA,
1166 (unsigned long long) secondHeader.currentLBA);
1167 } // if
1168 if (mainHeader.firstUsableLBA != secondHeader.firstUsableLBA) {
1169 problems++;
1170 printf("\nProblem: main GPT header's first usable LBA pointer (%llu) doesn't\n"
1171 "match the backup GPT header's first usable LBA pointer (%llu)\n",
1172 (unsigned long long) mainHeader.firstUsableLBA,
1173 (unsigned long long) secondHeader.firstUsableLBA);
1174 } // if
1175 if (mainHeader.lastUsableLBA != secondHeader.lastUsableLBA) {
1176 problems++;
1177 printf("\nProblem: main GPT header's last usable LBA pointer (%llu) doesn't\n"
1178 "match the backup GPT header's last usable LBA pointer (%llu)\n",
1179 (unsigned long long) mainHeader.lastUsableLBA,
1180 (unsigned long long) secondHeader.lastUsableLBA);
1181 } // if
1182 if ((mainHeader.diskGUID.data1 != secondHeader.diskGUID.data1) ||
1183 (mainHeader.diskGUID.data2 != secondHeader.diskGUID.data2)) {
1184 problems++;
1185 printf("\nProblem: main header's disk GUID (%s) doesn't\n",
1186 GUIDToStr(mainHeader.diskGUID, tempStr));
1187 printf("match the backup GPT header's disk GUID (%s)\n",
1188 GUIDToStr(secondHeader.diskGUID, tempStr));
1189 } // if
1190 if (mainHeader.numParts != secondHeader.numParts) {
1191 problems++;
1192 printf("\nProblem: main GPT header's number of partitions (%lu) doesn't\n"
1193 "match the backup GPT header's number of partitions (%lu)\n",
1194 (unsigned long) mainHeader.numParts,
1195 (unsigned long) secondHeader.numParts);
1196 } // if
1197 if (mainHeader.sizeOfPartitionEntries != secondHeader.sizeOfPartitionEntries) {
1198 problems++;
1199 printf("\nProblem: main GPT header's size of partition entries (%lu) doesn't\n"
1200 "match the backup GPT header's size of partition entries (%lu)\n",
1201 (unsigned long) mainHeader.sizeOfPartitionEntries,
1202 (unsigned long) secondHeader.sizeOfPartitionEntries);
1203 } // if
1204
1205 // Now check for a few other miscellaneous problems...
1206 // Check that the disk size will hold the data...
1207 if (mainHeader.backupLBA > diskSize) {
1208 problems++;
1209 printf("\nProblem: Disk is too small to hold all the data!\n");
1210 printf("(Disk size is %llu sectors, needs to be %llu sectors.)\n",
1211 (unsigned long long) diskSize,
1212 (unsigned long long) mainHeader.backupLBA);
1213 } // if
1214
1215 // Check for overlapping partitions....
1216 for (i = 1; i < mainHeader.numParts; i++) {
1217 for (j = 0; j < i; j++) {
srs5694221e0872009-08-29 15:00:31 -04001218 if (partitions[i].DoTheyOverlap(&partitions[j])) {
srs5694e7b4ff92009-08-18 13:16:10 -04001219 problems++;
1220 printf("\nProblem: partitions %d and %d overlap:\n", i + 1, j + 1);
1221 printf(" Partition %d: %llu to %llu\n", i,
srs5694221e0872009-08-29 15:00:31 -04001222 (unsigned long long) partitions[i].GetFirstLBA(),
1223 (unsigned long long) partitions[i].GetLastLBA());
srs5694e7b4ff92009-08-18 13:16:10 -04001224 printf(" Partition %d: %llu to %llu\n", j,
srs5694221e0872009-08-29 15:00:31 -04001225 (unsigned long long) partitions[j].GetFirstLBA(),
1226 (unsigned long long) partitions[j].GetLastLBA());
srs5694e7b4ff92009-08-18 13:16:10 -04001227 } // if
1228 } // for j...
1229 } // for i...
1230
srs5694221e0872009-08-29 15:00:31 -04001231 // Verify that partitions don't run into GPT data areas....
1232 problems += CheckGPTSize();
1233
srs5694e7b4ff92009-08-18 13:16:10 -04001234 // Now compute available space, but only if no problems found, since
1235 // problems could affect the results
1236 if (problems == 0) {
1237 totalFree = FindFreeBlocks(&numSegments, &largestSegment);
1238 BytesToSI(totalFree * (uint64_t) blockSize, siTotal);
1239 BytesToSI(largestSegment * (uint64_t) blockSize, siLargest);
1240 printf("No problems found. %llu free sectors (%s) available in %u\n"
1241 "segments, the largest of which is %llu sectors (%s) in size\n",
1242 (unsigned long long) totalFree,
1243 siTotal, numSegments, (unsigned long long) largestSegment,
1244 siLargest);
1245 } else {
1246 printf("\nIdentified %d problems!\n", problems);
1247 } // if/else
1248
1249 return (problems);
1250} // GPTData::Verify()
1251
1252// Rebuild the main GPT header, using the secondary header as a model.
1253// Typically called when the main header has been found to be corrupt.
1254void GPTData::RebuildMainHeader(void) {
1255 int i;
1256
1257 mainHeader.signature = GPT_SIGNATURE;
1258 mainHeader.revision = secondHeader.revision;
1259 mainHeader.headerSize = HEADER_SIZE;
1260 mainHeader.headerCRC = UINT32_C(0);
1261 mainHeader.reserved = secondHeader.reserved;
1262 mainHeader.currentLBA = secondHeader.backupLBA;
1263 mainHeader.backupLBA = secondHeader.currentLBA;
1264 mainHeader.firstUsableLBA = secondHeader.firstUsableLBA;
1265 mainHeader.lastUsableLBA = secondHeader.lastUsableLBA;
1266 mainHeader.diskGUID.data1 = secondHeader.diskGUID.data1;
1267 mainHeader.diskGUID.data2 = secondHeader.diskGUID.data2;
1268 mainHeader.partitionEntriesLBA = UINT64_C(2);
1269 mainHeader.numParts = secondHeader.numParts;
1270 mainHeader.sizeOfPartitionEntries = secondHeader.sizeOfPartitionEntries;
1271 mainHeader.partitionEntriesCRC = secondHeader.partitionEntriesCRC;
1272 for (i = 0 ; i < GPT_RESERVED; i++)
1273 mainHeader.reserved2[i] = secondHeader.reserved2[i];
1274} // GPTData::RebuildMainHeader()
1275
1276// Rebuild the secondary GPT header, using the main header as a model.
1277void GPTData::RebuildSecondHeader(void) {
1278 int i;
1279
1280 secondHeader.signature = GPT_SIGNATURE;
1281 secondHeader.revision = mainHeader.revision;
1282 secondHeader.headerSize = HEADER_SIZE;
1283 secondHeader.headerCRC = UINT32_C(0);
1284 secondHeader.reserved = mainHeader.reserved;
1285 secondHeader.currentLBA = mainHeader.backupLBA;
1286 secondHeader.backupLBA = mainHeader.currentLBA;
1287 secondHeader.firstUsableLBA = mainHeader.firstUsableLBA;
1288 secondHeader.lastUsableLBA = mainHeader.lastUsableLBA;
1289 secondHeader.diskGUID.data1 = mainHeader.diskGUID.data1;
1290 secondHeader.diskGUID.data2 = mainHeader.diskGUID.data2;
1291 secondHeader.partitionEntriesLBA = secondHeader.lastUsableLBA + UINT64_C(1);
1292 secondHeader.numParts = mainHeader.numParts;
1293 secondHeader.sizeOfPartitionEntries = mainHeader.sizeOfPartitionEntries;
1294 secondHeader.partitionEntriesCRC = mainHeader.partitionEntriesCRC;
1295 for (i = 0 ; i < GPT_RESERVED; i++)
1296 secondHeader.reserved2[i] = mainHeader.reserved2[i];
1297} // RebuildSecondHeader()
1298
1299// Load the second (backup) partition table as the primary partition
1300// table. Used in repair functions
1301void GPTData::LoadSecondTableAsMain(void) {
1302 int fd;
1303 off_t seekTo;
1304 uint32_t sizeOfParts, newCRC;
1305
1306 if ((fd = open(device, O_RDONLY)) != -1) {
1307 seekTo = secondHeader.partitionEntriesLBA * (off_t) blockSize;
1308 if (lseek64(fd, seekTo, SEEK_SET) != (off_t) -1) {
1309 SetGPTSize(secondHeader.numParts);
1310 sizeOfParts = secondHeader.numParts * secondHeader.sizeOfPartitionEntries;
1311 read(fd, partitions, sizeOfParts);
1312 newCRC = chksum_crc32((unsigned char*) partitions, sizeOfParts);
1313 secondPartsCrcOk = (newCRC == secondHeader.partitionEntriesCRC);
1314 mainPartsCrcOk = secondPartsCrcOk;
srs56942a9f5da2009-08-26 00:48:01 -04001315 if (IsLittleEndian() == 0)
1316 ReversePartitionBytes();
srs5694e7b4ff92009-08-18 13:16:10 -04001317 if (!secondPartsCrcOk) {
1318 printf("Error! After loading backup partitions, the CRC still doesn't check out!\n");
1319 } // if
1320 } else {
1321 printf("Error! Couldn't seek to backup partition table!\n");
1322 } // if/else
1323 } else {
1324 printf("Error! Couldn't open device %s when recovering backup partition table!\n");
1325 } // if/else
1326} // GPTData::LoadSecondTableAsMain()
1327
1328// Finds the total number of free blocks, the number of segments in which
1329// they reside, and the size of the largest of those segments
1330uint64_t GPTData::FindFreeBlocks(int *numSegments, uint64_t *largestSegment) {
1331 uint64_t start = UINT64_C(0); // starting point for each search
1332 uint64_t totalFound = UINT64_C(0); // running total
1333 uint64_t firstBlock; // first block in a segment
1334 uint64_t lastBlock; // last block in a segment
1335 uint64_t segmentSize; // size of segment in blocks
1336 int num = 0;
1337
1338 *largestSegment = UINT64_C(0);
1339 do {
1340 firstBlock = FindFirstAvailable(start);
1341 if (firstBlock != UINT64_C(0)) { // something's free...
1342 lastBlock = FindLastInFree(firstBlock);
1343 segmentSize = lastBlock - firstBlock + UINT64_C(1);
1344 if (segmentSize > *largestSegment) {
1345 *largestSegment = segmentSize;
1346 } // if
1347 totalFound += segmentSize;
1348 num++;
1349 start = lastBlock + 1;
1350 } // if
1351 } while (firstBlock != 0);
1352 *numSegments = num;
1353 return totalFound;
1354} // GPTData::FindFreeBlocks()
1355
srs5694e7b4ff92009-08-18 13:16:10 -04001356// Create a hybrid MBR -- an ugly, funky thing that helps GPT work with
1357// OSes that don't understand GPT.
1358void GPTData::MakeHybrid(void) {
1359 uint32_t partNums[3];
1360 char line[255];
srs5694e19ba092009-08-24 14:10:35 -04001361 int numParts, i, j, typeCode, bootable, mbrNum;
srs5694e7b4ff92009-08-18 13:16:10 -04001362 uint64_t length;
srs5694c0ca8f82009-08-20 21:35:25 -04001363 char fillItUp = 'M'; // fill extra partition entries? (Yes/No/Maybe)
srs5694e19ba092009-08-24 14:10:35 -04001364 char eeFirst; // Whether EFI GPT (0xEE) partition comes first in table
srs5694e7b4ff92009-08-18 13:16:10 -04001365
srs5694c0ca8f82009-08-20 21:35:25 -04001366 printf("\nWARNING! Hybrid MBRs are flaky and potentially dangerous! If you decide not\n"
1367 "to use one, just hit the Enter key at the below prompt and your MBR\n"
1368 "partition table will be untouched.\n\n\a");
srs5694e7b4ff92009-08-18 13:16:10 -04001369
1370 // Now get the numbers of up to three partitions to add to the
1371 // hybrid MBR....
srs5694c0ca8f82009-08-20 21:35:25 -04001372 printf("Type from one to three GPT partition numbers, separated by spaces, to be\n"
1373 "added to the hybrid MBR, in sequence: ");
srs5694e7b4ff92009-08-18 13:16:10 -04001374 fgets(line, 255, stdin);
1375 numParts = sscanf(line, "%d %d %d", &partNums[0], &partNums[1], &partNums[2]);
srs5694c0ca8f82009-08-20 21:35:25 -04001376
1377 if (numParts > 0) {
1378 // Blank out the protective MBR, but leave the boot loader code
1379 // alone....
1380 protectiveMBR.EmptyMBR(0);
1381 protectiveMBR.SetDiskSize(diskSize);
srs5694e19ba092009-08-24 14:10:35 -04001382 printf("Place EFI GPT (0xEE) partition first in MBR (good for GRUB)? ");
1383 eeFirst = GetYN();
srs5694c0ca8f82009-08-20 21:35:25 -04001384 } // if
1385
srs5694e7b4ff92009-08-18 13:16:10 -04001386 for (i = 0; i < numParts; i++) {
1387 j = partNums[i] - 1;
srs5694e19ba092009-08-24 14:10:35 -04001388 printf("\nCreating entry for partition #%d\n", j + 1);
srs5694e7b4ff92009-08-18 13:16:10 -04001389 if ((j >= 0) && (j < mainHeader.numParts)) {
srs5694221e0872009-08-29 15:00:31 -04001390 if (partitions[j].GetLastLBA() < UINT32_MAX) {
srs5694e19ba092009-08-24 14:10:35 -04001391 do {
1392 printf("Enter an MBR hex code (default %02X): ",
srs5694221e0872009-08-29 15:00:31 -04001393 typeHelper.GUIDToID(partitions[j].GetType()) / 256);
srs5694e19ba092009-08-24 14:10:35 -04001394 fgets(line, 255, stdin);
1395 sscanf(line, "%x", &typeCode);
1396 if (line[0] == '\n')
srs5694221e0872009-08-29 15:00:31 -04001397 typeCode = partitions[j].GetHexType() / 256;
srs5694e19ba092009-08-24 14:10:35 -04001398 } while ((typeCode <= 0) || (typeCode > 255));
srs5694e7b4ff92009-08-18 13:16:10 -04001399 printf("Set the bootable flag? ");
1400 bootable = (GetYN() == 'Y');
srs5694221e0872009-08-29 15:00:31 -04001401 length = partitions[j].GetLengthLBA();
srs5694e19ba092009-08-24 14:10:35 -04001402 if (eeFirst == 'Y')
1403 mbrNum = i + 1;
1404 else
1405 mbrNum = i;
srs5694221e0872009-08-29 15:00:31 -04001406 protectiveMBR.MakePart(mbrNum, (uint32_t) partitions[j].GetFirstLBA(),
1407 (uint32_t) length, typeCode, bootable);
srs5694e7b4ff92009-08-18 13:16:10 -04001408 } else { // partition out of range
1409 printf("Partition %d ends beyond the 2TiB limit of MBR partitions; omitting it.\n",
1410 j + 1);
1411 } // if/else
1412 } else {
1413 printf("Partition %d is out of range; omitting it.\n", j + 1);
1414 } // if/else
1415 } // for
srs5694c0ca8f82009-08-20 21:35:25 -04001416
1417 if (numParts > 0) { // User opted to create a hybrid MBR....
1418 // Create EFI protective partition that covers the start of the disk.
1419 // If this location (covering the main GPT data structures) is omitted,
1420 // Linux won't find any partitions on the disk. Note that this is
1421 // NUMBERED AFTER the hybrid partitions, contrary to what the
1422 // gptsync utility does. This is because Windows seems to choke on
1423 // disks with a 0xEE partition in the first slot and subsequent
1424 // additional partitions, unless it boots from the disk.
srs5694e19ba092009-08-24 14:10:35 -04001425 if (eeFirst == 'Y')
1426 mbrNum = 0;
1427 else
1428 mbrNum = numParts;
1429 protectiveMBR.MakePart(mbrNum, 1, protectiveMBR.FindLastInFree(1), 0xEE);
srs5694c0ca8f82009-08-20 21:35:25 -04001430
1431 // ... and for good measure, if there are any partition spaces left,
1432 // optionally create more protective EFI partitions to cover as much
1433 // space as possible....
1434 for (i = 0; i < 4; i++) {
1435 if (protectiveMBR.GetType(i) == 0x00) { // unused entry....
1436 if (fillItUp == 'M') {
srs5694e19ba092009-08-24 14:10:35 -04001437 printf("\nUnused partition space(s) found. Use one to protect more partitions? ");
srs5694c0ca8f82009-08-20 21:35:25 -04001438 fillItUp = GetYN();
1439 typeCode = 0x00; // use this to flag a need to get type code
1440 } // if
1441 if (fillItUp == 'Y') {
srs5694e19ba092009-08-24 14:10:35 -04001442 while ((typeCode <= 0) || (typeCode > 255)) {
1443 printf("Enter an MBR hex code (EE is EFI GPT, but may confuse MacOS): ");
srs5694c0ca8f82009-08-20 21:35:25 -04001444 // Comment on above: Mac OS treats disks with more than one
srs5694e19ba092009-08-24 14:10:35 -04001445 // 0xEE MBR partition as MBR disks, not as GPT disks.
srs5694c0ca8f82009-08-20 21:35:25 -04001446 fgets(line, 255, stdin);
1447 sscanf(line, "%x", &typeCode);
srs5694e19ba092009-08-24 14:10:35 -04001448 if (line[0] == '\n')
1449 typeCode = 0;
1450 } // while
srs5694c0ca8f82009-08-20 21:35:25 -04001451 protectiveMBR.MakeBiggestPart(i, typeCode); // make a partition
1452 } // if (fillItUp == 'Y')
1453 } // if unused entry
1454 } // for (i = 0; i < 4; i++)
1455 } // if (numParts > 0)
srs5694e7b4ff92009-08-18 13:16:10 -04001456} // GPTData::MakeHybrid()
srs5694c0ca8f82009-08-20 21:35:25 -04001457
1458// Create a fresh protective MBR.
1459void GPTData::MakeProtectiveMBR(void) {
1460 protectiveMBR.MakeProtectiveMBR();
1461} // GPTData::MakeProtectiveMBR(void)
srs5694e7b4ff92009-08-18 13:16:10 -04001462
1463// Writes GPT (and protective MBR) to disk. Returns 1 on successful
1464// write, 0 if there was a problem.
1465int GPTData::SaveGPTData(void) {
1466 int allOK = 1, i, j;
1467 char answer, line[256];
1468 int fd;
1469 uint64_t secondTable;
srs56942a9f5da2009-08-26 00:48:01 -04001470 uint32_t numParts;
srs5694e7b4ff92009-08-18 13:16:10 -04001471 off_t offset;
1472
1473 if (strlen(device) == 0) {
1474 printf("Device not defined.\n");
1475 } // if
1476
1477 // First do some final sanity checks....
1478 // Is there enough space to hold the GPT headers and partition tables,
1479 // given the partition sizes?
srs5694221e0872009-08-29 15:00:31 -04001480 if (CheckGPTSize() > 0) {
srs5694e7b4ff92009-08-18 13:16:10 -04001481 allOK = 0;
1482 } // if
1483
1484 // Check that disk is really big enough to handle this...
1485 if (mainHeader.backupLBA > diskSize) {
1486 fprintf(stderr, "Error! Disk is too small -- either the original MBR is corrupt or you're\n");
1487 fprintf(stderr, "working from an MBR copied to a file! Aborting!\n");
1488 printf("(Disk size is %ld sectors, needs to be %ld sectors.)\n", diskSize,
1489 mainHeader.backupLBA);
1490 allOK = 0;
1491 } // if
1492
1493 // Check for overlapping partitions....
1494 for (i = 1; i < mainHeader.numParts; i++) {
1495 for (j = 0; j < i; j++) {
srs5694221e0872009-08-29 15:00:31 -04001496 if (partitions[i].DoTheyOverlap(&partitions[j])) {
srs5694e7b4ff92009-08-18 13:16:10 -04001497 fprintf(stderr, "\Error: partitions %d and %d overlap:\n", i + 1, j + 1);
1498 fprintf(stderr, " Partition %d: %llu to %llu\n", i,
srs5694221e0872009-08-29 15:00:31 -04001499 (unsigned long long) partitions[i].GetFirstLBA(),
1500 (unsigned long long) partitions[i].GetLastLBA());
srs5694e7b4ff92009-08-18 13:16:10 -04001501 fprintf(stderr, " Partition %d: %llu to %llu\n", j,
srs5694221e0872009-08-29 15:00:31 -04001502 (unsigned long long) partitions[j].GetFirstLBA(),
1503 (unsigned long long) partitions[j].GetLastLBA());
srs5694e7b4ff92009-08-18 13:16:10 -04001504 fprintf(stderr, "Aborting write operation!\n");
1505 allOK = 0;
1506 } // if
1507 } // for j...
1508 } // for i...
1509
srs56942a9f5da2009-08-26 00:48:01 -04001510 // Pull out some data that's needed before doing byte-order reversal on
1511 // big-endian systems....
1512 numParts = mainHeader.numParts;
1513 secondTable = secondHeader.partitionEntriesLBA;
1514 if (IsLittleEndian() == 0) {
1515 // Reverse partition bytes first, since that function requires non-reversed
1516 // data from the main header....
1517 ReversePartitionBytes();
1518 ReverseHeaderBytes(&mainHeader);
1519 ReverseHeaderBytes(&secondHeader);
1520 } // if
srs5694e7b4ff92009-08-18 13:16:10 -04001521 RecomputeCRCs();
1522
1523 if (allOK) {
1524 printf("\nFinal checks complete. About to write GPT data. THIS WILL OVERWRITE EXISTING\n");
1525 printf("MBR PARTITIONS!! THIS PROGRAM IS BETA QUALITY AT BEST. IF YOU LOSE ALL YOUR\n");
1526 printf("DATA, YOU HAVE ONLY YOURSELF TO BLAME IF YOU ANSWER 'Y' BELOW!\n\n");
1527 printf("Do you want to proceed, possibly destroying your data? (Y/N) ");
1528 fgets(line, 255, stdin);
1529 sscanf(line, "%c", &answer);
1530 if ((answer == 'Y') || (answer == 'y')) {
1531 printf("OK; writing new GPT partition table.\n");
1532 } else {
1533 allOK = 0;
1534 } // if/else
1535 } // if
1536
1537 // Do it!
1538 if (allOK) {
1539 fd = open(device, O_WRONLY); // try to open the device; may fail....
1540#ifdef __APPLE__
1541 // MacOS X requires a shared lock under some circumstances....
1542 if (fd < 0) {
1543 fd = open(device, O_WRONLY|O_SHLOCK);
1544 } // if
1545#endif
1546 if (fd != -1) {
1547 // First, write the protective MBR...
1548 protectiveMBR.WriteMBRData(fd);
1549
1550 // Now write the main GPT header...
1551 if (allOK)
1552 if (write(fd, &mainHeader, 512) == -1)
1553 allOK = 0;
1554
1555 // Now write the main partition tables...
1556 if (allOK) {
srs56942a9f5da2009-08-26 00:48:01 -04001557 if (write(fd, partitions, GPT_SIZE * numParts) == -1)
srs5694e7b4ff92009-08-18 13:16:10 -04001558 allOK = 0;
1559 } // if
1560
1561 // Now seek to near the end to write the secondary GPT....
1562 if (allOK) {
srs5694e7b4ff92009-08-18 13:16:10 -04001563 offset = (off_t) secondTable * (off_t) (blockSize);
1564 if (lseek64(fd, offset, SEEK_SET) == (off_t) - 1) {
1565 allOK = 0;
1566 printf("Unable to seek to end of disk!\n");
1567 } // if
1568 } // if
1569
1570 // Now write the secondary partition tables....
1571 if (allOK)
srs56942a9f5da2009-08-26 00:48:01 -04001572 if (write(fd, partitions, GPT_SIZE * numParts) == -1)
srs5694e7b4ff92009-08-18 13:16:10 -04001573 allOK = 0;
1574
1575 // Now write the secondary GPT header...
1576 if (allOK)
1577 if (write(fd, &secondHeader, 512) == -1)
1578 allOK = 0;
1579
1580 // re-read the partition table
1581 if (allOK) {
1582 sync();
1583#ifdef __APPLE__
1584 printf("Warning: The kernel may continue to use old or deleted partitions.\n"
1585 "You should reboot or remove the drive.\n");
1586 /* don't know if this helps
1587 * it definitely will get things on disk though:
1588 * http://topiks.org/mac-os-x/0321278542/ch12lev1sec8.html */
1589 i = ioctl(fd, DKIOCSYNCHRONIZECACHE);
1590#else
srs5694221e0872009-08-29 15:00:31 -04001591#ifdef __FreeBSD__
1592 sleep(2);
1593 i = ioctl(fd, DIOCGFLUSH);
1594 printf("Warning: The kernel is still using the old partition table.\n");
1595#else
srs5694e7b4ff92009-08-18 13:16:10 -04001596 sleep(2);
1597 i = ioctl(fd, BLKRRPART);
1598 if (i)
1599 printf("Warning: The kernel is still using the old partition table.\n"
1600 "The new table will be used at the next reboot.\n");
1601#endif
srs5694221e0872009-08-29 15:00:31 -04001602#endif
srs5694e7b4ff92009-08-18 13:16:10 -04001603 } // if
1604
1605 if (allOK) { // writes completed OK
1606 printf("The operation has completed successfully.\n");
1607 } else {
1608 printf("Warning! An error was reported when writing the partition table! This error\n");
1609 printf("MIGHT be harmless, but you may have trashed the disk! Use parted and, if\n");
1610 printf("necessary, restore your original partition table.\n");
1611 } // if/else
1612 close(fd);
1613 } else {
1614 fprintf(stderr, "Unable to open device %s for writing! Errno is %d! Aborting!\n", device, errno);
1615 allOK = 0;
1616 } // if/else
1617 } else {
1618 printf("Aborting write of new partition table.\n");
1619 } // if
1620
srs56942a9f5da2009-08-26 00:48:01 -04001621 if (IsLittleEndian() == 0) {
1622 // Reverse (normalize) header bytes first, since ReversePartitionBytes()
1623 // requires non-reversed data in mainHeader...
1624 ReverseHeaderBytes(&mainHeader);
1625 ReverseHeaderBytes(&secondHeader);
1626 ReversePartitionBytes();
1627 } // if
1628
srs5694e7b4ff92009-08-18 13:16:10 -04001629 return (allOK);
1630} // GPTData::SaveGPTData()
1631
1632// Save GPT data to a backup file. This function does much less error
1633// checking than SaveGPTData(). It can therefore preserve many types of
1634// corruption for later analysis; however, it preserves only the MBR,
1635// the main GPT header, the backup GPT header, and the main partition
1636// table; it discards the backup partition table, since it should be
1637// identical to the main partition table on healthy disks.
1638int GPTData::SaveGPTBackup(char* filename) {
srs56942a9f5da2009-08-26 00:48:01 -04001639 int fd, allOK = 1;
1640 uint32_t numParts;
srs5694e7b4ff92009-08-18 13:16:10 -04001641
1642 if ((fd = open(filename, O_WRONLY | O_CREAT, S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH)) != -1) {
srs56942a9f5da2009-08-26 00:48:01 -04001643 // Reverse the byte order, if necessary....
1644 numParts = mainHeader.numParts;
1645 if (IsLittleEndian() == 0) {
1646 ReversePartitionBytes();
1647 ReverseHeaderBytes(&mainHeader);
1648 ReverseHeaderBytes(&secondHeader);
1649 } // if
1650
1651 // Now write the protective MBR...
srs5694e7b4ff92009-08-18 13:16:10 -04001652 protectiveMBR.WriteMBRData(fd);
1653
1654 // Now write the main GPT header...
1655 if (allOK)
1656 if (write(fd, &mainHeader, 512) == -1)
1657 allOK = 0;
1658
1659 // Now write the secondary GPT header...
1660 if (allOK)
1661 if (write(fd, &secondHeader, 512) == -1)
1662 allOK = 0;
1663
1664 // Now write the main partition tables...
1665 if (allOK) {
srs56942a9f5da2009-08-26 00:48:01 -04001666 if (write(fd, partitions, GPT_SIZE * numParts) == -1)
srs5694e7b4ff92009-08-18 13:16:10 -04001667 allOK = 0;
1668 } // if
1669
1670 if (allOK) { // writes completed OK
1671 printf("The operation has completed successfully.\n");
1672 } else {
1673 printf("Warning! An error was reported when writing the backup file.\n");
1674 printf("It may not be useable!\n");
1675 } // if/else
1676 close(fd);
srs56942a9f5da2009-08-26 00:48:01 -04001677
1678 // Now reverse the byte-order reversal, if necessary....
1679 if (IsLittleEndian() == 0) {
1680 ReverseHeaderBytes(&mainHeader);
1681 ReverseHeaderBytes(&secondHeader);
1682 ReversePartitionBytes();
1683 } // if
srs5694e7b4ff92009-08-18 13:16:10 -04001684 } else {
1685 fprintf(stderr, "Unable to open file %s for writing! Aborting!\n", filename);
1686 allOK = 0;
1687 } // if/else
1688 return allOK;
1689} // GPTData::SaveGPTBackup()
1690
1691// Load GPT data from a backup file created by SaveGPTBackup(). This function
1692// does minimal error checking. It returns 1 if it completed successfully,
1693// 0 if there was a problem. In the latter case, it creates a new empty
1694// set of partitions.
1695int GPTData::LoadGPTBackup(char* filename) {
1696 int fd, allOK = 1, val;
1697 uint32_t numParts, sizeOfEntries, sizeOfParts, newCRC;
srs56942a9f5da2009-08-26 00:48:01 -04001698 int littleEndian = 1;
srs5694e7b4ff92009-08-18 13:16:10 -04001699
1700 if ((fd = open(filename, O_RDONLY)) != -1) {
srs56942a9f5da2009-08-26 00:48:01 -04001701 if (IsLittleEndian() == 0)
1702 littleEndian = 0;
1703
srs5694e7b4ff92009-08-18 13:16:10 -04001704 // Let the MBRData class load the saved MBR...
srs5694221e0872009-08-29 15:00:31 -04001705 protectiveMBR.ReadMBRData(fd, 0); // 0 = don't check block size
srs5694e7b4ff92009-08-18 13:16:10 -04001706
1707 // Load the main GPT header, check its vaility, and set the GPT
1708 // size based on the data
1709 read(fd, &mainHeader, 512);
1710 mainCrcOk = CheckHeaderCRC(&mainHeader);
1711
srs56942a9f5da2009-08-26 00:48:01 -04001712 // Reverse byte order, if necessary
1713 if (littleEndian == 0) {
1714 ReverseHeaderBytes(&mainHeader);
1715 } // if
1716
srs5694e7b4ff92009-08-18 13:16:10 -04001717 // Load the backup GPT header in much the same way as the main
1718 // GPT header....
1719 read(fd, &secondHeader, 512);
1720 secondCrcOk = CheckHeaderCRC(&secondHeader);
1721
srs56942a9f5da2009-08-26 00:48:01 -04001722 // Reverse byte order, if necessary
1723 if (littleEndian == 0) {
1724 ReverseHeaderBytes(&secondHeader);
1725 } // if
1726
srs5694e7b4ff92009-08-18 13:16:10 -04001727 // Return valid headers code: 0 = both headers bad; 1 = main header
1728 // good, backup bad; 2 = backup header good, main header bad;
1729 // 3 = both headers good. Note these codes refer to valid GPT
1730 // signatures and version numbers; more subtle problems will elude
1731 // this check!
1732 if ((val = CheckHeaderValidity()) > 0) {
1733 if (val == 2) { // only backup header seems to be good
1734 numParts = secondHeader.numParts;
1735 sizeOfEntries = secondHeader.sizeOfPartitionEntries;
1736 } else { // main header is OK
1737 numParts = mainHeader.numParts;
1738 sizeOfEntries = mainHeader.sizeOfPartitionEntries;
1739 } // if/else
1740
1741 SetGPTSize(numParts);
1742
1743 // If current disk size doesn't match that of backup....
1744 if (secondHeader.currentLBA != diskSize - UINT64_C(1)) {
1745 printf("Warning! Current disk size doesn't match that of the backup!\n"
1746 "Adjusting sizes to match, but subsequent problems are possible!\n");
1747 secondHeader.currentLBA = mainHeader.backupLBA = diskSize - UINT64_C(1);
1748 mainHeader.lastUsableLBA = diskSize - mainHeader.firstUsableLBA;
1749 secondHeader.lastUsableLBA = mainHeader.lastUsableLBA;
1750 secondHeader.partitionEntriesLBA = secondHeader.lastUsableLBA + UINT64_C(1);
1751 } // if
1752
1753 // Load main partition table, and record whether its CRC
1754 // matches the stored value
1755 sizeOfParts = numParts * sizeOfEntries;
1756 read(fd, partitions, sizeOfParts);
1757
1758 newCRC = chksum_crc32((unsigned char*) partitions, sizeOfParts);
1759 mainPartsCrcOk = (newCRC == mainHeader.partitionEntriesCRC);
1760 secondPartsCrcOk = (newCRC == secondHeader.partitionEntriesCRC);
srs56942a9f5da2009-08-26 00:48:01 -04001761 // Reverse byte order, if necessary
1762 if (littleEndian == 0) {
1763 ReversePartitionBytes();
1764 } // if
1765
srs5694e7b4ff92009-08-18 13:16:10 -04001766 } else {
1767 allOK = 0;
1768 } // if/else
1769 } else {
1770 allOK = 0;
1771 fprintf(stderr, "Unable to open file %s for reading! Aborting!\n", filename);
1772 } // if/else
1773
1774 // Something went badly wrong, so blank out partitions
1775 if (allOK == 0) {
1776 ClearGPTData();
1777 protectiveMBR.MakeProtectiveMBR();
1778 } // if
1779 return allOK;
1780} // GPTData::LoadGPTBackup()
1781
srs5694c0ca8f82009-08-20 21:35:25 -04001782// This function destroys the on-disk GPT structures. Returns 1 if the
1783// user confirms destruction, 0 if the user aborts.
1784int GPTData::DestroyGPT(void) {
1785 int fd, i, doMore;
1786 char blankSector[512], goOn;
1787
1788 for (i = 0; i < 512; i++) {
1789 blankSector[i] = '\0';
1790 } // for
1791
1792 printf("\a\aAbout to wipe out GPT on %s. Proceed? ", device);
1793 goOn = GetYN();
1794 if (goOn == 'Y') {
1795 fd = open(device, O_WRONLY);
1796#ifdef __APPLE__
1797 // MacOS X requires a shared lock under some circumstances....
1798 if (fd < 0) {
1799 fd = open(device, O_WRONLY|O_SHLOCK);
1800 } // if
1801#endif
1802 if (fd != -1) {
1803 lseek64(fd, mainHeader.currentLBA * 512, SEEK_SET); // seek to GPT header
1804 write(fd, blankSector, 512); // blank it out
1805 lseek64(fd, mainHeader.partitionEntriesLBA * 512, SEEK_SET); // seek to partition table
1806 for (i = 0; i < GetBlocksInPartTable(); i++)
1807 write(fd, blankSector, 512);
1808 lseek64(fd, secondHeader.partitionEntriesLBA * 512, SEEK_SET); // seek to partition table
1809 for (i = 0; i < GetBlocksInPartTable(); i++)
1810 write(fd, blankSector, 512);
1811 lseek64(fd, secondHeader.currentLBA * 512, SEEK_SET); // seek to GPT header
1812 write(fd, blankSector, 512); // blank it out
1813 printf("Blank out MBR? ");
1814 if (GetYN() == 'Y') {
1815 lseek64(fd, 0, SEEK_SET);
1816 write(fd, blankSector, 512); // blank it out
1817 } // if blank MBR
1818 close(fd);
1819 printf("GPT data structures destroyed! You may now partition the disk using fdisk or\n"
1820 "other utilities. Program will now terminate.\n");
1821 } else {
1822 printf("Problem opening %s for writing! Program will now terminate.\n");
1823 } // if/else (fd != -1)
1824 } // if (goOn == 'Y')
1825 return (goOn == 'Y');
1826} // GPTData::DestroyGPT()
1827
srs56942a9f5da2009-08-26 00:48:01 -04001828void GPTData::ReverseHeaderBytes(struct GPTHeader* header) {
srs5694221e0872009-08-29 15:00:31 -04001829 ReverseBytes(&header->signature, 8);
1830 ReverseBytes(&header->revision, 4);
1831 ReverseBytes(&header->headerSize, 4);
1832 ReverseBytes(&header->headerCRC, 4);
1833 ReverseBytes(&header->reserved, 4);
1834 ReverseBytes(&header->currentLBA, 8);
1835 ReverseBytes(&header->backupLBA, 8);
1836 ReverseBytes(&header->firstUsableLBA, 8);
1837 ReverseBytes(&header->lastUsableLBA, 8);
1838 ReverseBytes(&header->partitionEntriesLBA, 8);
1839 ReverseBytes(&header->numParts, 4);
1840 ReverseBytes(&header->sizeOfPartitionEntries, 4);
1841 ReverseBytes(&header->partitionEntriesCRC, 4);
1842 ReverseBytes(&header->reserved2, GPT_RESERVED);
1843 ReverseBytes(&header->diskGUID.data1, 8);
1844 ReverseBytes(&header->diskGUID.data2, 8);
srs56942a9f5da2009-08-26 00:48:01 -04001845} // GPTData::ReverseHeaderBytes()
1846
1847// IMPORTANT NOTE: This function requires non-reversed mainHeader
1848// structure!
1849void GPTData::ReversePartitionBytes() {
1850 uint32_t i;
1851
1852 // Check GPT signature on big-endian systems; this will mismatch
1853 // if the function is called out of order. Unfortunately, it'll also
1854 // mismatch if there's data corruption.
1855 if ((mainHeader.signature != GPT_SIGNATURE) && (IsLittleEndian() == 0)) {
1856 printf("GPT signature mismatch in GPTData::ReversePartitionBytes(). This indicates\n"
1857 "data corruption or a misplaced call to this function.\n");
1858 } // if signature mismatch....
1859 for (i = 0; i < mainHeader.numParts; i++) {
srs5694221e0872009-08-29 15:00:31 -04001860 partitions[i].ReversePartBytes();
srs56942a9f5da2009-08-26 00:48:01 -04001861 } // for
1862} // GPTData::ReversePartitionBytes()
1863
1864/******************************************
1865 * *
1866 * Additional non-class support functions *
1867 * *
1868 ******************************************/
1869
srs5694e7b4ff92009-08-18 13:16:10 -04001870// Check to be sure that data type sizes are correct. The basic types (uint*_t) should
1871// never fail these tests, but the struct types may fail depending on compile options.
1872// Specifically, the -fpack-struct option to gcc may be required to ensure proper structure
1873// sizes.
1874int SizesOK(void) {
1875 int allOK = 1;
srs5694e7b4ff92009-08-18 13:16:10 -04001876
1877 if (sizeof(uint8_t) != 1) {
1878 fprintf(stderr, "uint8_t is %d bytes, should be 1 byte; aborting!\n", sizeof(uint8_t));
1879 allOK = 0;
1880 } // if
1881 if (sizeof(uint16_t) != 2) {
1882 fprintf(stderr, "uint16_t is %d bytes, should be 2 bytes; aborting!\n", sizeof(uint16_t));
1883 allOK = 0;
1884 } // if
1885 if (sizeof(uint32_t) != 4) {
1886 fprintf(stderr, "uint32_t is %d bytes, should be 4 bytes; aborting!\n", sizeof(uint32_t));
1887 allOK = 0;
1888 } // if
1889 if (sizeof(uint64_t) != 8) {
1890 fprintf(stderr, "uint64_t is %d bytes, should be 8 bytes; aborting!\n", sizeof(uint64_t));
1891 allOK = 0;
1892 } // if
1893 if (sizeof(struct MBRRecord) != 16) {
srs5694221e0872009-08-29 15:00:31 -04001894 fprintf(stderr, "MBRRecord is %d bytes, should be 16 bytes; aborting!\n", sizeof(MBRRecord));
srs5694e7b4ff92009-08-18 13:16:10 -04001895 allOK = 0;
1896 } // if
1897 if (sizeof(struct EBRRecord) != 512) {
srs5694221e0872009-08-29 15:00:31 -04001898 fprintf(stderr, "EBRRecord is %d bytes, should be 512 bytes; aborting!\n", sizeof(EBRRecord));
srs5694e7b4ff92009-08-18 13:16:10 -04001899 allOK = 0;
1900 } // if
1901 if (sizeof(struct GPTHeader) != 512) {
srs5694221e0872009-08-29 15:00:31 -04001902 fprintf(stderr, "GPTHeader is %d bytes, should be 512 bytes; aborting!\n", sizeof(GPTHeader));
srs5694e7b4ff92009-08-18 13:16:10 -04001903 allOK = 0;
1904 } // if
srs5694221e0872009-08-29 15:00:31 -04001905 if (sizeof(GPTPart) != 128) {
1906 fprintf(stderr, "GPTPart is %d bytes, should be 128 bytes; aborting!\n", sizeof(GPTPart));
1907 allOK = 0;
1908 } // if
1909// Determine endianness; set allOK = 0 if running on big-endian hardware
srs56942a9f5da2009-08-26 00:48:01 -04001910 if (IsLittleEndian() == 0) {
1911 fprintf(stderr, "\aRunning on big-endian hardware. Big-endian support is new and poorly"
1912 " tested!\nBeware!\n");
1913 // allOK = 0;
srs5694e7b4ff92009-08-18 13:16:10 -04001914 } // if
1915 return (allOK);
1916} // SizesOK()