blob: f339a7271b9b41a440610810e1e302c365399bde [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
srs5694978041c2009-09-21 20:51:47 -0400115 // Now check that the main and backup headers both point to themselves....
116 if (mainHeader.currentLBA != 1) {
117 problems++;
118 printf("\nProblem: The main header's self-pointer doesn't point to itself. This problem\n"
119 "is being automatically corrected, but it may be a symptom of more serious\n"
120 "problems. Think carefully before saving changes with 'w' or using this disk.\n");
121 mainHeader.currentLBA = 1;
122 } // if
123 if (secondHeader.currentLBA != (diskSize - UINT64_C(1))) {
124 problems++;
srs56943f2fe992009-11-24 18:28:18 -0500125 printf("\nProblem: The secondary header's self-pointer indicates that it doesn't reside\n"
126 "at the end of the disk. If you've added a disk to a RAID array, use the 'e'\n"
127 "option on the experts' menu to adjust the secondary header's and partition"
128 "table's locations.");
srs5694978041c2009-09-21 20:51:47 -0400129 } // if
130
131 // Now check that critical main and backup GPT entries match each other
srs5694e4ac11e2009-08-31 10:13:04 -0400132 if (mainHeader.currentLBA != secondHeader.backupLBA) {
133 problems++;
134 printf("\nProblem: main GPT header's current LBA pointer (%llu) doesn't\n"
135 "match the backup GPT header's LBA pointer(%llu)\n",
136 (unsigned long long) mainHeader.currentLBA,
137 (unsigned long long) secondHeader.backupLBA);
138 } // if
139 if (mainHeader.backupLBA != secondHeader.currentLBA) {
140 problems++;
141 printf("\nProblem: main GPT header's backup LBA pointer (%llu) doesn't\n"
142 "match the backup GPT header's current LBA pointer (%llu)\n",
143 (unsigned long long) mainHeader.backupLBA,
144 (unsigned long long) secondHeader.currentLBA);
145 } // if
146 if (mainHeader.firstUsableLBA != secondHeader.firstUsableLBA) {
147 problems++;
148 printf("\nProblem: main GPT header's first usable LBA pointer (%llu) doesn't\n"
149 "match the backup GPT header's first usable LBA pointer (%llu)\n",
150 (unsigned long long) mainHeader.firstUsableLBA,
151 (unsigned long long) secondHeader.firstUsableLBA);
152 } // if
153 if (mainHeader.lastUsableLBA != secondHeader.lastUsableLBA) {
154 problems++;
155 printf("\nProblem: main GPT header's last usable LBA pointer (%llu) doesn't\n"
156 "match the backup GPT header's last usable LBA pointer (%llu)\n",
157 (unsigned long long) mainHeader.lastUsableLBA,
158 (unsigned long long) secondHeader.lastUsableLBA);
159 } // if
160 if ((mainHeader.diskGUID.data1 != secondHeader.diskGUID.data1) ||
161 (mainHeader.diskGUID.data2 != secondHeader.diskGUID.data2)) {
162 problems++;
163 printf("\nProblem: main header's disk GUID (%s) doesn't\n",
164 GUIDToStr(mainHeader.diskGUID, tempStr));
165 printf("match the backup GPT header's disk GUID (%s)\n",
166 GUIDToStr(secondHeader.diskGUID, tempStr));
167 } // if
168 if (mainHeader.numParts != secondHeader.numParts) {
169 problems++;
170 printf("\nProblem: main GPT header's number of partitions (%lu) doesn't\n"
171 "match the backup GPT header's number of partitions (%lu)\n",
172 (unsigned long) mainHeader.numParts,
173 (unsigned long) secondHeader.numParts);
174 } // if
175 if (mainHeader.sizeOfPartitionEntries != secondHeader.sizeOfPartitionEntries) {
176 problems++;
177 printf("\nProblem: main GPT header's size of partition entries (%lu) doesn't\n"
178 "match the backup GPT header's size of partition entries (%lu)\n",
179 (unsigned long) mainHeader.sizeOfPartitionEntries,
180 (unsigned long) secondHeader.sizeOfPartitionEntries);
181 } // if
182
183 // Now check for a few other miscellaneous problems...
184 // Check that the disk size will hold the data...
185 if (mainHeader.backupLBA > diskSize) {
186 problems++;
187 printf("\nProblem: Disk is too small to hold all the data!\n");
188 printf("(Disk size is %llu sectors, needs to be %llu sectors.)\n",
189 (unsigned long long) diskSize,
190 (unsigned long long) mainHeader.backupLBA);
191 } // if
192
193 // Check for overlapping partitions....
194 problems += FindOverlaps();
195
196 // Check for mismatched MBR and GPT partitions...
197 problems += FindHybridMismatches();
198
199 // Verify that partitions don't run into GPT data areas....
200 problems += CheckGPTSize();
201
202 // Now compute available space, but only if no problems found, since
203 // problems could affect the results
204 if (problems == 0) {
205 totalFree = FindFreeBlocks(&numSegments, &largestSegment);
206 BytesToSI(totalFree * (uint64_t) blockSize, siTotal);
207 BytesToSI(largestSegment * (uint64_t) blockSize, siLargest);
208 printf("No problems found. %llu free sectors (%s) available in %u\n"
209 "segments, the largest of which is %llu sectors (%s) in size\n",
210 (unsigned long long) totalFree,
211 siTotal, numSegments, (unsigned long long) largestSegment,
212 siLargest);
213 } else {
214 printf("\nIdentified %d problems!\n", problems);
srs5694e7b4ff92009-08-18 13:16:10 -0400215 } // if/else
srs5694e4ac11e2009-08-31 10:13:04 -0400216
217 return (problems);
218} // GPTData::Verify()
srs5694e7b4ff92009-08-18 13:16:10 -0400219
220// Checks to see if the GPT tables overrun existing partitions; if they
srs5694221e0872009-08-29 15:00:31 -0400221// do, issues a warning but takes no action. Returns number of problems
222// detected (0 if OK, 1 to 2 if problems).
srs5694e7b4ff92009-08-18 13:16:10 -0400223int GPTData::CheckGPTSize(void) {
224 uint64_t overlap, firstUsedBlock, lastUsedBlock;
225 uint32_t i;
srs5694221e0872009-08-29 15:00:31 -0400226 int numProbs = 0;
srs5694e7b4ff92009-08-18 13:16:10 -0400227
228 // first, locate the first & last used blocks
229 firstUsedBlock = UINT64_MAX;
230 lastUsedBlock = 0;
231 for (i = 0; i < mainHeader.numParts; i++) {
srs5694221e0872009-08-29 15:00:31 -0400232 if ((partitions[i].GetFirstLBA() < firstUsedBlock) &&
srs5694e4ac11e2009-08-31 10:13:04 -0400233 (partitions[i].GetFirstLBA() != 0))
srs5694221e0872009-08-29 15:00:31 -0400234 firstUsedBlock = partitions[i].GetFirstLBA();
235 if (partitions[i].GetLastLBA() > lastUsedBlock)
236 lastUsedBlock = partitions[i].GetLastLBA();
srs5694e7b4ff92009-08-18 13:16:10 -0400237 } // for
238
239 // If the disk size is 0 (the default), then it means that various
240 // variables aren't yet set, so the below tests will be useless;
241 // therefore we should skip everything
242 if (diskSize != 0) {
243 if (mainHeader.firstUsableLBA > firstUsedBlock) {
244 overlap = mainHeader.firstUsableLBA - firstUsedBlock;
srs5694221e0872009-08-29 15:00:31 -0400245 printf("Warning! Main partition table overlaps the first partition by %lu blocks!\n",
246 (unsigned long) overlap);
247 if (firstUsedBlock > 2) {
248 printf("Try reducing the partition table size by %lu entries.\n",
249 (unsigned long) (overlap * 4));
250 printf("(Use the 's' item on the experts' menu.)\n");
251 } else {
252 printf("You will need to delete this partition or resize it in another utility.\n");
253 } // if/else
254 numProbs++;
srs5694e7b4ff92009-08-18 13:16:10 -0400255 } // Problem at start of disk
256 if (mainHeader.lastUsableLBA < lastUsedBlock) {
257 overlap = lastUsedBlock - mainHeader.lastUsableLBA;
srs5694221e0872009-08-29 15:00:31 -0400258 printf("Warning! Secondary partition table overlaps the last partition by %lu blocks\n",
259 (unsigned long) overlap);
260 if (lastUsedBlock > (diskSize - 2)) {
261 printf("You will need to delete this partition or resize it in another utility.\n");
262 } else {
263 printf("Try reducing the partition table size by %lu entries.\n",
264 (unsigned long) (overlap * 4));
265 printf("(Use the 's' item on the experts' menu.)\n");
266 } // if/else
267 numProbs++;
srs5694e7b4ff92009-08-18 13:16:10 -0400268 } // Problem at end of disk
269 } // if (diskSize != 0)
srs5694221e0872009-08-29 15:00:31 -0400270 return numProbs;
srs5694e7b4ff92009-08-18 13:16:10 -0400271} // GPTData::CheckGPTSize()
272
srs5694e7b4ff92009-08-18 13:16:10 -0400273// Check the validity of the GPT header. Returns 1 if the main header
274// is valid, 2 if the backup header is valid, 3 if both are valid, and
275// 0 if neither is valid. Note that this function just checks the GPT
276// signature and revision numbers, not CRCs or other data.
277int GPTData::CheckHeaderValidity(void) {
278 int valid = 3;
279
280 if (mainHeader.signature != GPT_SIGNATURE) {
281 valid -= 1;
srs5694221e0872009-08-29 15:00:31 -0400282// printf("Main GPT signature invalid; read 0x%016llX, should be\n0x%016llX\n",
283// (unsigned long long) mainHeader.signature, (unsigned long long) GPT_SIGNATURE);
srs5694e7b4ff92009-08-18 13:16:10 -0400284 } else if ((mainHeader.revision != 0x00010000) && valid) {
285 valid -= 1;
286 printf("Unsupported GPT version in main header; read 0x%08lX, should be\n0x%08lX\n",
287 (unsigned long) mainHeader.revision, UINT32_C(0x00010000));
288 } // if/else/if
289
290 if (secondHeader.signature != GPT_SIGNATURE) {
291 valid -= 2;
srs5694221e0872009-08-29 15:00:31 -0400292// printf("Secondary GPT signature invalid; read 0x%016llX, should be\n0x%016llX\n",
293// (unsigned long long) secondHeader.signature, (unsigned long long) GPT_SIGNATURE);
srs5694e7b4ff92009-08-18 13:16:10 -0400294 } else if ((secondHeader.revision != 0x00010000) && valid) {
295 valid -= 2;
296 printf("Unsupported GPT version in backup header; read 0x%08lX, should be\n0x%08lX\n",
297 (unsigned long) mainHeader.revision, UINT32_C(0x00010000));
298 } // if/else/if
299
srs56942a9f5da2009-08-26 00:48:01 -0400300 // If MBR bad, check for an Apple disk signature
srs5694e35eb1b2009-09-14 00:29:34 -0400301 if ((protectiveMBR.GetValidity() == invalid) &&
srs5694e4ac11e2009-08-31 10:13:04 -0400302 (((mainHeader.signature << 32) == APM_SIGNATURE1) ||
srs56942a9f5da2009-08-26 00:48:01 -0400303 (mainHeader.signature << 32) == APM_SIGNATURE2)) {
srs5694221e0872009-08-29 15:00:31 -0400304 apmFound = 1; // Will display warning message later
srs56943f2fe992009-11-24 18:28:18 -0500305 } // if
srs56942a9f5da2009-08-26 00:48:01 -0400306
srs5694e4ac11e2009-08-31 10:13:04 -0400307 return valid;
srs5694e7b4ff92009-08-18 13:16:10 -0400308} // GPTData::CheckHeaderValidity()
309
310// Check the header CRC to see if it's OK...
srs56942a9f5da2009-08-26 00:48:01 -0400311// Note: Must be called BEFORE byte-order reversal on big-endian
312// systems!
srs5694e7b4ff92009-08-18 13:16:10 -0400313int GPTData::CheckHeaderCRC(struct GPTHeader* header) {
srs5694978041c2009-09-21 20:51:47 -0400314 uint32_t oldCRC, newCRC, hSize;
srs5694e7b4ff92009-08-18 13:16:10 -0400315
srs56942a9f5da2009-08-26 00:48:01 -0400316 // Back up old header CRC and then blank it, since it must be 0 for
srs5694e7b4ff92009-08-18 13:16:10 -0400317 // computation to be valid
318 oldCRC = header->headerCRC;
319 header->headerCRC = UINT32_C(0);
srs5694978041c2009-09-21 20:51:47 -0400320 hSize = header->headerSize;
321
322 // If big-endian system, reverse byte order
323 if (IsLittleEndian() == 0) {
324 ReverseBytes(&oldCRC, 4);
325 } // if
srs5694e7b4ff92009-08-18 13:16:10 -0400326
327 // Initialize CRC functions...
328 chksum_crc32gentab();
329
330 // Compute CRC, restore original, and return result of comparison
331 newCRC = chksum_crc32((unsigned char*) header, HEADER_SIZE);
srs5694978041c2009-09-21 20:51:47 -0400332 header->headerCRC = oldCRC;
srs5694e7b4ff92009-08-18 13:16:10 -0400333 return (oldCRC == newCRC);
334} // GPTData::CheckHeaderCRC()
335
srs56942a9f5da2009-08-26 00:48:01 -0400336// Recompute all the CRCs. Must be called before saving (but after reversing
337// byte order on big-endian systems) if any changes have been made.
srs5694e7b4ff92009-08-18 13:16:10 -0400338void GPTData::RecomputeCRCs(void) {
srs5694978041c2009-09-21 20:51:47 -0400339 uint32_t crc, hSize, trueNumParts;
srs56942a9f5da2009-08-26 00:48:01 -0400340 int littleEndian = 1;
srs5694e7b4ff92009-08-18 13:16:10 -0400341
342 // Initialize CRC functions...
343 chksum_crc32gentab();
344
srs5694978041c2009-09-21 20:51:47 -0400345 hSize = mainHeader.headerSize;
srs56942a9f5da2009-08-26 00:48:01 -0400346 littleEndian = IsLittleEndian();
347
srs5694e7b4ff92009-08-18 13:16:10 -0400348 // Compute CRC of partition tables & store in main and secondary headers
srs56942a9f5da2009-08-26 00:48:01 -0400349 trueNumParts = mainHeader.numParts;
350 if (littleEndian == 0)
srs5694221e0872009-08-29 15:00:31 -0400351 ReverseBytes(&trueNumParts, 4); // unreverse this key piece of data....
srs56942a9f5da2009-08-26 00:48:01 -0400352 crc = chksum_crc32((unsigned char*) partitions, trueNumParts * GPT_SIZE);
srs5694e7b4ff92009-08-18 13:16:10 -0400353 mainHeader.partitionEntriesCRC = crc;
354 secondHeader.partitionEntriesCRC = crc;
srs56942a9f5da2009-08-26 00:48:01 -0400355 if (littleEndian == 0) {
srs5694221e0872009-08-29 15:00:31 -0400356 ReverseBytes(&mainHeader.partitionEntriesCRC, 4);
357 ReverseBytes(&secondHeader.partitionEntriesCRC, 4);
srs56942a9f5da2009-08-26 00:48:01 -0400358 } // if
srs5694e7b4ff92009-08-18 13:16:10 -0400359
360 // Zero out GPT tables' own CRCs (required for correct computation)
361 mainHeader.headerCRC = 0;
362 secondHeader.headerCRC = 0;
363
364 // Compute & store CRCs of main & secondary headers...
srs5694978041c2009-09-21 20:51:47 -0400365 crc = chksum_crc32((unsigned char*) &mainHeader, hSize);
srs56942a9f5da2009-08-26 00:48:01 -0400366 if (littleEndian == 0)
srs5694221e0872009-08-29 15:00:31 -0400367 ReverseBytes(&crc, 4);
srs5694e7b4ff92009-08-18 13:16:10 -0400368 mainHeader.headerCRC = crc;
srs5694978041c2009-09-21 20:51:47 -0400369 crc = chksum_crc32((unsigned char*) &secondHeader, hSize);
srs56942a9f5da2009-08-26 00:48:01 -0400370 if (littleEndian == 0)
srs5694221e0872009-08-29 15:00:31 -0400371 ReverseBytes(&crc, 4);
srs5694e7b4ff92009-08-18 13:16:10 -0400372 secondHeader.headerCRC = crc;
373} // GPTData::RecomputeCRCs()
374
srs5694e7b4ff92009-08-18 13:16:10 -0400375// Rebuild the main GPT header, using the secondary header as a model.
376// Typically called when the main header has been found to be corrupt.
377void GPTData::RebuildMainHeader(void) {
378 int i;
379
380 mainHeader.signature = GPT_SIGNATURE;
381 mainHeader.revision = secondHeader.revision;
srs5694978041c2009-09-21 20:51:47 -0400382 mainHeader.headerSize = secondHeader.headerSize;
srs5694e7b4ff92009-08-18 13:16:10 -0400383 mainHeader.headerCRC = UINT32_C(0);
384 mainHeader.reserved = secondHeader.reserved;
385 mainHeader.currentLBA = secondHeader.backupLBA;
386 mainHeader.backupLBA = secondHeader.currentLBA;
387 mainHeader.firstUsableLBA = secondHeader.firstUsableLBA;
388 mainHeader.lastUsableLBA = secondHeader.lastUsableLBA;
389 mainHeader.diskGUID.data1 = secondHeader.diskGUID.data1;
390 mainHeader.diskGUID.data2 = secondHeader.diskGUID.data2;
391 mainHeader.partitionEntriesLBA = UINT64_C(2);
392 mainHeader.numParts = secondHeader.numParts;
393 mainHeader.sizeOfPartitionEntries = secondHeader.sizeOfPartitionEntries;
394 mainHeader.partitionEntriesCRC = secondHeader.partitionEntriesCRC;
395 for (i = 0 ; i < GPT_RESERVED; i++)
396 mainHeader.reserved2[i] = secondHeader.reserved2[i];
397} // GPTData::RebuildMainHeader()
398
399// Rebuild the secondary GPT header, using the main header as a model.
400void GPTData::RebuildSecondHeader(void) {
401 int i;
402
403 secondHeader.signature = GPT_SIGNATURE;
404 secondHeader.revision = mainHeader.revision;
srs5694978041c2009-09-21 20:51:47 -0400405 secondHeader.headerSize = mainHeader.headerSize;
srs5694e7b4ff92009-08-18 13:16:10 -0400406 secondHeader.headerCRC = UINT32_C(0);
407 secondHeader.reserved = mainHeader.reserved;
408 secondHeader.currentLBA = mainHeader.backupLBA;
409 secondHeader.backupLBA = mainHeader.currentLBA;
410 secondHeader.firstUsableLBA = mainHeader.firstUsableLBA;
411 secondHeader.lastUsableLBA = mainHeader.lastUsableLBA;
412 secondHeader.diskGUID.data1 = mainHeader.diskGUID.data1;
413 secondHeader.diskGUID.data2 = mainHeader.diskGUID.data2;
414 secondHeader.partitionEntriesLBA = secondHeader.lastUsableLBA + UINT64_C(1);
415 secondHeader.numParts = mainHeader.numParts;
416 secondHeader.sizeOfPartitionEntries = mainHeader.sizeOfPartitionEntries;
417 secondHeader.partitionEntriesCRC = mainHeader.partitionEntriesCRC;
418 for (i = 0 ; i < GPT_RESERVED; i++)
419 secondHeader.reserved2[i] = mainHeader.reserved2[i];
srs5694e4ac11e2009-08-31 10:13:04 -0400420} // GPTData::RebuildSecondHeader()
421
422// Search for hybrid MBR entries that have no corresponding GPT partition.
423// Returns number of such mismatches found
424int GPTData::FindHybridMismatches(void) {
425 int i, j, found, numFound = 0;
426 uint64_t mbrFirst, mbrLast;
427
428 for (i = 0; i < 4; i++) {
429 if ((protectiveMBR.GetType(i) != 0xEE) && (protectiveMBR.GetType(i) != 0x00)) {
430 j = 0;
431 found = 0;
432 do {
433 mbrFirst = (uint64_t) protectiveMBR.GetFirstSector(i);
434 mbrLast = mbrFirst + (uint64_t) protectiveMBR.GetLength(i) - UINT64_C(1);
435 if ((partitions[j].GetFirstLBA() == mbrFirst) &&
436 (partitions[j].GetLastLBA() == mbrLast))
437 found = 1;
438 j++;
439 } while ((!found) && (j < mainHeader.numParts));
440 if (!found) {
441 numFound++;
442 printf("\nWarning! Mismatched GPT and MBR partitions! MBR partition "
443 "%d, of type 0x%02X,\nhas no corresponding GPT partition! "
444 "You may continue, but this condition\nmight cause data loss"
445 " in the future!\a\n", i + 1, protectiveMBR.GetType(i));
446 } // if
447 } // if
448 } // for
449 return numFound;
450} // GPTData::FindHybridMismatches
451
452// Find overlapping partitions and warn user about them. Returns number of
453// overlapping partitions.
454int GPTData::FindOverlaps(void) {
455 int i, j, problems = 0;
456
457 for (i = 1; i < mainHeader.numParts; i++) {
458 for (j = 0; j < i; j++) {
459 if (partitions[i].DoTheyOverlap(&partitions[j])) {
460 problems++;
461 printf("\nProblem: partitions %d and %d overlap:\n", i + 1, j + 1);
462 printf(" Partition %d: %llu to %llu\n", i,
463 (unsigned long long) partitions[i].GetFirstLBA(),
464 (unsigned long long) partitions[i].GetLastLBA());
465 printf(" Partition %d: %llu to %llu\n", j,
466 (unsigned long long) partitions[j].GetFirstLBA(),
467 (unsigned long long) partitions[j].GetLastLBA());
468 } // if
469 } // for j...
470 } // for i...
471 return problems;
472} // GPTData::FindOverlaps()
473
474/******************************************************************
475 * *
476 * Begin functions that load data from disk or save data to disk. *
477 * *
478 ******************************************************************/
479
480// Scan for partition data. This function loads the MBR data (regular MBR or
481// protective MBR) and loads BSD disklabel data (which is probably invalid).
482// It also looks for APM data, forces a load of GPT data, and summarizes
483// the results.
484void GPTData::PartitionScan(int fd) {
485 BSDData bsdDisklabel;
486// int bsdFound;
487
488 printf("Partition table scan:\n");
489
490 // Read the MBR & check for BSD disklabel
491 protectiveMBR.ReadMBRData(fd);
492 protectiveMBR.ShowState();
493 bsdDisklabel.ReadBSDData(fd, 0, diskSize - 1);
494 bsdFound = bsdDisklabel.ShowState();
495// bsdDisklabel.DisplayBSDData();
496
497 // Load the GPT data, whether or not it's valid
498 ForceLoadGPTData(fd);
499 ShowAPMState(); // Show whether there's an Apple Partition Map present
500 ShowGPTState(); // Show GPT status
501 printf("\n");
502
503 if (apmFound) {
504 printf("\n*******************************************************************\n");
505 printf("This disk appears to contain an Apple-format (APM) partition table!\n");
506 printf("It will be destroyed if you continue!\n");
507 printf("*******************************************************************\n\n\a");
508 } // if
srs5694e4ac11e2009-08-31 10:13:04 -0400509} // GPTData::PartitionScan()
510
511// Read GPT data from a disk.
512int GPTData::LoadPartitions(char* deviceFilename) {
513 int fd, err;
514 int allOK = 1, i;
515 uint64_t firstBlock, lastBlock;
516 BSDData bsdDisklabel;
517
518 // First, do a test to see if writing will be possible later....
519 fd = OpenForWrite(deviceFilename);
520 if (fd == -1)
521 printf("\aNOTE: Write test failed with error number %d. It will be "
522 "impossible to save\nchanges to this disk's partition table!\n\n",
523 errno);
524 close(fd);
525
526 if ((fd = open(deviceFilename, O_RDONLY)) != -1) {
527 // store disk information....
528 diskSize = disksize(fd, &err);
529 blockSize = (uint32_t) GetBlockSize(fd);
530 strcpy(device, deviceFilename);
531 PartitionScan(fd); // Check for partition types & print summary
532
533 switch (UseWhichPartitions()) {
534 case use_mbr:
535 XFormPartitions();
536 break;
537 case use_bsd:
538 bsdDisklabel.ReadBSDData(fd, 0, diskSize - 1);
539// bsdDisklabel.DisplayBSDData();
540 ClearGPTData();
541 protectiveMBR.MakeProtectiveMBR(1); // clear boot area (option 1)
542 XFormDisklabel(&bsdDisklabel, 0);
543 break;
544 case use_gpt:
545 break;
546 case use_new:
547 ClearGPTData();
548 protectiveMBR.MakeProtectiveMBR();
549 break;
550 } // switch
551
552 // Now find the first and last sectors used by partitions...
553 if (allOK) {
554 firstBlock = mainHeader.backupLBA; // start high
555 lastBlock = 0; // start low
556 for (i = 0; i < mainHeader.numParts; i++) {
557 if ((partitions[i].GetFirstLBA() < firstBlock) &&
558 (partitions[i].GetFirstLBA() > 0))
559 firstBlock = partitions[i].GetFirstLBA();
560 if (partitions[i].GetLastLBA() > lastBlock)
561 lastBlock = partitions[i].GetLastLBA();
562 } // for
563 } // if
564 CheckGPTSize();
565 } else {
566 allOK = 0;
567 fprintf(stderr, "Problem opening %s for reading! Error is %d\n",
568 deviceFilename, errno);
569 if (errno == EACCES) { // User is probably not running as root
570 fprintf(stderr, "You must run this program as root or use sudo!\n");
571 } // if
572 } // if/else
573 return (allOK);
574} // GPTData::LoadPartitions()
575
576// Loads the GPT, as much as possible. Returns 1 if this seems to have
577// succeeded, 0 if there are obvious problems....
578int GPTData::ForceLoadGPTData(int fd) {
579 int allOK = 1, validHeaders;
580 off_t seekTo;
581 char* storage;
582 uint32_t newCRC, sizeOfParts;
583
584 // Seek to and read the main GPT header
585 lseek64(fd, 512, SEEK_SET);
586 read(fd, &mainHeader, 512); // read main GPT header
587 mainCrcOk = CheckHeaderCRC(&mainHeader);
588 if (IsLittleEndian() == 0) // big-endian system; adjust header byte order....
589 ReverseHeaderBytes(&mainHeader);
590
srs56943f2fe992009-11-24 18:28:18 -0500591 // Load backup header, check its CRC, and store the results of the
592 // check for future reference. Load backup header using pointer in main
593 // header if possible; but if main header has a CRC error, or if it
594 // points to beyond the end of the disk, load the last sector of the
595 // disk instead.
596 if (mainCrcOk) {
597 if (mainHeader.backupLBA < diskSize) {
598 seekTo = mainHeader.backupLBA * blockSize;
599 } else {
600 seekTo = (diskSize * blockSize) - UINT64_C(512);
601 printf("Warning! Disk size is smaller than the main header indicates! Loading\n"
602 "secondary header from the last sector of the disk! You should use 'v' to\n"
603 "verify disk integrity, and perhaps options on the experts' menu to repair\n"
604 "the disk.\n");
605 } // else
606 } else {
607 seekTo = (diskSize * blockSize) - UINT64_C(512);
608 } // if/else (mainCrcOk)
609
srs5694e4ac11e2009-08-31 10:13:04 -0400610 if (lseek64(fd, seekTo, SEEK_SET) != (off_t) -1) {
611 read(fd, &secondHeader, 512); // read secondary GPT header
612 secondCrcOk = CheckHeaderCRC(&secondHeader);
613 if (IsLittleEndian() == 0) // big-endian system; adjust header byte order....
614 ReverseHeaderBytes(&secondHeader);
615 } else {
616 allOK = 0;
617 state = gpt_invalid;
srs56943f2fe992009-11-24 18:28:18 -0500618 fprintf(stderr, "Unable to seek to secondary GPT header at sector %llu!\n",
srs5694e4ac11e2009-08-31 10:13:04 -0400619 diskSize - (UINT64_C(1)));
620 } // if/else lseek
621
622 // Return valid headers code: 0 = both headers bad; 1 = main header
623 // good, backup bad; 2 = backup header good, main header bad;
624 // 3 = both headers good. Note these codes refer to valid GPT
625 // signatures and version numbers; more subtle problems will elude
626 // this check!
627 validHeaders = CheckHeaderValidity();
628
629 // Read partitions (from primary array)
630 if (validHeaders > 0) { // if at least one header is OK....
631 // GPT appears to be valid....
632 state = gpt_valid;
633
634 // We're calling the GPT valid, but there's a possibility that one
635 // of the two headers is corrupt. If so, use the one that seems to
636 // be in better shape to regenerate the bad one
637 if (validHeaders == 2) { // valid backup header, invalid main header
638 printf("Caution: invalid main GPT header, but valid backup; regenerating main header\n"
639 "from backup!\n");
640 RebuildMainHeader();
641 mainCrcOk = secondCrcOk; // Since copied, use CRC validity of backup
642 } else if (validHeaders == 1) { // valid main header, invalid backup
643 printf("Caution: invalid backup GPT header, but valid main header; regenerating\n"
644 "backup header from main header.\n");
645 RebuildSecondHeader();
646 secondCrcOk = mainCrcOk; // Since regenerated, use CRC validity of main
647 } // if/else/if
648
649 // Load the main partition table, including storing results of its
650 // CRC check
651 if (LoadMainTable() == 0)
652 allOK = 0;
653
654 // Load backup partition table into temporary storage to check
655 // its CRC and store the results, then discard this temporary
656 // storage, since we don't use it in any but recovery operations
657 seekTo = secondHeader.partitionEntriesLBA * (off_t) blockSize;
658 if ((lseek64(fd, seekTo, SEEK_SET) != (off_t) -1) && (secondCrcOk)) {
659 sizeOfParts = secondHeader.numParts * secondHeader.sizeOfPartitionEntries;
660 storage = (char*) malloc(sizeOfParts);
661 read(fd, storage, sizeOfParts);
662 newCRC = chksum_crc32((unsigned char*) storage, sizeOfParts);
663 free(storage);
664 secondPartsCrcOk = (newCRC == secondHeader.partitionEntriesCRC);
665 } // if
666
667 // Check for valid CRCs and warn if there are problems
668 if ((mainCrcOk == 0) || (secondCrcOk == 0) || (mainPartsCrcOk == 0) ||
669 (secondPartsCrcOk == 0)) {
670 printf("Warning! One or more CRCs don't match. You should repair the disk!\n");
671 state = gpt_corrupt;
672 } // if
673 } else {
674 state = gpt_invalid;
675 } // if/else
676 return allOK;
677} // GPTData::ForceLoadGPTData()
678
679// Loads the partition tables pointed to by the main GPT header. The
680// main GPT header in memory MUST be valid for this call to do anything
681// sensible!
682int GPTData::LoadMainTable(void) {
683 int fd, retval = 0;
684 uint32_t newCRC, sizeOfParts;
685
686 if ((fd = open(device, O_RDONLY)) != -1) {
687 // Set internal data structures for number of partitions on the disk
688 SetGPTSize(mainHeader.numParts);
689
690 // Load main partition table, and record whether its CRC
691 // matches the stored value
692 lseek64(fd, mainHeader.partitionEntriesLBA * blockSize, SEEK_SET);
693 sizeOfParts = mainHeader.numParts * mainHeader.sizeOfPartitionEntries;
694 read(fd, partitions, sizeOfParts);
695 newCRC = chksum_crc32((unsigned char*) partitions, sizeOfParts);
696 mainPartsCrcOk = (newCRC == mainHeader.partitionEntriesCRC);
697 if (IsLittleEndian() == 0)
698 ReversePartitionBytes();
699 retval = 1;
700 } // if
701 return retval;
702} // GPTData::LoadMainTable()
srs5694e7b4ff92009-08-18 13:16:10 -0400703
704// Load the second (backup) partition table as the primary partition
705// table. Used in repair functions
706void GPTData::LoadSecondTableAsMain(void) {
707 int fd;
708 off_t seekTo;
709 uint32_t sizeOfParts, newCRC;
710
711 if ((fd = open(device, O_RDONLY)) != -1) {
712 seekTo = secondHeader.partitionEntriesLBA * (off_t) blockSize;
713 if (lseek64(fd, seekTo, SEEK_SET) != (off_t) -1) {
714 SetGPTSize(secondHeader.numParts);
715 sizeOfParts = secondHeader.numParts * secondHeader.sizeOfPartitionEntries;
716 read(fd, partitions, sizeOfParts);
717 newCRC = chksum_crc32((unsigned char*) partitions, sizeOfParts);
718 secondPartsCrcOk = (newCRC == secondHeader.partitionEntriesCRC);
srs5694e4ac11e2009-08-31 10:13:04 -0400719 mainPartsCrcOk = secondPartsCrcOk;
srs56942a9f5da2009-08-26 00:48:01 -0400720 if (IsLittleEndian() == 0)
721 ReversePartitionBytes();
srs5694e7b4ff92009-08-18 13:16:10 -0400722 if (!secondPartsCrcOk) {
723 printf("Error! After loading backup partitions, the CRC still doesn't check out!\n");
724 } // if
725 } else {
726 printf("Error! Couldn't seek to backup partition table!\n");
727 } // if/else
728 } else {
729 printf("Error! Couldn't open device %s when recovering backup partition table!\n");
730 } // if/else
731} // GPTData::LoadSecondTableAsMain()
732
srs5694e7b4ff92009-08-18 13:16:10 -0400733// Writes GPT (and protective MBR) to disk. Returns 1 on successful
734// write, 0 if there was a problem.
735int GPTData::SaveGPTData(void) {
srs5694978041c2009-09-21 20:51:47 -0400736 int allOK = 1;
srs5694e7b4ff92009-08-18 13:16:10 -0400737 char answer, line[256];
738 int fd;
739 uint64_t secondTable;
srs56942a9f5da2009-08-26 00:48:01 -0400740 uint32_t numParts;
srs5694e7b4ff92009-08-18 13:16:10 -0400741 off_t offset;
742
743 if (strlen(device) == 0) {
744 printf("Device not defined.\n");
745 } // if
746
747 // First do some final sanity checks....
748 // Is there enough space to hold the GPT headers and partition tables,
749 // given the partition sizes?
srs5694221e0872009-08-29 15:00:31 -0400750 if (CheckGPTSize() > 0) {
srs5694e7b4ff92009-08-18 13:16:10 -0400751 allOK = 0;
752 } // if
753
754 // Check that disk is really big enough to handle this...
755 if (mainHeader.backupLBA > diskSize) {
756 fprintf(stderr, "Error! Disk is too small -- either the original MBR is corrupt or you're\n");
757 fprintf(stderr, "working from an MBR copied to a file! Aborting!\n");
758 printf("(Disk size is %ld sectors, needs to be %ld sectors.)\n", diskSize,
759 mainHeader.backupLBA);
760 allOK = 0;
761 } // if
762
763 // Check for overlapping partitions....
srs5694e4ac11e2009-08-31 10:13:04 -0400764 if (FindOverlaps() > 0) {
765 allOK = 0;
766 fprintf(stderr, "Aborting write operation!\n");
767 } // if
768
769 // Check for mismatched MBR and GPT data, but let it pass if found
770 // (function displays warning message)
771 FindHybridMismatches();
srs5694e7b4ff92009-08-18 13:16:10 -0400772
srs56942a9f5da2009-08-26 00:48:01 -0400773 // Pull out some data that's needed before doing byte-order reversal on
774 // big-endian systems....
775 numParts = mainHeader.numParts;
776 secondTable = secondHeader.partitionEntriesLBA;
777 if (IsLittleEndian() == 0) {
778 // Reverse partition bytes first, since that function requires non-reversed
779 // data from the main header....
780 ReversePartitionBytes();
781 ReverseHeaderBytes(&mainHeader);
782 ReverseHeaderBytes(&secondHeader);
783 } // if
srs5694e7b4ff92009-08-18 13:16:10 -0400784 RecomputeCRCs();
785
786 if (allOK) {
787 printf("\nFinal checks complete. About to write GPT data. THIS WILL OVERWRITE EXISTING\n");
788 printf("MBR PARTITIONS!! THIS PROGRAM IS BETA QUALITY AT BEST. IF YOU LOSE ALL YOUR\n");
789 printf("DATA, YOU HAVE ONLY YOURSELF TO BLAME IF YOU ANSWER 'Y' BELOW!\n\n");
790 printf("Do you want to proceed, possibly destroying your data? (Y/N) ");
791 fgets(line, 255, stdin);
792 sscanf(line, "%c", &answer);
793 if ((answer == 'Y') || (answer == 'y')) {
794 printf("OK; writing new GPT partition table.\n");
795 } else {
796 allOK = 0;
797 } // if/else
798 } // if
799
800 // Do it!
801 if (allOK) {
srs5694e4ac11e2009-08-31 10:13:04 -0400802 fd = OpenForWrite(device);
srs5694e7b4ff92009-08-18 13:16:10 -0400803 if (fd != -1) {
804 // First, write the protective MBR...
srs5694e4ac11e2009-08-31 10:13:04 -0400805 protectiveMBR.WriteMBRData(fd);
srs5694e7b4ff92009-08-18 13:16:10 -0400806
807 // Now write the main GPT header...
808 if (allOK)
809 if (write(fd, &mainHeader, 512) == -1)
srs5694e4ac11e2009-08-31 10:13:04 -0400810 allOK = 0;
srs5694e7b4ff92009-08-18 13:16:10 -0400811
812 // Now write the main partition tables...
srs5694e4ac11e2009-08-31 10:13:04 -0400813 if (allOK) {
814 if (write(fd, partitions, GPT_SIZE * numParts) == -1)
srs5694e7b4ff92009-08-18 13:16:10 -0400815 allOK = 0;
816 } // if
817
818 // Now seek to near the end to write the secondary GPT....
srs5694e4ac11e2009-08-31 10:13:04 -0400819 if (allOK) {
srs5694e7b4ff92009-08-18 13:16:10 -0400820 offset = (off_t) secondTable * (off_t) (blockSize);
821 if (lseek64(fd, offset, SEEK_SET) == (off_t) - 1) {
822 allOK = 0;
823 printf("Unable to seek to end of disk!\n");
824 } // if
825 } // if
826
827 // Now write the secondary partition tables....
srs5694e4ac11e2009-08-31 10:13:04 -0400828 if (allOK)
srs56942a9f5da2009-08-26 00:48:01 -0400829 if (write(fd, partitions, GPT_SIZE * numParts) == -1)
srs5694e7b4ff92009-08-18 13:16:10 -0400830 allOK = 0;
831
832 // Now write the secondary GPT header...
srs5694e4ac11e2009-08-31 10:13:04 -0400833 if (allOK)
srs5694e7b4ff92009-08-18 13:16:10 -0400834 if (write(fd, &secondHeader, 512) == -1)
srs5694e4ac11e2009-08-31 10:13:04 -0400835 allOK = 0;
srs5694e7b4ff92009-08-18 13:16:10 -0400836
837 // re-read the partition table
838 if (allOK) {
srs5694e35eb1b2009-09-14 00:29:34 -0400839 DiskSync(fd);
srs5694e7b4ff92009-08-18 13:16:10 -0400840 } // if
841
842 if (allOK) { // writes completed OK
srs5694e4ac11e2009-08-31 10:13:04 -0400843 printf("The operation has completed successfully.\n");
srs5694e7b4ff92009-08-18 13:16:10 -0400844 } else {
srs5694e4ac11e2009-08-31 10:13:04 -0400845 printf("Warning! An error was reported when writing the partition table! This error\n");
846 printf("MIGHT be harmless, but you may have trashed the disk! Use parted and, if\n");
847 printf("necessary, restore your original partition table.\n");
srs5694e7b4ff92009-08-18 13:16:10 -0400848 } // if/else
srs5694e4ac11e2009-08-31 10:13:04 -0400849 close(fd);
srs5694e7b4ff92009-08-18 13:16:10 -0400850 } else {
srs5694e4ac11e2009-08-31 10:13:04 -0400851 fprintf(stderr, "Unable to open device %s for writing! Errno is %d! Aborting write!\n",
852 device, errno);
853 allOK = 0;
srs5694e7b4ff92009-08-18 13:16:10 -0400854 } // if/else
855 } else {
856 printf("Aborting write of new partition table.\n");
857 } // if
858
srs56942a9f5da2009-08-26 00:48:01 -0400859 if (IsLittleEndian() == 0) {
860 // Reverse (normalize) header bytes first, since ReversePartitionBytes()
861 // requires non-reversed data in mainHeader...
862 ReverseHeaderBytes(&mainHeader);
863 ReverseHeaderBytes(&secondHeader);
864 ReversePartitionBytes();
865 } // if
866
srs5694e7b4ff92009-08-18 13:16:10 -0400867 return (allOK);
868} // GPTData::SaveGPTData()
869
870// Save GPT data to a backup file. This function does much less error
871// checking than SaveGPTData(). It can therefore preserve many types of
872// corruption for later analysis; however, it preserves only the MBR,
873// the main GPT header, the backup GPT header, and the main partition
874// table; it discards the backup partition table, since it should be
875// identical to the main partition table on healthy disks.
876int GPTData::SaveGPTBackup(char* filename) {
srs56942a9f5da2009-08-26 00:48:01 -0400877 int fd, allOK = 1;
878 uint32_t numParts;
srs5694e7b4ff92009-08-18 13:16:10 -0400879
880 if ((fd = open(filename, O_WRONLY | O_CREAT, S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH)) != -1) {
srs56942a9f5da2009-08-26 00:48:01 -0400881 // Reverse the byte order, if necessary....
882 numParts = mainHeader.numParts;
883 if (IsLittleEndian() == 0) {
884 ReversePartitionBytes();
885 ReverseHeaderBytes(&mainHeader);
886 ReverseHeaderBytes(&secondHeader);
887 } // if
888
srs5694978041c2009-09-21 20:51:47 -0400889 // Recomputing the CRCs is likely to alter them, which could be bad
890 // if the intent is to save a potentially bad GPT for later analysis;
891 // but if we don't do this, we get bogus errors when we load the
892 // backup. I'm favoring misses over false alarms....
893 RecomputeCRCs();
894
srs56942a9f5da2009-08-26 00:48:01 -0400895 // Now write the protective MBR...
srs5694e7b4ff92009-08-18 13:16:10 -0400896 protectiveMBR.WriteMBRData(fd);
897
898 // Now write the main GPT header...
899 if (allOK)
900 if (write(fd, &mainHeader, 512) == -1)
901 allOK = 0;
902
903 // Now write the secondary GPT header...
904 if (allOK)
905 if (write(fd, &secondHeader, 512) == -1)
srs5694e4ac11e2009-08-31 10:13:04 -0400906 allOK = 0;
srs5694e7b4ff92009-08-18 13:16:10 -0400907
908 // Now write the main partition tables...
909 if (allOK) {
srs56942a9f5da2009-08-26 00:48:01 -0400910 if (write(fd, partitions, GPT_SIZE * numParts) == -1)
srs5694e7b4ff92009-08-18 13:16:10 -0400911 allOK = 0;
912 } // if
913
914 if (allOK) { // writes completed OK
915 printf("The operation has completed successfully.\n");
916 } else {
917 printf("Warning! An error was reported when writing the backup file.\n");
srs56948bb78762009-11-24 15:43:49 -0500918 printf("It may not be usable!\n");
srs5694e7b4ff92009-08-18 13:16:10 -0400919 } // if/else
920 close(fd);
srs56942a9f5da2009-08-26 00:48:01 -0400921
922 // Now reverse the byte-order reversal, if necessary....
923 if (IsLittleEndian() == 0) {
924 ReverseHeaderBytes(&mainHeader);
925 ReverseHeaderBytes(&secondHeader);
926 ReversePartitionBytes();
927 } // if
srs5694e7b4ff92009-08-18 13:16:10 -0400928 } else {
929 fprintf(stderr, "Unable to open file %s for writing! Aborting!\n", filename);
930 allOK = 0;
931 } // if/else
932 return allOK;
933} // GPTData::SaveGPTBackup()
934
935// Load GPT data from a backup file created by SaveGPTBackup(). This function
936// does minimal error checking. It returns 1 if it completed successfully,
937// 0 if there was a problem. In the latter case, it creates a new empty
938// set of partitions.
939int GPTData::LoadGPTBackup(char* filename) {
940 int fd, allOK = 1, val;
941 uint32_t numParts, sizeOfEntries, sizeOfParts, newCRC;
srs56942a9f5da2009-08-26 00:48:01 -0400942 int littleEndian = 1;
srs5694e7b4ff92009-08-18 13:16:10 -0400943
944 if ((fd = open(filename, O_RDONLY)) != -1) {
srs56942a9f5da2009-08-26 00:48:01 -0400945 if (IsLittleEndian() == 0)
946 littleEndian = 0;
947
srs5694e7b4ff92009-08-18 13:16:10 -0400948 // Let the MBRData class load the saved MBR...
srs5694221e0872009-08-29 15:00:31 -0400949 protectiveMBR.ReadMBRData(fd, 0); // 0 = don't check block size
srs5694e7b4ff92009-08-18 13:16:10 -0400950
951 // Load the main GPT header, check its vaility, and set the GPT
952 // size based on the data
953 read(fd, &mainHeader, 512);
954 mainCrcOk = CheckHeaderCRC(&mainHeader);
955
srs56942a9f5da2009-08-26 00:48:01 -0400956 // Reverse byte order, if necessary
957 if (littleEndian == 0) {
958 ReverseHeaderBytes(&mainHeader);
959 } // if
960
srs5694e7b4ff92009-08-18 13:16:10 -0400961 // Load the backup GPT header in much the same way as the main
962 // GPT header....
963 read(fd, &secondHeader, 512);
964 secondCrcOk = CheckHeaderCRC(&secondHeader);
965
srs56942a9f5da2009-08-26 00:48:01 -0400966 // Reverse byte order, if necessary
967 if (littleEndian == 0) {
968 ReverseHeaderBytes(&secondHeader);
969 } // if
970
srs5694e7b4ff92009-08-18 13:16:10 -0400971 // Return valid headers code: 0 = both headers bad; 1 = main header
972 // good, backup bad; 2 = backup header good, main header bad;
973 // 3 = both headers good. Note these codes refer to valid GPT
974 // signatures and version numbers; more subtle problems will elude
975 // this check!
976 if ((val = CheckHeaderValidity()) > 0) {
977 if (val == 2) { // only backup header seems to be good
978 numParts = secondHeader.numParts;
srs5694e4ac11e2009-08-31 10:13:04 -0400979 sizeOfEntries = secondHeader.sizeOfPartitionEntries;
srs5694e7b4ff92009-08-18 13:16:10 -0400980 } else { // main header is OK
981 numParts = mainHeader.numParts;
982 sizeOfEntries = mainHeader.sizeOfPartitionEntries;
983 } // if/else
984
985 SetGPTSize(numParts);
986
987 // If current disk size doesn't match that of backup....
988 if (secondHeader.currentLBA != diskSize - UINT64_C(1)) {
989 printf("Warning! Current disk size doesn't match that of the backup!\n"
srs5694e4ac11e2009-08-31 10:13:04 -0400990 "Adjusting sizes to match, but subsequent problems are possible!\n");
srs5694e7b4ff92009-08-18 13:16:10 -0400991 secondHeader.currentLBA = mainHeader.backupLBA = diskSize - UINT64_C(1);
992 mainHeader.lastUsableLBA = diskSize - mainHeader.firstUsableLBA;
993 secondHeader.lastUsableLBA = mainHeader.lastUsableLBA;
994 secondHeader.partitionEntriesLBA = secondHeader.lastUsableLBA + UINT64_C(1);
995 } // if
996
997 // Load main partition table, and record whether its CRC
998 // matches the stored value
999 sizeOfParts = numParts * sizeOfEntries;
1000 read(fd, partitions, sizeOfParts);
1001
1002 newCRC = chksum_crc32((unsigned char*) partitions, sizeOfParts);
1003 mainPartsCrcOk = (newCRC == mainHeader.partitionEntriesCRC);
1004 secondPartsCrcOk = (newCRC == secondHeader.partitionEntriesCRC);
srs56942a9f5da2009-08-26 00:48:01 -04001005 // Reverse byte order, if necessary
srs5694e4ac11e2009-08-31 10:13:04 -04001006 if (littleEndian == 0) {
1007 ReversePartitionBytes();
1008 } // if
srs56942a9f5da2009-08-26 00:48:01 -04001009
srs5694e7b4ff92009-08-18 13:16:10 -04001010 } else {
1011 allOK = 0;
1012 } // if/else
1013 } else {
1014 allOK = 0;
1015 fprintf(stderr, "Unable to open file %s for reading! Aborting!\n", filename);
1016 } // if/else
1017
1018 // Something went badly wrong, so blank out partitions
1019 if (allOK == 0) {
1020 ClearGPTData();
1021 protectiveMBR.MakeProtectiveMBR();
1022 } // if
1023 return allOK;
1024} // GPTData::LoadGPTBackup()
1025
srs5694e4ac11e2009-08-31 10:13:04 -04001026// Tell user whether Apple Partition Map (APM) was discovered....
1027void GPTData::ShowAPMState(void) {
1028 if (apmFound)
1029 printf(" APM: present\n");
1030 else
1031 printf(" APM: not present\n");
1032} // GPTData::ShowAPMState()
1033
1034// Tell user about the state of the GPT data....
1035void GPTData::ShowGPTState(void) {
1036 switch (state) {
1037 case gpt_invalid:
1038 printf(" GPT: not present\n");
1039 break;
1040 case gpt_valid:
1041 printf(" GPT: present\n");
1042 break;
1043 case gpt_corrupt:
1044 printf(" GPT: damaged\n");
1045 break;
1046 default:
1047 printf("\a GPT: unknown -- bug!\n");
1048 break;
1049 } // switch
1050} // GPTData::ShowGPTState()
1051
1052// Display the basic GPT data
1053void GPTData::DisplayGPTData(void) {
srs5694e35eb1b2009-09-14 00:29:34 -04001054 int i;
srs5694e4ac11e2009-08-31 10:13:04 -04001055 char sizeInSI[255]; // String to hold size of disk in SI units
1056 char tempStr[255];
1057 uint64_t temp, totalFree;
1058
1059 BytesToSI(diskSize * blockSize, sizeInSI);
srs5694978041c2009-09-21 20:51:47 -04001060 printf("Disk %s: %llu sectors, %s\n", device,
1061 (unsigned long long) diskSize, sizeInSI);
srs5694e4ac11e2009-08-31 10:13:04 -04001062 printf("Disk identifier (GUID): %s\n", GUIDToStr(mainHeader.diskGUID, tempStr));
1063 printf("Partition table holds up to %lu entries\n", (unsigned long) mainHeader.numParts);
1064 printf("First usable sector is %lu, last usable sector is %lu\n",
1065 (unsigned long) mainHeader.firstUsableLBA,
1066 (unsigned long) mainHeader.lastUsableLBA);
1067 totalFree = FindFreeBlocks(&i, &temp);
1068 printf("Total free space is %llu sectors (%s)\n", totalFree,
1069 BytesToSI(totalFree * (uint64_t) blockSize, sizeInSI));
1070 printf("\nNumber Start (sector) End (sector) Size Code Name\n");
1071 for (i = 0; i < mainHeader.numParts; i++) {
srs5694978041c2009-09-21 20:51:47 -04001072 partitions[i].ShowSummary(i, blockSize);
srs5694e4ac11e2009-08-31 10:13:04 -04001073 } // for
1074} // GPTData::DisplayGPTData()
1075
1076// Get partition number from user and then call ShowPartDetails(partNum)
1077// to show its detailed information
1078void GPTData::ShowDetails(void) {
1079 int partNum;
1080 uint32_t low, high;
1081
1082 if (GetPartRange(&low, &high) > 0) {
1083 partNum = GetPartNum();
1084 ShowPartDetails(partNum);
1085 } else {
1086 printf("No partitions\n");
1087 } // if/else
1088} // GPTData::ShowDetails()
1089
1090// Show detailed information on the specified partition
1091void GPTData::ShowPartDetails(uint32_t partNum) {
1092 if (partitions[partNum].GetFirstLBA() != 0) {
1093 partitions[partNum].ShowDetails(blockSize);
1094 } else {
1095 printf("Partition #%d does not exist.", (int) (partNum + 1));
1096 } // if
1097} // GPTData::ShowPartDetails()
1098
1099/*********************************************************************
1100 * *
1101 * Begin functions that obtain information from the users, and often *
1102 * do something with that information (call other functions) *
1103 * *
1104 *********************************************************************/
1105
1106// Prompts user for partition number and returns the result.
1107uint32_t GPTData::GetPartNum(void) {
1108 uint32_t partNum;
1109 uint32_t low, high;
1110 char prompt[255];
1111
1112 if (GetPartRange(&low, &high) > 0) {
1113 sprintf(prompt, "Partition number (%d-%d): ", low + 1, high + 1);
1114 partNum = GetNumber(low + 1, high + 1, low, prompt);
1115 } else partNum = 1;
1116 return (partNum - 1);
1117} // GPTData::GetPartNum()
1118
1119// What it says: Resize the partition table. (Default is 128 entries.)
1120void GPTData::ResizePartitionTable(void) {
1121 int newSize;
1122 char prompt[255];
1123 uint32_t curLow, curHigh;
1124
1125 printf("Current partition table size is %lu.\n",
1126 (unsigned long) mainHeader.numParts);
1127 GetPartRange(&curLow, &curHigh);
1128 curHigh++; // since GetPartRange() returns numbers starting from 0...
1129 // There's no point in having fewer than four partitions....
1130 if (curHigh < 4)
1131 curHigh = 4;
1132 sprintf(prompt, "Enter new size (%d up, default %d): ", (int) curHigh,
1133 (int) NUM_GPT_ENTRIES);
1134 newSize = GetNumber(4, 65535, 128, prompt);
1135 if (newSize < 128) {
1136 printf("Caution: The partition table size should officially be 16KB or larger,\n"
1137 "which works out to 128 entries. In practice, smaller tables seem to\n"
1138 "work with most OSes, but this practice is risky. I'm proceeding with\n"
1139 "the resize, but you may want to reconsider this action and undo it.\n\n");
1140 } // if
1141 SetGPTSize(newSize);
1142} // GPTData::ResizePartitionTable()
1143
1144// Interactively create a partition
1145void GPTData::CreatePartition(void) {
1146 uint64_t firstBlock, firstInLargest, lastBlock, sector;
1147 char prompt[255];
1148 int partNum, firstFreePart = 0;
1149
1150 // Find first free partition...
1151 while (partitions[firstFreePart].GetFirstLBA() != 0) {
1152 firstFreePart++;
1153 } // while
1154
1155 if (((firstBlock = FindFirstAvailable()) != 0) &&
1156 (firstFreePart < mainHeader.numParts)) {
1157 lastBlock = FindLastAvailable(firstBlock);
1158 firstInLargest = FindFirstInLargest();
1159
1160 // Get partition number....
1161 do {
1162 sprintf(prompt, "Partition number (%d-%d, default %d): ", firstFreePart + 1,
1163 mainHeader.numParts, firstFreePart + 1);
1164 partNum = GetNumber(firstFreePart + 1, mainHeader.numParts,
1165 firstFreePart + 1, prompt) - 1;
1166 if (partitions[partNum].GetFirstLBA() != 0)
1167 printf("partition %d is in use.\n", partNum + 1);
1168 } while (partitions[partNum].GetFirstLBA() != 0);
1169
1170 // Get first block for new partition...
1171 sprintf(prompt,
1172 "First sector (%llu-%llu, default = %llu) or {+-}size{KMGT}: ",
1173 firstBlock, lastBlock, firstInLargest);
1174 do {
1175 sector = GetSectorNum(firstBlock, lastBlock, firstInLargest, prompt);
1176 } while (IsFree(sector) == 0);
1177 firstBlock = sector;
1178
1179 // Get last block for new partitions...
1180 lastBlock = FindLastInFree(firstBlock);
1181 sprintf(prompt,
1182 "Last sector (%llu-%llu, default = %llu) or {+-}size{KMGT}: ",
1183 firstBlock, lastBlock, lastBlock);
1184 do {
1185 sector = GetSectorNum(firstBlock, lastBlock, lastBlock, prompt);
1186 } while (IsFree(sector) == 0);
1187 lastBlock = sector;
1188
1189 partitions[partNum].SetFirstLBA(firstBlock);
1190 partitions[partNum].SetLastLBA(lastBlock);
1191
1192 partitions[partNum].SetUniqueGUID(1);
1193 partitions[partNum].ChangeType();
1194 partitions[partNum].SetName((unsigned char*) partitions[partNum].GetNameType(prompt));
1195 } else {
1196 printf("No free sectors available\n");
1197 } // if/else
1198} // GPTData::CreatePartition()
1199
1200// Interactively delete a partition (duh!)
1201void GPTData::DeletePartition(void) {
1202 int partNum;
1203 uint32_t low, high;
1204 uint64_t startSector, length;
1205 char prompt[255];
1206
1207 if (GetPartRange(&low, &high) > 0) {
1208 sprintf(prompt, "Partition number (%d-%d): ", low + 1, high + 1);
1209 partNum = GetNumber(low + 1, high + 1, low, prompt);
1210
1211 // In case there's a protective MBR, look for & delete matching
1212 // MBR partition....
1213 startSector = partitions[partNum - 1].GetFirstLBA();
1214 length = partitions[partNum - 1].GetLengthLBA();
1215 protectiveMBR.DeleteByLocation(startSector, length);
1216
1217 // Now delete the GPT partition
1218 partitions[partNum - 1].BlankPartition();
1219 } else {
1220 printf("No partitions\n");
1221 } // if/else
1222} // GPTData::DeletePartition()
1223
1224// Prompt user for a partition number, then change its type code
1225// using ChangeGPTType(struct GPTPartition*) function.
1226void GPTData::ChangePartType(void) {
1227 int partNum;
1228 uint32_t low, high;
1229
1230 if (GetPartRange(&low, &high) > 0) {
1231 partNum = GetPartNum();
1232 partitions[partNum].ChangeType();
1233 } else {
1234 printf("No partitions\n");
1235 } // if/else
1236} // GPTData::ChangePartType()
1237
1238// Partition attributes seem to be rarely used, but I want a way to
1239// adjust them for completeness....
1240void GPTData::SetAttributes(uint32_t partNum) {
1241 Attributes theAttr;
1242
1243 theAttr.SetAttributes(partitions[partNum].GetAttributes());
1244 theAttr.DisplayAttributes();
1245 theAttr.ChangeAttributes();
1246 partitions[partNum].SetAttributes(theAttr.GetAttributes());
1247} // GPTData::SetAttributes()
1248
srs5694c0ca8f82009-08-20 21:35:25 -04001249// This function destroys the on-disk GPT structures. Returns 1 if the
1250// user confirms destruction, 0 if the user aborts.
srs5694978041c2009-09-21 20:51:47 -04001251// If prompt == 0, don't ask user about proceeding and do NOT wipe out
1252// MBR. (Set prompt == 0 when doing a GPT-to-MBR conversion.)
1253int GPTData::DestroyGPT(int prompt) {
srs5694e35eb1b2009-09-14 00:29:34 -04001254 int fd, i;
srs5694978041c2009-09-21 20:51:47 -04001255 char blankSector[512], goOn = 'Y', blank = 'N';
srs5694c0ca8f82009-08-20 21:35:25 -04001256
1257 for (i = 0; i < 512; i++) {
1258 blankSector[i] = '\0';
1259 } // for
1260
srs5694978041c2009-09-21 20:51:47 -04001261 if (((apmFound) || (bsdFound)) && prompt) {
srs5694e35eb1b2009-09-14 00:29:34 -04001262 printf("WARNING: APM or BSD disklabel structures detected! This operation could\n"
1263 "damage any APM or BSD partitions on this disk!\n");
1264 } // if APM or BSD
srs5694978041c2009-09-21 20:51:47 -04001265 if (prompt) {
1266 printf("\a\aAbout to wipe out GPT on %s. Proceed? ", device);
1267 goOn = GetYN();
1268 } // if
srs5694c0ca8f82009-08-20 21:35:25 -04001269 if (goOn == 'Y') {
1270 fd = open(device, O_WRONLY);
1271#ifdef __APPLE__
1272 // MacOS X requires a shared lock under some circumstances....
1273 if (fd < 0) {
1274 fd = open(device, O_WRONLY|O_SHLOCK);
1275 } // if
1276#endif
1277 if (fd != -1) {
1278 lseek64(fd, mainHeader.currentLBA * 512, SEEK_SET); // seek to GPT header
1279 write(fd, blankSector, 512); // blank it out
1280 lseek64(fd, mainHeader.partitionEntriesLBA * 512, SEEK_SET); // seek to partition table
1281 for (i = 0; i < GetBlocksInPartTable(); i++)
1282 write(fd, blankSector, 512);
1283 lseek64(fd, secondHeader.partitionEntriesLBA * 512, SEEK_SET); // seek to partition table
1284 for (i = 0; i < GetBlocksInPartTable(); i++)
1285 write(fd, blankSector, 512);
1286 lseek64(fd, secondHeader.currentLBA * 512, SEEK_SET); // seek to GPT header
1287 write(fd, blankSector, 512); // blank it out
srs5694978041c2009-09-21 20:51:47 -04001288 if (prompt) {
1289 printf("Blank out MBR? ");
1290 blank = GetYN();
1291 }// if
1292 // Note on below: Touch the MBR only if the user wants it completely
1293 // blanked out. Version 0.4.2 deleted the 0xEE partition and re-wrote
1294 // the MBR, but this could wipe out a valid MBR that the program
1295 // had subsequently discarded (say, if it conflicted with older GPT
1296 // structures).
1297 if (blank == 'Y') {
srs5694c0ca8f82009-08-20 21:35:25 -04001298 lseek64(fd, 0, SEEK_SET);
1299 write(fd, blankSector, 512); // blank it out
srs5694978041c2009-09-21 20:51:47 -04001300 } else {
1301 printf("MBR is unchanged. You may need to delete an EFI GPT (0xEE) partition\n"
1302 "with fdisk or another tool.\n");
srs5694e35eb1b2009-09-14 00:29:34 -04001303 } // if/else
1304 DiskSync(fd);
srs5694c0ca8f82009-08-20 21:35:25 -04001305 close(fd);
srs5694e4ac11e2009-08-31 10:13:04 -04001306 printf("GPT data structures destroyed! You may now partition the disk using fdisk or\n"
1307 "other utilities. Program will now terminate.\n");
srs5694c0ca8f82009-08-20 21:35:25 -04001308 } else {
1309 printf("Problem opening %s for writing! Program will now terminate.\n");
1310 } // if/else (fd != -1)
1311 } // if (goOn == 'Y')
1312 return (goOn == 'Y');
1313} // GPTData::DestroyGPT()
1314
srs5694e4ac11e2009-08-31 10:13:04 -04001315/**************************************************************************
1316 * *
1317 * Partition table transformation functions (MBR or BSD disklabel to GPT) *
1318 * (some of these functions may require user interaction) *
1319 * *
1320 **************************************************************************/
1321
1322// Examines the MBR & GPT data, and perhaps asks the user questions, to
1323// determine which set of data to use: the MBR (use_mbr), the GPT (use_gpt),
1324// or create a new set of partitions (use_new)
1325WhichToUse GPTData::UseWhichPartitions(void) {
1326 WhichToUse which = use_new;
1327 MBRValidity mbrState;
1328 int answer;
1329
1330 mbrState = protectiveMBR.GetValidity();
1331
1332 if ((state == gpt_invalid) && ((mbrState == mbr) || (mbrState == hybrid))) {
1333 printf("\n\a***************************************************************\n"
1334 "Found invalid GPT and valid MBR; converting MBR to GPT format.\n"
1335 "THIS OPERATON IS POTENTIALLY DESTRUCTIVE! Exit by typing 'q' if\n"
1336 "you don't want to convert your MBR partitions to GPT format!\n"
1337 "***************************************************************\n\n");
1338 which = use_mbr;
1339 } // if
1340
1341 if ((state == gpt_invalid) && bsdFound) {
1342 printf("\n\a**********************************************************************\n"
1343 "Found invalid GPT and valid BSD disklabel; converting BSD disklabel\n"
1344 "to GPT format. THIS OPERATON IS POTENTIALLY DESTRUCTIVE! Your first\n"
1345 "BSD partition will likely be unusable. Exit by typing 'q' if you don't\n"
1346 "want to convert your BSD partitions to GPT format!\n"
1347 "**********************************************************************\n\n");
1348 which = use_bsd;
1349 } // if
1350
1351 if ((state == gpt_valid) && (mbrState == gpt)) {
1352 printf("Found valid GPT with protective MBR; using GPT.\n");
1353 which = use_gpt;
1354 } // if
1355 if ((state == gpt_valid) && (mbrState == hybrid)) {
1356 printf("Found valid GPT with hybrid MBR; using GPT.\n");
1357 which = use_gpt;
1358 } // if
1359 if ((state == gpt_valid) && (mbrState == invalid)) {
1360 printf("\aFound valid GPT with corrupt MBR; using GPT and will create new\nprotective MBR on save.\n");
1361 which = use_gpt;
1362 protectiveMBR.MakeProtectiveMBR();
1363 } // if
1364 if ((state == gpt_valid) && (mbrState == mbr)) {
1365 printf("Found valid MBR and GPT. Which do you want to use?\n");
1366 answer = GetNumber(1, 3, 2, (char*) " 1 - MBR\n 2 - GPT\n 3 - Create blank GPT\n\nYour answer: ");
1367 if (answer == 1) {
1368 which = use_mbr;
1369 } else if (answer == 2) {
1370 which = use_gpt;
1371 protectiveMBR.MakeProtectiveMBR();
1372 printf("Using GPT and creating fresh protective MBR.\n");
1373 } else which = use_new;
1374 } // if
1375
1376 // Nasty decisions here -- GPT is present, but corrupt (bad CRCs or other
1377 // problems)
1378 if (state == gpt_corrupt) {
1379 if ((mbrState == mbr) || (mbrState == hybrid)) {
1380 printf("Found valid MBR and corrupt GPT. Which do you want to use? (Using the\n"
1381 "GPT MAY permit recovery of GPT data.)\n");
1382 answer = GetNumber(1, 3, 2, (char*) " 1 - MBR\n 2 - GPT\n 3 - Create blank GPT\n\nYour answer: ");
1383 if (answer == 1) {
1384 which = use_mbr;
1385// protectiveMBR.MakeProtectiveMBR();
1386 } else if (answer == 2) {
1387 which = use_gpt;
1388 } else which = use_new;
1389 } else if (mbrState == invalid) {
1390 printf("Found invalid MBR and corrupt GPT. What do you want to do? (Using the\n"
1391 "GPT MAY permit recovery of GPT data.)\n");
1392 answer = GetNumber(1, 2, 1, (char*) " 1 - GPT\n 2 - Create blank GPT\n\nYour answer: ");
1393 if (answer == 1) {
1394 which = use_gpt;
1395 } else which = use_new;
1396 } else { // corrupt GPT, MBR indicates it's a GPT disk....
1397 printf("\a\a****************************************************************************\n"
1398 "Caution: Found protective or hybrid MBR and corrupt GPT. Using GPT, but disk\n"
1399 "verification and recovery are STRONGLY recommended.\n"
1400 "****************************************************************************\n");
1401 } // if/else/else
1402 } // if (corrupt GPT)
1403
1404 if (which == use_new)
1405 printf("Creating new GPT entries.\n");
1406
1407 return which;
1408} // UseWhichPartitions()
1409
1410// Convert MBR partition table into GPT form
1411int GPTData::XFormPartitions(void) {
1412 int i, numToConvert;
1413 uint8_t origType;
1414 struct newGUID;
srs5694e4ac11e2009-08-31 10:13:04 -04001415
1416 // Clear out old data & prepare basics....
1417 ClearGPTData();
1418
1419 // Convert the smaller of the # of GPT or MBR partitions
srs5694978041c2009-09-21 20:51:47 -04001420 if (mainHeader.numParts > (MAX_MBR_PARTS))
1421 numToConvert = MAX_MBR_PARTS;
srs5694e4ac11e2009-08-31 10:13:04 -04001422 else
1423 numToConvert = mainHeader.numParts;
1424
1425 for (i = 0; i < numToConvert; i++) {
1426 origType = protectiveMBR.GetType(i);
1427 // don't waste CPU time trying to convert extended, hybrid protective, or
1428 // null (non-existent) partitions
srs5694e35eb1b2009-09-14 00:29:34 -04001429 if ((origType != 0x05) && (origType != 0x0f) && (origType != 0x85) &&
srs5694e4ac11e2009-08-31 10:13:04 -04001430 (origType != 0x00) && (origType != 0xEE))
1431 partitions[i] = protectiveMBR.AsGPT(i);
1432 } // for
1433
1434 // Convert MBR into protective MBR
1435 protectiveMBR.MakeProtectiveMBR();
1436
1437 // Record that all original CRCs were OK so as not to raise flags
1438 // when doing a disk verification
1439 mainCrcOk = secondCrcOk = mainPartsCrcOk = secondPartsCrcOk = 1;
1440
1441 return (1);
1442} // GPTData::XFormPartitions()
1443
1444// Transforms BSD disklabel on the specified partition (numbered from 0).
1445// If an invalid partition number is given, the program prompts for one.
1446// Returns the number of new partitions created.
1447int GPTData::XFormDisklabel(int i) {
1448 uint32_t low, high, partNum, startPart;
1449 uint16_t hexCode;
1450 int goOn = 1, numDone = 0;
1451 BSDData disklabel;
1452
1453 if (GetPartRange(&low, &high) != 0) {
1454 if ((i < low) || (i > high))
1455 partNum = GetPartNum();
1456 else
1457 partNum = (uint32_t) i;
1458
1459 // Find the partition after the last used one
1460 startPart = high + 1;
1461
1462 // Now see if the specified partition has a BSD type code....
1463 hexCode = partitions[partNum].GetHexType();
1464 if ((hexCode != 0xa500) && (hexCode != 0xa900)) {
1465 printf("Specified partition doesn't have a disklabel partition type "
1466 "code.\nContinue anyway?");
1467 goOn = (GetYN() == 'Y');
1468 } // if
1469
1470 // If all is OK, read the disklabel and convert it.
1471 if (goOn) {
1472 goOn = disklabel.ReadBSDData(device, partitions[partNum].GetFirstLBA(),
1473 partitions[partNum].GetLastLBA());
1474 if ((goOn) && (disklabel.IsDisklabel())) {
1475 numDone = XFormDisklabel(&disklabel, startPart);
1476 if (numDone == 1)
1477 printf("Converted %d BSD partition.\n", numDone);
1478 else
1479 printf("Converted %d BSD partitions.\n", numDone);
1480 } else {
1481 printf("Unable to convert partitions! Unrecognized BSD disklabel.\n");
1482 } // if/else
1483 } // if
1484 if (numDone > 0) { // converted partitions; delete carrier
1485 partitions[partNum].BlankPartition();
1486 } // if
1487 } else {
1488 printf("No partitions\n");
1489 } // if/else
1490 return numDone;
1491} // GPTData::XFormDisklable(int i)
1492
1493// Transform the partitions on an already-loaded BSD disklabel...
1494int GPTData::XFormDisklabel(BSDData* disklabel, int startPart) {
1495 int i, numDone = 0;
1496
1497 if ((disklabel->IsDisklabel()) && (startPart >= 0) &&
1498 (startPart < mainHeader.numParts)) {
1499 for (i = 0; i < disklabel->GetNumParts(); i++) {
1500 partitions[i + startPart] = disklabel->AsGPT(i);
1501 if (partitions[i + startPart].GetFirstLBA() != UINT64_C(0))
1502 numDone++;
1503 } // for
1504 } // if
1505
1506 // Record that all original CRCs were OK so as not to raise flags
1507 // when doing a disk verification
1508 mainCrcOk = secondCrcOk = mainPartsCrcOk = secondPartsCrcOk = 1;
1509
1510 return numDone;
1511} // GPTData::XFormDisklabel(BSDData* disklabel)
1512
srs5694978041c2009-09-21 20:51:47 -04001513// Add one GPT partition to MBR. Used by XFormToMBR() and MakeHybrid()
1514// functions. Returns 1 if operation was successful.
1515int GPTData::OnePartToMBR(uint32_t gptPart, int mbrPart) {
1516 int allOK = 1, typeCode, bootable;
1517 uint64_t length;
1518 char line[255];
1519
1520 if ((mbrPart < 0) || (mbrPart > 3)) {
1521 printf("MBR partition %d is out of range; omitting it.\n", mbrPart + 1);
1522 allOK = 0;
1523 } // if
1524 if (gptPart >= mainHeader.numParts) {
1525 printf("GPT partition %d is out of range; omitting it.\n", gptPart + 1);
1526 allOK = 0;
1527 } // if
1528 if (allOK && (partitions[gptPart].GetLastLBA() == UINT64_C(0))) {
1529 printf("GPT partition %d is undefined; omitting it.\n", gptPart + 1);
1530 allOK = 0;
1531 } // if
1532 if (allOK && (partitions[gptPart].GetFirstLBA() <= UINT32_MAX) &&
1533 (partitions[gptPart].GetLengthLBA() <= UINT32_MAX)) {
1534 if (partitions[gptPart].GetLastLBA() > UINT32_MAX) {
1535 printf("Caution: Partition end point past 32-bit pointer boundary;"
1536 " some OSes may\nreact strangely.\n");
1537 } // if partition ends past 32-bit (usually 2TiB) boundary
1538 do {
1539 printf("Enter an MBR hex code (default %02X): ",
1540 typeHelper.GUIDToID(partitions[gptPart].GetType()) / 256);
1541 fgets(line, 255, stdin);
1542 sscanf(line, "%x", &typeCode);
1543 if (line[0] == '\n')
1544 typeCode = partitions[gptPart].GetHexType() / 256;
1545 } while ((typeCode <= 0) || (typeCode > 255));
1546 printf("Set the bootable flag? ");
1547 bootable = (GetYN() == 'Y');
1548 length = partitions[gptPart].GetLengthLBA();
1549 protectiveMBR.MakePart(mbrPart, (uint32_t) partitions[gptPart].GetFirstLBA(),
1550 (uint32_t) length, typeCode, bootable);
1551 } else { // partition out of range
1552 printf("Partition %d begins beyond the 32-bit pointer limit of MBR "
1553 "partitions, or is\n too big; omitting it.\n", gptPart + 1);
1554 allOK = 0;
1555 } // if/else
1556 return allOK;
1557} // GPTData::OnePartToMBR()
1558
1559// Convert the GPT to MBR form. This function is necessarily limited; it
1560// handles at most four partitions and creates layouts that ignore CHS
1561// geometries. Returns the number of converted partitions; if this value
1562// is over 0, the calling function should call DestroyGPT() to destroy
1563// the GPT data, and then exit.
1564int GPTData::XFormToMBR(void) {
1565 char line[255];
1566 int i, j, numParts, numConverted = 0;
1567 uint32_t partNums[4];
1568
1569 // Get the numbers of up to four partitions to add to the
1570 // hybrid MBR....
1571 numParts = CountParts();
1572 printf("Counted %d partitions.\n", numParts);
1573
1574 // Prepare the MBR for conversion (empty it of existing partitions).
1575 protectiveMBR.EmptyMBR(0);
1576 protectiveMBR.SetDiskSize(diskSize);
1577
1578 if (numParts > 4) { // Over four partitions; engage in triage
1579 printf("Type from one to four GPT partition numbers, separated by spaces, to be\n"
1580 "used in the MBR, in sequence: ");
1581 fgets(line, 255, stdin);
1582 numParts = sscanf(line, "%d %d %d %d", &partNums[0], &partNums[1],
1583 &partNums[2], &partNums[3]);
1584 } else { // Four or fewer partitions; convert them all
1585 i = j = 0;
1586 while ((j < numParts) && (i < mainHeader.numParts)) {
1587 if (partitions[i].GetFirstLBA() > 0) { // if GPT part. is defined
1588 partNums[j++] = ++i; // flag it for conversion
1589 } else i++;
1590 } // while
1591 } // if/else
1592
1593 for (i = 0; i < numParts; i++) {
1594 j = partNums[i] - 1;
1595 printf("\nCreating entry for partition #%d\n", j + 1);
1596 numConverted += OnePartToMBR(j, i);
1597 } // for
1598 return numConverted;
1599} // GPTData::XFormToMBR()
1600
srs5694e4ac11e2009-08-31 10:13:04 -04001601// Create a hybrid MBR -- an ugly, funky thing that helps GPT work with
1602// OSes that don't understand GPT.
1603void GPTData::MakeHybrid(void) {
1604 uint32_t partNums[3];
1605 char line[255];
srs5694978041c2009-09-21 20:51:47 -04001606 int numParts, numConverted = 0, i, j, typeCode, mbrNum;
srs5694e4ac11e2009-08-31 10:13:04 -04001607 char fillItUp = 'M'; // fill extra partition entries? (Yes/No/Maybe)
srs5694978041c2009-09-21 20:51:47 -04001608 char eeFirst = 'Y'; // Whether EFI GPT (0xEE) partition comes first in table
srs5694e4ac11e2009-08-31 10:13:04 -04001609
1610 printf("\nWARNING! Hybrid MBRs are flaky and potentially dangerous! If you decide not\n"
1611 "to use one, just hit the Enter key at the below prompt and your MBR\n"
1612 "partition table will be untouched.\n\n\a");
1613
1614 // Now get the numbers of up to three partitions to add to the
1615 // hybrid MBR....
1616 printf("Type from one to three GPT partition numbers, separated by spaces, to be\n"
1617 "added to the hybrid MBR, in sequence: ");
1618 fgets(line, 255, stdin);
1619 numParts = sscanf(line, "%d %d %d", &partNums[0], &partNums[1], &partNums[2]);
1620
1621 if (numParts > 0) {
1622 // Blank out the protective MBR, but leave the boot loader code
1623 // alone....
1624 protectiveMBR.EmptyMBR(0);
1625 protectiveMBR.SetDiskSize(diskSize);
1626 printf("Place EFI GPT (0xEE) partition first in MBR (good for GRUB)? ");
1627 eeFirst = GetYN();
1628 } // if
1629
1630 for (i = 0; i < numParts; i++) {
1631 j = partNums[i] - 1;
1632 printf("\nCreating entry for partition #%d\n", j + 1);
srs5694978041c2009-09-21 20:51:47 -04001633 if (eeFirst == 'Y')
1634 mbrNum = i + 1;
1635 else
1636 mbrNum = i;
1637 numConverted += OnePartToMBR(j, mbrNum);
srs5694e4ac11e2009-08-31 10:13:04 -04001638 } // for
1639
srs5694978041c2009-09-21 20:51:47 -04001640 if ((numParts > 0) && (numConverted > 0)) { // User opted to create a hybrid MBR....
srs5694e4ac11e2009-08-31 10:13:04 -04001641 // Create EFI protective partition that covers the start of the disk.
1642 // If this location (covering the main GPT data structures) is omitted,
1643 // Linux won't find any partitions on the disk. Note that this is
1644 // NUMBERED AFTER the hybrid partitions, contrary to what the
1645 // gptsync utility does. This is because Windows seems to choke on
1646 // disks with a 0xEE partition in the first slot and subsequent
1647 // additional partitions, unless it boots from the disk.
1648 if (eeFirst == 'Y')
1649 mbrNum = 0;
1650 else
1651 mbrNum = numParts;
1652 protectiveMBR.MakePart(mbrNum, 1, protectiveMBR.FindLastInFree(1), 0xEE);
srs5694978041c2009-09-21 20:51:47 -04001653 protectiveMBR.SetHybrid();
srs5694e4ac11e2009-08-31 10:13:04 -04001654
1655 // ... and for good measure, if there are any partition spaces left,
1656 // optionally create another protective EFI partition to cover as much
1657 // space as possible....
1658 for (i = 0; i < 4; i++) {
1659 if (protectiveMBR.GetType(i) == 0x00) { // unused entry....
1660 if (fillItUp == 'M') {
1661 printf("\nUnused partition space(s) found. Use one to protect more partitions? ");
1662 fillItUp = GetYN();
1663 typeCode = 0x00; // use this to flag a need to get type code
1664 } // if
1665 if (fillItUp == 'Y') {
1666 while ((typeCode <= 0) || (typeCode > 255)) {
1667 printf("Enter an MBR hex code (EE is EFI GPT, but may confuse MacOS): ");
1668 // Comment on above: Mac OS treats disks with more than one
1669 // 0xEE MBR partition as MBR disks, not as GPT disks.
1670 fgets(line, 255, stdin);
1671 sscanf(line, "%x", &typeCode);
1672 if (line[0] == '\n')
1673 typeCode = 0;
1674 } // while
1675 protectiveMBR.MakeBiggestPart(i, typeCode); // make a partition
1676 } // if (fillItUp == 'Y')
1677 } // if unused entry
1678 } // for (i = 0; i < 4; i++)
1679 } // if (numParts > 0)
1680} // GPTData::MakeHybrid()
1681
1682/**********************************************************************
1683 * *
1684 * Functions that adjust GPT data structures WITHOUT user interaction *
1685 * (they may display information for the user's benefit, though) *
1686 * *
1687 **********************************************************************/
1688
1689// Resizes GPT to specified number of entries. Creates a new table if
1690// necessary, copies data if it already exists.
1691int GPTData::SetGPTSize(uint32_t numEntries) {
1692 struct GPTPart* newParts;
1693 struct GPTPart* trash;
1694 uint32_t i, high, copyNum;
1695 int allOK = 1;
1696
1697 // First, adjust numEntries upward, if necessary, to get a number
1698 // that fills the allocated sectors
1699 i = blockSize / GPT_SIZE;
1700 if ((numEntries % i) != 0) {
1701 printf("Adjusting GPT size from %lu ", (unsigned long) numEntries);
1702 numEntries = ((numEntries / i) + 1) * i;
1703 printf("to %lu to fill the sector\n", (unsigned long) numEntries);
1704 } // if
1705
1706 newParts = (GPTPart*) calloc(numEntries, sizeof (GPTPart));
1707 if (newParts != NULL) {
1708 if (partitions != NULL) { // existing partitions; copy them over
1709 GetPartRange(&i, &high);
1710 if (numEntries < (high + 1)) { // Highest entry too high for new #
1711 printf("The highest-numbered partition is %lu, which is greater than the requested\n"
1712 "partition table size of %d; cannot resize. Perhaps sorting will help.\n",
1713 (unsigned long) (high + 1), numEntries);
1714 allOK = 0;
1715 } else { // go ahead with copy
1716 if (numEntries < mainHeader.numParts)
1717 copyNum = numEntries;
1718 else
1719 copyNum = mainHeader.numParts;
1720 for (i = 0; i < copyNum; i++) {
1721 newParts[i] = partitions[i];
1722 } // for
1723 trash = partitions;
1724 partitions = newParts;
1725 free(trash);
1726 } // if
1727 } else { // No existing partition table; just create it
1728 partitions = newParts;
1729 } // if/else existing partitions
1730 mainHeader.numParts = numEntries;
1731 secondHeader.numParts = numEntries;
1732 mainHeader.firstUsableLBA = ((numEntries * GPT_SIZE) / blockSize) + 2 ;
1733 secondHeader.firstUsableLBA = mainHeader.firstUsableLBA;
1734 mainHeader.lastUsableLBA = diskSize - mainHeader.firstUsableLBA;
1735 secondHeader.lastUsableLBA = mainHeader.lastUsableLBA;
1736 secondHeader.partitionEntriesLBA = secondHeader.lastUsableLBA + UINT64_C(1);
1737 if (diskSize > 0)
1738 CheckGPTSize();
1739 } else { // Bad memory allocation
1740 fprintf(stderr, "Error allocating memory for partition table!\n");
1741 allOK = 0;
1742 } // if/else
1743 return (allOK);
1744} // GPTData::SetGPTSize()
1745
1746// Blank the partition array
1747void GPTData::BlankPartitions(void) {
1748 uint32_t i;
1749
1750 for (i = 0; i < mainHeader.numParts; i++) {
1751 partitions[i].BlankPartition();
1752 } // for
1753} // GPTData::BlankPartitions()
1754
1755// Sort the GPT entries, eliminating gaps and making for a logical
1756// ordering. Relies on QuickSortGPT() for the bulk of the work
1757void GPTData::SortGPT(void) {
1758 int i, lastPart = 0;
1759 GPTPart temp;
1760
1761 // First, find the last partition with data, so as not to
1762 // spend needless time sorting empty entries....
1763 for (i = 0; i < mainHeader.numParts; i++) {
1764 if (partitions[i].GetFirstLBA() > 0)
1765 lastPart = i;
1766 } // for
1767
1768 // Now swap empties with the last partitions, to simplify the logic
1769 // in the Quicksort function....
1770 i = 0;
1771 while (i < lastPart) {
1772 if (partitions[i].GetFirstLBA() == 0) {
1773 temp = partitions[i];
1774 partitions[i] = partitions[lastPart];
1775 partitions[lastPart] = temp;
1776 lastPart--;
1777 } // if
1778 i++;
1779 } // while
1780
1781 // Now call the recursive quick sort routine to do the real work....
1782 QuickSortGPT(partitions, 0, lastPart);
1783} // GPTData::SortGPT()
1784
1785// Set up data structures for entirely new set of partitions on the
1786// specified device. Returns 1 if OK, 0 if there were problems.
srs5694e35eb1b2009-09-14 00:29:34 -04001787// Note that this function does NOT clear the protectiveMBR data
1788// structure, since it may hold the original MBR partitions if the
1789// program was launched on an MBR disk, and those may need to be
1790// converted to GPT format.
srs5694e4ac11e2009-08-31 10:13:04 -04001791int GPTData::ClearGPTData(void) {
srs5694e35eb1b2009-09-14 00:29:34 -04001792 int goOn = 1, i;
srs5694e4ac11e2009-08-31 10:13:04 -04001793
1794 // Set up the partition table....
1795 free(partitions);
1796 partitions = NULL;
1797 SetGPTSize(NUM_GPT_ENTRIES);
1798
1799 // Now initialize a bunch of stuff that's static....
1800 mainHeader.signature = GPT_SIGNATURE;
1801 mainHeader.revision = 0x00010000;
srs5694978041c2009-09-21 20:51:47 -04001802 mainHeader.headerSize = HEADER_SIZE;
srs5694e4ac11e2009-08-31 10:13:04 -04001803 mainHeader.reserved = 0;
1804 mainHeader.currentLBA = UINT64_C(1);
1805 mainHeader.partitionEntriesLBA = (uint64_t) 2;
1806 mainHeader.sizeOfPartitionEntries = GPT_SIZE;
1807 for (i = 0; i < GPT_RESERVED; i++) {
1808 mainHeader.reserved2[i] = '\0';
1809 } // for
1810
1811 // Now some semi-static items (computed based on end of disk)
1812 mainHeader.backupLBA = diskSize - UINT64_C(1);
1813 mainHeader.lastUsableLBA = diskSize - mainHeader.firstUsableLBA;
1814
1815 // Set a unique GUID for the disk, based on random numbers
1816 // rand() is only 32 bits, so multiply together to fill a 64-bit value
1817 mainHeader.diskGUID.data1 = (uint64_t) rand() * (uint64_t) rand();
1818 mainHeader.diskGUID.data2 = (uint64_t) rand() * (uint64_t) rand();
1819
1820 // Copy main header to backup header
1821 RebuildSecondHeader();
1822
1823 // Blank out the partitions array....
1824 BlankPartitions();
1825
1826 // Flag all CRCs as being OK....
1827 mainCrcOk = 1;
1828 secondCrcOk = 1;
1829 mainPartsCrcOk = 1;
1830 secondPartsCrcOk = 1;
1831
1832 return (goOn);
1833} // GPTData::ClearGPTData()
1834
srs56948bb78762009-11-24 15:43:49 -05001835// Set the location of the second GPT header data to the correct location.
1836// Intended to help users of RAID arrays that have been resized.
1837void GPTData::FixSecondHeaderLocation() {
1838 mainHeader.backupLBA = secondHeader.currentLBA = diskSize - UINT64_C(1);
1839 mainHeader.lastUsableLBA = secondHeader.lastUsableLBA = diskSize - mainHeader.firstUsableLBA;
1840 secondHeader.partitionEntriesLBA = secondHeader.lastUsableLBA + UINT64_C(1);
1841} // GPTData::FixSecondHeaderLocation()
1842
srs5694e4ac11e2009-08-31 10:13:04 -04001843void GPTData::SetName(uint32_t partNum, char* theName) {
1844 if ((partNum >= 0) && (partNum < mainHeader.numParts))
1845 if (partitions[partNum].GetFirstLBA() > 0)
1846 partitions[partNum].SetName((unsigned char*) theName);
1847} // GPTData::SetName
1848
1849// Set the disk GUID to the specified value. Note that the header CRCs must
1850// be recomputed after calling this function.
1851void GPTData::SetDiskGUID(GUIDData newGUID) {
1852 mainHeader.diskGUID = newGUID;
1853 secondHeader.diskGUID = newGUID;
1854} // SetDiskGUID()
1855
1856// Set the unique GUID of the specified partition. Returns 1 on
1857// successful completion, 0 if there were problems (invalid
1858// partition number).
1859int GPTData::SetPartitionGUID(uint32_t pn, GUIDData theGUID) {
1860 int retval = 0;
1861
1862 if (pn < mainHeader.numParts) {
1863 if (partitions[pn].GetFirstLBA() != UINT64_C(0)) {
1864 partitions[pn].SetUniqueGUID(theGUID);
1865 retval = 1;
1866 } // if
1867 } // if
1868 return retval;
1869} // GPTData::SetPartitionGUID()
1870
1871/********************************************************
1872 * *
1873 * Functions that return data about GPT data structures *
1874 * (most of these are inline in gpt.h) *
1875 * *
1876 ********************************************************/
1877
1878// Find the low and high used partition numbers (numbered from 0).
1879// Return value is the number of partitions found. Note that the
1880// *low and *high values are both set to 0 when no partitions
1881// are found, as well as when a single partition in the first
1882// position exists. Thus, the return value is the only way to
1883// tell when no partitions exist.
1884int GPTData::GetPartRange(uint32_t *low, uint32_t *high) {
1885 uint32_t i;
1886 int numFound = 0;
1887
1888 *low = mainHeader.numParts + 1; // code for "not found"
1889 *high = 0;
1890 if (mainHeader.numParts > 0) { // only try if partition table exists...
1891 for (i = 0; i < mainHeader.numParts; i++) {
1892 if (partitions[i].GetFirstLBA() != UINT64_C(0)) { // it exists
1893 *high = i; // since we're counting up, set the high value
1894 // Set the low value only if it's not yet found...
1895 if (*low == (mainHeader.numParts + 1)) *low = i;
1896 numFound++;
1897 } // if
1898 } // for
1899 } // if
1900
1901 // Above will leave *low pointing to its "not found" value if no partitions
1902 // are defined, so reset to 0 if this is the case....
1903 if (*low == (mainHeader.numParts + 1))
1904 *low = 0;
1905 return numFound;
1906} // GPTData::GetPartRange()
1907
srs5694978041c2009-09-21 20:51:47 -04001908// Returns the number of defined partitions.
1909uint32_t GPTData::CountParts(void) {
1910 int i, counted = 0;
1911
1912 for (i = 0; i < mainHeader.numParts; i++) {
1913 if (partitions[i].GetFirstLBA() > 0)
1914 counted++;
1915 } // for
1916 return counted;
1917} // GPTData::CountParts()
1918
srs5694e4ac11e2009-08-31 10:13:04 -04001919/****************************************************
1920 * *
1921 * Functions that return data about disk free space *
1922 * *
1923 ****************************************************/
1924
1925// Find the first available block after the starting point; returns 0 if
1926// there are no available blocks left
1927uint64_t GPTData::FindFirstAvailable(uint64_t start) {
1928 uint64_t first;
1929 uint32_t i;
1930 int firstMoved = 0;
1931
1932 // Begin from the specified starting point or from the first usable
1933 // LBA, whichever is greater...
1934 if (start < mainHeader.firstUsableLBA)
1935 first = mainHeader.firstUsableLBA;
1936 else
1937 first = start;
1938
1939 // ...now search through all partitions; if first is within an
1940 // existing partition, move it to the next sector after that
1941 // partition and repeat. If first was moved, set firstMoved
1942 // flag; repeat until firstMoved is not set, so as to catch
1943 // cases where partitions are out of sequential order....
1944 do {
1945 firstMoved = 0;
1946 for (i = 0; i < mainHeader.numParts; i++) {
1947 if ((first >= partitions[i].GetFirstLBA()) &&
1948 (first <= partitions[i].GetLastLBA())) { // in existing part.
1949 first = partitions[i].GetLastLBA() + 1;
1950 firstMoved = 1;
1951 } // if
1952 } // for
1953 } while (firstMoved == 1);
1954 if (first > mainHeader.lastUsableLBA)
1955 first = 0;
1956 return (first);
1957} // GPTData::FindFirstAvailable()
1958
1959// Finds the first available sector in the largest block of unallocated
1960// space on the disk. Returns 0 if there are no available blocks left
1961uint64_t GPTData::FindFirstInLargest(void) {
srs5694e35eb1b2009-09-14 00:29:34 -04001962 uint64_t start, firstBlock, lastBlock, segmentSize, selectedSize = 0, selectedSegment = 0;
srs5694e4ac11e2009-08-31 10:13:04 -04001963
1964 start = 0;
1965 do {
1966 firstBlock = FindFirstAvailable(start);
1967 if (firstBlock != UINT32_C(0)) { // something's free...
1968 lastBlock = FindLastInFree(firstBlock);
1969 segmentSize = lastBlock - firstBlock + UINT32_C(1);
1970 if (segmentSize > selectedSize) {
1971 selectedSize = segmentSize;
1972 selectedSegment = firstBlock;
1973 } // if
1974 start = lastBlock + 1;
1975 } // if
1976 } while (firstBlock != 0);
1977 return selectedSegment;
1978} // GPTData::FindFirstInLargest()
1979
1980// Find the last available block on the disk at or after the start
1981// block. Returns 0 if there are no available partitions after
1982// (or including) start.
1983uint64_t GPTData::FindLastAvailable(uint64_t start) {
1984 uint64_t last;
1985 uint32_t i;
1986 int lastMoved = 0;
1987
1988 // Start by assuming the last usable LBA is available....
1989 last = mainHeader.lastUsableLBA;
1990
1991 // ...now, similar to algorithm in FindFirstAvailable(), search
1992 // through all partitions, moving last when it's in an existing
1993 // partition. Set the lastMoved flag so we repeat to catch cases
1994 // where partitions are out of logical order.
1995 do {
1996 lastMoved = 0;
1997 for (i = 0; i < mainHeader.numParts; i++) {
1998 if ((last >= partitions[i].GetFirstLBA()) &&
1999 (last <= partitions[i].GetLastLBA())) { // in existing part.
2000 last = partitions[i].GetFirstLBA() - 1;
2001 lastMoved = 1;
2002 } // if
2003 } // for
2004 } while (lastMoved == 1);
2005 if (last < mainHeader.firstUsableLBA)
2006 last = 0;
2007 return (last);
2008} // GPTData::FindLastAvailable()
2009
2010// Find the last available block in the free space pointed to by start.
2011uint64_t GPTData::FindLastInFree(uint64_t start) {
2012 uint64_t nearestStart;
2013 uint32_t i;
2014
2015 nearestStart = mainHeader.lastUsableLBA;
2016 for (i = 0; i < mainHeader.numParts; i++) {
2017 if ((nearestStart > partitions[i].GetFirstLBA()) &&
2018 (partitions[i].GetFirstLBA() > start)) {
2019 nearestStart = partitions[i].GetFirstLBA() - 1;
2020 } // if
2021 } // for
2022 return (nearestStart);
2023} // GPTData::FindLastInFree()
2024
2025// Finds the total number of free blocks, the number of segments in which
2026// they reside, and the size of the largest of those segments
2027uint64_t GPTData::FindFreeBlocks(int *numSegments, uint64_t *largestSegment) {
2028 uint64_t start = UINT64_C(0); // starting point for each search
2029 uint64_t totalFound = UINT64_C(0); // running total
2030 uint64_t firstBlock; // first block in a segment
2031 uint64_t lastBlock; // last block in a segment
2032 uint64_t segmentSize; // size of segment in blocks
2033 int num = 0;
2034
2035 *largestSegment = UINT64_C(0);
2036 do {
2037 firstBlock = FindFirstAvailable(start);
2038 if (firstBlock != UINT64_C(0)) { // something's free...
2039 lastBlock = FindLastInFree(firstBlock);
2040 segmentSize = lastBlock - firstBlock + UINT64_C(1);
2041 if (segmentSize > *largestSegment) {
2042 *largestSegment = segmentSize;
2043 } // if
2044 totalFound += segmentSize;
2045 num++;
2046 start = lastBlock + 1;
2047 } // if
2048 } while (firstBlock != 0);
2049 *numSegments = num;
2050 return totalFound;
2051} // GPTData::FindFreeBlocks()
2052
2053// Returns 1 if sector is unallocated, 0 if it's allocated to a partition
2054int GPTData::IsFree(uint64_t sector) {
2055 int isFree = 1;
2056 uint32_t i;
2057
2058 for (i = 0; i < mainHeader.numParts; i++) {
2059 if ((sector >= partitions[i].GetFirstLBA()) &&
2060 (sector <= partitions[i].GetLastLBA())) {
2061 isFree = 0;
2062 } // if
2063 } // for
srs5694e35eb1b2009-09-14 00:29:34 -04002064 if ((sector < mainHeader.firstUsableLBA) ||
srs5694e4ac11e2009-08-31 10:13:04 -04002065 (sector > mainHeader.lastUsableLBA)) {
2066 isFree = 0;
2067 } // if
2068 return (isFree);
2069} // GPTData::IsFree()
2070
2071/********************************
2072 * *
2073 * Endianness support functions *
2074 * *
2075 ********************************/
2076
srs56942a9f5da2009-08-26 00:48:01 -04002077void GPTData::ReverseHeaderBytes(struct GPTHeader* header) {
srs5694221e0872009-08-29 15:00:31 -04002078 ReverseBytes(&header->signature, 8);
2079 ReverseBytes(&header->revision, 4);
2080 ReverseBytes(&header->headerSize, 4);
2081 ReverseBytes(&header->headerCRC, 4);
2082 ReverseBytes(&header->reserved, 4);
2083 ReverseBytes(&header->currentLBA, 8);
2084 ReverseBytes(&header->backupLBA, 8);
2085 ReverseBytes(&header->firstUsableLBA, 8);
2086 ReverseBytes(&header->lastUsableLBA, 8);
2087 ReverseBytes(&header->partitionEntriesLBA, 8);
2088 ReverseBytes(&header->numParts, 4);
2089 ReverseBytes(&header->sizeOfPartitionEntries, 4);
2090 ReverseBytes(&header->partitionEntriesCRC, 4);
2091 ReverseBytes(&header->reserved2, GPT_RESERVED);
2092 ReverseBytes(&header->diskGUID.data1, 8);
2093 ReverseBytes(&header->diskGUID.data2, 8);
srs56942a9f5da2009-08-26 00:48:01 -04002094} // GPTData::ReverseHeaderBytes()
2095
2096// IMPORTANT NOTE: This function requires non-reversed mainHeader
2097// structure!
2098void GPTData::ReversePartitionBytes() {
2099 uint32_t i;
2100
2101 // Check GPT signature on big-endian systems; this will mismatch
2102 // if the function is called out of order. Unfortunately, it'll also
2103 // mismatch if there's data corruption.
2104 if ((mainHeader.signature != GPT_SIGNATURE) && (IsLittleEndian() == 0)) {
2105 printf("GPT signature mismatch in GPTData::ReversePartitionBytes(). This indicates\n"
srs5694e4ac11e2009-08-31 10:13:04 -04002106 "data corruption or a misplaced call to this function.\n");
srs56942a9f5da2009-08-26 00:48:01 -04002107 } // if signature mismatch....
2108 for (i = 0; i < mainHeader.numParts; i++) {
srs5694221e0872009-08-29 15:00:31 -04002109 partitions[i].ReversePartBytes();
srs56942a9f5da2009-08-26 00:48:01 -04002110 } // for
2111} // GPTData::ReversePartitionBytes()
2112
2113/******************************************
2114 * *
2115 * Additional non-class support functions *
2116 * *
2117 ******************************************/
2118
srs5694e7b4ff92009-08-18 13:16:10 -04002119// Check to be sure that data type sizes are correct. The basic types (uint*_t) should
2120// never fail these tests, but the struct types may fail depending on compile options.
2121// Specifically, the -fpack-struct option to gcc may be required to ensure proper structure
2122// sizes.
2123int SizesOK(void) {
2124 int allOK = 1;
srs5694e7b4ff92009-08-18 13:16:10 -04002125
2126 if (sizeof(uint8_t) != 1) {
2127 fprintf(stderr, "uint8_t is %d bytes, should be 1 byte; aborting!\n", sizeof(uint8_t));
2128 allOK = 0;
2129 } // if
2130 if (sizeof(uint16_t) != 2) {
2131 fprintf(stderr, "uint16_t is %d bytes, should be 2 bytes; aborting!\n", sizeof(uint16_t));
2132 allOK = 0;
2133 } // if
2134 if (sizeof(uint32_t) != 4) {
2135 fprintf(stderr, "uint32_t is %d bytes, should be 4 bytes; aborting!\n", sizeof(uint32_t));
2136 allOK = 0;
2137 } // if
2138 if (sizeof(uint64_t) != 8) {
2139 fprintf(stderr, "uint64_t is %d bytes, should be 8 bytes; aborting!\n", sizeof(uint64_t));
2140 allOK = 0;
2141 } // if
2142 if (sizeof(struct MBRRecord) != 16) {
srs5694221e0872009-08-29 15:00:31 -04002143 fprintf(stderr, "MBRRecord is %d bytes, should be 16 bytes; aborting!\n", sizeof(MBRRecord));
srs5694e7b4ff92009-08-18 13:16:10 -04002144 allOK = 0;
2145 } // if
srs5694978041c2009-09-21 20:51:47 -04002146 if (sizeof(struct TempMBR) != 512) {
2147 fprintf(stderr, "TempMBR is %d bytes, should be 512 bytes; aborting!\n", sizeof(TempMBR));
srs5694e7b4ff92009-08-18 13:16:10 -04002148 allOK = 0;
2149 } // if
2150 if (sizeof(struct GPTHeader) != 512) {
srs5694221e0872009-08-29 15:00:31 -04002151 fprintf(stderr, "GPTHeader is %d bytes, should be 512 bytes; aborting!\n", sizeof(GPTHeader));
srs5694e7b4ff92009-08-18 13:16:10 -04002152 allOK = 0;
2153 } // if
srs5694221e0872009-08-29 15:00:31 -04002154 if (sizeof(GPTPart) != 128) {
2155 fprintf(stderr, "GPTPart is %d bytes, should be 128 bytes; aborting!\n", sizeof(GPTPart));
2156 allOK = 0;
2157 } // if
2158// Determine endianness; set allOK = 0 if running on big-endian hardware
srs56942a9f5da2009-08-26 00:48:01 -04002159 if (IsLittleEndian() == 0) {
2160 fprintf(stderr, "\aRunning on big-endian hardware. Big-endian support is new and poorly"
srs5694e4ac11e2009-08-31 10:13:04 -04002161 " tested!\nBeware!\n");
srs56942a9f5da2009-08-26 00:48:01 -04002162 // allOK = 0;
srs5694e7b4ff92009-08-18 13:16:10 -04002163 } // if
2164 return (allOK);
2165} // SizesOK()
srs5694e4ac11e2009-08-31 10:13:04 -04002166