blob: 9498a8638cff11b958b2665d786f57fe97575bac [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) {
86 int problems = 0, numSegments, i, j;
87 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
285 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;
srs56942a9f5da2009-08-26 00:48:01 -0400320 uint32_t trueNumParts, crcTemp;
321 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) {
706 int allOK = 1, i, j;
707 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) {
809 sync();
810#ifdef __APPLE__
srs5694e4ac11e2009-08-31 10:13:04 -0400811 printf("Warning: The kernel may continue to use old or deleted partitions.\n"
812 "You should reboot or remove the drive.\n");
srs5694e7b4ff92009-08-18 13:16:10 -0400813 /* don't know if this helps
814 * it definitely will get things on disk though:
srs5694e4ac11e2009-08-31 10:13:04 -0400815 * http://topiks.org/mac-os-x/0321278542/ch12lev1sec8.html */
816 i = ioctl(fd, DKIOCSYNCHRONIZECACHE);
srs5694e7b4ff92009-08-18 13:16:10 -0400817#else
srs5694221e0872009-08-29 15:00:31 -0400818#ifdef __FreeBSD__
819 sleep(2);
820 i = ioctl(fd, DIOCGFLUSH);
srs5694e4ac11e2009-08-31 10:13:04 -0400821 printf("Warning: The kernel may still provide disk access using old partition IDs.\n");
srs5694221e0872009-08-29 15:00:31 -0400822#else
srs5694e4ac11e2009-08-31 10:13:04 -0400823 sleep(2);
srs5694e7b4ff92009-08-18 13:16:10 -0400824 i = ioctl(fd, BLKRRPART);
825 if (i)
826 printf("Warning: The kernel is still using the old partition table.\n"
srs5694e4ac11e2009-08-31 10:13:04 -0400827 "The new table will be used at the next reboot.\n");
srs5694e7b4ff92009-08-18 13:16:10 -0400828#endif
srs5694221e0872009-08-29 15:00:31 -0400829#endif
srs5694e7b4ff92009-08-18 13:16:10 -0400830 } // if
831
832 if (allOK) { // writes completed OK
srs5694e4ac11e2009-08-31 10:13:04 -0400833 printf("The operation has completed successfully.\n");
srs5694e7b4ff92009-08-18 13:16:10 -0400834 } else {
srs5694e4ac11e2009-08-31 10:13:04 -0400835 printf("Warning! An error was reported when writing the partition table! This error\n");
836 printf("MIGHT be harmless, but you may have trashed the disk! Use parted and, if\n");
837 printf("necessary, restore your original partition table.\n");
srs5694e7b4ff92009-08-18 13:16:10 -0400838 } // if/else
srs5694e4ac11e2009-08-31 10:13:04 -0400839 close(fd);
srs5694e7b4ff92009-08-18 13:16:10 -0400840 } else {
srs5694e4ac11e2009-08-31 10:13:04 -0400841 fprintf(stderr, "Unable to open device %s for writing! Errno is %d! Aborting write!\n",
842 device, errno);
843 allOK = 0;
srs5694e7b4ff92009-08-18 13:16:10 -0400844 } // if/else
845 } else {
846 printf("Aborting write of new partition table.\n");
847 } // if
848
srs56942a9f5da2009-08-26 00:48:01 -0400849 if (IsLittleEndian() == 0) {
850 // Reverse (normalize) header bytes first, since ReversePartitionBytes()
851 // requires non-reversed data in mainHeader...
852 ReverseHeaderBytes(&mainHeader);
853 ReverseHeaderBytes(&secondHeader);
854 ReversePartitionBytes();
855 } // if
856
srs5694e7b4ff92009-08-18 13:16:10 -0400857 return (allOK);
858} // GPTData::SaveGPTData()
859
860// Save GPT data to a backup file. This function does much less error
861// checking than SaveGPTData(). It can therefore preserve many types of
862// corruption for later analysis; however, it preserves only the MBR,
863// the main GPT header, the backup GPT header, and the main partition
864// table; it discards the backup partition table, since it should be
865// identical to the main partition table on healthy disks.
866int GPTData::SaveGPTBackup(char* filename) {
srs56942a9f5da2009-08-26 00:48:01 -0400867 int fd, allOK = 1;
868 uint32_t numParts;
srs5694e7b4ff92009-08-18 13:16:10 -0400869
870 if ((fd = open(filename, O_WRONLY | O_CREAT, S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH)) != -1) {
srs56942a9f5da2009-08-26 00:48:01 -0400871 // Reverse the byte order, if necessary....
872 numParts = mainHeader.numParts;
873 if (IsLittleEndian() == 0) {
874 ReversePartitionBytes();
875 ReverseHeaderBytes(&mainHeader);
876 ReverseHeaderBytes(&secondHeader);
877 } // if
878
879 // Now write the protective MBR...
srs5694e7b4ff92009-08-18 13:16:10 -0400880 protectiveMBR.WriteMBRData(fd);
881
882 // Now write the main GPT header...
883 if (allOK)
884 if (write(fd, &mainHeader, 512) == -1)
885 allOK = 0;
886
887 // Now write the secondary GPT header...
888 if (allOK)
889 if (write(fd, &secondHeader, 512) == -1)
srs5694e4ac11e2009-08-31 10:13:04 -0400890 allOK = 0;
srs5694e7b4ff92009-08-18 13:16:10 -0400891
892 // Now write the main partition tables...
893 if (allOK) {
srs56942a9f5da2009-08-26 00:48:01 -0400894 if (write(fd, partitions, GPT_SIZE * numParts) == -1)
srs5694e7b4ff92009-08-18 13:16:10 -0400895 allOK = 0;
896 } // if
897
898 if (allOK) { // writes completed OK
899 printf("The operation has completed successfully.\n");
900 } else {
901 printf("Warning! An error was reported when writing the backup file.\n");
902 printf("It may not be useable!\n");
903 } // if/else
904 close(fd);
srs56942a9f5da2009-08-26 00:48:01 -0400905
906 // Now reverse the byte-order reversal, if necessary....
907 if (IsLittleEndian() == 0) {
908 ReverseHeaderBytes(&mainHeader);
909 ReverseHeaderBytes(&secondHeader);
910 ReversePartitionBytes();
911 } // if
srs5694e7b4ff92009-08-18 13:16:10 -0400912 } else {
913 fprintf(stderr, "Unable to open file %s for writing! Aborting!\n", filename);
914 allOK = 0;
915 } // if/else
916 return allOK;
917} // GPTData::SaveGPTBackup()
918
919// Load GPT data from a backup file created by SaveGPTBackup(). This function
920// does minimal error checking. It returns 1 if it completed successfully,
921// 0 if there was a problem. In the latter case, it creates a new empty
922// set of partitions.
923int GPTData::LoadGPTBackup(char* filename) {
924 int fd, allOK = 1, val;
925 uint32_t numParts, sizeOfEntries, sizeOfParts, newCRC;
srs56942a9f5da2009-08-26 00:48:01 -0400926 int littleEndian = 1;
srs5694e7b4ff92009-08-18 13:16:10 -0400927
928 if ((fd = open(filename, O_RDONLY)) != -1) {
srs56942a9f5da2009-08-26 00:48:01 -0400929 if (IsLittleEndian() == 0)
930 littleEndian = 0;
931
srs5694e7b4ff92009-08-18 13:16:10 -0400932 // Let the MBRData class load the saved MBR...
srs5694221e0872009-08-29 15:00:31 -0400933 protectiveMBR.ReadMBRData(fd, 0); // 0 = don't check block size
srs5694e7b4ff92009-08-18 13:16:10 -0400934
935 // Load the main GPT header, check its vaility, and set the GPT
936 // size based on the data
937 read(fd, &mainHeader, 512);
938 mainCrcOk = CheckHeaderCRC(&mainHeader);
939
srs56942a9f5da2009-08-26 00:48:01 -0400940 // Reverse byte order, if necessary
941 if (littleEndian == 0) {
942 ReverseHeaderBytes(&mainHeader);
943 } // if
944
srs5694e7b4ff92009-08-18 13:16:10 -0400945 // Load the backup GPT header in much the same way as the main
946 // GPT header....
947 read(fd, &secondHeader, 512);
948 secondCrcOk = CheckHeaderCRC(&secondHeader);
949
srs56942a9f5da2009-08-26 00:48:01 -0400950 // Reverse byte order, if necessary
951 if (littleEndian == 0) {
952 ReverseHeaderBytes(&secondHeader);
953 } // if
954
srs5694e7b4ff92009-08-18 13:16:10 -0400955 // Return valid headers code: 0 = both headers bad; 1 = main header
956 // good, backup bad; 2 = backup header good, main header bad;
957 // 3 = both headers good. Note these codes refer to valid GPT
958 // signatures and version numbers; more subtle problems will elude
959 // this check!
960 if ((val = CheckHeaderValidity()) > 0) {
961 if (val == 2) { // only backup header seems to be good
962 numParts = secondHeader.numParts;
srs5694e4ac11e2009-08-31 10:13:04 -0400963 sizeOfEntries = secondHeader.sizeOfPartitionEntries;
srs5694e7b4ff92009-08-18 13:16:10 -0400964 } else { // main header is OK
965 numParts = mainHeader.numParts;
966 sizeOfEntries = mainHeader.sizeOfPartitionEntries;
967 } // if/else
968
969 SetGPTSize(numParts);
970
971 // If current disk size doesn't match that of backup....
972 if (secondHeader.currentLBA != diskSize - UINT64_C(1)) {
973 printf("Warning! Current disk size doesn't match that of the backup!\n"
srs5694e4ac11e2009-08-31 10:13:04 -0400974 "Adjusting sizes to match, but subsequent problems are possible!\n");
srs5694e7b4ff92009-08-18 13:16:10 -0400975 secondHeader.currentLBA = mainHeader.backupLBA = diskSize - UINT64_C(1);
976 mainHeader.lastUsableLBA = diskSize - mainHeader.firstUsableLBA;
977 secondHeader.lastUsableLBA = mainHeader.lastUsableLBA;
978 secondHeader.partitionEntriesLBA = secondHeader.lastUsableLBA + UINT64_C(1);
979 } // if
980
981 // Load main partition table, and record whether its CRC
982 // matches the stored value
983 sizeOfParts = numParts * sizeOfEntries;
984 read(fd, partitions, sizeOfParts);
985
986 newCRC = chksum_crc32((unsigned char*) partitions, sizeOfParts);
987 mainPartsCrcOk = (newCRC == mainHeader.partitionEntriesCRC);
988 secondPartsCrcOk = (newCRC == secondHeader.partitionEntriesCRC);
srs56942a9f5da2009-08-26 00:48:01 -0400989 // Reverse byte order, if necessary
srs5694e4ac11e2009-08-31 10:13:04 -0400990 if (littleEndian == 0) {
991 ReversePartitionBytes();
992 } // if
srs56942a9f5da2009-08-26 00:48:01 -0400993
srs5694e7b4ff92009-08-18 13:16:10 -0400994 } else {
995 allOK = 0;
996 } // if/else
997 } else {
998 allOK = 0;
999 fprintf(stderr, "Unable to open file %s for reading! Aborting!\n", filename);
1000 } // if/else
1001
1002 // Something went badly wrong, so blank out partitions
1003 if (allOK == 0) {
1004 ClearGPTData();
1005 protectiveMBR.MakeProtectiveMBR();
1006 } // if
1007 return allOK;
1008} // GPTData::LoadGPTBackup()
1009
srs5694e4ac11e2009-08-31 10:13:04 -04001010// Tell user whether Apple Partition Map (APM) was discovered....
1011void GPTData::ShowAPMState(void) {
1012 if (apmFound)
1013 printf(" APM: present\n");
1014 else
1015 printf(" APM: not present\n");
1016} // GPTData::ShowAPMState()
1017
1018// Tell user about the state of the GPT data....
1019void GPTData::ShowGPTState(void) {
1020 switch (state) {
1021 case gpt_invalid:
1022 printf(" GPT: not present\n");
1023 break;
1024 case gpt_valid:
1025 printf(" GPT: present\n");
1026 break;
1027 case gpt_corrupt:
1028 printf(" GPT: damaged\n");
1029 break;
1030 default:
1031 printf("\a GPT: unknown -- bug!\n");
1032 break;
1033 } // switch
1034} // GPTData::ShowGPTState()
1035
1036// Display the basic GPT data
1037void GPTData::DisplayGPTData(void) {
1038 int i, j;
1039 char sizeInSI[255]; // String to hold size of disk in SI units
1040 char tempStr[255];
1041 uint64_t temp, totalFree;
1042
1043 BytesToSI(diskSize * blockSize, sizeInSI);
1044 printf("Disk %s: %lu sectors, %s\n", device,
1045 (unsigned long) diskSize, sizeInSI);
1046 printf("Disk identifier (GUID): %s\n", GUIDToStr(mainHeader.diskGUID, tempStr));
1047 printf("Partition table holds up to %lu entries\n", (unsigned long) mainHeader.numParts);
1048 printf("First usable sector is %lu, last usable sector is %lu\n",
1049 (unsigned long) mainHeader.firstUsableLBA,
1050 (unsigned long) mainHeader.lastUsableLBA);
1051 totalFree = FindFreeBlocks(&i, &temp);
1052 printf("Total free space is %llu sectors (%s)\n", totalFree,
1053 BytesToSI(totalFree * (uint64_t) blockSize, sizeInSI));
1054 printf("\nNumber Start (sector) End (sector) Size Code Name\n");
1055 for (i = 0; i < mainHeader.numParts; i++) {
1056 partitions[i].ShowSummary(i, blockSize, sizeInSI);
1057 } // for
1058} // GPTData::DisplayGPTData()
1059
1060// Get partition number from user and then call ShowPartDetails(partNum)
1061// to show its detailed information
1062void GPTData::ShowDetails(void) {
1063 int partNum;
1064 uint32_t low, high;
1065
1066 if (GetPartRange(&low, &high) > 0) {
1067 partNum = GetPartNum();
1068 ShowPartDetails(partNum);
1069 } else {
1070 printf("No partitions\n");
1071 } // if/else
1072} // GPTData::ShowDetails()
1073
1074// Show detailed information on the specified partition
1075void GPTData::ShowPartDetails(uint32_t partNum) {
1076 if (partitions[partNum].GetFirstLBA() != 0) {
1077 partitions[partNum].ShowDetails(blockSize);
1078 } else {
1079 printf("Partition #%d does not exist.", (int) (partNum + 1));
1080 } // if
1081} // GPTData::ShowPartDetails()
1082
1083/*********************************************************************
1084 * *
1085 * Begin functions that obtain information from the users, and often *
1086 * do something with that information (call other functions) *
1087 * *
1088 *********************************************************************/
1089
1090// Prompts user for partition number and returns the result.
1091uint32_t GPTData::GetPartNum(void) {
1092 uint32_t partNum;
1093 uint32_t low, high;
1094 char prompt[255];
1095
1096 if (GetPartRange(&low, &high) > 0) {
1097 sprintf(prompt, "Partition number (%d-%d): ", low + 1, high + 1);
1098 partNum = GetNumber(low + 1, high + 1, low, prompt);
1099 } else partNum = 1;
1100 return (partNum - 1);
1101} // GPTData::GetPartNum()
1102
1103// What it says: Resize the partition table. (Default is 128 entries.)
1104void GPTData::ResizePartitionTable(void) {
1105 int newSize;
1106 char prompt[255];
1107 uint32_t curLow, curHigh;
1108
1109 printf("Current partition table size is %lu.\n",
1110 (unsigned long) mainHeader.numParts);
1111 GetPartRange(&curLow, &curHigh);
1112 curHigh++; // since GetPartRange() returns numbers starting from 0...
1113 // There's no point in having fewer than four partitions....
1114 if (curHigh < 4)
1115 curHigh = 4;
1116 sprintf(prompt, "Enter new size (%d up, default %d): ", (int) curHigh,
1117 (int) NUM_GPT_ENTRIES);
1118 newSize = GetNumber(4, 65535, 128, prompt);
1119 if (newSize < 128) {
1120 printf("Caution: The partition table size should officially be 16KB or larger,\n"
1121 "which works out to 128 entries. In practice, smaller tables seem to\n"
1122 "work with most OSes, but this practice is risky. I'm proceeding with\n"
1123 "the resize, but you may want to reconsider this action and undo it.\n\n");
1124 } // if
1125 SetGPTSize(newSize);
1126} // GPTData::ResizePartitionTable()
1127
1128// Interactively create a partition
1129void GPTData::CreatePartition(void) {
1130 uint64_t firstBlock, firstInLargest, lastBlock, sector;
1131 char prompt[255];
1132 int partNum, firstFreePart = 0;
1133
1134 // Find first free partition...
1135 while (partitions[firstFreePart].GetFirstLBA() != 0) {
1136 firstFreePart++;
1137 } // while
1138
1139 if (((firstBlock = FindFirstAvailable()) != 0) &&
1140 (firstFreePart < mainHeader.numParts)) {
1141 lastBlock = FindLastAvailable(firstBlock);
1142 firstInLargest = FindFirstInLargest();
1143
1144 // Get partition number....
1145 do {
1146 sprintf(prompt, "Partition number (%d-%d, default %d): ", firstFreePart + 1,
1147 mainHeader.numParts, firstFreePart + 1);
1148 partNum = GetNumber(firstFreePart + 1, mainHeader.numParts,
1149 firstFreePart + 1, prompt) - 1;
1150 if (partitions[partNum].GetFirstLBA() != 0)
1151 printf("partition %d is in use.\n", partNum + 1);
1152 } while (partitions[partNum].GetFirstLBA() != 0);
1153
1154 // Get first block for new partition...
1155 sprintf(prompt,
1156 "First sector (%llu-%llu, default = %llu) or {+-}size{KMGT}: ",
1157 firstBlock, lastBlock, firstInLargest);
1158 do {
1159 sector = GetSectorNum(firstBlock, lastBlock, firstInLargest, prompt);
1160 } while (IsFree(sector) == 0);
1161 firstBlock = sector;
1162
1163 // Get last block for new partitions...
1164 lastBlock = FindLastInFree(firstBlock);
1165 sprintf(prompt,
1166 "Last sector (%llu-%llu, default = %llu) or {+-}size{KMGT}: ",
1167 firstBlock, lastBlock, lastBlock);
1168 do {
1169 sector = GetSectorNum(firstBlock, lastBlock, lastBlock, prompt);
1170 } while (IsFree(sector) == 0);
1171 lastBlock = sector;
1172
1173 partitions[partNum].SetFirstLBA(firstBlock);
1174 partitions[partNum].SetLastLBA(lastBlock);
1175
1176 partitions[partNum].SetUniqueGUID(1);
1177 partitions[partNum].ChangeType();
1178 partitions[partNum].SetName((unsigned char*) partitions[partNum].GetNameType(prompt));
1179 } else {
1180 printf("No free sectors available\n");
1181 } // if/else
1182} // GPTData::CreatePartition()
1183
1184// Interactively delete a partition (duh!)
1185void GPTData::DeletePartition(void) {
1186 int partNum;
1187 uint32_t low, high;
1188 uint64_t startSector, length;
1189 char prompt[255];
1190
1191 if (GetPartRange(&low, &high) > 0) {
1192 sprintf(prompt, "Partition number (%d-%d): ", low + 1, high + 1);
1193 partNum = GetNumber(low + 1, high + 1, low, prompt);
1194
1195 // In case there's a protective MBR, look for & delete matching
1196 // MBR partition....
1197 startSector = partitions[partNum - 1].GetFirstLBA();
1198 length = partitions[partNum - 1].GetLengthLBA();
1199 protectiveMBR.DeleteByLocation(startSector, length);
1200
1201 // Now delete the GPT partition
1202 partitions[partNum - 1].BlankPartition();
1203 } else {
1204 printf("No partitions\n");
1205 } // if/else
1206} // GPTData::DeletePartition()
1207
1208// Prompt user for a partition number, then change its type code
1209// using ChangeGPTType(struct GPTPartition*) function.
1210void GPTData::ChangePartType(void) {
1211 int partNum;
1212 uint32_t low, high;
1213
1214 if (GetPartRange(&low, &high) > 0) {
1215 partNum = GetPartNum();
1216 partitions[partNum].ChangeType();
1217 } else {
1218 printf("No partitions\n");
1219 } // if/else
1220} // GPTData::ChangePartType()
1221
1222// Partition attributes seem to be rarely used, but I want a way to
1223// adjust them for completeness....
1224void GPTData::SetAttributes(uint32_t partNum) {
1225 Attributes theAttr;
1226
1227 theAttr.SetAttributes(partitions[partNum].GetAttributes());
1228 theAttr.DisplayAttributes();
1229 theAttr.ChangeAttributes();
1230 partitions[partNum].SetAttributes(theAttr.GetAttributes());
1231} // GPTData::SetAttributes()
1232
srs5694c0ca8f82009-08-20 21:35:25 -04001233// This function destroys the on-disk GPT structures. Returns 1 if the
1234// user confirms destruction, 0 if the user aborts.
1235int GPTData::DestroyGPT(void) {
1236 int fd, i, doMore;
1237 char blankSector[512], goOn;
1238
1239 for (i = 0; i < 512; i++) {
1240 blankSector[i] = '\0';
1241 } // for
1242
1243 printf("\a\aAbout to wipe out GPT on %s. Proceed? ", device);
1244 goOn = GetYN();
1245 if (goOn == 'Y') {
1246 fd = open(device, O_WRONLY);
1247#ifdef __APPLE__
1248 // MacOS X requires a shared lock under some circumstances....
1249 if (fd < 0) {
1250 fd = open(device, O_WRONLY|O_SHLOCK);
1251 } // if
1252#endif
1253 if (fd != -1) {
1254 lseek64(fd, mainHeader.currentLBA * 512, SEEK_SET); // seek to GPT header
1255 write(fd, blankSector, 512); // blank it out
1256 lseek64(fd, mainHeader.partitionEntriesLBA * 512, SEEK_SET); // seek to partition table
1257 for (i = 0; i < GetBlocksInPartTable(); i++)
1258 write(fd, blankSector, 512);
1259 lseek64(fd, secondHeader.partitionEntriesLBA * 512, SEEK_SET); // seek to partition table
1260 for (i = 0; i < GetBlocksInPartTable(); i++)
1261 write(fd, blankSector, 512);
1262 lseek64(fd, secondHeader.currentLBA * 512, SEEK_SET); // seek to GPT header
1263 write(fd, blankSector, 512); // blank it out
srs5694e4ac11e2009-08-31 10:13:04 -04001264 printf("Blank out MBR? ");
1265 if (GetYN() == 'Y') {
srs5694c0ca8f82009-08-20 21:35:25 -04001266 lseek64(fd, 0, SEEK_SET);
1267 write(fd, blankSector, 512); // blank it out
1268 } // if blank MBR
1269 close(fd);
srs5694e4ac11e2009-08-31 10:13:04 -04001270 printf("GPT data structures destroyed! You may now partition the disk using fdisk or\n"
1271 "other utilities. Program will now terminate.\n");
srs5694c0ca8f82009-08-20 21:35:25 -04001272 } else {
1273 printf("Problem opening %s for writing! Program will now terminate.\n");
1274 } // if/else (fd != -1)
1275 } // if (goOn == 'Y')
1276 return (goOn == 'Y');
1277} // GPTData::DestroyGPT()
1278
srs5694e4ac11e2009-08-31 10:13:04 -04001279/**************************************************************************
1280 * *
1281 * Partition table transformation functions (MBR or BSD disklabel to GPT) *
1282 * (some of these functions may require user interaction) *
1283 * *
1284 **************************************************************************/
1285
1286// Examines the MBR & GPT data, and perhaps asks the user questions, to
1287// determine which set of data to use: the MBR (use_mbr), the GPT (use_gpt),
1288// or create a new set of partitions (use_new)
1289WhichToUse GPTData::UseWhichPartitions(void) {
1290 WhichToUse which = use_new;
1291 MBRValidity mbrState;
1292 int answer;
1293
1294 mbrState = protectiveMBR.GetValidity();
1295
1296 if ((state == gpt_invalid) && ((mbrState == mbr) || (mbrState == hybrid))) {
1297 printf("\n\a***************************************************************\n"
1298 "Found invalid GPT and valid MBR; converting MBR to GPT format.\n"
1299 "THIS OPERATON IS POTENTIALLY DESTRUCTIVE! Exit by typing 'q' if\n"
1300 "you don't want to convert your MBR partitions to GPT format!\n"
1301 "***************************************************************\n\n");
1302 which = use_mbr;
1303 } // if
1304
1305 if ((state == gpt_invalid) && bsdFound) {
1306 printf("\n\a**********************************************************************\n"
1307 "Found invalid GPT and valid BSD disklabel; converting BSD disklabel\n"
1308 "to GPT format. THIS OPERATON IS POTENTIALLY DESTRUCTIVE! Your first\n"
1309 "BSD partition will likely be unusable. Exit by typing 'q' if you don't\n"
1310 "want to convert your BSD partitions to GPT format!\n"
1311 "**********************************************************************\n\n");
1312 which = use_bsd;
1313 } // if
1314
1315 if ((state == gpt_valid) && (mbrState == gpt)) {
1316 printf("Found valid GPT with protective MBR; using GPT.\n");
1317 which = use_gpt;
1318 } // if
1319 if ((state == gpt_valid) && (mbrState == hybrid)) {
1320 printf("Found valid GPT with hybrid MBR; using GPT.\n");
1321 which = use_gpt;
1322 } // if
1323 if ((state == gpt_valid) && (mbrState == invalid)) {
1324 printf("\aFound valid GPT with corrupt MBR; using GPT and will create new\nprotective MBR on save.\n");
1325 which = use_gpt;
1326 protectiveMBR.MakeProtectiveMBR();
1327 } // if
1328 if ((state == gpt_valid) && (mbrState == mbr)) {
1329 printf("Found valid MBR and GPT. Which do you want to use?\n");
1330 answer = GetNumber(1, 3, 2, (char*) " 1 - MBR\n 2 - GPT\n 3 - Create blank GPT\n\nYour answer: ");
1331 if (answer == 1) {
1332 which = use_mbr;
1333 } else if (answer == 2) {
1334 which = use_gpt;
1335 protectiveMBR.MakeProtectiveMBR();
1336 printf("Using GPT and creating fresh protective MBR.\n");
1337 } else which = use_new;
1338 } // if
1339
1340 // Nasty decisions here -- GPT is present, but corrupt (bad CRCs or other
1341 // problems)
1342 if (state == gpt_corrupt) {
1343 if ((mbrState == mbr) || (mbrState == hybrid)) {
1344 printf("Found valid MBR and corrupt GPT. Which do you want to use? (Using the\n"
1345 "GPT MAY permit recovery of GPT data.)\n");
1346 answer = GetNumber(1, 3, 2, (char*) " 1 - MBR\n 2 - GPT\n 3 - Create blank GPT\n\nYour answer: ");
1347 if (answer == 1) {
1348 which = use_mbr;
1349// protectiveMBR.MakeProtectiveMBR();
1350 } else if (answer == 2) {
1351 which = use_gpt;
1352 } else which = use_new;
1353 } else if (mbrState == invalid) {
1354 printf("Found invalid MBR and corrupt GPT. What do you want to do? (Using the\n"
1355 "GPT MAY permit recovery of GPT data.)\n");
1356 answer = GetNumber(1, 2, 1, (char*) " 1 - GPT\n 2 - Create blank GPT\n\nYour answer: ");
1357 if (answer == 1) {
1358 which = use_gpt;
1359 } else which = use_new;
1360 } else { // corrupt GPT, MBR indicates it's a GPT disk....
1361 printf("\a\a****************************************************************************\n"
1362 "Caution: Found protective or hybrid MBR and corrupt GPT. Using GPT, but disk\n"
1363 "verification and recovery are STRONGLY recommended.\n"
1364 "****************************************************************************\n");
1365 } // if/else/else
1366 } // if (corrupt GPT)
1367
1368 if (which == use_new)
1369 printf("Creating new GPT entries.\n");
1370
1371 return which;
1372} // UseWhichPartitions()
1373
1374// Convert MBR partition table into GPT form
1375int GPTData::XFormPartitions(void) {
1376 int i, numToConvert;
1377 uint8_t origType;
1378 struct newGUID;
1379 char name[NAME_SIZE];
1380
1381 // Clear out old data & prepare basics....
1382 ClearGPTData();
1383
1384 // Convert the smaller of the # of GPT or MBR partitions
1385 if (mainHeader.numParts > (NUM_LOGICALS + 4))
1386 numToConvert = NUM_LOGICALS + 4;
1387 else
1388 numToConvert = mainHeader.numParts;
1389
1390 for (i = 0; i < numToConvert; i++) {
1391 origType = protectiveMBR.GetType(i);
1392 // don't waste CPU time trying to convert extended, hybrid protective, or
1393 // null (non-existent) partitions
1394 if ((origType != 0x05) && (origType != 0x0f) && (origType != 0x85) &&
1395 (origType != 0x00) && (origType != 0xEE))
1396 partitions[i] = protectiveMBR.AsGPT(i);
1397 } // for
1398
1399 // Convert MBR into protective MBR
1400 protectiveMBR.MakeProtectiveMBR();
1401
1402 // Record that all original CRCs were OK so as not to raise flags
1403 // when doing a disk verification
1404 mainCrcOk = secondCrcOk = mainPartsCrcOk = secondPartsCrcOk = 1;
1405
1406 return (1);
1407} // GPTData::XFormPartitions()
1408
1409// Transforms BSD disklabel on the specified partition (numbered from 0).
1410// If an invalid partition number is given, the program prompts for one.
1411// Returns the number of new partitions created.
1412int GPTData::XFormDisklabel(int i) {
1413 uint32_t low, high, partNum, startPart;
1414 uint16_t hexCode;
1415 int goOn = 1, numDone = 0;
1416 BSDData disklabel;
1417
1418 if (GetPartRange(&low, &high) != 0) {
1419 if ((i < low) || (i > high))
1420 partNum = GetPartNum();
1421 else
1422 partNum = (uint32_t) i;
1423
1424 // Find the partition after the last used one
1425 startPart = high + 1;
1426
1427 // Now see if the specified partition has a BSD type code....
1428 hexCode = partitions[partNum].GetHexType();
1429 if ((hexCode != 0xa500) && (hexCode != 0xa900)) {
1430 printf("Specified partition doesn't have a disklabel partition type "
1431 "code.\nContinue anyway?");
1432 goOn = (GetYN() == 'Y');
1433 } // if
1434
1435 // If all is OK, read the disklabel and convert it.
1436 if (goOn) {
1437 goOn = disklabel.ReadBSDData(device, partitions[partNum].GetFirstLBA(),
1438 partitions[partNum].GetLastLBA());
1439 if ((goOn) && (disklabel.IsDisklabel())) {
1440 numDone = XFormDisklabel(&disklabel, startPart);
1441 if (numDone == 1)
1442 printf("Converted %d BSD partition.\n", numDone);
1443 else
1444 printf("Converted %d BSD partitions.\n", numDone);
1445 } else {
1446 printf("Unable to convert partitions! Unrecognized BSD disklabel.\n");
1447 } // if/else
1448 } // if
1449 if (numDone > 0) { // converted partitions; delete carrier
1450 partitions[partNum].BlankPartition();
1451 } // if
1452 } else {
1453 printf("No partitions\n");
1454 } // if/else
1455 return numDone;
1456} // GPTData::XFormDisklable(int i)
1457
1458// Transform the partitions on an already-loaded BSD disklabel...
1459int GPTData::XFormDisklabel(BSDData* disklabel, int startPart) {
1460 int i, numDone = 0;
1461
1462 if ((disklabel->IsDisklabel()) && (startPart >= 0) &&
1463 (startPart < mainHeader.numParts)) {
1464 for (i = 0; i < disklabel->GetNumParts(); i++) {
1465 partitions[i + startPart] = disklabel->AsGPT(i);
1466 if (partitions[i + startPart].GetFirstLBA() != UINT64_C(0))
1467 numDone++;
1468 } // for
1469 } // if
1470
1471 // Record that all original CRCs were OK so as not to raise flags
1472 // when doing a disk verification
1473 mainCrcOk = secondCrcOk = mainPartsCrcOk = secondPartsCrcOk = 1;
1474
1475 return numDone;
1476} // GPTData::XFormDisklabel(BSDData* disklabel)
1477
1478// Create a hybrid MBR -- an ugly, funky thing that helps GPT work with
1479// OSes that don't understand GPT.
1480void GPTData::MakeHybrid(void) {
1481 uint32_t partNums[3];
1482 char line[255];
1483 int numParts, i, j, typeCode, bootable, mbrNum;
1484 uint64_t length;
1485 char fillItUp = 'M'; // fill extra partition entries? (Yes/No/Maybe)
1486 char eeFirst; // Whether EFI GPT (0xEE) partition comes first in table
1487
1488 printf("\nWARNING! Hybrid MBRs are flaky and potentially dangerous! If you decide not\n"
1489 "to use one, just hit the Enter key at the below prompt and your MBR\n"
1490 "partition table will be untouched.\n\n\a");
1491
1492 // Now get the numbers of up to three partitions to add to the
1493 // hybrid MBR....
1494 printf("Type from one to three GPT partition numbers, separated by spaces, to be\n"
1495 "added to the hybrid MBR, in sequence: ");
1496 fgets(line, 255, stdin);
1497 numParts = sscanf(line, "%d %d %d", &partNums[0], &partNums[1], &partNums[2]);
1498
1499 if (numParts > 0) {
1500 // Blank out the protective MBR, but leave the boot loader code
1501 // alone....
1502 protectiveMBR.EmptyMBR(0);
1503 protectiveMBR.SetDiskSize(diskSize);
1504 printf("Place EFI GPT (0xEE) partition first in MBR (good for GRUB)? ");
1505 eeFirst = GetYN();
1506 } // if
1507
1508 for (i = 0; i < numParts; i++) {
1509 j = partNums[i] - 1;
1510 printf("\nCreating entry for partition #%d\n", j + 1);
1511 if ((j >= 0) && (j < mainHeader.numParts)) {
1512 if (partitions[j].GetLastLBA() < UINT32_MAX) {
1513 do {
1514 printf("Enter an MBR hex code (default %02X): ",
1515 typeHelper.GUIDToID(partitions[j].GetType()) / 256);
1516 fgets(line, 255, stdin);
1517 sscanf(line, "%x", &typeCode);
1518 if (line[0] == '\n')
1519 typeCode = partitions[j].GetHexType() / 256;
1520 } while ((typeCode <= 0) || (typeCode > 255));
1521 printf("Set the bootable flag? ");
1522 bootable = (GetYN() == 'Y');
1523 length = partitions[j].GetLengthLBA();
1524 if (eeFirst == 'Y')
1525 mbrNum = i + 1;
1526 else
1527 mbrNum = i;
1528 protectiveMBR.MakePart(mbrNum, (uint32_t) partitions[j].GetFirstLBA(),
1529 (uint32_t) length, typeCode, bootable);
1530 protectiveMBR.SetHybrid();
1531 } else { // partition out of range
1532 printf("Partition %d ends beyond the 2TiB limit of MBR partitions; omitting it.\n",
1533 j + 1);
1534 } // if/else
1535 } else {
1536 printf("Partition %d is out of range; omitting it.\n", j + 1);
1537 } // if/else
1538 } // for
1539
1540 if (numParts > 0) { // User opted to create a hybrid MBR....
1541 // Create EFI protective partition that covers the start of the disk.
1542 // If this location (covering the main GPT data structures) is omitted,
1543 // Linux won't find any partitions on the disk. Note that this is
1544 // NUMBERED AFTER the hybrid partitions, contrary to what the
1545 // gptsync utility does. This is because Windows seems to choke on
1546 // disks with a 0xEE partition in the first slot and subsequent
1547 // additional partitions, unless it boots from the disk.
1548 if (eeFirst == 'Y')
1549 mbrNum = 0;
1550 else
1551 mbrNum = numParts;
1552 protectiveMBR.MakePart(mbrNum, 1, protectiveMBR.FindLastInFree(1), 0xEE);
1553
1554 // ... and for good measure, if there are any partition spaces left,
1555 // optionally create another protective EFI partition to cover as much
1556 // space as possible....
1557 for (i = 0; i < 4; i++) {
1558 if (protectiveMBR.GetType(i) == 0x00) { // unused entry....
1559 if (fillItUp == 'M') {
1560 printf("\nUnused partition space(s) found. Use one to protect more partitions? ");
1561 fillItUp = GetYN();
1562 typeCode = 0x00; // use this to flag a need to get type code
1563 } // if
1564 if (fillItUp == 'Y') {
1565 while ((typeCode <= 0) || (typeCode > 255)) {
1566 printf("Enter an MBR hex code (EE is EFI GPT, but may confuse MacOS): ");
1567 // Comment on above: Mac OS treats disks with more than one
1568 // 0xEE MBR partition as MBR disks, not as GPT disks.
1569 fgets(line, 255, stdin);
1570 sscanf(line, "%x", &typeCode);
1571 if (line[0] == '\n')
1572 typeCode = 0;
1573 } // while
1574 protectiveMBR.MakeBiggestPart(i, typeCode); // make a partition
1575 } // if (fillItUp == 'Y')
1576 } // if unused entry
1577 } // for (i = 0; i < 4; i++)
1578 } // if (numParts > 0)
1579} // GPTData::MakeHybrid()
1580
1581/**********************************************************************
1582 * *
1583 * Functions that adjust GPT data structures WITHOUT user interaction *
1584 * (they may display information for the user's benefit, though) *
1585 * *
1586 **********************************************************************/
1587
1588// Resizes GPT to specified number of entries. Creates a new table if
1589// necessary, copies data if it already exists.
1590int GPTData::SetGPTSize(uint32_t numEntries) {
1591 struct GPTPart* newParts;
1592 struct GPTPart* trash;
1593 uint32_t i, high, copyNum;
1594 int allOK = 1;
1595
1596 // First, adjust numEntries upward, if necessary, to get a number
1597 // that fills the allocated sectors
1598 i = blockSize / GPT_SIZE;
1599 if ((numEntries % i) != 0) {
1600 printf("Adjusting GPT size from %lu ", (unsigned long) numEntries);
1601 numEntries = ((numEntries / i) + 1) * i;
1602 printf("to %lu to fill the sector\n", (unsigned long) numEntries);
1603 } // if
1604
1605 newParts = (GPTPart*) calloc(numEntries, sizeof (GPTPart));
1606 if (newParts != NULL) {
1607 if (partitions != NULL) { // existing partitions; copy them over
1608 GetPartRange(&i, &high);
1609 if (numEntries < (high + 1)) { // Highest entry too high for new #
1610 printf("The highest-numbered partition is %lu, which is greater than the requested\n"
1611 "partition table size of %d; cannot resize. Perhaps sorting will help.\n",
1612 (unsigned long) (high + 1), numEntries);
1613 allOK = 0;
1614 } else { // go ahead with copy
1615 if (numEntries < mainHeader.numParts)
1616 copyNum = numEntries;
1617 else
1618 copyNum = mainHeader.numParts;
1619 for (i = 0; i < copyNum; i++) {
1620 newParts[i] = partitions[i];
1621 } // for
1622 trash = partitions;
1623 partitions = newParts;
1624 free(trash);
1625 } // if
1626 } else { // No existing partition table; just create it
1627 partitions = newParts;
1628 } // if/else existing partitions
1629 mainHeader.numParts = numEntries;
1630 secondHeader.numParts = numEntries;
1631 mainHeader.firstUsableLBA = ((numEntries * GPT_SIZE) / blockSize) + 2 ;
1632 secondHeader.firstUsableLBA = mainHeader.firstUsableLBA;
1633 mainHeader.lastUsableLBA = diskSize - mainHeader.firstUsableLBA;
1634 secondHeader.lastUsableLBA = mainHeader.lastUsableLBA;
1635 secondHeader.partitionEntriesLBA = secondHeader.lastUsableLBA + UINT64_C(1);
1636 if (diskSize > 0)
1637 CheckGPTSize();
1638 } else { // Bad memory allocation
1639 fprintf(stderr, "Error allocating memory for partition table!\n");
1640 allOK = 0;
1641 } // if/else
1642 return (allOK);
1643} // GPTData::SetGPTSize()
1644
1645// Blank the partition array
1646void GPTData::BlankPartitions(void) {
1647 uint32_t i;
1648
1649 for (i = 0; i < mainHeader.numParts; i++) {
1650 partitions[i].BlankPartition();
1651 } // for
1652} // GPTData::BlankPartitions()
1653
1654// Sort the GPT entries, eliminating gaps and making for a logical
1655// ordering. Relies on QuickSortGPT() for the bulk of the work
1656void GPTData::SortGPT(void) {
1657 int i, lastPart = 0;
1658 GPTPart temp;
1659
1660 // First, find the last partition with data, so as not to
1661 // spend needless time sorting empty entries....
1662 for (i = 0; i < mainHeader.numParts; i++) {
1663 if (partitions[i].GetFirstLBA() > 0)
1664 lastPart = i;
1665 } // for
1666
1667 // Now swap empties with the last partitions, to simplify the logic
1668 // in the Quicksort function....
1669 i = 0;
1670 while (i < lastPart) {
1671 if (partitions[i].GetFirstLBA() == 0) {
1672 temp = partitions[i];
1673 partitions[i] = partitions[lastPart];
1674 partitions[lastPart] = temp;
1675 lastPart--;
1676 } // if
1677 i++;
1678 } // while
1679
1680 // Now call the recursive quick sort routine to do the real work....
1681 QuickSortGPT(partitions, 0, lastPart);
1682} // GPTData::SortGPT()
1683
1684// Set up data structures for entirely new set of partitions on the
1685// specified device. Returns 1 if OK, 0 if there were problems.
1686int GPTData::ClearGPTData(void) {
1687 int goOn, i;
1688
1689 // Set up the partition table....
1690 free(partitions);
1691 partitions = NULL;
1692 SetGPTSize(NUM_GPT_ENTRIES);
1693
1694 // Now initialize a bunch of stuff that's static....
1695 mainHeader.signature = GPT_SIGNATURE;
1696 mainHeader.revision = 0x00010000;
1697 mainHeader.headerSize = (uint32_t) HEADER_SIZE;
1698 mainHeader.reserved = 0;
1699 mainHeader.currentLBA = UINT64_C(1);
1700 mainHeader.partitionEntriesLBA = (uint64_t) 2;
1701 mainHeader.sizeOfPartitionEntries = GPT_SIZE;
1702 for (i = 0; i < GPT_RESERVED; i++) {
1703 mainHeader.reserved2[i] = '\0';
1704 } // for
1705
1706 // Now some semi-static items (computed based on end of disk)
1707 mainHeader.backupLBA = diskSize - UINT64_C(1);
1708 mainHeader.lastUsableLBA = diskSize - mainHeader.firstUsableLBA;
1709
1710 // Set a unique GUID for the disk, based on random numbers
1711 // rand() is only 32 bits, so multiply together to fill a 64-bit value
1712 mainHeader.diskGUID.data1 = (uint64_t) rand() * (uint64_t) rand();
1713 mainHeader.diskGUID.data2 = (uint64_t) rand() * (uint64_t) rand();
1714
1715 // Copy main header to backup header
1716 RebuildSecondHeader();
1717
1718 // Blank out the partitions array....
1719 BlankPartitions();
1720
1721 // Flag all CRCs as being OK....
1722 mainCrcOk = 1;
1723 secondCrcOk = 1;
1724 mainPartsCrcOk = 1;
1725 secondPartsCrcOk = 1;
1726
1727 return (goOn);
1728} // GPTData::ClearGPTData()
1729
1730void GPTData::SetName(uint32_t partNum, char* theName) {
1731 if ((partNum >= 0) && (partNum < mainHeader.numParts))
1732 if (partitions[partNum].GetFirstLBA() > 0)
1733 partitions[partNum].SetName((unsigned char*) theName);
1734} // GPTData::SetName
1735
1736// Set the disk GUID to the specified value. Note that the header CRCs must
1737// be recomputed after calling this function.
1738void GPTData::SetDiskGUID(GUIDData newGUID) {
1739 mainHeader.diskGUID = newGUID;
1740 secondHeader.diskGUID = newGUID;
1741} // SetDiskGUID()
1742
1743// Set the unique GUID of the specified partition. Returns 1 on
1744// successful completion, 0 if there were problems (invalid
1745// partition number).
1746int GPTData::SetPartitionGUID(uint32_t pn, GUIDData theGUID) {
1747 int retval = 0;
1748
1749 if (pn < mainHeader.numParts) {
1750 if (partitions[pn].GetFirstLBA() != UINT64_C(0)) {
1751 partitions[pn].SetUniqueGUID(theGUID);
1752 retval = 1;
1753 } // if
1754 } // if
1755 return retval;
1756} // GPTData::SetPartitionGUID()
1757
1758/********************************************************
1759 * *
1760 * Functions that return data about GPT data structures *
1761 * (most of these are inline in gpt.h) *
1762 * *
1763 ********************************************************/
1764
1765// Find the low and high used partition numbers (numbered from 0).
1766// Return value is the number of partitions found. Note that the
1767// *low and *high values are both set to 0 when no partitions
1768// are found, as well as when a single partition in the first
1769// position exists. Thus, the return value is the only way to
1770// tell when no partitions exist.
1771int GPTData::GetPartRange(uint32_t *low, uint32_t *high) {
1772 uint32_t i;
1773 int numFound = 0;
1774
1775 *low = mainHeader.numParts + 1; // code for "not found"
1776 *high = 0;
1777 if (mainHeader.numParts > 0) { // only try if partition table exists...
1778 for (i = 0; i < mainHeader.numParts; i++) {
1779 if (partitions[i].GetFirstLBA() != UINT64_C(0)) { // it exists
1780 *high = i; // since we're counting up, set the high value
1781 // Set the low value only if it's not yet found...
1782 if (*low == (mainHeader.numParts + 1)) *low = i;
1783 numFound++;
1784 } // if
1785 } // for
1786 } // if
1787
1788 // Above will leave *low pointing to its "not found" value if no partitions
1789 // are defined, so reset to 0 if this is the case....
1790 if (*low == (mainHeader.numParts + 1))
1791 *low = 0;
1792 return numFound;
1793} // GPTData::GetPartRange()
1794
1795/****************************************************
1796 * *
1797 * Functions that return data about disk free space *
1798 * *
1799 ****************************************************/
1800
1801// Find the first available block after the starting point; returns 0 if
1802// there are no available blocks left
1803uint64_t GPTData::FindFirstAvailable(uint64_t start) {
1804 uint64_t first;
1805 uint32_t i;
1806 int firstMoved = 0;
1807
1808 // Begin from the specified starting point or from the first usable
1809 // LBA, whichever is greater...
1810 if (start < mainHeader.firstUsableLBA)
1811 first = mainHeader.firstUsableLBA;
1812 else
1813 first = start;
1814
1815 // ...now search through all partitions; if first is within an
1816 // existing partition, move it to the next sector after that
1817 // partition and repeat. If first was moved, set firstMoved
1818 // flag; repeat until firstMoved is not set, so as to catch
1819 // cases where partitions are out of sequential order....
1820 do {
1821 firstMoved = 0;
1822 for (i = 0; i < mainHeader.numParts; i++) {
1823 if ((first >= partitions[i].GetFirstLBA()) &&
1824 (first <= partitions[i].GetLastLBA())) { // in existing part.
1825 first = partitions[i].GetLastLBA() + 1;
1826 firstMoved = 1;
1827 } // if
1828 } // for
1829 } while (firstMoved == 1);
1830 if (first > mainHeader.lastUsableLBA)
1831 first = 0;
1832 return (first);
1833} // GPTData::FindFirstAvailable()
1834
1835// Finds the first available sector in the largest block of unallocated
1836// space on the disk. Returns 0 if there are no available blocks left
1837uint64_t GPTData::FindFirstInLargest(void) {
1838 uint64_t start, firstBlock, lastBlock, segmentSize, selectedSize = 0, selectedSegment;
1839
1840 start = 0;
1841 do {
1842 firstBlock = FindFirstAvailable(start);
1843 if (firstBlock != UINT32_C(0)) { // something's free...
1844 lastBlock = FindLastInFree(firstBlock);
1845 segmentSize = lastBlock - firstBlock + UINT32_C(1);
1846 if (segmentSize > selectedSize) {
1847 selectedSize = segmentSize;
1848 selectedSegment = firstBlock;
1849 } // if
1850 start = lastBlock + 1;
1851 } // if
1852 } while (firstBlock != 0);
1853 return selectedSegment;
1854} // GPTData::FindFirstInLargest()
1855
1856// Find the last available block on the disk at or after the start
1857// block. Returns 0 if there are no available partitions after
1858// (or including) start.
1859uint64_t GPTData::FindLastAvailable(uint64_t start) {
1860 uint64_t last;
1861 uint32_t i;
1862 int lastMoved = 0;
1863
1864 // Start by assuming the last usable LBA is available....
1865 last = mainHeader.lastUsableLBA;
1866
1867 // ...now, similar to algorithm in FindFirstAvailable(), search
1868 // through all partitions, moving last when it's in an existing
1869 // partition. Set the lastMoved flag so we repeat to catch cases
1870 // where partitions are out of logical order.
1871 do {
1872 lastMoved = 0;
1873 for (i = 0; i < mainHeader.numParts; i++) {
1874 if ((last >= partitions[i].GetFirstLBA()) &&
1875 (last <= partitions[i].GetLastLBA())) { // in existing part.
1876 last = partitions[i].GetFirstLBA() - 1;
1877 lastMoved = 1;
1878 } // if
1879 } // for
1880 } while (lastMoved == 1);
1881 if (last < mainHeader.firstUsableLBA)
1882 last = 0;
1883 return (last);
1884} // GPTData::FindLastAvailable()
1885
1886// Find the last available block in the free space pointed to by start.
1887uint64_t GPTData::FindLastInFree(uint64_t start) {
1888 uint64_t nearestStart;
1889 uint32_t i;
1890
1891 nearestStart = mainHeader.lastUsableLBA;
1892 for (i = 0; i < mainHeader.numParts; i++) {
1893 if ((nearestStart > partitions[i].GetFirstLBA()) &&
1894 (partitions[i].GetFirstLBA() > start)) {
1895 nearestStart = partitions[i].GetFirstLBA() - 1;
1896 } // if
1897 } // for
1898 return (nearestStart);
1899} // GPTData::FindLastInFree()
1900
1901// Finds the total number of free blocks, the number of segments in which
1902// they reside, and the size of the largest of those segments
1903uint64_t GPTData::FindFreeBlocks(int *numSegments, uint64_t *largestSegment) {
1904 uint64_t start = UINT64_C(0); // starting point for each search
1905 uint64_t totalFound = UINT64_C(0); // running total
1906 uint64_t firstBlock; // first block in a segment
1907 uint64_t lastBlock; // last block in a segment
1908 uint64_t segmentSize; // size of segment in blocks
1909 int num = 0;
1910
1911 *largestSegment = UINT64_C(0);
1912 do {
1913 firstBlock = FindFirstAvailable(start);
1914 if (firstBlock != UINT64_C(0)) { // something's free...
1915 lastBlock = FindLastInFree(firstBlock);
1916 segmentSize = lastBlock - firstBlock + UINT64_C(1);
1917 if (segmentSize > *largestSegment) {
1918 *largestSegment = segmentSize;
1919 } // if
1920 totalFound += segmentSize;
1921 num++;
1922 start = lastBlock + 1;
1923 } // if
1924 } while (firstBlock != 0);
1925 *numSegments = num;
1926 return totalFound;
1927} // GPTData::FindFreeBlocks()
1928
1929// Returns 1 if sector is unallocated, 0 if it's allocated to a partition
1930int GPTData::IsFree(uint64_t sector) {
1931 int isFree = 1;
1932 uint32_t i;
1933
1934 for (i = 0; i < mainHeader.numParts; i++) {
1935 if ((sector >= partitions[i].GetFirstLBA()) &&
1936 (sector <= partitions[i].GetLastLBA())) {
1937 isFree = 0;
1938 } // if
1939 } // for
1940 if ((sector < mainHeader.firstUsableLBA) ||
1941 (sector > mainHeader.lastUsableLBA)) {
1942 isFree = 0;
1943 } // if
1944 return (isFree);
1945} // GPTData::IsFree()
1946
1947/********************************
1948 * *
1949 * Endianness support functions *
1950 * *
1951 ********************************/
1952
srs56942a9f5da2009-08-26 00:48:01 -04001953void GPTData::ReverseHeaderBytes(struct GPTHeader* header) {
srs5694221e0872009-08-29 15:00:31 -04001954 ReverseBytes(&header->signature, 8);
1955 ReverseBytes(&header->revision, 4);
1956 ReverseBytes(&header->headerSize, 4);
1957 ReverseBytes(&header->headerCRC, 4);
1958 ReverseBytes(&header->reserved, 4);
1959 ReverseBytes(&header->currentLBA, 8);
1960 ReverseBytes(&header->backupLBA, 8);
1961 ReverseBytes(&header->firstUsableLBA, 8);
1962 ReverseBytes(&header->lastUsableLBA, 8);
1963 ReverseBytes(&header->partitionEntriesLBA, 8);
1964 ReverseBytes(&header->numParts, 4);
1965 ReverseBytes(&header->sizeOfPartitionEntries, 4);
1966 ReverseBytes(&header->partitionEntriesCRC, 4);
1967 ReverseBytes(&header->reserved2, GPT_RESERVED);
1968 ReverseBytes(&header->diskGUID.data1, 8);
1969 ReverseBytes(&header->diskGUID.data2, 8);
srs56942a9f5da2009-08-26 00:48:01 -04001970} // GPTData::ReverseHeaderBytes()
1971
1972// IMPORTANT NOTE: This function requires non-reversed mainHeader
1973// structure!
1974void GPTData::ReversePartitionBytes() {
1975 uint32_t i;
1976
1977 // Check GPT signature on big-endian systems; this will mismatch
1978 // if the function is called out of order. Unfortunately, it'll also
1979 // mismatch if there's data corruption.
1980 if ((mainHeader.signature != GPT_SIGNATURE) && (IsLittleEndian() == 0)) {
1981 printf("GPT signature mismatch in GPTData::ReversePartitionBytes(). This indicates\n"
srs5694e4ac11e2009-08-31 10:13:04 -04001982 "data corruption or a misplaced call to this function.\n");
srs56942a9f5da2009-08-26 00:48:01 -04001983 } // if signature mismatch....
1984 for (i = 0; i < mainHeader.numParts; i++) {
srs5694221e0872009-08-29 15:00:31 -04001985 partitions[i].ReversePartBytes();
srs56942a9f5da2009-08-26 00:48:01 -04001986 } // for
1987} // GPTData::ReversePartitionBytes()
1988
1989/******************************************
1990 * *
1991 * Additional non-class support functions *
1992 * *
1993 ******************************************/
1994
srs5694e7b4ff92009-08-18 13:16:10 -04001995// Check to be sure that data type sizes are correct. The basic types (uint*_t) should
1996// never fail these tests, but the struct types may fail depending on compile options.
1997// Specifically, the -fpack-struct option to gcc may be required to ensure proper structure
1998// sizes.
1999int SizesOK(void) {
2000 int allOK = 1;
srs5694e7b4ff92009-08-18 13:16:10 -04002001
2002 if (sizeof(uint8_t) != 1) {
2003 fprintf(stderr, "uint8_t is %d bytes, should be 1 byte; aborting!\n", sizeof(uint8_t));
2004 allOK = 0;
2005 } // if
2006 if (sizeof(uint16_t) != 2) {
2007 fprintf(stderr, "uint16_t is %d bytes, should be 2 bytes; aborting!\n", sizeof(uint16_t));
2008 allOK = 0;
2009 } // if
2010 if (sizeof(uint32_t) != 4) {
2011 fprintf(stderr, "uint32_t is %d bytes, should be 4 bytes; aborting!\n", sizeof(uint32_t));
2012 allOK = 0;
2013 } // if
2014 if (sizeof(uint64_t) != 8) {
2015 fprintf(stderr, "uint64_t is %d bytes, should be 8 bytes; aborting!\n", sizeof(uint64_t));
2016 allOK = 0;
2017 } // if
2018 if (sizeof(struct MBRRecord) != 16) {
srs5694221e0872009-08-29 15:00:31 -04002019 fprintf(stderr, "MBRRecord is %d bytes, should be 16 bytes; aborting!\n", sizeof(MBRRecord));
srs5694e7b4ff92009-08-18 13:16:10 -04002020 allOK = 0;
2021 } // if
2022 if (sizeof(struct EBRRecord) != 512) {
srs5694221e0872009-08-29 15:00:31 -04002023 fprintf(stderr, "EBRRecord is %d bytes, should be 512 bytes; aborting!\n", sizeof(EBRRecord));
srs5694e7b4ff92009-08-18 13:16:10 -04002024 allOK = 0;
2025 } // if
2026 if (sizeof(struct GPTHeader) != 512) {
srs5694221e0872009-08-29 15:00:31 -04002027 fprintf(stderr, "GPTHeader is %d bytes, should be 512 bytes; aborting!\n", sizeof(GPTHeader));
srs5694e7b4ff92009-08-18 13:16:10 -04002028 allOK = 0;
2029 } // if
srs5694221e0872009-08-29 15:00:31 -04002030 if (sizeof(GPTPart) != 128) {
2031 fprintf(stderr, "GPTPart is %d bytes, should be 128 bytes; aborting!\n", sizeof(GPTPart));
2032 allOK = 0;
2033 } // if
2034// Determine endianness; set allOK = 0 if running on big-endian hardware
srs56942a9f5da2009-08-26 00:48:01 -04002035 if (IsLittleEndian() == 0) {
2036 fprintf(stderr, "\aRunning on big-endian hardware. Big-endian support is new and poorly"
srs5694e4ac11e2009-08-31 10:13:04 -04002037 " tested!\nBeware!\n");
srs56942a9f5da2009-08-26 00:48:01 -04002038 // allOK = 0;
srs5694e7b4ff92009-08-18 13:16:10 -04002039 } // if
2040 return (allOK);
2041} // SizesOK()
srs5694e4ac11e2009-08-31 10:13:04 -04002042