blob: 3b7f9d94ca1d80d8078b0f0a393fc9fe122630ea [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
srs5694e4ac11e2009-08-31 10:13:04 -04004/* By Rod Smith, initial coding January to February, 2009 */
srs5694e7b4ff92009-08-18 13:16:10 -04005
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
srs5694e4ac11e2009-08-31 10:13:04 -040036// Default constructor
srs5694e7b4ff92009-08-18 13:16:10 -040037GPTData::GPTData(void) {
38 blockSize = SECTOR_SIZE; // set a default
39 diskSize = 0;
40 partitions = NULL;
41 state = gpt_valid;
42 strcpy(device, "");
43 mainCrcOk = 0;
44 secondCrcOk = 0;
45 mainPartsCrcOk = 0;
46 secondPartsCrcOk = 0;
srs5694221e0872009-08-29 15:00:31 -040047 apmFound = 0;
48 bsdFound = 0;
srs5694e7b4ff92009-08-18 13:16:10 -040049 srand((unsigned int) time(NULL));
50 SetGPTSize(NUM_GPT_ENTRIES);
51} // GPTData default constructor
52
53// The following constructor loads GPT data from a device file
54GPTData::GPTData(char* filename) {
55 blockSize = SECTOR_SIZE; // set a default
56 diskSize = 0;
57 partitions = NULL;
58 state = gpt_invalid;
59 strcpy(device, "");
60 mainCrcOk = 0;
61 secondCrcOk = 0;
62 mainPartsCrcOk = 0;
63 secondPartsCrcOk = 0;
srs5694221e0872009-08-29 15:00:31 -040064 apmFound = 0;
65 bsdFound = 0;
srs5694e7b4ff92009-08-18 13:16:10 -040066 srand((unsigned int) time(NULL));
67 LoadPartitions(filename);
68} // GPTData(char* filename) constructor
69
srs5694e4ac11e2009-08-31 10:13:04 -040070// Destructor
srs5694e7b4ff92009-08-18 13:16:10 -040071GPTData::~GPTData(void) {
72 free(partitions);
73} // GPTData destructor
74
srs5694e4ac11e2009-08-31 10:13:04 -040075/*********************************************************************
76 * *
77 * Begin functions that verify data, or that adjust the verification *
78 * information (compute CRCs, rebuild headers) *
79 * *
80 *********************************************************************/
srs5694e7b4ff92009-08-18 13:16:10 -040081
srs5694e4ac11e2009-08-31 10:13:04 -040082// Perform detailed verification, reporting on any problems found, but
83// do *NOT* recover from these problems. Returns the total number of
84// problems identified.
85int GPTData::Verify(void) {
srs5694e35eb1b2009-09-14 00:29:34 -040086 int problems = 0, numSegments;
srs5694e4ac11e2009-08-31 10:13:04 -040087 uint64_t totalFree, largestSegment;
88 char tempStr[255], siTotal[255], siLargest[255];
89
90 // First, check for CRC errors in the GPT data....
91 if (!mainCrcOk) {
92 problems++;
93 printf("\nProblem: The CRC for the main GPT header is invalid. The main GPT header may\n"
94 "be corrupt. Consider loading the backup GPT header to rebuild the main GPT\n"
95 "header\n");
96 } // if
97 if (!mainPartsCrcOk) {
98 problems++;
99 printf("\nProblem: The CRC for the main partition table is invalid. This table may be\n"
100 "corrupt. Consider loading the backup partition table.\n");
101 } // if
102 if (!secondCrcOk) {
103 problems++;
104 printf("\nProblem: The CRC for the backup GPT header is invalid. The backup GPT header\n"
105 "may be corrupt. Consider using the main GPT header to rebuild the backup GPT\n"
106 "header.\n");
107 } // if
108 if (!secondPartsCrcOk) {
109 problems++;
110 printf("\nCaution: The CRC for the backup partition table is invalid. This table may\n"
111 "be corrupt. This program will automatically create a new backup partition\n"
112 "table when you save your partitions.\n");
srs5694e7b4ff92009-08-18 13:16:10 -0400113 } // if
114
srs5694e4ac11e2009-08-31 10:13:04 -0400115 // Now check that critical main and backup GPT entries match
116 if (mainHeader.currentLBA != secondHeader.backupLBA) {
117 problems++;
118 printf("\nProblem: main GPT header's current LBA pointer (%llu) doesn't\n"
119 "match the backup GPT header's LBA pointer(%llu)\n",
120 (unsigned long long) mainHeader.currentLBA,
121 (unsigned long long) secondHeader.backupLBA);
122 } // if
123 if (mainHeader.backupLBA != secondHeader.currentLBA) {
124 problems++;
125 printf("\nProblem: main GPT header's backup LBA pointer (%llu) doesn't\n"
126 "match the backup GPT header's current LBA pointer (%llu)\n",
127 (unsigned long long) mainHeader.backupLBA,
128 (unsigned long long) secondHeader.currentLBA);
129 } // if
130 if (mainHeader.firstUsableLBA != secondHeader.firstUsableLBA) {
131 problems++;
132 printf("\nProblem: main GPT header's first usable LBA pointer (%llu) doesn't\n"
133 "match the backup GPT header's first usable LBA pointer (%llu)\n",
134 (unsigned long long) mainHeader.firstUsableLBA,
135 (unsigned long long) secondHeader.firstUsableLBA);
136 } // if
137 if (mainHeader.lastUsableLBA != secondHeader.lastUsableLBA) {
138 problems++;
139 printf("\nProblem: main GPT header's last usable LBA pointer (%llu) doesn't\n"
140 "match the backup GPT header's last usable LBA pointer (%llu)\n",
141 (unsigned long long) mainHeader.lastUsableLBA,
142 (unsigned long long) secondHeader.lastUsableLBA);
143 } // if
144 if ((mainHeader.diskGUID.data1 != secondHeader.diskGUID.data1) ||
145 (mainHeader.diskGUID.data2 != secondHeader.diskGUID.data2)) {
146 problems++;
147 printf("\nProblem: main header's disk GUID (%s) doesn't\n",
148 GUIDToStr(mainHeader.diskGUID, tempStr));
149 printf("match the backup GPT header's disk GUID (%s)\n",
150 GUIDToStr(secondHeader.diskGUID, tempStr));
151 } // if
152 if (mainHeader.numParts != secondHeader.numParts) {
153 problems++;
154 printf("\nProblem: main GPT header's number of partitions (%lu) doesn't\n"
155 "match the backup GPT header's number of partitions (%lu)\n",
156 (unsigned long) mainHeader.numParts,
157 (unsigned long) secondHeader.numParts);
158 } // if
159 if (mainHeader.sizeOfPartitionEntries != secondHeader.sizeOfPartitionEntries) {
160 problems++;
161 printf("\nProblem: main GPT header's size of partition entries (%lu) doesn't\n"
162 "match the backup GPT header's size of partition entries (%lu)\n",
163 (unsigned long) mainHeader.sizeOfPartitionEntries,
164 (unsigned long) secondHeader.sizeOfPartitionEntries);
165 } // if
166
167 // Now check for a few other miscellaneous problems...
168 // Check that the disk size will hold the data...
169 if (mainHeader.backupLBA > diskSize) {
170 problems++;
171 printf("\nProblem: Disk is too small to hold all the data!\n");
172 printf("(Disk size is %llu sectors, needs to be %llu sectors.)\n",
173 (unsigned long long) diskSize,
174 (unsigned long long) mainHeader.backupLBA);
175 } // if
176
177 // Check for overlapping partitions....
178 problems += FindOverlaps();
179
180 // Check for mismatched MBR and GPT partitions...
181 problems += FindHybridMismatches();
182
183 // Verify that partitions don't run into GPT data areas....
184 problems += CheckGPTSize();
185
186 // Now compute available space, but only if no problems found, since
187 // problems could affect the results
188 if (problems == 0) {
189 totalFree = FindFreeBlocks(&numSegments, &largestSegment);
190 BytesToSI(totalFree * (uint64_t) blockSize, siTotal);
191 BytesToSI(largestSegment * (uint64_t) blockSize, siLargest);
192 printf("No problems found. %llu free sectors (%s) available in %u\n"
193 "segments, the largest of which is %llu sectors (%s) in size\n",
194 (unsigned long long) totalFree,
195 siTotal, numSegments, (unsigned long long) largestSegment,
196 siLargest);
197 } else {
198 printf("\nIdentified %d problems!\n", problems);
srs5694e7b4ff92009-08-18 13:16:10 -0400199 } // if/else
srs5694e4ac11e2009-08-31 10:13:04 -0400200
201 return (problems);
202} // GPTData::Verify()
srs5694e7b4ff92009-08-18 13:16:10 -0400203
204// Checks to see if the GPT tables overrun existing partitions; if they
srs5694221e0872009-08-29 15:00:31 -0400205// do, issues a warning but takes no action. Returns number of problems
206// detected (0 if OK, 1 to 2 if problems).
srs5694e7b4ff92009-08-18 13:16:10 -0400207int GPTData::CheckGPTSize(void) {
208 uint64_t overlap, firstUsedBlock, lastUsedBlock;
209 uint32_t i;
srs5694221e0872009-08-29 15:00:31 -0400210 int numProbs = 0;
srs5694e7b4ff92009-08-18 13:16:10 -0400211
212 // first, locate the first & last used blocks
213 firstUsedBlock = UINT64_MAX;
214 lastUsedBlock = 0;
215 for (i = 0; i < mainHeader.numParts; i++) {
srs5694221e0872009-08-29 15:00:31 -0400216 if ((partitions[i].GetFirstLBA() < firstUsedBlock) &&
srs5694e4ac11e2009-08-31 10:13:04 -0400217 (partitions[i].GetFirstLBA() != 0))
srs5694221e0872009-08-29 15:00:31 -0400218 firstUsedBlock = partitions[i].GetFirstLBA();
219 if (partitions[i].GetLastLBA() > lastUsedBlock)
220 lastUsedBlock = partitions[i].GetLastLBA();
srs5694e7b4ff92009-08-18 13:16:10 -0400221 } // for
222
223 // If the disk size is 0 (the default), then it means that various
224 // variables aren't yet set, so the below tests will be useless;
225 // therefore we should skip everything
226 if (diskSize != 0) {
227 if (mainHeader.firstUsableLBA > firstUsedBlock) {
228 overlap = mainHeader.firstUsableLBA - firstUsedBlock;
srs5694221e0872009-08-29 15:00:31 -0400229 printf("Warning! Main partition table overlaps the first partition by %lu blocks!\n",
230 (unsigned long) overlap);
231 if (firstUsedBlock > 2) {
232 printf("Try reducing the partition table size by %lu entries.\n",
233 (unsigned long) (overlap * 4));
234 printf("(Use the 's' item on the experts' menu.)\n");
235 } else {
236 printf("You will need to delete this partition or resize it in another utility.\n");
237 } // if/else
238 numProbs++;
srs5694e7b4ff92009-08-18 13:16:10 -0400239 } // Problem at start of disk
240 if (mainHeader.lastUsableLBA < lastUsedBlock) {
241 overlap = lastUsedBlock - mainHeader.lastUsableLBA;
srs5694221e0872009-08-29 15:00:31 -0400242 printf("Warning! Secondary partition table overlaps the last partition by %lu blocks\n",
243 (unsigned long) overlap);
244 if (lastUsedBlock > (diskSize - 2)) {
245 printf("You will need to delete this partition or resize it in another utility.\n");
246 } else {
247 printf("Try reducing the partition table size by %lu entries.\n",
248 (unsigned long) (overlap * 4));
249 printf("(Use the 's' item on the experts' menu.)\n");
250 } // if/else
251 numProbs++;
srs5694e7b4ff92009-08-18 13:16:10 -0400252 } // Problem at end of disk
253 } // if (diskSize != 0)
srs5694221e0872009-08-29 15:00:31 -0400254 return numProbs;
srs5694e7b4ff92009-08-18 13:16:10 -0400255} // GPTData::CheckGPTSize()
256
srs5694e7b4ff92009-08-18 13:16:10 -0400257// Check the validity of the GPT header. Returns 1 if the main header
258// is valid, 2 if the backup header is valid, 3 if both are valid, and
259// 0 if neither is valid. Note that this function just checks the GPT
260// signature and revision numbers, not CRCs or other data.
261int GPTData::CheckHeaderValidity(void) {
262 int valid = 3;
263
264 if (mainHeader.signature != GPT_SIGNATURE) {
265 valid -= 1;
srs5694221e0872009-08-29 15:00:31 -0400266// printf("Main GPT signature invalid; read 0x%016llX, should be\n0x%016llX\n",
267// (unsigned long long) mainHeader.signature, (unsigned long long) GPT_SIGNATURE);
srs5694e7b4ff92009-08-18 13:16:10 -0400268 } else if ((mainHeader.revision != 0x00010000) && valid) {
269 valid -= 1;
270 printf("Unsupported GPT version in main header; read 0x%08lX, should be\n0x%08lX\n",
271 (unsigned long) mainHeader.revision, UINT32_C(0x00010000));
272 } // if/else/if
273
274 if (secondHeader.signature != GPT_SIGNATURE) {
275 valid -= 2;
srs5694221e0872009-08-29 15:00:31 -0400276// printf("Secondary GPT signature invalid; read 0x%016llX, should be\n0x%016llX\n",
277// (unsigned long long) secondHeader.signature, (unsigned long long) GPT_SIGNATURE);
srs5694e7b4ff92009-08-18 13:16:10 -0400278 } else if ((secondHeader.revision != 0x00010000) && valid) {
279 valid -= 2;
280 printf("Unsupported GPT version in backup header; read 0x%08lX, should be\n0x%08lX\n",
281 (unsigned long) mainHeader.revision, UINT32_C(0x00010000));
282 } // if/else/if
283
srs56942a9f5da2009-08-26 00:48:01 -0400284 // If MBR bad, check for an Apple disk signature
srs5694e35eb1b2009-09-14 00:29:34 -0400285 if ((protectiveMBR.GetValidity() == invalid) &&
srs5694e4ac11e2009-08-31 10:13:04 -0400286 (((mainHeader.signature << 32) == APM_SIGNATURE1) ||
srs56942a9f5da2009-08-26 00:48:01 -0400287 (mainHeader.signature << 32) == APM_SIGNATURE2)) {
srs5694221e0872009-08-29 15:00:31 -0400288 apmFound = 1; // Will display warning message later
srs5694e4ac11e2009-08-31 10:13:04 -0400289 } // if
srs56942a9f5da2009-08-26 00:48:01 -0400290
srs5694e4ac11e2009-08-31 10:13:04 -0400291 return valid;
srs5694e7b4ff92009-08-18 13:16:10 -0400292} // GPTData::CheckHeaderValidity()
293
294// Check the header CRC to see if it's OK...
srs56942a9f5da2009-08-26 00:48:01 -0400295// Note: Must be called BEFORE byte-order reversal on big-endian
296// systems!
srs5694e7b4ff92009-08-18 13:16:10 -0400297int GPTData::CheckHeaderCRC(struct GPTHeader* header) {
298 uint32_t oldCRC, newCRC;
299
srs56942a9f5da2009-08-26 00:48:01 -0400300 // Back up old header CRC and then blank it, since it must be 0 for
srs5694e7b4ff92009-08-18 13:16:10 -0400301 // computation to be valid
302 oldCRC = header->headerCRC;
srs56942a9f5da2009-08-26 00:48:01 -0400303 if (IsLittleEndian() == 0)
srs5694221e0872009-08-29 15:00:31 -0400304 ReverseBytes(&oldCRC, 4);
srs5694e7b4ff92009-08-18 13:16:10 -0400305 header->headerCRC = UINT32_C(0);
306
307 // Initialize CRC functions...
308 chksum_crc32gentab();
309
310 // Compute CRC, restore original, and return result of comparison
311 newCRC = chksum_crc32((unsigned char*) header, HEADER_SIZE);
312 mainHeader.headerCRC = oldCRC;
313 return (oldCRC == newCRC);
314} // GPTData::CheckHeaderCRC()
315
srs56942a9f5da2009-08-26 00:48:01 -0400316// Recompute all the CRCs. Must be called before saving (but after reversing
317// byte order on big-endian systems) if any changes have been made.
srs5694e7b4ff92009-08-18 13:16:10 -0400318void GPTData::RecomputeCRCs(void) {
319 uint32_t crc;
srs5694e35eb1b2009-09-14 00:29:34 -0400320 uint32_t trueNumParts;
srs56942a9f5da2009-08-26 00:48:01 -0400321 int littleEndian = 1;
srs5694e7b4ff92009-08-18 13:16:10 -0400322
323 // Initialize CRC functions...
324 chksum_crc32gentab();
325
srs56942a9f5da2009-08-26 00:48:01 -0400326 littleEndian = IsLittleEndian();
327
srs5694e7b4ff92009-08-18 13:16:10 -0400328 // Compute CRC of partition tables & store in main and secondary headers
srs56942a9f5da2009-08-26 00:48:01 -0400329 trueNumParts = mainHeader.numParts;
330 if (littleEndian == 0)
srs5694221e0872009-08-29 15:00:31 -0400331 ReverseBytes(&trueNumParts, 4); // unreverse this key piece of data....
srs56942a9f5da2009-08-26 00:48:01 -0400332 crc = chksum_crc32((unsigned char*) partitions, trueNumParts * GPT_SIZE);
srs5694e7b4ff92009-08-18 13:16:10 -0400333 mainHeader.partitionEntriesCRC = crc;
334 secondHeader.partitionEntriesCRC = crc;
srs56942a9f5da2009-08-26 00:48:01 -0400335 if (littleEndian == 0) {
srs5694221e0872009-08-29 15:00:31 -0400336 ReverseBytes(&mainHeader.partitionEntriesCRC, 4);
337 ReverseBytes(&secondHeader.partitionEntriesCRC, 4);
srs56942a9f5da2009-08-26 00:48:01 -0400338 } // if
srs5694e7b4ff92009-08-18 13:16:10 -0400339
340 // Zero out GPT tables' own CRCs (required for correct computation)
341 mainHeader.headerCRC = 0;
342 secondHeader.headerCRC = 0;
343
344 // Compute & store CRCs of main & secondary headers...
345 crc = chksum_crc32((unsigned char*) &mainHeader, HEADER_SIZE);
srs56942a9f5da2009-08-26 00:48:01 -0400346 if (littleEndian == 0)
srs5694221e0872009-08-29 15:00:31 -0400347 ReverseBytes(&crc, 4);
srs5694e7b4ff92009-08-18 13:16:10 -0400348 mainHeader.headerCRC = crc;
349 crc = chksum_crc32((unsigned char*) &secondHeader, HEADER_SIZE);
srs56942a9f5da2009-08-26 00:48:01 -0400350 if (littleEndian == 0)
srs5694221e0872009-08-29 15:00:31 -0400351 ReverseBytes(&crc, 4);
srs5694e7b4ff92009-08-18 13:16:10 -0400352 secondHeader.headerCRC = crc;
353} // GPTData::RecomputeCRCs()
354
srs5694e7b4ff92009-08-18 13:16:10 -0400355// Rebuild the main GPT header, using the secondary header as a model.
356// Typically called when the main header has been found to be corrupt.
357void GPTData::RebuildMainHeader(void) {
358 int i;
359
360 mainHeader.signature = GPT_SIGNATURE;
361 mainHeader.revision = secondHeader.revision;
362 mainHeader.headerSize = HEADER_SIZE;
363 mainHeader.headerCRC = UINT32_C(0);
364 mainHeader.reserved = secondHeader.reserved;
365 mainHeader.currentLBA = secondHeader.backupLBA;
366 mainHeader.backupLBA = secondHeader.currentLBA;
367 mainHeader.firstUsableLBA = secondHeader.firstUsableLBA;
368 mainHeader.lastUsableLBA = secondHeader.lastUsableLBA;
369 mainHeader.diskGUID.data1 = secondHeader.diskGUID.data1;
370 mainHeader.diskGUID.data2 = secondHeader.diskGUID.data2;
371 mainHeader.partitionEntriesLBA = UINT64_C(2);
372 mainHeader.numParts = secondHeader.numParts;
373 mainHeader.sizeOfPartitionEntries = secondHeader.sizeOfPartitionEntries;
374 mainHeader.partitionEntriesCRC = secondHeader.partitionEntriesCRC;
375 for (i = 0 ; i < GPT_RESERVED; i++)
376 mainHeader.reserved2[i] = secondHeader.reserved2[i];
377} // GPTData::RebuildMainHeader()
378
379// Rebuild the secondary GPT header, using the main header as a model.
380void GPTData::RebuildSecondHeader(void) {
381 int i;
382
383 secondHeader.signature = GPT_SIGNATURE;
384 secondHeader.revision = mainHeader.revision;
385 secondHeader.headerSize = HEADER_SIZE;
386 secondHeader.headerCRC = UINT32_C(0);
387 secondHeader.reserved = mainHeader.reserved;
388 secondHeader.currentLBA = mainHeader.backupLBA;
389 secondHeader.backupLBA = mainHeader.currentLBA;
390 secondHeader.firstUsableLBA = mainHeader.firstUsableLBA;
391 secondHeader.lastUsableLBA = mainHeader.lastUsableLBA;
392 secondHeader.diskGUID.data1 = mainHeader.diskGUID.data1;
393 secondHeader.diskGUID.data2 = mainHeader.diskGUID.data2;
394 secondHeader.partitionEntriesLBA = secondHeader.lastUsableLBA + UINT64_C(1);
395 secondHeader.numParts = mainHeader.numParts;
396 secondHeader.sizeOfPartitionEntries = mainHeader.sizeOfPartitionEntries;
397 secondHeader.partitionEntriesCRC = mainHeader.partitionEntriesCRC;
398 for (i = 0 ; i < GPT_RESERVED; i++)
399 secondHeader.reserved2[i] = mainHeader.reserved2[i];
srs5694e4ac11e2009-08-31 10:13:04 -0400400} // GPTData::RebuildSecondHeader()
401
402// Search for hybrid MBR entries that have no corresponding GPT partition.
403// Returns number of such mismatches found
404int GPTData::FindHybridMismatches(void) {
405 int i, j, found, numFound = 0;
406 uint64_t mbrFirst, mbrLast;
407
408 for (i = 0; i < 4; i++) {
409 if ((protectiveMBR.GetType(i) != 0xEE) && (protectiveMBR.GetType(i) != 0x00)) {
410 j = 0;
411 found = 0;
412 do {
413 mbrFirst = (uint64_t) protectiveMBR.GetFirstSector(i);
414 mbrLast = mbrFirst + (uint64_t) protectiveMBR.GetLength(i) - UINT64_C(1);
415 if ((partitions[j].GetFirstLBA() == mbrFirst) &&
416 (partitions[j].GetLastLBA() == mbrLast))
417 found = 1;
418 j++;
419 } while ((!found) && (j < mainHeader.numParts));
420 if (!found) {
421 numFound++;
422 printf("\nWarning! Mismatched GPT and MBR partitions! MBR partition "
423 "%d, of type 0x%02X,\nhas no corresponding GPT partition! "
424 "You may continue, but this condition\nmight cause data loss"
425 " in the future!\a\n", i + 1, protectiveMBR.GetType(i));
426 } // if
427 } // if
428 } // for
429 return numFound;
430} // GPTData::FindHybridMismatches
431
432// Find overlapping partitions and warn user about them. Returns number of
433// overlapping partitions.
434int GPTData::FindOverlaps(void) {
435 int i, j, problems = 0;
436
437 for (i = 1; i < mainHeader.numParts; i++) {
438 for (j = 0; j < i; j++) {
439 if (partitions[i].DoTheyOverlap(&partitions[j])) {
440 problems++;
441 printf("\nProblem: partitions %d and %d overlap:\n", i + 1, j + 1);
442 printf(" Partition %d: %llu to %llu\n", i,
443 (unsigned long long) partitions[i].GetFirstLBA(),
444 (unsigned long long) partitions[i].GetLastLBA());
445 printf(" Partition %d: %llu to %llu\n", j,
446 (unsigned long long) partitions[j].GetFirstLBA(),
447 (unsigned long long) partitions[j].GetLastLBA());
448 } // if
449 } // for j...
450 } // for i...
451 return problems;
452} // GPTData::FindOverlaps()
453
454/******************************************************************
455 * *
456 * Begin functions that load data from disk or save data to disk. *
457 * *
458 ******************************************************************/
459
460// Scan for partition data. This function loads the MBR data (regular MBR or
461// protective MBR) and loads BSD disklabel data (which is probably invalid).
462// It also looks for APM data, forces a load of GPT data, and summarizes
463// the results.
464void GPTData::PartitionScan(int fd) {
465 BSDData bsdDisklabel;
466// int bsdFound;
467
468 printf("Partition table scan:\n");
469
470 // Read the MBR & check for BSD disklabel
471 protectiveMBR.ReadMBRData(fd);
472 protectiveMBR.ShowState();
473 bsdDisklabel.ReadBSDData(fd, 0, diskSize - 1);
474 bsdFound = bsdDisklabel.ShowState();
475// bsdDisklabel.DisplayBSDData();
476
477 // Load the GPT data, whether or not it's valid
478 ForceLoadGPTData(fd);
479 ShowAPMState(); // Show whether there's an Apple Partition Map present
480 ShowGPTState(); // Show GPT status
481 printf("\n");
482
483 if (apmFound) {
484 printf("\n*******************************************************************\n");
485 printf("This disk appears to contain an Apple-format (APM) partition table!\n");
486 printf("It will be destroyed if you continue!\n");
487 printf("*******************************************************************\n\n\a");
488 } // if
489/* if (bsdFound) {
490 printf("\n*************************************************************************\n");
491 printf("This disk appears to contain a BSD disklabel! It will be destroyed if you\n"
492 "continue!\n");
493 printf("*************************************************************************\n\n\a");
494} // if */
495} // GPTData::PartitionScan()
496
497// Read GPT data from a disk.
498int GPTData::LoadPartitions(char* deviceFilename) {
499 int fd, err;
500 int allOK = 1, i;
501 uint64_t firstBlock, lastBlock;
502 BSDData bsdDisklabel;
503
504 // First, do a test to see if writing will be possible later....
505 fd = OpenForWrite(deviceFilename);
506 if (fd == -1)
507 printf("\aNOTE: Write test failed with error number %d. It will be "
508 "impossible to save\nchanges to this disk's partition table!\n\n",
509 errno);
510 close(fd);
511
512 if ((fd = open(deviceFilename, O_RDONLY)) != -1) {
513 // store disk information....
514 diskSize = disksize(fd, &err);
515 blockSize = (uint32_t) GetBlockSize(fd);
516 strcpy(device, deviceFilename);
517 PartitionScan(fd); // Check for partition types & print summary
518
519 switch (UseWhichPartitions()) {
520 case use_mbr:
521 XFormPartitions();
522 break;
523 case use_bsd:
524 bsdDisklabel.ReadBSDData(fd, 0, diskSize - 1);
525// bsdDisklabel.DisplayBSDData();
526 ClearGPTData();
527 protectiveMBR.MakeProtectiveMBR(1); // clear boot area (option 1)
528 XFormDisklabel(&bsdDisklabel, 0);
529 break;
530 case use_gpt:
531 break;
532 case use_new:
533 ClearGPTData();
534 protectiveMBR.MakeProtectiveMBR();
535 break;
536 } // switch
537
538 // Now find the first and last sectors used by partitions...
539 if (allOK) {
540 firstBlock = mainHeader.backupLBA; // start high
541 lastBlock = 0; // start low
542 for (i = 0; i < mainHeader.numParts; i++) {
543 if ((partitions[i].GetFirstLBA() < firstBlock) &&
544 (partitions[i].GetFirstLBA() > 0))
545 firstBlock = partitions[i].GetFirstLBA();
546 if (partitions[i].GetLastLBA() > lastBlock)
547 lastBlock = partitions[i].GetLastLBA();
548 } // for
549 } // if
550 CheckGPTSize();
551 } else {
552 allOK = 0;
553 fprintf(stderr, "Problem opening %s for reading! Error is %d\n",
554 deviceFilename, errno);
555 if (errno == EACCES) { // User is probably not running as root
556 fprintf(stderr, "You must run this program as root or use sudo!\n");
557 } // if
558 } // if/else
559 return (allOK);
560} // GPTData::LoadPartitions()
561
562// Loads the GPT, as much as possible. Returns 1 if this seems to have
563// succeeded, 0 if there are obvious problems....
564int GPTData::ForceLoadGPTData(int fd) {
565 int allOK = 1, validHeaders;
566 off_t seekTo;
567 char* storage;
568 uint32_t newCRC, sizeOfParts;
569
570 // Seek to and read the main GPT header
571 lseek64(fd, 512, SEEK_SET);
572 read(fd, &mainHeader, 512); // read main GPT header
573 mainCrcOk = CheckHeaderCRC(&mainHeader);
574 if (IsLittleEndian() == 0) // big-endian system; adjust header byte order....
575 ReverseHeaderBytes(&mainHeader);
576
577 // Load backup header, check its CRC, and store the results of
578 // the check for future reference
579 seekTo = (diskSize * blockSize) - UINT64_C(512);
580 if (lseek64(fd, seekTo, SEEK_SET) != (off_t) -1) {
581 read(fd, &secondHeader, 512); // read secondary GPT header
582 secondCrcOk = CheckHeaderCRC(&secondHeader);
583 if (IsLittleEndian() == 0) // big-endian system; adjust header byte order....
584 ReverseHeaderBytes(&secondHeader);
585 } else {
586 allOK = 0;
587 state = gpt_invalid;
588 fprintf(stderr, "Unable to seek to secondary GPT at sector %llu!\n",
589 diskSize - (UINT64_C(1)));
590 } // if/else lseek
591
592 // Return valid headers code: 0 = both headers bad; 1 = main header
593 // good, backup bad; 2 = backup header good, main header bad;
594 // 3 = both headers good. Note these codes refer to valid GPT
595 // signatures and version numbers; more subtle problems will elude
596 // this check!
597 validHeaders = CheckHeaderValidity();
598
599 // Read partitions (from primary array)
600 if (validHeaders > 0) { // if at least one header is OK....
601 // GPT appears to be valid....
602 state = gpt_valid;
603
604 // We're calling the GPT valid, but there's a possibility that one
605 // of the two headers is corrupt. If so, use the one that seems to
606 // be in better shape to regenerate the bad one
607 if (validHeaders == 2) { // valid backup header, invalid main header
608 printf("Caution: invalid main GPT header, but valid backup; regenerating main header\n"
609 "from backup!\n");
610 RebuildMainHeader();
611 mainCrcOk = secondCrcOk; // Since copied, use CRC validity of backup
612 } else if (validHeaders == 1) { // valid main header, invalid backup
613 printf("Caution: invalid backup GPT header, but valid main header; regenerating\n"
614 "backup header from main header.\n");
615 RebuildSecondHeader();
616 secondCrcOk = mainCrcOk; // Since regenerated, use CRC validity of main
617 } // if/else/if
618
619 // Load the main partition table, including storing results of its
620 // CRC check
621 if (LoadMainTable() == 0)
622 allOK = 0;
623
624 // Load backup partition table into temporary storage to check
625 // its CRC and store the results, then discard this temporary
626 // storage, since we don't use it in any but recovery operations
627 seekTo = secondHeader.partitionEntriesLBA * (off_t) blockSize;
628 if ((lseek64(fd, seekTo, SEEK_SET) != (off_t) -1) && (secondCrcOk)) {
629 sizeOfParts = secondHeader.numParts * secondHeader.sizeOfPartitionEntries;
630 storage = (char*) malloc(sizeOfParts);
631 read(fd, storage, sizeOfParts);
632 newCRC = chksum_crc32((unsigned char*) storage, sizeOfParts);
633 free(storage);
634 secondPartsCrcOk = (newCRC == secondHeader.partitionEntriesCRC);
635 } // if
636
637 // Check for valid CRCs and warn if there are problems
638 if ((mainCrcOk == 0) || (secondCrcOk == 0) || (mainPartsCrcOk == 0) ||
639 (secondPartsCrcOk == 0)) {
640 printf("Warning! One or more CRCs don't match. You should repair the disk!\n");
641 state = gpt_corrupt;
642 } // if
643 } else {
644 state = gpt_invalid;
645 } // if/else
646 return allOK;
647} // GPTData::ForceLoadGPTData()
648
649// Loads the partition tables pointed to by the main GPT header. The
650// main GPT header in memory MUST be valid for this call to do anything
651// sensible!
652int GPTData::LoadMainTable(void) {
653 int fd, retval = 0;
654 uint32_t newCRC, sizeOfParts;
655
656 if ((fd = open(device, O_RDONLY)) != -1) {
657 // Set internal data structures for number of partitions on the disk
658 SetGPTSize(mainHeader.numParts);
659
660 // Load main partition table, and record whether its CRC
661 // matches the stored value
662 lseek64(fd, mainHeader.partitionEntriesLBA * blockSize, SEEK_SET);
663 sizeOfParts = mainHeader.numParts * mainHeader.sizeOfPartitionEntries;
664 read(fd, partitions, sizeOfParts);
665 newCRC = chksum_crc32((unsigned char*) partitions, sizeOfParts);
666 mainPartsCrcOk = (newCRC == mainHeader.partitionEntriesCRC);
667 if (IsLittleEndian() == 0)
668 ReversePartitionBytes();
669 retval = 1;
670 } // if
671 return retval;
672} // GPTData::LoadMainTable()
srs5694e7b4ff92009-08-18 13:16:10 -0400673
674// Load the second (backup) partition table as the primary partition
675// table. Used in repair functions
676void GPTData::LoadSecondTableAsMain(void) {
677 int fd;
678 off_t seekTo;
679 uint32_t sizeOfParts, newCRC;
680
681 if ((fd = open(device, O_RDONLY)) != -1) {
682 seekTo = secondHeader.partitionEntriesLBA * (off_t) blockSize;
683 if (lseek64(fd, seekTo, SEEK_SET) != (off_t) -1) {
684 SetGPTSize(secondHeader.numParts);
685 sizeOfParts = secondHeader.numParts * secondHeader.sizeOfPartitionEntries;
686 read(fd, partitions, sizeOfParts);
687 newCRC = chksum_crc32((unsigned char*) partitions, sizeOfParts);
688 secondPartsCrcOk = (newCRC == secondHeader.partitionEntriesCRC);
srs5694e4ac11e2009-08-31 10:13:04 -0400689 mainPartsCrcOk = secondPartsCrcOk;
srs56942a9f5da2009-08-26 00:48:01 -0400690 if (IsLittleEndian() == 0)
691 ReversePartitionBytes();
srs5694e7b4ff92009-08-18 13:16:10 -0400692 if (!secondPartsCrcOk) {
693 printf("Error! After loading backup partitions, the CRC still doesn't check out!\n");
694 } // if
695 } else {
696 printf("Error! Couldn't seek to backup partition table!\n");
697 } // if/else
698 } else {
699 printf("Error! Couldn't open device %s when recovering backup partition table!\n");
700 } // if/else
701} // GPTData::LoadSecondTableAsMain()
702
srs5694e7b4ff92009-08-18 13:16:10 -0400703// Writes GPT (and protective MBR) to disk. Returns 1 on successful
704// write, 0 if there was a problem.
705int GPTData::SaveGPTData(void) {
srs5694e35eb1b2009-09-14 00:29:34 -0400706 int allOK = 1, i;
srs5694e7b4ff92009-08-18 13:16:10 -0400707 char answer, line[256];
708 int fd;
709 uint64_t secondTable;
srs56942a9f5da2009-08-26 00:48:01 -0400710 uint32_t numParts;
srs5694e7b4ff92009-08-18 13:16:10 -0400711 off_t offset;
712
713 if (strlen(device) == 0) {
714 printf("Device not defined.\n");
715 } // if
716
717 // First do some final sanity checks....
718 // Is there enough space to hold the GPT headers and partition tables,
719 // given the partition sizes?
srs5694221e0872009-08-29 15:00:31 -0400720 if (CheckGPTSize() > 0) {
srs5694e7b4ff92009-08-18 13:16:10 -0400721 allOK = 0;
722 } // if
723
724 // Check that disk is really big enough to handle this...
725 if (mainHeader.backupLBA > diskSize) {
726 fprintf(stderr, "Error! Disk is too small -- either the original MBR is corrupt or you're\n");
727 fprintf(stderr, "working from an MBR copied to a file! Aborting!\n");
728 printf("(Disk size is %ld sectors, needs to be %ld sectors.)\n", diskSize,
729 mainHeader.backupLBA);
730 allOK = 0;
731 } // if
732
733 // Check for overlapping partitions....
srs5694e4ac11e2009-08-31 10:13:04 -0400734 if (FindOverlaps() > 0) {
735 allOK = 0;
736 fprintf(stderr, "Aborting write operation!\n");
737 } // if
738
739 // Check for mismatched MBR and GPT data, but let it pass if found
740 // (function displays warning message)
741 FindHybridMismatches();
srs5694e7b4ff92009-08-18 13:16:10 -0400742
srs56942a9f5da2009-08-26 00:48:01 -0400743 // Pull out some data that's needed before doing byte-order reversal on
744 // big-endian systems....
745 numParts = mainHeader.numParts;
746 secondTable = secondHeader.partitionEntriesLBA;
747 if (IsLittleEndian() == 0) {
748 // Reverse partition bytes first, since that function requires non-reversed
749 // data from the main header....
750 ReversePartitionBytes();
751 ReverseHeaderBytes(&mainHeader);
752 ReverseHeaderBytes(&secondHeader);
753 } // if
srs5694e7b4ff92009-08-18 13:16:10 -0400754 RecomputeCRCs();
755
756 if (allOK) {
757 printf("\nFinal checks complete. About to write GPT data. THIS WILL OVERWRITE EXISTING\n");
758 printf("MBR PARTITIONS!! THIS PROGRAM IS BETA QUALITY AT BEST. IF YOU LOSE ALL YOUR\n");
759 printf("DATA, YOU HAVE ONLY YOURSELF TO BLAME IF YOU ANSWER 'Y' BELOW!\n\n");
760 printf("Do you want to proceed, possibly destroying your data? (Y/N) ");
761 fgets(line, 255, stdin);
762 sscanf(line, "%c", &answer);
763 if ((answer == 'Y') || (answer == 'y')) {
764 printf("OK; writing new GPT partition table.\n");
765 } else {
766 allOK = 0;
767 } // if/else
768 } // if
769
770 // Do it!
771 if (allOK) {
srs5694e4ac11e2009-08-31 10:13:04 -0400772 fd = OpenForWrite(device);
srs5694e7b4ff92009-08-18 13:16:10 -0400773 if (fd != -1) {
774 // First, write the protective MBR...
srs5694e4ac11e2009-08-31 10:13:04 -0400775 protectiveMBR.WriteMBRData(fd);
srs5694e7b4ff92009-08-18 13:16:10 -0400776
777 // Now write the main GPT header...
778 if (allOK)
779 if (write(fd, &mainHeader, 512) == -1)
srs5694e4ac11e2009-08-31 10:13:04 -0400780 allOK = 0;
srs5694e7b4ff92009-08-18 13:16:10 -0400781
782 // Now write the main partition tables...
srs5694e4ac11e2009-08-31 10:13:04 -0400783 if (allOK) {
784 if (write(fd, partitions, GPT_SIZE * numParts) == -1)
srs5694e7b4ff92009-08-18 13:16:10 -0400785 allOK = 0;
786 } // if
787
788 // Now seek to near the end to write the secondary GPT....
srs5694e4ac11e2009-08-31 10:13:04 -0400789 if (allOK) {
srs5694e7b4ff92009-08-18 13:16:10 -0400790 offset = (off_t) secondTable * (off_t) (blockSize);
791 if (lseek64(fd, offset, SEEK_SET) == (off_t) - 1) {
792 allOK = 0;
793 printf("Unable to seek to end of disk!\n");
794 } // if
795 } // if
796
797 // Now write the secondary partition tables....
srs5694e4ac11e2009-08-31 10:13:04 -0400798 if (allOK)
srs56942a9f5da2009-08-26 00:48:01 -0400799 if (write(fd, partitions, GPT_SIZE * numParts) == -1)
srs5694e7b4ff92009-08-18 13:16:10 -0400800 allOK = 0;
801
802 // Now write the secondary GPT header...
srs5694e4ac11e2009-08-31 10:13:04 -0400803 if (allOK)
srs5694e7b4ff92009-08-18 13:16:10 -0400804 if (write(fd, &secondHeader, 512) == -1)
srs5694e4ac11e2009-08-31 10:13:04 -0400805 allOK = 0;
srs5694e7b4ff92009-08-18 13:16:10 -0400806
807 // re-read the partition table
808 if (allOK) {
srs5694e35eb1b2009-09-14 00:29:34 -0400809 DiskSync(fd);
srs5694e7b4ff92009-08-18 13:16:10 -0400810 } // if
811
812 if (allOK) { // writes completed OK
srs5694e4ac11e2009-08-31 10:13:04 -0400813 printf("The operation has completed successfully.\n");
srs5694e7b4ff92009-08-18 13:16:10 -0400814 } else {
srs5694e4ac11e2009-08-31 10:13:04 -0400815 printf("Warning! An error was reported when writing the partition table! This error\n");
816 printf("MIGHT be harmless, but you may have trashed the disk! Use parted and, if\n");
817 printf("necessary, restore your original partition table.\n");
srs5694e7b4ff92009-08-18 13:16:10 -0400818 } // if/else
srs5694e4ac11e2009-08-31 10:13:04 -0400819 close(fd);
srs5694e7b4ff92009-08-18 13:16:10 -0400820 } else {
srs5694e4ac11e2009-08-31 10:13:04 -0400821 fprintf(stderr, "Unable to open device %s for writing! Errno is %d! Aborting write!\n",
822 device, errno);
823 allOK = 0;
srs5694e7b4ff92009-08-18 13:16:10 -0400824 } // if/else
825 } else {
826 printf("Aborting write of new partition table.\n");
827 } // if
828
srs56942a9f5da2009-08-26 00:48:01 -0400829 if (IsLittleEndian() == 0) {
830 // Reverse (normalize) header bytes first, since ReversePartitionBytes()
831 // requires non-reversed data in mainHeader...
832 ReverseHeaderBytes(&mainHeader);
833 ReverseHeaderBytes(&secondHeader);
834 ReversePartitionBytes();
835 } // if
836
srs5694e7b4ff92009-08-18 13:16:10 -0400837 return (allOK);
838} // GPTData::SaveGPTData()
839
840// Save GPT data to a backup file. This function does much less error
841// checking than SaveGPTData(). It can therefore preserve many types of
842// corruption for later analysis; however, it preserves only the MBR,
843// the main GPT header, the backup GPT header, and the main partition
844// table; it discards the backup partition table, since it should be
845// identical to the main partition table on healthy disks.
846int GPTData::SaveGPTBackup(char* filename) {
srs56942a9f5da2009-08-26 00:48:01 -0400847 int fd, allOK = 1;
848 uint32_t numParts;
srs5694e7b4ff92009-08-18 13:16:10 -0400849
850 if ((fd = open(filename, O_WRONLY | O_CREAT, S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH)) != -1) {
srs56942a9f5da2009-08-26 00:48:01 -0400851 // Reverse the byte order, if necessary....
852 numParts = mainHeader.numParts;
853 if (IsLittleEndian() == 0) {
854 ReversePartitionBytes();
855 ReverseHeaderBytes(&mainHeader);
856 ReverseHeaderBytes(&secondHeader);
857 } // if
858
859 // Now write the protective MBR...
srs5694e7b4ff92009-08-18 13:16:10 -0400860 protectiveMBR.WriteMBRData(fd);
861
862 // Now write the main GPT header...
863 if (allOK)
864 if (write(fd, &mainHeader, 512) == -1)
865 allOK = 0;
866
867 // Now write the secondary GPT header...
868 if (allOK)
869 if (write(fd, &secondHeader, 512) == -1)
srs5694e4ac11e2009-08-31 10:13:04 -0400870 allOK = 0;
srs5694e7b4ff92009-08-18 13:16:10 -0400871
872 // Now write the main partition tables...
873 if (allOK) {
srs56942a9f5da2009-08-26 00:48:01 -0400874 if (write(fd, partitions, GPT_SIZE * numParts) == -1)
srs5694e7b4ff92009-08-18 13:16:10 -0400875 allOK = 0;
876 } // if
877
878 if (allOK) { // writes completed OK
879 printf("The operation has completed successfully.\n");
880 } else {
881 printf("Warning! An error was reported when writing the backup file.\n");
882 printf("It may not be useable!\n");
883 } // if/else
884 close(fd);
srs56942a9f5da2009-08-26 00:48:01 -0400885
886 // Now reverse the byte-order reversal, if necessary....
887 if (IsLittleEndian() == 0) {
888 ReverseHeaderBytes(&mainHeader);
889 ReverseHeaderBytes(&secondHeader);
890 ReversePartitionBytes();
891 } // if
srs5694e7b4ff92009-08-18 13:16:10 -0400892 } else {
893 fprintf(stderr, "Unable to open file %s for writing! Aborting!\n", filename);
894 allOK = 0;
895 } // if/else
896 return allOK;
897} // GPTData::SaveGPTBackup()
898
899// Load GPT data from a backup file created by SaveGPTBackup(). This function
900// does minimal error checking. It returns 1 if it completed successfully,
901// 0 if there was a problem. In the latter case, it creates a new empty
902// set of partitions.
903int GPTData::LoadGPTBackup(char* filename) {
904 int fd, allOK = 1, val;
905 uint32_t numParts, sizeOfEntries, sizeOfParts, newCRC;
srs56942a9f5da2009-08-26 00:48:01 -0400906 int littleEndian = 1;
srs5694e7b4ff92009-08-18 13:16:10 -0400907
908 if ((fd = open(filename, O_RDONLY)) != -1) {
srs56942a9f5da2009-08-26 00:48:01 -0400909 if (IsLittleEndian() == 0)
910 littleEndian = 0;
911
srs5694e7b4ff92009-08-18 13:16:10 -0400912 // Let the MBRData class load the saved MBR...
srs5694221e0872009-08-29 15:00:31 -0400913 protectiveMBR.ReadMBRData(fd, 0); // 0 = don't check block size
srs5694e7b4ff92009-08-18 13:16:10 -0400914
915 // Load the main GPT header, check its vaility, and set the GPT
916 // size based on the data
917 read(fd, &mainHeader, 512);
918 mainCrcOk = CheckHeaderCRC(&mainHeader);
919
srs56942a9f5da2009-08-26 00:48:01 -0400920 // Reverse byte order, if necessary
921 if (littleEndian == 0) {
922 ReverseHeaderBytes(&mainHeader);
923 } // if
924
srs5694e7b4ff92009-08-18 13:16:10 -0400925 // Load the backup GPT header in much the same way as the main
926 // GPT header....
927 read(fd, &secondHeader, 512);
928 secondCrcOk = CheckHeaderCRC(&secondHeader);
929
srs56942a9f5da2009-08-26 00:48:01 -0400930 // Reverse byte order, if necessary
931 if (littleEndian == 0) {
932 ReverseHeaderBytes(&secondHeader);
933 } // if
934
srs5694e7b4ff92009-08-18 13:16:10 -0400935 // Return valid headers code: 0 = both headers bad; 1 = main header
936 // good, backup bad; 2 = backup header good, main header bad;
937 // 3 = both headers good. Note these codes refer to valid GPT
938 // signatures and version numbers; more subtle problems will elude
939 // this check!
940 if ((val = CheckHeaderValidity()) > 0) {
941 if (val == 2) { // only backup header seems to be good
942 numParts = secondHeader.numParts;
srs5694e4ac11e2009-08-31 10:13:04 -0400943 sizeOfEntries = secondHeader.sizeOfPartitionEntries;
srs5694e7b4ff92009-08-18 13:16:10 -0400944 } else { // main header is OK
945 numParts = mainHeader.numParts;
946 sizeOfEntries = mainHeader.sizeOfPartitionEntries;
947 } // if/else
948
949 SetGPTSize(numParts);
950
951 // If current disk size doesn't match that of backup....
952 if (secondHeader.currentLBA != diskSize - UINT64_C(1)) {
953 printf("Warning! Current disk size doesn't match that of the backup!\n"
srs5694e4ac11e2009-08-31 10:13:04 -0400954 "Adjusting sizes to match, but subsequent problems are possible!\n");
srs5694e7b4ff92009-08-18 13:16:10 -0400955 secondHeader.currentLBA = mainHeader.backupLBA = diskSize - UINT64_C(1);
956 mainHeader.lastUsableLBA = diskSize - mainHeader.firstUsableLBA;
957 secondHeader.lastUsableLBA = mainHeader.lastUsableLBA;
958 secondHeader.partitionEntriesLBA = secondHeader.lastUsableLBA + UINT64_C(1);
959 } // if
960
961 // Load main partition table, and record whether its CRC
962 // matches the stored value
963 sizeOfParts = numParts * sizeOfEntries;
964 read(fd, partitions, sizeOfParts);
965
966 newCRC = chksum_crc32((unsigned char*) partitions, sizeOfParts);
967 mainPartsCrcOk = (newCRC == mainHeader.partitionEntriesCRC);
968 secondPartsCrcOk = (newCRC == secondHeader.partitionEntriesCRC);
srs56942a9f5da2009-08-26 00:48:01 -0400969 // Reverse byte order, if necessary
srs5694e4ac11e2009-08-31 10:13:04 -0400970 if (littleEndian == 0) {
971 ReversePartitionBytes();
972 } // if
srs56942a9f5da2009-08-26 00:48:01 -0400973
srs5694e7b4ff92009-08-18 13:16:10 -0400974 } else {
975 allOK = 0;
976 } // if/else
977 } else {
978 allOK = 0;
979 fprintf(stderr, "Unable to open file %s for reading! Aborting!\n", filename);
980 } // if/else
981
982 // Something went badly wrong, so blank out partitions
983 if (allOK == 0) {
984 ClearGPTData();
985 protectiveMBR.MakeProtectiveMBR();
986 } // if
987 return allOK;
988} // GPTData::LoadGPTBackup()
989
srs5694e4ac11e2009-08-31 10:13:04 -0400990// Tell user whether Apple Partition Map (APM) was discovered....
991void GPTData::ShowAPMState(void) {
992 if (apmFound)
993 printf(" APM: present\n");
994 else
995 printf(" APM: not present\n");
996} // GPTData::ShowAPMState()
997
998// Tell user about the state of the GPT data....
999void GPTData::ShowGPTState(void) {
1000 switch (state) {
1001 case gpt_invalid:
1002 printf(" GPT: not present\n");
1003 break;
1004 case gpt_valid:
1005 printf(" GPT: present\n");
1006 break;
1007 case gpt_corrupt:
1008 printf(" GPT: damaged\n");
1009 break;
1010 default:
1011 printf("\a GPT: unknown -- bug!\n");
1012 break;
1013 } // switch
1014} // GPTData::ShowGPTState()
1015
1016// Display the basic GPT data
1017void GPTData::DisplayGPTData(void) {
srs5694e35eb1b2009-09-14 00:29:34 -04001018 int i;
srs5694e4ac11e2009-08-31 10:13:04 -04001019 char sizeInSI[255]; // String to hold size of disk in SI units
1020 char tempStr[255];
1021 uint64_t temp, totalFree;
1022
1023 BytesToSI(diskSize * blockSize, sizeInSI);
1024 printf("Disk %s: %lu sectors, %s\n", device,
1025 (unsigned long) diskSize, sizeInSI);
1026 printf("Disk identifier (GUID): %s\n", GUIDToStr(mainHeader.diskGUID, tempStr));
1027 printf("Partition table holds up to %lu entries\n", (unsigned long) mainHeader.numParts);
1028 printf("First usable sector is %lu, last usable sector is %lu\n",
1029 (unsigned long) mainHeader.firstUsableLBA,
1030 (unsigned long) mainHeader.lastUsableLBA);
1031 totalFree = FindFreeBlocks(&i, &temp);
1032 printf("Total free space is %llu sectors (%s)\n", totalFree,
1033 BytesToSI(totalFree * (uint64_t) blockSize, sizeInSI));
1034 printf("\nNumber Start (sector) End (sector) Size Code Name\n");
1035 for (i = 0; i < mainHeader.numParts; i++) {
1036 partitions[i].ShowSummary(i, blockSize, sizeInSI);
1037 } // for
1038} // GPTData::DisplayGPTData()
1039
1040// Get partition number from user and then call ShowPartDetails(partNum)
1041// to show its detailed information
1042void GPTData::ShowDetails(void) {
1043 int partNum;
1044 uint32_t low, high;
1045
1046 if (GetPartRange(&low, &high) > 0) {
1047 partNum = GetPartNum();
1048 ShowPartDetails(partNum);
1049 } else {
1050 printf("No partitions\n");
1051 } // if/else
1052} // GPTData::ShowDetails()
1053
1054// Show detailed information on the specified partition
1055void GPTData::ShowPartDetails(uint32_t partNum) {
1056 if (partitions[partNum].GetFirstLBA() != 0) {
1057 partitions[partNum].ShowDetails(blockSize);
1058 } else {
1059 printf("Partition #%d does not exist.", (int) (partNum + 1));
1060 } // if
1061} // GPTData::ShowPartDetails()
1062
1063/*********************************************************************
1064 * *
1065 * Begin functions that obtain information from the users, and often *
1066 * do something with that information (call other functions) *
1067 * *
1068 *********************************************************************/
1069
1070// Prompts user for partition number and returns the result.
1071uint32_t GPTData::GetPartNum(void) {
1072 uint32_t partNum;
1073 uint32_t low, high;
1074 char prompt[255];
1075
1076 if (GetPartRange(&low, &high) > 0) {
1077 sprintf(prompt, "Partition number (%d-%d): ", low + 1, high + 1);
1078 partNum = GetNumber(low + 1, high + 1, low, prompt);
1079 } else partNum = 1;
1080 return (partNum - 1);
1081} // GPTData::GetPartNum()
1082
1083// What it says: Resize the partition table. (Default is 128 entries.)
1084void GPTData::ResizePartitionTable(void) {
1085 int newSize;
1086 char prompt[255];
1087 uint32_t curLow, curHigh;
1088
1089 printf("Current partition table size is %lu.\n",
1090 (unsigned long) mainHeader.numParts);
1091 GetPartRange(&curLow, &curHigh);
1092 curHigh++; // since GetPartRange() returns numbers starting from 0...
1093 // There's no point in having fewer than four partitions....
1094 if (curHigh < 4)
1095 curHigh = 4;
1096 sprintf(prompt, "Enter new size (%d up, default %d): ", (int) curHigh,
1097 (int) NUM_GPT_ENTRIES);
1098 newSize = GetNumber(4, 65535, 128, prompt);
1099 if (newSize < 128) {
1100 printf("Caution: The partition table size should officially be 16KB or larger,\n"
1101 "which works out to 128 entries. In practice, smaller tables seem to\n"
1102 "work with most OSes, but this practice is risky. I'm proceeding with\n"
1103 "the resize, but you may want to reconsider this action and undo it.\n\n");
1104 } // if
1105 SetGPTSize(newSize);
1106} // GPTData::ResizePartitionTable()
1107
1108// Interactively create a partition
1109void GPTData::CreatePartition(void) {
1110 uint64_t firstBlock, firstInLargest, lastBlock, sector;
1111 char prompt[255];
1112 int partNum, firstFreePart = 0;
1113
1114 // Find first free partition...
1115 while (partitions[firstFreePart].GetFirstLBA() != 0) {
1116 firstFreePart++;
1117 } // while
1118
1119 if (((firstBlock = FindFirstAvailable()) != 0) &&
1120 (firstFreePart < mainHeader.numParts)) {
1121 lastBlock = FindLastAvailable(firstBlock);
1122 firstInLargest = FindFirstInLargest();
1123
1124 // Get partition number....
1125 do {
1126 sprintf(prompt, "Partition number (%d-%d, default %d): ", firstFreePart + 1,
1127 mainHeader.numParts, firstFreePart + 1);
1128 partNum = GetNumber(firstFreePart + 1, mainHeader.numParts,
1129 firstFreePart + 1, prompt) - 1;
1130 if (partitions[partNum].GetFirstLBA() != 0)
1131 printf("partition %d is in use.\n", partNum + 1);
1132 } while (partitions[partNum].GetFirstLBA() != 0);
1133
1134 // Get first block for new partition...
1135 sprintf(prompt,
1136 "First sector (%llu-%llu, default = %llu) or {+-}size{KMGT}: ",
1137 firstBlock, lastBlock, firstInLargest);
1138 do {
1139 sector = GetSectorNum(firstBlock, lastBlock, firstInLargest, prompt);
1140 } while (IsFree(sector) == 0);
1141 firstBlock = sector;
1142
1143 // Get last block for new partitions...
1144 lastBlock = FindLastInFree(firstBlock);
1145 sprintf(prompt,
1146 "Last sector (%llu-%llu, default = %llu) or {+-}size{KMGT}: ",
1147 firstBlock, lastBlock, lastBlock);
1148 do {
1149 sector = GetSectorNum(firstBlock, lastBlock, lastBlock, prompt);
1150 } while (IsFree(sector) == 0);
1151 lastBlock = sector;
1152
1153 partitions[partNum].SetFirstLBA(firstBlock);
1154 partitions[partNum].SetLastLBA(lastBlock);
1155
1156 partitions[partNum].SetUniqueGUID(1);
1157 partitions[partNum].ChangeType();
1158 partitions[partNum].SetName((unsigned char*) partitions[partNum].GetNameType(prompt));
1159 } else {
1160 printf("No free sectors available\n");
1161 } // if/else
1162} // GPTData::CreatePartition()
1163
1164// Interactively delete a partition (duh!)
1165void GPTData::DeletePartition(void) {
1166 int partNum;
1167 uint32_t low, high;
1168 uint64_t startSector, length;
1169 char prompt[255];
1170
1171 if (GetPartRange(&low, &high) > 0) {
1172 sprintf(prompt, "Partition number (%d-%d): ", low + 1, high + 1);
1173 partNum = GetNumber(low + 1, high + 1, low, prompt);
1174
1175 // In case there's a protective MBR, look for & delete matching
1176 // MBR partition....
1177 startSector = partitions[partNum - 1].GetFirstLBA();
1178 length = partitions[partNum - 1].GetLengthLBA();
1179 protectiveMBR.DeleteByLocation(startSector, length);
1180
1181 // Now delete the GPT partition
1182 partitions[partNum - 1].BlankPartition();
1183 } else {
1184 printf("No partitions\n");
1185 } // if/else
1186} // GPTData::DeletePartition()
1187
1188// Prompt user for a partition number, then change its type code
1189// using ChangeGPTType(struct GPTPartition*) function.
1190void GPTData::ChangePartType(void) {
1191 int partNum;
1192 uint32_t low, high;
1193
1194 if (GetPartRange(&low, &high) > 0) {
1195 partNum = GetPartNum();
1196 partitions[partNum].ChangeType();
1197 } else {
1198 printf("No partitions\n");
1199 } // if/else
1200} // GPTData::ChangePartType()
1201
1202// Partition attributes seem to be rarely used, but I want a way to
1203// adjust them for completeness....
1204void GPTData::SetAttributes(uint32_t partNum) {
1205 Attributes theAttr;
1206
1207 theAttr.SetAttributes(partitions[partNum].GetAttributes());
1208 theAttr.DisplayAttributes();
1209 theAttr.ChangeAttributes();
1210 partitions[partNum].SetAttributes(theAttr.GetAttributes());
1211} // GPTData::SetAttributes()
1212
srs5694c0ca8f82009-08-20 21:35:25 -04001213// This function destroys the on-disk GPT structures. Returns 1 if the
1214// user confirms destruction, 0 if the user aborts.
1215int GPTData::DestroyGPT(void) {
srs5694e35eb1b2009-09-14 00:29:34 -04001216 int fd, i;
srs5694c0ca8f82009-08-20 21:35:25 -04001217 char blankSector[512], goOn;
1218
1219 for (i = 0; i < 512; i++) {
1220 blankSector[i] = '\0';
1221 } // for
1222
srs5694e35eb1b2009-09-14 00:29:34 -04001223 if ((apmFound) || (bsdFound)) {
1224 printf("WARNING: APM or BSD disklabel structures detected! This operation could\n"
1225 "damage any APM or BSD partitions on this disk!\n");
1226 } // if APM or BSD
srs5694c0ca8f82009-08-20 21:35:25 -04001227 printf("\a\aAbout to wipe out GPT on %s. Proceed? ", device);
1228 goOn = GetYN();
1229 if (goOn == 'Y') {
1230 fd = open(device, O_WRONLY);
1231#ifdef __APPLE__
1232 // MacOS X requires a shared lock under some circumstances....
1233 if (fd < 0) {
1234 fd = open(device, O_WRONLY|O_SHLOCK);
1235 } // if
1236#endif
1237 if (fd != -1) {
1238 lseek64(fd, mainHeader.currentLBA * 512, SEEK_SET); // seek to GPT header
1239 write(fd, blankSector, 512); // blank it out
1240 lseek64(fd, mainHeader.partitionEntriesLBA * 512, SEEK_SET); // seek to partition table
1241 for (i = 0; i < GetBlocksInPartTable(); i++)
1242 write(fd, blankSector, 512);
1243 lseek64(fd, secondHeader.partitionEntriesLBA * 512, SEEK_SET); // seek to partition table
1244 for (i = 0; i < GetBlocksInPartTable(); i++)
1245 write(fd, blankSector, 512);
1246 lseek64(fd, secondHeader.currentLBA * 512, SEEK_SET); // seek to GPT header
1247 write(fd, blankSector, 512); // blank it out
srs5694e4ac11e2009-08-31 10:13:04 -04001248 printf("Blank out MBR? ");
1249 if (GetYN() == 'Y') {
srs5694c0ca8f82009-08-20 21:35:25 -04001250 lseek64(fd, 0, SEEK_SET);
1251 write(fd, blankSector, 512); // blank it out
srs5694e35eb1b2009-09-14 00:29:34 -04001252 } else { // write current protective MBR, in case it's hybrid....
1253 // find and delete 0xEE partitions in MBR
1254 for (i = 0; i < 4; i++) {
1255 if (protectiveMBR.GetType(i) == (uint8_t) 0xEE) {
1256 protectiveMBR.DeletePartition(i);
1257 } // if
1258 } // for
1259 protectiveMBR.WriteMBRData(fd);
1260 } // if/else
1261 DiskSync(fd);
srs5694c0ca8f82009-08-20 21:35:25 -04001262 close(fd);
srs5694e4ac11e2009-08-31 10:13:04 -04001263 printf("GPT data structures destroyed! You may now partition the disk using fdisk or\n"
1264 "other utilities. Program will now terminate.\n");
srs5694c0ca8f82009-08-20 21:35:25 -04001265 } else {
1266 printf("Problem opening %s for writing! Program will now terminate.\n");
1267 } // if/else (fd != -1)
1268 } // if (goOn == 'Y')
1269 return (goOn == 'Y');
1270} // GPTData::DestroyGPT()
1271
srs5694e4ac11e2009-08-31 10:13:04 -04001272/**************************************************************************
1273 * *
1274 * Partition table transformation functions (MBR or BSD disklabel to GPT) *
1275 * (some of these functions may require user interaction) *
1276 * *
1277 **************************************************************************/
1278
1279// Examines the MBR & GPT data, and perhaps asks the user questions, to
1280// determine which set of data to use: the MBR (use_mbr), the GPT (use_gpt),
1281// or create a new set of partitions (use_new)
1282WhichToUse GPTData::UseWhichPartitions(void) {
1283 WhichToUse which = use_new;
1284 MBRValidity mbrState;
1285 int answer;
1286
1287 mbrState = protectiveMBR.GetValidity();
1288
1289 if ((state == gpt_invalid) && ((mbrState == mbr) || (mbrState == hybrid))) {
1290 printf("\n\a***************************************************************\n"
1291 "Found invalid GPT and valid MBR; converting MBR to GPT format.\n"
1292 "THIS OPERATON IS POTENTIALLY DESTRUCTIVE! Exit by typing 'q' if\n"
1293 "you don't want to convert your MBR partitions to GPT format!\n"
1294 "***************************************************************\n\n");
1295 which = use_mbr;
1296 } // if
1297
1298 if ((state == gpt_invalid) && bsdFound) {
1299 printf("\n\a**********************************************************************\n"
1300 "Found invalid GPT and valid BSD disklabel; converting BSD disklabel\n"
1301 "to GPT format. THIS OPERATON IS POTENTIALLY DESTRUCTIVE! Your first\n"
1302 "BSD partition will likely be unusable. Exit by typing 'q' if you don't\n"
1303 "want to convert your BSD partitions to GPT format!\n"
1304 "**********************************************************************\n\n");
1305 which = use_bsd;
1306 } // if
1307
1308 if ((state == gpt_valid) && (mbrState == gpt)) {
1309 printf("Found valid GPT with protective MBR; using GPT.\n");
1310 which = use_gpt;
1311 } // if
1312 if ((state == gpt_valid) && (mbrState == hybrid)) {
1313 printf("Found valid GPT with hybrid MBR; using GPT.\n");
1314 which = use_gpt;
1315 } // if
1316 if ((state == gpt_valid) && (mbrState == invalid)) {
1317 printf("\aFound valid GPT with corrupt MBR; using GPT and will create new\nprotective MBR on save.\n");
1318 which = use_gpt;
1319 protectiveMBR.MakeProtectiveMBR();
1320 } // if
1321 if ((state == gpt_valid) && (mbrState == mbr)) {
1322 printf("Found valid MBR and GPT. Which do you want to use?\n");
1323 answer = GetNumber(1, 3, 2, (char*) " 1 - MBR\n 2 - GPT\n 3 - Create blank GPT\n\nYour answer: ");
1324 if (answer == 1) {
1325 which = use_mbr;
1326 } else if (answer == 2) {
1327 which = use_gpt;
1328 protectiveMBR.MakeProtectiveMBR();
1329 printf("Using GPT and creating fresh protective MBR.\n");
1330 } else which = use_new;
1331 } // if
1332
1333 // Nasty decisions here -- GPT is present, but corrupt (bad CRCs or other
1334 // problems)
1335 if (state == gpt_corrupt) {
1336 if ((mbrState == mbr) || (mbrState == hybrid)) {
1337 printf("Found valid MBR and corrupt GPT. Which do you want to use? (Using the\n"
1338 "GPT MAY permit recovery of GPT data.)\n");
1339 answer = GetNumber(1, 3, 2, (char*) " 1 - MBR\n 2 - GPT\n 3 - Create blank GPT\n\nYour answer: ");
1340 if (answer == 1) {
1341 which = use_mbr;
1342// protectiveMBR.MakeProtectiveMBR();
1343 } else if (answer == 2) {
1344 which = use_gpt;
1345 } else which = use_new;
1346 } else if (mbrState == invalid) {
1347 printf("Found invalid MBR and corrupt GPT. What do you want to do? (Using the\n"
1348 "GPT MAY permit recovery of GPT data.)\n");
1349 answer = GetNumber(1, 2, 1, (char*) " 1 - GPT\n 2 - Create blank GPT\n\nYour answer: ");
1350 if (answer == 1) {
1351 which = use_gpt;
1352 } else which = use_new;
1353 } else { // corrupt GPT, MBR indicates it's a GPT disk....
1354 printf("\a\a****************************************************************************\n"
1355 "Caution: Found protective or hybrid MBR and corrupt GPT. Using GPT, but disk\n"
1356 "verification and recovery are STRONGLY recommended.\n"
1357 "****************************************************************************\n");
1358 } // if/else/else
1359 } // if (corrupt GPT)
1360
1361 if (which == use_new)
1362 printf("Creating new GPT entries.\n");
1363
1364 return which;
1365} // UseWhichPartitions()
1366
1367// Convert MBR partition table into GPT form
1368int GPTData::XFormPartitions(void) {
1369 int i, numToConvert;
1370 uint8_t origType;
1371 struct newGUID;
srs5694e4ac11e2009-08-31 10:13:04 -04001372
1373 // Clear out old data & prepare basics....
1374 ClearGPTData();
1375
1376 // Convert the smaller of the # of GPT or MBR partitions
1377 if (mainHeader.numParts > (NUM_LOGICALS + 4))
1378 numToConvert = NUM_LOGICALS + 4;
1379 else
1380 numToConvert = mainHeader.numParts;
1381
1382 for (i = 0; i < numToConvert; i++) {
1383 origType = protectiveMBR.GetType(i);
1384 // don't waste CPU time trying to convert extended, hybrid protective, or
1385 // null (non-existent) partitions
srs5694e35eb1b2009-09-14 00:29:34 -04001386 if ((origType != 0x05) && (origType != 0x0f) && (origType != 0x85) &&
srs5694e4ac11e2009-08-31 10:13:04 -04001387 (origType != 0x00) && (origType != 0xEE))
1388 partitions[i] = protectiveMBR.AsGPT(i);
1389 } // for
1390
1391 // Convert MBR into protective MBR
1392 protectiveMBR.MakeProtectiveMBR();
1393
1394 // Record that all original CRCs were OK so as not to raise flags
1395 // when doing a disk verification
1396 mainCrcOk = secondCrcOk = mainPartsCrcOk = secondPartsCrcOk = 1;
1397
1398 return (1);
1399} // GPTData::XFormPartitions()
1400
1401// Transforms BSD disklabel on the specified partition (numbered from 0).
1402// If an invalid partition number is given, the program prompts for one.
1403// Returns the number of new partitions created.
1404int GPTData::XFormDisklabel(int i) {
1405 uint32_t low, high, partNum, startPart;
1406 uint16_t hexCode;
1407 int goOn = 1, numDone = 0;
1408 BSDData disklabel;
1409
1410 if (GetPartRange(&low, &high) != 0) {
1411 if ((i < low) || (i > high))
1412 partNum = GetPartNum();
1413 else
1414 partNum = (uint32_t) i;
1415
1416 // Find the partition after the last used one
1417 startPart = high + 1;
1418
1419 // Now see if the specified partition has a BSD type code....
1420 hexCode = partitions[partNum].GetHexType();
1421 if ((hexCode != 0xa500) && (hexCode != 0xa900)) {
1422 printf("Specified partition doesn't have a disklabel partition type "
1423 "code.\nContinue anyway?");
1424 goOn = (GetYN() == 'Y');
1425 } // if
1426
1427 // If all is OK, read the disklabel and convert it.
1428 if (goOn) {
1429 goOn = disklabel.ReadBSDData(device, partitions[partNum].GetFirstLBA(),
1430 partitions[partNum].GetLastLBA());
1431 if ((goOn) && (disklabel.IsDisklabel())) {
1432 numDone = XFormDisklabel(&disklabel, startPart);
1433 if (numDone == 1)
1434 printf("Converted %d BSD partition.\n", numDone);
1435 else
1436 printf("Converted %d BSD partitions.\n", numDone);
1437 } else {
1438 printf("Unable to convert partitions! Unrecognized BSD disklabel.\n");
1439 } // if/else
1440 } // if
1441 if (numDone > 0) { // converted partitions; delete carrier
1442 partitions[partNum].BlankPartition();
1443 } // if
1444 } else {
1445 printf("No partitions\n");
1446 } // if/else
1447 return numDone;
1448} // GPTData::XFormDisklable(int i)
1449
1450// Transform the partitions on an already-loaded BSD disklabel...
1451int GPTData::XFormDisklabel(BSDData* disklabel, int startPart) {
1452 int i, numDone = 0;
1453
1454 if ((disklabel->IsDisklabel()) && (startPart >= 0) &&
1455 (startPart < mainHeader.numParts)) {
1456 for (i = 0; i < disklabel->GetNumParts(); i++) {
1457 partitions[i + startPart] = disklabel->AsGPT(i);
1458 if (partitions[i + startPart].GetFirstLBA() != UINT64_C(0))
1459 numDone++;
1460 } // for
1461 } // if
1462
1463 // Record that all original CRCs were OK so as not to raise flags
1464 // when doing a disk verification
1465 mainCrcOk = secondCrcOk = mainPartsCrcOk = secondPartsCrcOk = 1;
1466
1467 return numDone;
1468} // GPTData::XFormDisklabel(BSDData* disklabel)
1469
1470// Create a hybrid MBR -- an ugly, funky thing that helps GPT work with
1471// OSes that don't understand GPT.
1472void GPTData::MakeHybrid(void) {
1473 uint32_t partNums[3];
1474 char line[255];
1475 int numParts, i, j, typeCode, bootable, mbrNum;
1476 uint64_t length;
1477 char fillItUp = 'M'; // fill extra partition entries? (Yes/No/Maybe)
1478 char eeFirst; // Whether EFI GPT (0xEE) partition comes first in table
1479
1480 printf("\nWARNING! Hybrid MBRs are flaky and potentially dangerous! If you decide not\n"
1481 "to use one, just hit the Enter key at the below prompt and your MBR\n"
1482 "partition table will be untouched.\n\n\a");
1483
1484 // Now get the numbers of up to three partitions to add to the
1485 // hybrid MBR....
1486 printf("Type from one to three GPT partition numbers, separated by spaces, to be\n"
1487 "added to the hybrid MBR, in sequence: ");
1488 fgets(line, 255, stdin);
1489 numParts = sscanf(line, "%d %d %d", &partNums[0], &partNums[1], &partNums[2]);
1490
1491 if (numParts > 0) {
1492 // Blank out the protective MBR, but leave the boot loader code
1493 // alone....
1494 protectiveMBR.EmptyMBR(0);
1495 protectiveMBR.SetDiskSize(diskSize);
1496 printf("Place EFI GPT (0xEE) partition first in MBR (good for GRUB)? ");
1497 eeFirst = GetYN();
1498 } // if
1499
1500 for (i = 0; i < numParts; i++) {
1501 j = partNums[i] - 1;
1502 printf("\nCreating entry for partition #%d\n", j + 1);
1503 if ((j >= 0) && (j < mainHeader.numParts)) {
srs5694e35eb1b2009-09-14 00:29:34 -04001504 if ((partitions[j].GetLastLBA() < UINT32_MAX) &&
1505 (partitions[j].GetLastLBA() > UINT64_C(0))) {
srs5694e4ac11e2009-08-31 10:13:04 -04001506 do {
1507 printf("Enter an MBR hex code (default %02X): ",
1508 typeHelper.GUIDToID(partitions[j].GetType()) / 256);
1509 fgets(line, 255, stdin);
1510 sscanf(line, "%x", &typeCode);
1511 if (line[0] == '\n')
1512 typeCode = partitions[j].GetHexType() / 256;
1513 } while ((typeCode <= 0) || (typeCode > 255));
1514 printf("Set the bootable flag? ");
1515 bootable = (GetYN() == 'Y');
1516 length = partitions[j].GetLengthLBA();
1517 if (eeFirst == 'Y')
1518 mbrNum = i + 1;
1519 else
1520 mbrNum = i;
1521 protectiveMBR.MakePart(mbrNum, (uint32_t) partitions[j].GetFirstLBA(),
1522 (uint32_t) length, typeCode, bootable);
1523 protectiveMBR.SetHybrid();
1524 } else { // partition out of range
srs5694e35eb1b2009-09-14 00:29:34 -04001525 printf("Partition %d ends beyond the 2TiB limit of MBR partitions or does not exist;\n"
1526 "omitting it.\n",
srs5694e4ac11e2009-08-31 10:13:04 -04001527 j + 1);
1528 } // if/else
1529 } else {
1530 printf("Partition %d is out of range; omitting it.\n", j + 1);
1531 } // if/else
1532 } // for
1533
1534 if (numParts > 0) { // User opted to create a hybrid MBR....
1535 // Create EFI protective partition that covers the start of the disk.
1536 // If this location (covering the main GPT data structures) is omitted,
1537 // Linux won't find any partitions on the disk. Note that this is
1538 // NUMBERED AFTER the hybrid partitions, contrary to what the
1539 // gptsync utility does. This is because Windows seems to choke on
1540 // disks with a 0xEE partition in the first slot and subsequent
1541 // additional partitions, unless it boots from the disk.
1542 if (eeFirst == 'Y')
1543 mbrNum = 0;
1544 else
1545 mbrNum = numParts;
1546 protectiveMBR.MakePart(mbrNum, 1, protectiveMBR.FindLastInFree(1), 0xEE);
1547
1548 // ... and for good measure, if there are any partition spaces left,
1549 // optionally create another protective EFI partition to cover as much
1550 // space as possible....
1551 for (i = 0; i < 4; i++) {
1552 if (protectiveMBR.GetType(i) == 0x00) { // unused entry....
1553 if (fillItUp == 'M') {
1554 printf("\nUnused partition space(s) found. Use one to protect more partitions? ");
1555 fillItUp = GetYN();
1556 typeCode = 0x00; // use this to flag a need to get type code
1557 } // if
1558 if (fillItUp == 'Y') {
1559 while ((typeCode <= 0) || (typeCode > 255)) {
1560 printf("Enter an MBR hex code (EE is EFI GPT, but may confuse MacOS): ");
1561 // Comment on above: Mac OS treats disks with more than one
1562 // 0xEE MBR partition as MBR disks, not as GPT disks.
1563 fgets(line, 255, stdin);
1564 sscanf(line, "%x", &typeCode);
1565 if (line[0] == '\n')
1566 typeCode = 0;
1567 } // while
1568 protectiveMBR.MakeBiggestPart(i, typeCode); // make a partition
1569 } // if (fillItUp == 'Y')
1570 } // if unused entry
1571 } // for (i = 0; i < 4; i++)
1572 } // if (numParts > 0)
1573} // GPTData::MakeHybrid()
1574
1575/**********************************************************************
1576 * *
1577 * Functions that adjust GPT data structures WITHOUT user interaction *
1578 * (they may display information for the user's benefit, though) *
1579 * *
1580 **********************************************************************/
1581
1582// Resizes GPT to specified number of entries. Creates a new table if
1583// necessary, copies data if it already exists.
1584int GPTData::SetGPTSize(uint32_t numEntries) {
1585 struct GPTPart* newParts;
1586 struct GPTPart* trash;
1587 uint32_t i, high, copyNum;
1588 int allOK = 1;
1589
1590 // First, adjust numEntries upward, if necessary, to get a number
1591 // that fills the allocated sectors
1592 i = blockSize / GPT_SIZE;
1593 if ((numEntries % i) != 0) {
1594 printf("Adjusting GPT size from %lu ", (unsigned long) numEntries);
1595 numEntries = ((numEntries / i) + 1) * i;
1596 printf("to %lu to fill the sector\n", (unsigned long) numEntries);
1597 } // if
1598
1599 newParts = (GPTPart*) calloc(numEntries, sizeof (GPTPart));
1600 if (newParts != NULL) {
1601 if (partitions != NULL) { // existing partitions; copy them over
1602 GetPartRange(&i, &high);
1603 if (numEntries < (high + 1)) { // Highest entry too high for new #
1604 printf("The highest-numbered partition is %lu, which is greater than the requested\n"
1605 "partition table size of %d; cannot resize. Perhaps sorting will help.\n",
1606 (unsigned long) (high + 1), numEntries);
1607 allOK = 0;
1608 } else { // go ahead with copy
1609 if (numEntries < mainHeader.numParts)
1610 copyNum = numEntries;
1611 else
1612 copyNum = mainHeader.numParts;
1613 for (i = 0; i < copyNum; i++) {
1614 newParts[i] = partitions[i];
1615 } // for
1616 trash = partitions;
1617 partitions = newParts;
1618 free(trash);
1619 } // if
1620 } else { // No existing partition table; just create it
1621 partitions = newParts;
1622 } // if/else existing partitions
1623 mainHeader.numParts = numEntries;
1624 secondHeader.numParts = numEntries;
1625 mainHeader.firstUsableLBA = ((numEntries * GPT_SIZE) / blockSize) + 2 ;
1626 secondHeader.firstUsableLBA = mainHeader.firstUsableLBA;
1627 mainHeader.lastUsableLBA = diskSize - mainHeader.firstUsableLBA;
1628 secondHeader.lastUsableLBA = mainHeader.lastUsableLBA;
1629 secondHeader.partitionEntriesLBA = secondHeader.lastUsableLBA + UINT64_C(1);
1630 if (diskSize > 0)
1631 CheckGPTSize();
1632 } else { // Bad memory allocation
1633 fprintf(stderr, "Error allocating memory for partition table!\n");
1634 allOK = 0;
1635 } // if/else
1636 return (allOK);
1637} // GPTData::SetGPTSize()
1638
1639// Blank the partition array
1640void GPTData::BlankPartitions(void) {
1641 uint32_t i;
1642
1643 for (i = 0; i < mainHeader.numParts; i++) {
1644 partitions[i].BlankPartition();
1645 } // for
1646} // GPTData::BlankPartitions()
1647
1648// Sort the GPT entries, eliminating gaps and making for a logical
1649// ordering. Relies on QuickSortGPT() for the bulk of the work
1650void GPTData::SortGPT(void) {
1651 int i, lastPart = 0;
1652 GPTPart temp;
1653
1654 // First, find the last partition with data, so as not to
1655 // spend needless time sorting empty entries....
1656 for (i = 0; i < mainHeader.numParts; i++) {
1657 if (partitions[i].GetFirstLBA() > 0)
1658 lastPart = i;
1659 } // for
1660
1661 // Now swap empties with the last partitions, to simplify the logic
1662 // in the Quicksort function....
1663 i = 0;
1664 while (i < lastPart) {
1665 if (partitions[i].GetFirstLBA() == 0) {
1666 temp = partitions[i];
1667 partitions[i] = partitions[lastPart];
1668 partitions[lastPart] = temp;
1669 lastPart--;
1670 } // if
1671 i++;
1672 } // while
1673
1674 // Now call the recursive quick sort routine to do the real work....
1675 QuickSortGPT(partitions, 0, lastPart);
1676} // GPTData::SortGPT()
1677
1678// Set up data structures for entirely new set of partitions on the
1679// specified device. Returns 1 if OK, 0 if there were problems.
srs5694e35eb1b2009-09-14 00:29:34 -04001680// Note that this function does NOT clear the protectiveMBR data
1681// structure, since it may hold the original MBR partitions if the
1682// program was launched on an MBR disk, and those may need to be
1683// converted to GPT format.
srs5694e4ac11e2009-08-31 10:13:04 -04001684int GPTData::ClearGPTData(void) {
srs5694e35eb1b2009-09-14 00:29:34 -04001685 int goOn = 1, i;
srs5694e4ac11e2009-08-31 10:13:04 -04001686
1687 // Set up the partition table....
1688 free(partitions);
1689 partitions = NULL;
1690 SetGPTSize(NUM_GPT_ENTRIES);
1691
1692 // Now initialize a bunch of stuff that's static....
1693 mainHeader.signature = GPT_SIGNATURE;
1694 mainHeader.revision = 0x00010000;
1695 mainHeader.headerSize = (uint32_t) HEADER_SIZE;
1696 mainHeader.reserved = 0;
1697 mainHeader.currentLBA = UINT64_C(1);
1698 mainHeader.partitionEntriesLBA = (uint64_t) 2;
1699 mainHeader.sizeOfPartitionEntries = GPT_SIZE;
1700 for (i = 0; i < GPT_RESERVED; i++) {
1701 mainHeader.reserved2[i] = '\0';
1702 } // for
1703
1704 // Now some semi-static items (computed based on end of disk)
1705 mainHeader.backupLBA = diskSize - UINT64_C(1);
1706 mainHeader.lastUsableLBA = diskSize - mainHeader.firstUsableLBA;
1707
1708 // Set a unique GUID for the disk, based on random numbers
1709 // rand() is only 32 bits, so multiply together to fill a 64-bit value
1710 mainHeader.diskGUID.data1 = (uint64_t) rand() * (uint64_t) rand();
1711 mainHeader.diskGUID.data2 = (uint64_t) rand() * (uint64_t) rand();
1712
1713 // Copy main header to backup header
1714 RebuildSecondHeader();
1715
1716 // Blank out the partitions array....
1717 BlankPartitions();
1718
1719 // Flag all CRCs as being OK....
1720 mainCrcOk = 1;
1721 secondCrcOk = 1;
1722 mainPartsCrcOk = 1;
1723 secondPartsCrcOk = 1;
1724
1725 return (goOn);
1726} // GPTData::ClearGPTData()
1727
1728void GPTData::SetName(uint32_t partNum, char* theName) {
1729 if ((partNum >= 0) && (partNum < mainHeader.numParts))
1730 if (partitions[partNum].GetFirstLBA() > 0)
1731 partitions[partNum].SetName((unsigned char*) theName);
1732} // GPTData::SetName
1733
1734// Set the disk GUID to the specified value. Note that the header CRCs must
1735// be recomputed after calling this function.
1736void GPTData::SetDiskGUID(GUIDData newGUID) {
1737 mainHeader.diskGUID = newGUID;
1738 secondHeader.diskGUID = newGUID;
1739} // SetDiskGUID()
1740
1741// Set the unique GUID of the specified partition. Returns 1 on
1742// successful completion, 0 if there were problems (invalid
1743// partition number).
1744int GPTData::SetPartitionGUID(uint32_t pn, GUIDData theGUID) {
1745 int retval = 0;
1746
1747 if (pn < mainHeader.numParts) {
1748 if (partitions[pn].GetFirstLBA() != UINT64_C(0)) {
1749 partitions[pn].SetUniqueGUID(theGUID);
1750 retval = 1;
1751 } // if
1752 } // if
1753 return retval;
1754} // GPTData::SetPartitionGUID()
1755
1756/********************************************************
1757 * *
1758 * Functions that return data about GPT data structures *
1759 * (most of these are inline in gpt.h) *
1760 * *
1761 ********************************************************/
1762
1763// Find the low and high used partition numbers (numbered from 0).
1764// Return value is the number of partitions found. Note that the
1765// *low and *high values are both set to 0 when no partitions
1766// are found, as well as when a single partition in the first
1767// position exists. Thus, the return value is the only way to
1768// tell when no partitions exist.
1769int GPTData::GetPartRange(uint32_t *low, uint32_t *high) {
1770 uint32_t i;
1771 int numFound = 0;
1772
1773 *low = mainHeader.numParts + 1; // code for "not found"
1774 *high = 0;
1775 if (mainHeader.numParts > 0) { // only try if partition table exists...
1776 for (i = 0; i < mainHeader.numParts; i++) {
1777 if (partitions[i].GetFirstLBA() != UINT64_C(0)) { // it exists
1778 *high = i; // since we're counting up, set the high value
1779 // Set the low value only if it's not yet found...
1780 if (*low == (mainHeader.numParts + 1)) *low = i;
1781 numFound++;
1782 } // if
1783 } // for
1784 } // if
1785
1786 // Above will leave *low pointing to its "not found" value if no partitions
1787 // are defined, so reset to 0 if this is the case....
1788 if (*low == (mainHeader.numParts + 1))
1789 *low = 0;
1790 return numFound;
1791} // GPTData::GetPartRange()
1792
1793/****************************************************
1794 * *
1795 * Functions that return data about disk free space *
1796 * *
1797 ****************************************************/
1798
1799// Find the first available block after the starting point; returns 0 if
1800// there are no available blocks left
1801uint64_t GPTData::FindFirstAvailable(uint64_t start) {
1802 uint64_t first;
1803 uint32_t i;
1804 int firstMoved = 0;
1805
1806 // Begin from the specified starting point or from the first usable
1807 // LBA, whichever is greater...
1808 if (start < mainHeader.firstUsableLBA)
1809 first = mainHeader.firstUsableLBA;
1810 else
1811 first = start;
1812
1813 // ...now search through all partitions; if first is within an
1814 // existing partition, move it to the next sector after that
1815 // partition and repeat. If first was moved, set firstMoved
1816 // flag; repeat until firstMoved is not set, so as to catch
1817 // cases where partitions are out of sequential order....
1818 do {
1819 firstMoved = 0;
1820 for (i = 0; i < mainHeader.numParts; i++) {
1821 if ((first >= partitions[i].GetFirstLBA()) &&
1822 (first <= partitions[i].GetLastLBA())) { // in existing part.
1823 first = partitions[i].GetLastLBA() + 1;
1824 firstMoved = 1;
1825 } // if
1826 } // for
1827 } while (firstMoved == 1);
1828 if (first > mainHeader.lastUsableLBA)
1829 first = 0;
1830 return (first);
1831} // GPTData::FindFirstAvailable()
1832
1833// Finds the first available sector in the largest block of unallocated
1834// space on the disk. Returns 0 if there are no available blocks left
1835uint64_t GPTData::FindFirstInLargest(void) {
srs5694e35eb1b2009-09-14 00:29:34 -04001836 uint64_t start, firstBlock, lastBlock, segmentSize, selectedSize = 0, selectedSegment = 0;
srs5694e4ac11e2009-08-31 10:13:04 -04001837
1838 start = 0;
1839 do {
1840 firstBlock = FindFirstAvailable(start);
1841 if (firstBlock != UINT32_C(0)) { // something's free...
1842 lastBlock = FindLastInFree(firstBlock);
1843 segmentSize = lastBlock - firstBlock + UINT32_C(1);
1844 if (segmentSize > selectedSize) {
1845 selectedSize = segmentSize;
1846 selectedSegment = firstBlock;
1847 } // if
1848 start = lastBlock + 1;
1849 } // if
1850 } while (firstBlock != 0);
1851 return selectedSegment;
1852} // GPTData::FindFirstInLargest()
1853
1854// Find the last available block on the disk at or after the start
1855// block. Returns 0 if there are no available partitions after
1856// (or including) start.
1857uint64_t GPTData::FindLastAvailable(uint64_t start) {
1858 uint64_t last;
1859 uint32_t i;
1860 int lastMoved = 0;
1861
1862 // Start by assuming the last usable LBA is available....
1863 last = mainHeader.lastUsableLBA;
1864
1865 // ...now, similar to algorithm in FindFirstAvailable(), search
1866 // through all partitions, moving last when it's in an existing
1867 // partition. Set the lastMoved flag so we repeat to catch cases
1868 // where partitions are out of logical order.
1869 do {
1870 lastMoved = 0;
1871 for (i = 0; i < mainHeader.numParts; i++) {
1872 if ((last >= partitions[i].GetFirstLBA()) &&
1873 (last <= partitions[i].GetLastLBA())) { // in existing part.
1874 last = partitions[i].GetFirstLBA() - 1;
1875 lastMoved = 1;
1876 } // if
1877 } // for
1878 } while (lastMoved == 1);
1879 if (last < mainHeader.firstUsableLBA)
1880 last = 0;
1881 return (last);
1882} // GPTData::FindLastAvailable()
1883
1884// Find the last available block in the free space pointed to by start.
1885uint64_t GPTData::FindLastInFree(uint64_t start) {
1886 uint64_t nearestStart;
1887 uint32_t i;
1888
1889 nearestStart = mainHeader.lastUsableLBA;
1890 for (i = 0; i < mainHeader.numParts; i++) {
1891 if ((nearestStart > partitions[i].GetFirstLBA()) &&
1892 (partitions[i].GetFirstLBA() > start)) {
1893 nearestStart = partitions[i].GetFirstLBA() - 1;
1894 } // if
1895 } // for
1896 return (nearestStart);
1897} // GPTData::FindLastInFree()
1898
1899// Finds the total number of free blocks, the number of segments in which
1900// they reside, and the size of the largest of those segments
1901uint64_t GPTData::FindFreeBlocks(int *numSegments, uint64_t *largestSegment) {
1902 uint64_t start = UINT64_C(0); // starting point for each search
1903 uint64_t totalFound = UINT64_C(0); // running total
1904 uint64_t firstBlock; // first block in a segment
1905 uint64_t lastBlock; // last block in a segment
1906 uint64_t segmentSize; // size of segment in blocks
1907 int num = 0;
1908
1909 *largestSegment = UINT64_C(0);
1910 do {
1911 firstBlock = FindFirstAvailable(start);
1912 if (firstBlock != UINT64_C(0)) { // something's free...
1913 lastBlock = FindLastInFree(firstBlock);
1914 segmentSize = lastBlock - firstBlock + UINT64_C(1);
1915 if (segmentSize > *largestSegment) {
1916 *largestSegment = segmentSize;
1917 } // if
1918 totalFound += segmentSize;
1919 num++;
1920 start = lastBlock + 1;
1921 } // if
1922 } while (firstBlock != 0);
1923 *numSegments = num;
1924 return totalFound;
1925} // GPTData::FindFreeBlocks()
1926
1927// Returns 1 if sector is unallocated, 0 if it's allocated to a partition
1928int GPTData::IsFree(uint64_t sector) {
1929 int isFree = 1;
1930 uint32_t i;
1931
1932 for (i = 0; i < mainHeader.numParts; i++) {
1933 if ((sector >= partitions[i].GetFirstLBA()) &&
1934 (sector <= partitions[i].GetLastLBA())) {
1935 isFree = 0;
1936 } // if
1937 } // for
srs5694e35eb1b2009-09-14 00:29:34 -04001938 if ((sector < mainHeader.firstUsableLBA) ||
srs5694e4ac11e2009-08-31 10:13:04 -04001939 (sector > mainHeader.lastUsableLBA)) {
1940 isFree = 0;
1941 } // if
1942 return (isFree);
1943} // GPTData::IsFree()
1944
1945/********************************
1946 * *
1947 * Endianness support functions *
1948 * *
1949 ********************************/
1950
srs56942a9f5da2009-08-26 00:48:01 -04001951void GPTData::ReverseHeaderBytes(struct GPTHeader* header) {
srs5694221e0872009-08-29 15:00:31 -04001952 ReverseBytes(&header->signature, 8);
1953 ReverseBytes(&header->revision, 4);
1954 ReverseBytes(&header->headerSize, 4);
1955 ReverseBytes(&header->headerCRC, 4);
1956 ReverseBytes(&header->reserved, 4);
1957 ReverseBytes(&header->currentLBA, 8);
1958 ReverseBytes(&header->backupLBA, 8);
1959 ReverseBytes(&header->firstUsableLBA, 8);
1960 ReverseBytes(&header->lastUsableLBA, 8);
1961 ReverseBytes(&header->partitionEntriesLBA, 8);
1962 ReverseBytes(&header->numParts, 4);
1963 ReverseBytes(&header->sizeOfPartitionEntries, 4);
1964 ReverseBytes(&header->partitionEntriesCRC, 4);
1965 ReverseBytes(&header->reserved2, GPT_RESERVED);
1966 ReverseBytes(&header->diskGUID.data1, 8);
1967 ReverseBytes(&header->diskGUID.data2, 8);
srs56942a9f5da2009-08-26 00:48:01 -04001968} // GPTData::ReverseHeaderBytes()
1969
1970// IMPORTANT NOTE: This function requires non-reversed mainHeader
1971// structure!
1972void GPTData::ReversePartitionBytes() {
1973 uint32_t i;
1974
1975 // Check GPT signature on big-endian systems; this will mismatch
1976 // if the function is called out of order. Unfortunately, it'll also
1977 // mismatch if there's data corruption.
1978 if ((mainHeader.signature != GPT_SIGNATURE) && (IsLittleEndian() == 0)) {
1979 printf("GPT signature mismatch in GPTData::ReversePartitionBytes(). This indicates\n"
srs5694e4ac11e2009-08-31 10:13:04 -04001980 "data corruption or a misplaced call to this function.\n");
srs56942a9f5da2009-08-26 00:48:01 -04001981 } // if signature mismatch....
1982 for (i = 0; i < mainHeader.numParts; i++) {
srs5694221e0872009-08-29 15:00:31 -04001983 partitions[i].ReversePartBytes();
srs56942a9f5da2009-08-26 00:48:01 -04001984 } // for
1985} // GPTData::ReversePartitionBytes()
1986
1987/******************************************
1988 * *
1989 * Additional non-class support functions *
1990 * *
1991 ******************************************/
1992
srs5694e7b4ff92009-08-18 13:16:10 -04001993// Check to be sure that data type sizes are correct. The basic types (uint*_t) should
1994// never fail these tests, but the struct types may fail depending on compile options.
1995// Specifically, the -fpack-struct option to gcc may be required to ensure proper structure
1996// sizes.
1997int SizesOK(void) {
1998 int allOK = 1;
srs5694e7b4ff92009-08-18 13:16:10 -04001999
2000 if (sizeof(uint8_t) != 1) {
2001 fprintf(stderr, "uint8_t is %d bytes, should be 1 byte; aborting!\n", sizeof(uint8_t));
2002 allOK = 0;
2003 } // if
2004 if (sizeof(uint16_t) != 2) {
2005 fprintf(stderr, "uint16_t is %d bytes, should be 2 bytes; aborting!\n", sizeof(uint16_t));
2006 allOK = 0;
2007 } // if
2008 if (sizeof(uint32_t) != 4) {
2009 fprintf(stderr, "uint32_t is %d bytes, should be 4 bytes; aborting!\n", sizeof(uint32_t));
2010 allOK = 0;
2011 } // if
2012 if (sizeof(uint64_t) != 8) {
2013 fprintf(stderr, "uint64_t is %d bytes, should be 8 bytes; aborting!\n", sizeof(uint64_t));
2014 allOK = 0;
2015 } // if
2016 if (sizeof(struct MBRRecord) != 16) {
srs5694221e0872009-08-29 15:00:31 -04002017 fprintf(stderr, "MBRRecord is %d bytes, should be 16 bytes; aborting!\n", sizeof(MBRRecord));
srs5694e7b4ff92009-08-18 13:16:10 -04002018 allOK = 0;
2019 } // if
2020 if (sizeof(struct EBRRecord) != 512) {
srs5694221e0872009-08-29 15:00:31 -04002021 fprintf(stderr, "EBRRecord is %d bytes, should be 512 bytes; aborting!\n", sizeof(EBRRecord));
srs5694e7b4ff92009-08-18 13:16:10 -04002022 allOK = 0;
2023 } // if
2024 if (sizeof(struct GPTHeader) != 512) {
srs5694221e0872009-08-29 15:00:31 -04002025 fprintf(stderr, "GPTHeader is %d bytes, should be 512 bytes; aborting!\n", sizeof(GPTHeader));
srs5694e7b4ff92009-08-18 13:16:10 -04002026 allOK = 0;
2027 } // if
srs5694221e0872009-08-29 15:00:31 -04002028 if (sizeof(GPTPart) != 128) {
2029 fprintf(stderr, "GPTPart is %d bytes, should be 128 bytes; aborting!\n", sizeof(GPTPart));
2030 allOK = 0;
2031 } // if
2032// Determine endianness; set allOK = 0 if running on big-endian hardware
srs56942a9f5da2009-08-26 00:48:01 -04002033 if (IsLittleEndian() == 0) {
2034 fprintf(stderr, "\aRunning on big-endian hardware. Big-endian support is new and poorly"
srs5694e4ac11e2009-08-31 10:13:04 -04002035 " tested!\nBeware!\n");
srs56942a9f5da2009-08-26 00:48:01 -04002036 // allOK = 0;
srs5694e7b4ff92009-08-18 13:16:10 -04002037 } // if
2038 return (allOK);
2039} // SizesOK()
srs5694e4ac11e2009-08-31 10:13:04 -04002040