blob: 2e997143bb90d54ae3eec1c5c5ed9bdb1904ccb3 [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"
srs5694247657a2009-11-26 18:36:12 -0500127 "option on the experts' menu to adjust the secondary header's and partition\n"
128 "table's locations.\n");
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
srs5694247657a2009-11-26 18:36:12 -0500679// Loads the partition table pointed to by the main GPT header. The
srs5694e4ac11e2009-08-31 10:13:04 -0400680// 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) {
srs5694247657a2009-11-26 18:36:12 -0500756 fprintf(stderr, "Error! Disk is too small! The 'e' option on the experts' menu might fix the\n"
757 "problem (or it might not). Aborting!\n");
srs5694e7b4ff92009-08-18 13:16:10 -0400758 printf("(Disk size is %ld sectors, needs to be %ld sectors.)\n", diskSize,
759 mainHeader.backupLBA);
760 allOK = 0;
761 } // if
srs5694247657a2009-11-26 18:36:12 -0500762 // Check that second header is properly placed. Warn and ask if this should
763 // be corrected if the test fails....
764 if (mainHeader.backupLBA < (diskSize - UINT64_C(1))) {
765 printf("Warning! Secondary header is placed too early on the disk! Do you want to\n"
766 "correct this problem? ");
767 if (GetYN() == 'Y') {
768 MoveSecondHeaderToEnd();
769 printf("Have moved second header and partition table to correct location.\n");
770 } else {
771 printf("Have not corrected the problem. Strange problems may occur in the future!\n");
772 } // if correction requested
773 } // if
srs5694e7b4ff92009-08-18 13:16:10 -0400774
775 // Check for overlapping partitions....
srs5694e4ac11e2009-08-31 10:13:04 -0400776 if (FindOverlaps() > 0) {
777 allOK = 0;
778 fprintf(stderr, "Aborting write operation!\n");
779 } // if
780
781 // Check for mismatched MBR and GPT data, but let it pass if found
782 // (function displays warning message)
783 FindHybridMismatches();
srs5694e7b4ff92009-08-18 13:16:10 -0400784
srs56942a9f5da2009-08-26 00:48:01 -0400785 // Pull out some data that's needed before doing byte-order reversal on
786 // big-endian systems....
787 numParts = mainHeader.numParts;
788 secondTable = secondHeader.partitionEntriesLBA;
789 if (IsLittleEndian() == 0) {
790 // Reverse partition bytes first, since that function requires non-reversed
791 // data from the main header....
792 ReversePartitionBytes();
793 ReverseHeaderBytes(&mainHeader);
794 ReverseHeaderBytes(&secondHeader);
795 } // if
srs5694e7b4ff92009-08-18 13:16:10 -0400796 RecomputeCRCs();
797
798 if (allOK) {
799 printf("\nFinal checks complete. About to write GPT data. THIS WILL OVERWRITE EXISTING\n");
800 printf("MBR PARTITIONS!! THIS PROGRAM IS BETA QUALITY AT BEST. IF YOU LOSE ALL YOUR\n");
801 printf("DATA, YOU HAVE ONLY YOURSELF TO BLAME IF YOU ANSWER 'Y' BELOW!\n\n");
802 printf("Do you want to proceed, possibly destroying your data? (Y/N) ");
803 fgets(line, 255, stdin);
804 sscanf(line, "%c", &answer);
805 if ((answer == 'Y') || (answer == 'y')) {
806 printf("OK; writing new GPT partition table.\n");
807 } else {
808 allOK = 0;
809 } // if/else
810 } // if
811
812 // Do it!
813 if (allOK) {
srs5694e4ac11e2009-08-31 10:13:04 -0400814 fd = OpenForWrite(device);
srs5694e7b4ff92009-08-18 13:16:10 -0400815 if (fd != -1) {
816 // First, write the protective MBR...
srs5694e4ac11e2009-08-31 10:13:04 -0400817 protectiveMBR.WriteMBRData(fd);
srs5694e7b4ff92009-08-18 13:16:10 -0400818
819 // Now write the main GPT header...
820 if (allOK)
821 if (write(fd, &mainHeader, 512) == -1)
srs5694e4ac11e2009-08-31 10:13:04 -0400822 allOK = 0;
srs5694e7b4ff92009-08-18 13:16:10 -0400823
824 // Now write the main partition tables...
srs5694e4ac11e2009-08-31 10:13:04 -0400825 if (allOK) {
826 if (write(fd, partitions, GPT_SIZE * numParts) == -1)
srs5694e7b4ff92009-08-18 13:16:10 -0400827 allOK = 0;
828 } // if
829
830 // Now seek to near the end to write the secondary GPT....
srs5694e4ac11e2009-08-31 10:13:04 -0400831 if (allOK) {
srs5694e7b4ff92009-08-18 13:16:10 -0400832 offset = (off_t) secondTable * (off_t) (blockSize);
833 if (lseek64(fd, offset, SEEK_SET) == (off_t) - 1) {
834 allOK = 0;
835 printf("Unable to seek to end of disk!\n");
836 } // if
837 } // if
838
839 // Now write the secondary partition tables....
srs5694e4ac11e2009-08-31 10:13:04 -0400840 if (allOK)
srs56942a9f5da2009-08-26 00:48:01 -0400841 if (write(fd, partitions, GPT_SIZE * numParts) == -1)
srs5694e7b4ff92009-08-18 13:16:10 -0400842 allOK = 0;
843
844 // Now write the secondary GPT header...
srs5694e4ac11e2009-08-31 10:13:04 -0400845 if (allOK)
srs5694e7b4ff92009-08-18 13:16:10 -0400846 if (write(fd, &secondHeader, 512) == -1)
srs5694e4ac11e2009-08-31 10:13:04 -0400847 allOK = 0;
srs5694e7b4ff92009-08-18 13:16:10 -0400848
849 // re-read the partition table
850 if (allOK) {
srs5694e35eb1b2009-09-14 00:29:34 -0400851 DiskSync(fd);
srs5694e7b4ff92009-08-18 13:16:10 -0400852 } // if
853
854 if (allOK) { // writes completed OK
srs5694e4ac11e2009-08-31 10:13:04 -0400855 printf("The operation has completed successfully.\n");
srs5694e7b4ff92009-08-18 13:16:10 -0400856 } else {
srs5694e4ac11e2009-08-31 10:13:04 -0400857 printf("Warning! An error was reported when writing the partition table! This error\n");
858 printf("MIGHT be harmless, but you may have trashed the disk! Use parted and, if\n");
859 printf("necessary, restore your original partition table.\n");
srs5694e7b4ff92009-08-18 13:16:10 -0400860 } // if/else
srs5694e4ac11e2009-08-31 10:13:04 -0400861 close(fd);
srs5694e7b4ff92009-08-18 13:16:10 -0400862 } else {
srs5694e4ac11e2009-08-31 10:13:04 -0400863 fprintf(stderr, "Unable to open device %s for writing! Errno is %d! Aborting write!\n",
864 device, errno);
865 allOK = 0;
srs5694e7b4ff92009-08-18 13:16:10 -0400866 } // if/else
867 } else {
868 printf("Aborting write of new partition table.\n");
869 } // if
870
srs56942a9f5da2009-08-26 00:48:01 -0400871 if (IsLittleEndian() == 0) {
872 // Reverse (normalize) header bytes first, since ReversePartitionBytes()
873 // requires non-reversed data in mainHeader...
874 ReverseHeaderBytes(&mainHeader);
875 ReverseHeaderBytes(&secondHeader);
876 ReversePartitionBytes();
877 } // if
878
srs5694e7b4ff92009-08-18 13:16:10 -0400879 return (allOK);
880} // GPTData::SaveGPTData()
881
882// Save GPT data to a backup file. This function does much less error
883// checking than SaveGPTData(). It can therefore preserve many types of
884// corruption for later analysis; however, it preserves only the MBR,
885// the main GPT header, the backup GPT header, and the main partition
886// table; it discards the backup partition table, since it should be
887// identical to the main partition table on healthy disks.
888int GPTData::SaveGPTBackup(char* filename) {
srs56942a9f5da2009-08-26 00:48:01 -0400889 int fd, allOK = 1;
890 uint32_t numParts;
srs5694e7b4ff92009-08-18 13:16:10 -0400891
892 if ((fd = open(filename, O_WRONLY | O_CREAT, S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH)) != -1) {
srs56942a9f5da2009-08-26 00:48:01 -0400893 // Reverse the byte order, if necessary....
894 numParts = mainHeader.numParts;
895 if (IsLittleEndian() == 0) {
896 ReversePartitionBytes();
897 ReverseHeaderBytes(&mainHeader);
898 ReverseHeaderBytes(&secondHeader);
899 } // if
900
srs5694978041c2009-09-21 20:51:47 -0400901 // Recomputing the CRCs is likely to alter them, which could be bad
902 // if the intent is to save a potentially bad GPT for later analysis;
903 // but if we don't do this, we get bogus errors when we load the
904 // backup. I'm favoring misses over false alarms....
905 RecomputeCRCs();
906
srs56942a9f5da2009-08-26 00:48:01 -0400907 // Now write the protective MBR...
srs5694e7b4ff92009-08-18 13:16:10 -0400908 protectiveMBR.WriteMBRData(fd);
909
910 // Now write the main GPT header...
911 if (allOK)
912 if (write(fd, &mainHeader, 512) == -1)
913 allOK = 0;
914
915 // Now write the secondary GPT header...
916 if (allOK)
917 if (write(fd, &secondHeader, 512) == -1)
srs5694e4ac11e2009-08-31 10:13:04 -0400918 allOK = 0;
srs5694e7b4ff92009-08-18 13:16:10 -0400919
920 // Now write the main partition tables...
921 if (allOK) {
srs56942a9f5da2009-08-26 00:48:01 -0400922 if (write(fd, partitions, GPT_SIZE * numParts) == -1)
srs5694e7b4ff92009-08-18 13:16:10 -0400923 allOK = 0;
924 } // if
925
926 if (allOK) { // writes completed OK
927 printf("The operation has completed successfully.\n");
928 } else {
929 printf("Warning! An error was reported when writing the backup file.\n");
srs56948bb78762009-11-24 15:43:49 -0500930 printf("It may not be usable!\n");
srs5694e7b4ff92009-08-18 13:16:10 -0400931 } // if/else
932 close(fd);
srs56942a9f5da2009-08-26 00:48:01 -0400933
934 // Now reverse the byte-order reversal, if necessary....
935 if (IsLittleEndian() == 0) {
936 ReverseHeaderBytes(&mainHeader);
937 ReverseHeaderBytes(&secondHeader);
938 ReversePartitionBytes();
939 } // if
srs5694e7b4ff92009-08-18 13:16:10 -0400940 } else {
941 fprintf(stderr, "Unable to open file %s for writing! Aborting!\n", filename);
942 allOK = 0;
943 } // if/else
944 return allOK;
945} // GPTData::SaveGPTBackup()
946
947// Load GPT data from a backup file created by SaveGPTBackup(). This function
948// does minimal error checking. It returns 1 if it completed successfully,
949// 0 if there was a problem. In the latter case, it creates a new empty
950// set of partitions.
951int GPTData::LoadGPTBackup(char* filename) {
952 int fd, allOK = 1, val;
953 uint32_t numParts, sizeOfEntries, sizeOfParts, newCRC;
srs56942a9f5da2009-08-26 00:48:01 -0400954 int littleEndian = 1;
srs5694e7b4ff92009-08-18 13:16:10 -0400955
956 if ((fd = open(filename, O_RDONLY)) != -1) {
srs56942a9f5da2009-08-26 00:48:01 -0400957 if (IsLittleEndian() == 0)
958 littleEndian = 0;
959
srs5694e7b4ff92009-08-18 13:16:10 -0400960 // Let the MBRData class load the saved MBR...
srs5694221e0872009-08-29 15:00:31 -0400961 protectiveMBR.ReadMBRData(fd, 0); // 0 = don't check block size
srs5694e7b4ff92009-08-18 13:16:10 -0400962
963 // Load the main GPT header, check its vaility, and set the GPT
964 // size based on the data
965 read(fd, &mainHeader, 512);
966 mainCrcOk = CheckHeaderCRC(&mainHeader);
967
srs56942a9f5da2009-08-26 00:48:01 -0400968 // Reverse byte order, if necessary
969 if (littleEndian == 0) {
970 ReverseHeaderBytes(&mainHeader);
971 } // if
972
srs5694e7b4ff92009-08-18 13:16:10 -0400973 // Load the backup GPT header in much the same way as the main
974 // GPT header....
975 read(fd, &secondHeader, 512);
976 secondCrcOk = CheckHeaderCRC(&secondHeader);
977
srs56942a9f5da2009-08-26 00:48:01 -0400978 // Reverse byte order, if necessary
979 if (littleEndian == 0) {
980 ReverseHeaderBytes(&secondHeader);
981 } // if
982
srs5694e7b4ff92009-08-18 13:16:10 -0400983 // Return valid headers code: 0 = both headers bad; 1 = main header
984 // good, backup bad; 2 = backup header good, main header bad;
985 // 3 = both headers good. Note these codes refer to valid GPT
986 // signatures and version numbers; more subtle problems will elude
987 // this check!
988 if ((val = CheckHeaderValidity()) > 0) {
989 if (val == 2) { // only backup header seems to be good
990 numParts = secondHeader.numParts;
srs5694e4ac11e2009-08-31 10:13:04 -0400991 sizeOfEntries = secondHeader.sizeOfPartitionEntries;
srs5694e7b4ff92009-08-18 13:16:10 -0400992 } else { // main header is OK
993 numParts = mainHeader.numParts;
994 sizeOfEntries = mainHeader.sizeOfPartitionEntries;
995 } // if/else
996
997 SetGPTSize(numParts);
998
999 // If current disk size doesn't match that of backup....
1000 if (secondHeader.currentLBA != diskSize - UINT64_C(1)) {
1001 printf("Warning! Current disk size doesn't match that of the backup!\n"
srs5694e4ac11e2009-08-31 10:13:04 -04001002 "Adjusting sizes to match, but subsequent problems are possible!\n");
srs5694247657a2009-11-26 18:36:12 -05001003 MoveSecondHeaderToEnd();
srs5694e7b4ff92009-08-18 13:16:10 -04001004 } // if
1005
1006 // Load main partition table, and record whether its CRC
1007 // matches the stored value
1008 sizeOfParts = numParts * sizeOfEntries;
1009 read(fd, partitions, sizeOfParts);
1010
1011 newCRC = chksum_crc32((unsigned char*) partitions, sizeOfParts);
1012 mainPartsCrcOk = (newCRC == mainHeader.partitionEntriesCRC);
1013 secondPartsCrcOk = (newCRC == secondHeader.partitionEntriesCRC);
srs56942a9f5da2009-08-26 00:48:01 -04001014 // Reverse byte order, if necessary
srs5694e4ac11e2009-08-31 10:13:04 -04001015 if (littleEndian == 0) {
1016 ReversePartitionBytes();
1017 } // if
srs56942a9f5da2009-08-26 00:48:01 -04001018
srs5694e7b4ff92009-08-18 13:16:10 -04001019 } else {
1020 allOK = 0;
1021 } // if/else
1022 } else {
1023 allOK = 0;
1024 fprintf(stderr, "Unable to open file %s for reading! Aborting!\n", filename);
1025 } // if/else
1026
1027 // Something went badly wrong, so blank out partitions
1028 if (allOK == 0) {
1029 ClearGPTData();
1030 protectiveMBR.MakeProtectiveMBR();
1031 } // if
1032 return allOK;
1033} // GPTData::LoadGPTBackup()
1034
srs5694e4ac11e2009-08-31 10:13:04 -04001035// Tell user whether Apple Partition Map (APM) was discovered....
1036void GPTData::ShowAPMState(void) {
1037 if (apmFound)
1038 printf(" APM: present\n");
1039 else
1040 printf(" APM: not present\n");
1041} // GPTData::ShowAPMState()
1042
1043// Tell user about the state of the GPT data....
1044void GPTData::ShowGPTState(void) {
1045 switch (state) {
1046 case gpt_invalid:
1047 printf(" GPT: not present\n");
1048 break;
1049 case gpt_valid:
1050 printf(" GPT: present\n");
1051 break;
1052 case gpt_corrupt:
1053 printf(" GPT: damaged\n");
1054 break;
1055 default:
1056 printf("\a GPT: unknown -- bug!\n");
1057 break;
1058 } // switch
1059} // GPTData::ShowGPTState()
1060
1061// Display the basic GPT data
1062void GPTData::DisplayGPTData(void) {
srs5694e35eb1b2009-09-14 00:29:34 -04001063 int i;
srs5694e4ac11e2009-08-31 10:13:04 -04001064 char sizeInSI[255]; // String to hold size of disk in SI units
1065 char tempStr[255];
1066 uint64_t temp, totalFree;
1067
1068 BytesToSI(diskSize * blockSize, sizeInSI);
srs5694978041c2009-09-21 20:51:47 -04001069 printf("Disk %s: %llu sectors, %s\n", device,
1070 (unsigned long long) diskSize, sizeInSI);
srs5694e4ac11e2009-08-31 10:13:04 -04001071 printf("Disk identifier (GUID): %s\n", GUIDToStr(mainHeader.diskGUID, tempStr));
1072 printf("Partition table holds up to %lu entries\n", (unsigned long) mainHeader.numParts);
1073 printf("First usable sector is %lu, last usable sector is %lu\n",
1074 (unsigned long) mainHeader.firstUsableLBA,
1075 (unsigned long) mainHeader.lastUsableLBA);
1076 totalFree = FindFreeBlocks(&i, &temp);
1077 printf("Total free space is %llu sectors (%s)\n", totalFree,
1078 BytesToSI(totalFree * (uint64_t) blockSize, sizeInSI));
1079 printf("\nNumber Start (sector) End (sector) Size Code Name\n");
1080 for (i = 0; i < mainHeader.numParts; i++) {
srs5694978041c2009-09-21 20:51:47 -04001081 partitions[i].ShowSummary(i, blockSize);
srs5694e4ac11e2009-08-31 10:13:04 -04001082 } // for
1083} // GPTData::DisplayGPTData()
1084
1085// Get partition number from user and then call ShowPartDetails(partNum)
1086// to show its detailed information
1087void GPTData::ShowDetails(void) {
1088 int partNum;
1089 uint32_t low, high;
1090
1091 if (GetPartRange(&low, &high) > 0) {
1092 partNum = GetPartNum();
1093 ShowPartDetails(partNum);
1094 } else {
1095 printf("No partitions\n");
1096 } // if/else
1097} // GPTData::ShowDetails()
1098
1099// Show detailed information on the specified partition
1100void GPTData::ShowPartDetails(uint32_t partNum) {
1101 if (partitions[partNum].GetFirstLBA() != 0) {
1102 partitions[partNum].ShowDetails(blockSize);
1103 } else {
1104 printf("Partition #%d does not exist.", (int) (partNum + 1));
1105 } // if
1106} // GPTData::ShowPartDetails()
1107
1108/*********************************************************************
1109 * *
1110 * Begin functions that obtain information from the users, and often *
1111 * do something with that information (call other functions) *
1112 * *
1113 *********************************************************************/
1114
1115// Prompts user for partition number and returns the result.
1116uint32_t GPTData::GetPartNum(void) {
1117 uint32_t partNum;
1118 uint32_t low, high;
1119 char prompt[255];
1120
1121 if (GetPartRange(&low, &high) > 0) {
1122 sprintf(prompt, "Partition number (%d-%d): ", low + 1, high + 1);
1123 partNum = GetNumber(low + 1, high + 1, low, prompt);
1124 } else partNum = 1;
1125 return (partNum - 1);
1126} // GPTData::GetPartNum()
1127
1128// What it says: Resize the partition table. (Default is 128 entries.)
1129void GPTData::ResizePartitionTable(void) {
1130 int newSize;
1131 char prompt[255];
1132 uint32_t curLow, curHigh;
1133
1134 printf("Current partition table size is %lu.\n",
1135 (unsigned long) mainHeader.numParts);
1136 GetPartRange(&curLow, &curHigh);
1137 curHigh++; // since GetPartRange() returns numbers starting from 0...
1138 // There's no point in having fewer than four partitions....
1139 if (curHigh < 4)
1140 curHigh = 4;
1141 sprintf(prompt, "Enter new size (%d up, default %d): ", (int) curHigh,
1142 (int) NUM_GPT_ENTRIES);
1143 newSize = GetNumber(4, 65535, 128, prompt);
1144 if (newSize < 128) {
1145 printf("Caution: The partition table size should officially be 16KB or larger,\n"
1146 "which works out to 128 entries. In practice, smaller tables seem to\n"
1147 "work with most OSes, but this practice is risky. I'm proceeding with\n"
1148 "the resize, but you may want to reconsider this action and undo it.\n\n");
1149 } // if
1150 SetGPTSize(newSize);
1151} // GPTData::ResizePartitionTable()
1152
1153// Interactively create a partition
1154void GPTData::CreatePartition(void) {
1155 uint64_t firstBlock, firstInLargest, lastBlock, sector;
1156 char prompt[255];
1157 int partNum, firstFreePart = 0;
1158
1159 // Find first free partition...
1160 while (partitions[firstFreePart].GetFirstLBA() != 0) {
1161 firstFreePart++;
1162 } // while
1163
1164 if (((firstBlock = FindFirstAvailable()) != 0) &&
1165 (firstFreePart < mainHeader.numParts)) {
1166 lastBlock = FindLastAvailable(firstBlock);
1167 firstInLargest = FindFirstInLargest();
1168
1169 // Get partition number....
1170 do {
1171 sprintf(prompt, "Partition number (%d-%d, default %d): ", firstFreePart + 1,
1172 mainHeader.numParts, firstFreePart + 1);
1173 partNum = GetNumber(firstFreePart + 1, mainHeader.numParts,
1174 firstFreePart + 1, prompt) - 1;
1175 if (partitions[partNum].GetFirstLBA() != 0)
1176 printf("partition %d is in use.\n", partNum + 1);
1177 } while (partitions[partNum].GetFirstLBA() != 0);
1178
1179 // Get first block for new partition...
1180 sprintf(prompt,
1181 "First sector (%llu-%llu, default = %llu) or {+-}size{KMGT}: ",
1182 firstBlock, lastBlock, firstInLargest);
1183 do {
1184 sector = GetSectorNum(firstBlock, lastBlock, firstInLargest, prompt);
1185 } while (IsFree(sector) == 0);
1186 firstBlock = sector;
1187
1188 // Get last block for new partitions...
1189 lastBlock = FindLastInFree(firstBlock);
1190 sprintf(prompt,
1191 "Last sector (%llu-%llu, default = %llu) or {+-}size{KMGT}: ",
1192 firstBlock, lastBlock, lastBlock);
1193 do {
1194 sector = GetSectorNum(firstBlock, lastBlock, lastBlock, prompt);
1195 } while (IsFree(sector) == 0);
1196 lastBlock = sector;
1197
1198 partitions[partNum].SetFirstLBA(firstBlock);
1199 partitions[partNum].SetLastLBA(lastBlock);
1200
1201 partitions[partNum].SetUniqueGUID(1);
1202 partitions[partNum].ChangeType();
1203 partitions[partNum].SetName((unsigned char*) partitions[partNum].GetNameType(prompt));
1204 } else {
1205 printf("No free sectors available\n");
1206 } // if/else
1207} // GPTData::CreatePartition()
1208
1209// Interactively delete a partition (duh!)
1210void GPTData::DeletePartition(void) {
1211 int partNum;
1212 uint32_t low, high;
1213 uint64_t startSector, length;
1214 char prompt[255];
1215
1216 if (GetPartRange(&low, &high) > 0) {
1217 sprintf(prompt, "Partition number (%d-%d): ", low + 1, high + 1);
1218 partNum = GetNumber(low + 1, high + 1, low, prompt);
1219
1220 // In case there's a protective MBR, look for & delete matching
1221 // MBR partition....
1222 startSector = partitions[partNum - 1].GetFirstLBA();
1223 length = partitions[partNum - 1].GetLengthLBA();
1224 protectiveMBR.DeleteByLocation(startSector, length);
1225
1226 // Now delete the GPT partition
1227 partitions[partNum - 1].BlankPartition();
1228 } else {
1229 printf("No partitions\n");
1230 } // if/else
1231} // GPTData::DeletePartition()
1232
1233// Prompt user for a partition number, then change its type code
1234// using ChangeGPTType(struct GPTPartition*) function.
1235void GPTData::ChangePartType(void) {
1236 int partNum;
1237 uint32_t low, high;
1238
1239 if (GetPartRange(&low, &high) > 0) {
1240 partNum = GetPartNum();
1241 partitions[partNum].ChangeType();
1242 } else {
1243 printf("No partitions\n");
1244 } // if/else
1245} // GPTData::ChangePartType()
1246
1247// Partition attributes seem to be rarely used, but I want a way to
1248// adjust them for completeness....
1249void GPTData::SetAttributes(uint32_t partNum) {
1250 Attributes theAttr;
1251
1252 theAttr.SetAttributes(partitions[partNum].GetAttributes());
1253 theAttr.DisplayAttributes();
1254 theAttr.ChangeAttributes();
1255 partitions[partNum].SetAttributes(theAttr.GetAttributes());
1256} // GPTData::SetAttributes()
1257
srs5694c0ca8f82009-08-20 21:35:25 -04001258// This function destroys the on-disk GPT structures. Returns 1 if the
1259// user confirms destruction, 0 if the user aborts.
srs5694978041c2009-09-21 20:51:47 -04001260// If prompt == 0, don't ask user about proceeding and do NOT wipe out
1261// MBR. (Set prompt == 0 when doing a GPT-to-MBR conversion.)
1262int GPTData::DestroyGPT(int prompt) {
srs5694e35eb1b2009-09-14 00:29:34 -04001263 int fd, i;
srs5694978041c2009-09-21 20:51:47 -04001264 char blankSector[512], goOn = 'Y', blank = 'N';
srs5694c0ca8f82009-08-20 21:35:25 -04001265
1266 for (i = 0; i < 512; i++) {
1267 blankSector[i] = '\0';
1268 } // for
1269
srs5694978041c2009-09-21 20:51:47 -04001270 if (((apmFound) || (bsdFound)) && prompt) {
srs5694e35eb1b2009-09-14 00:29:34 -04001271 printf("WARNING: APM or BSD disklabel structures detected! This operation could\n"
1272 "damage any APM or BSD partitions on this disk!\n");
1273 } // if APM or BSD
srs5694978041c2009-09-21 20:51:47 -04001274 if (prompt) {
1275 printf("\a\aAbout to wipe out GPT on %s. Proceed? ", device);
1276 goOn = GetYN();
1277 } // if
srs5694c0ca8f82009-08-20 21:35:25 -04001278 if (goOn == 'Y') {
1279 fd = open(device, O_WRONLY);
1280#ifdef __APPLE__
1281 // MacOS X requires a shared lock under some circumstances....
1282 if (fd < 0) {
1283 fd = open(device, O_WRONLY|O_SHLOCK);
1284 } // if
1285#endif
1286 if (fd != -1) {
1287 lseek64(fd, mainHeader.currentLBA * 512, SEEK_SET); // seek to GPT header
1288 write(fd, blankSector, 512); // blank it out
1289 lseek64(fd, mainHeader.partitionEntriesLBA * 512, SEEK_SET); // seek to partition table
1290 for (i = 0; i < GetBlocksInPartTable(); i++)
1291 write(fd, blankSector, 512);
1292 lseek64(fd, secondHeader.partitionEntriesLBA * 512, SEEK_SET); // seek to partition table
1293 for (i = 0; i < GetBlocksInPartTable(); i++)
1294 write(fd, blankSector, 512);
1295 lseek64(fd, secondHeader.currentLBA * 512, SEEK_SET); // seek to GPT header
1296 write(fd, blankSector, 512); // blank it out
srs5694978041c2009-09-21 20:51:47 -04001297 if (prompt) {
1298 printf("Blank out MBR? ");
1299 blank = GetYN();
1300 }// if
1301 // Note on below: Touch the MBR only if the user wants it completely
1302 // blanked out. Version 0.4.2 deleted the 0xEE partition and re-wrote
1303 // the MBR, but this could wipe out a valid MBR that the program
1304 // had subsequently discarded (say, if it conflicted with older GPT
1305 // structures).
1306 if (blank == 'Y') {
srs5694c0ca8f82009-08-20 21:35:25 -04001307 lseek64(fd, 0, SEEK_SET);
1308 write(fd, blankSector, 512); // blank it out
srs5694978041c2009-09-21 20:51:47 -04001309 } else {
1310 printf("MBR is unchanged. You may need to delete an EFI GPT (0xEE) partition\n"
1311 "with fdisk or another tool.\n");
srs5694e35eb1b2009-09-14 00:29:34 -04001312 } // if/else
1313 DiskSync(fd);
srs5694c0ca8f82009-08-20 21:35:25 -04001314 close(fd);
srs5694e4ac11e2009-08-31 10:13:04 -04001315 printf("GPT data structures destroyed! You may now partition the disk using fdisk or\n"
1316 "other utilities. Program will now terminate.\n");
srs5694c0ca8f82009-08-20 21:35:25 -04001317 } else {
1318 printf("Problem opening %s for writing! Program will now terminate.\n");
1319 } // if/else (fd != -1)
1320 } // if (goOn == 'Y')
1321 return (goOn == 'Y');
1322} // GPTData::DestroyGPT()
1323
srs5694e4ac11e2009-08-31 10:13:04 -04001324/**************************************************************************
1325 * *
1326 * Partition table transformation functions (MBR or BSD disklabel to GPT) *
1327 * (some of these functions may require user interaction) *
1328 * *
1329 **************************************************************************/
1330
1331// Examines the MBR & GPT data, and perhaps asks the user questions, to
1332// determine which set of data to use: the MBR (use_mbr), the GPT (use_gpt),
1333// or create a new set of partitions (use_new)
1334WhichToUse GPTData::UseWhichPartitions(void) {
1335 WhichToUse which = use_new;
1336 MBRValidity mbrState;
1337 int answer;
1338
1339 mbrState = protectiveMBR.GetValidity();
1340
1341 if ((state == gpt_invalid) && ((mbrState == mbr) || (mbrState == hybrid))) {
1342 printf("\n\a***************************************************************\n"
1343 "Found invalid GPT and valid MBR; converting MBR to GPT format.\n"
1344 "THIS OPERATON IS POTENTIALLY DESTRUCTIVE! Exit by typing 'q' if\n"
1345 "you don't want to convert your MBR partitions to GPT format!\n"
1346 "***************************************************************\n\n");
1347 which = use_mbr;
1348 } // if
1349
1350 if ((state == gpt_invalid) && bsdFound) {
1351 printf("\n\a**********************************************************************\n"
1352 "Found invalid GPT and valid BSD disklabel; converting BSD disklabel\n"
1353 "to GPT format. THIS OPERATON IS POTENTIALLY DESTRUCTIVE! Your first\n"
1354 "BSD partition will likely be unusable. Exit by typing 'q' if you don't\n"
1355 "want to convert your BSD partitions to GPT format!\n"
1356 "**********************************************************************\n\n");
1357 which = use_bsd;
1358 } // if
1359
1360 if ((state == gpt_valid) && (mbrState == gpt)) {
1361 printf("Found valid GPT with protective MBR; using GPT.\n");
1362 which = use_gpt;
1363 } // if
1364 if ((state == gpt_valid) && (mbrState == hybrid)) {
1365 printf("Found valid GPT with hybrid MBR; using GPT.\n");
1366 which = use_gpt;
1367 } // if
1368 if ((state == gpt_valid) && (mbrState == invalid)) {
1369 printf("\aFound valid GPT with corrupt MBR; using GPT and will create new\nprotective MBR on save.\n");
1370 which = use_gpt;
1371 protectiveMBR.MakeProtectiveMBR();
1372 } // if
1373 if ((state == gpt_valid) && (mbrState == mbr)) {
1374 printf("Found valid MBR and GPT. Which do you want to use?\n");
1375 answer = GetNumber(1, 3, 2, (char*) " 1 - MBR\n 2 - GPT\n 3 - Create blank GPT\n\nYour answer: ");
1376 if (answer == 1) {
1377 which = use_mbr;
1378 } else if (answer == 2) {
1379 which = use_gpt;
1380 protectiveMBR.MakeProtectiveMBR();
1381 printf("Using GPT and creating fresh protective MBR.\n");
1382 } else which = use_new;
1383 } // if
1384
1385 // Nasty decisions here -- GPT is present, but corrupt (bad CRCs or other
1386 // problems)
1387 if (state == gpt_corrupt) {
1388 if ((mbrState == mbr) || (mbrState == hybrid)) {
1389 printf("Found valid MBR and corrupt GPT. Which do you want to use? (Using the\n"
1390 "GPT MAY permit recovery of GPT data.)\n");
1391 answer = GetNumber(1, 3, 2, (char*) " 1 - MBR\n 2 - GPT\n 3 - Create blank GPT\n\nYour answer: ");
1392 if (answer == 1) {
1393 which = use_mbr;
1394// protectiveMBR.MakeProtectiveMBR();
1395 } else if (answer == 2) {
1396 which = use_gpt;
1397 } else which = use_new;
1398 } else if (mbrState == invalid) {
1399 printf("Found invalid MBR and corrupt GPT. What do you want to do? (Using the\n"
1400 "GPT MAY permit recovery of GPT data.)\n");
1401 answer = GetNumber(1, 2, 1, (char*) " 1 - GPT\n 2 - Create blank GPT\n\nYour answer: ");
1402 if (answer == 1) {
1403 which = use_gpt;
1404 } else which = use_new;
1405 } else { // corrupt GPT, MBR indicates it's a GPT disk....
1406 printf("\a\a****************************************************************************\n"
1407 "Caution: Found protective or hybrid MBR and corrupt GPT. Using GPT, but disk\n"
1408 "verification and recovery are STRONGLY recommended.\n"
1409 "****************************************************************************\n");
srs5694247657a2009-11-26 18:36:12 -05001410 which = use_gpt;
srs5694e4ac11e2009-08-31 10:13:04 -04001411 } // if/else/else
1412 } // if (corrupt GPT)
1413
1414 if (which == use_new)
1415 printf("Creating new GPT entries.\n");
1416
1417 return which;
1418} // UseWhichPartitions()
1419
1420// Convert MBR partition table into GPT form
1421int GPTData::XFormPartitions(void) {
1422 int i, numToConvert;
1423 uint8_t origType;
1424 struct newGUID;
srs5694e4ac11e2009-08-31 10:13:04 -04001425
1426 // Clear out old data & prepare basics....
1427 ClearGPTData();
1428
1429 // Convert the smaller of the # of GPT or MBR partitions
srs5694978041c2009-09-21 20:51:47 -04001430 if (mainHeader.numParts > (MAX_MBR_PARTS))
1431 numToConvert = MAX_MBR_PARTS;
srs5694e4ac11e2009-08-31 10:13:04 -04001432 else
1433 numToConvert = mainHeader.numParts;
1434
1435 for (i = 0; i < numToConvert; i++) {
1436 origType = protectiveMBR.GetType(i);
1437 // don't waste CPU time trying to convert extended, hybrid protective, or
1438 // null (non-existent) partitions
srs5694e35eb1b2009-09-14 00:29:34 -04001439 if ((origType != 0x05) && (origType != 0x0f) && (origType != 0x85) &&
srs5694e4ac11e2009-08-31 10:13:04 -04001440 (origType != 0x00) && (origType != 0xEE))
1441 partitions[i] = protectiveMBR.AsGPT(i);
1442 } // for
1443
1444 // Convert MBR into protective MBR
1445 protectiveMBR.MakeProtectiveMBR();
1446
1447 // Record that all original CRCs were OK so as not to raise flags
1448 // when doing a disk verification
1449 mainCrcOk = secondCrcOk = mainPartsCrcOk = secondPartsCrcOk = 1;
1450
1451 return (1);
1452} // GPTData::XFormPartitions()
1453
1454// Transforms BSD disklabel on the specified partition (numbered from 0).
1455// If an invalid partition number is given, the program prompts for one.
1456// Returns the number of new partitions created.
1457int GPTData::XFormDisklabel(int i) {
1458 uint32_t low, high, partNum, startPart;
1459 uint16_t hexCode;
1460 int goOn = 1, numDone = 0;
1461 BSDData disklabel;
1462
1463 if (GetPartRange(&low, &high) != 0) {
1464 if ((i < low) || (i > high))
1465 partNum = GetPartNum();
1466 else
1467 partNum = (uint32_t) i;
1468
1469 // Find the partition after the last used one
1470 startPart = high + 1;
1471
1472 // Now see if the specified partition has a BSD type code....
1473 hexCode = partitions[partNum].GetHexType();
1474 if ((hexCode != 0xa500) && (hexCode != 0xa900)) {
1475 printf("Specified partition doesn't have a disklabel partition type "
1476 "code.\nContinue anyway?");
1477 goOn = (GetYN() == 'Y');
1478 } // if
1479
1480 // If all is OK, read the disklabel and convert it.
1481 if (goOn) {
1482 goOn = disklabel.ReadBSDData(device, partitions[partNum].GetFirstLBA(),
1483 partitions[partNum].GetLastLBA());
1484 if ((goOn) && (disklabel.IsDisklabel())) {
1485 numDone = XFormDisklabel(&disklabel, startPart);
1486 if (numDone == 1)
1487 printf("Converted %d BSD partition.\n", numDone);
1488 else
1489 printf("Converted %d BSD partitions.\n", numDone);
1490 } else {
1491 printf("Unable to convert partitions! Unrecognized BSD disklabel.\n");
1492 } // if/else
1493 } // if
1494 if (numDone > 0) { // converted partitions; delete carrier
1495 partitions[partNum].BlankPartition();
1496 } // if
1497 } else {
1498 printf("No partitions\n");
1499 } // if/else
1500 return numDone;
1501} // GPTData::XFormDisklable(int i)
1502
1503// Transform the partitions on an already-loaded BSD disklabel...
1504int GPTData::XFormDisklabel(BSDData* disklabel, int startPart) {
1505 int i, numDone = 0;
1506
1507 if ((disklabel->IsDisklabel()) && (startPart >= 0) &&
1508 (startPart < mainHeader.numParts)) {
1509 for (i = 0; i < disklabel->GetNumParts(); i++) {
1510 partitions[i + startPart] = disklabel->AsGPT(i);
1511 if (partitions[i + startPart].GetFirstLBA() != UINT64_C(0))
1512 numDone++;
1513 } // for
1514 } // if
1515
1516 // Record that all original CRCs were OK so as not to raise flags
1517 // when doing a disk verification
1518 mainCrcOk = secondCrcOk = mainPartsCrcOk = secondPartsCrcOk = 1;
1519
1520 return numDone;
1521} // GPTData::XFormDisklabel(BSDData* disklabel)
1522
srs5694978041c2009-09-21 20:51:47 -04001523// Add one GPT partition to MBR. Used by XFormToMBR() and MakeHybrid()
1524// functions. Returns 1 if operation was successful.
1525int GPTData::OnePartToMBR(uint32_t gptPart, int mbrPart) {
1526 int allOK = 1, typeCode, bootable;
1527 uint64_t length;
1528 char line[255];
1529
1530 if ((mbrPart < 0) || (mbrPart > 3)) {
1531 printf("MBR partition %d is out of range; omitting it.\n", mbrPart + 1);
1532 allOK = 0;
1533 } // if
1534 if (gptPart >= mainHeader.numParts) {
1535 printf("GPT partition %d is out of range; omitting it.\n", gptPart + 1);
1536 allOK = 0;
1537 } // if
1538 if (allOK && (partitions[gptPart].GetLastLBA() == UINT64_C(0))) {
1539 printf("GPT partition %d is undefined; omitting it.\n", gptPart + 1);
1540 allOK = 0;
1541 } // if
1542 if (allOK && (partitions[gptPart].GetFirstLBA() <= UINT32_MAX) &&
1543 (partitions[gptPart].GetLengthLBA() <= UINT32_MAX)) {
1544 if (partitions[gptPart].GetLastLBA() > UINT32_MAX) {
1545 printf("Caution: Partition end point past 32-bit pointer boundary;"
1546 " some OSes may\nreact strangely.\n");
1547 } // if partition ends past 32-bit (usually 2TiB) boundary
1548 do {
1549 printf("Enter an MBR hex code (default %02X): ",
1550 typeHelper.GUIDToID(partitions[gptPart].GetType()) / 256);
1551 fgets(line, 255, stdin);
1552 sscanf(line, "%x", &typeCode);
1553 if (line[0] == '\n')
1554 typeCode = partitions[gptPart].GetHexType() / 256;
1555 } while ((typeCode <= 0) || (typeCode > 255));
1556 printf("Set the bootable flag? ");
1557 bootable = (GetYN() == 'Y');
1558 length = partitions[gptPart].GetLengthLBA();
1559 protectiveMBR.MakePart(mbrPart, (uint32_t) partitions[gptPart].GetFirstLBA(),
1560 (uint32_t) length, typeCode, bootable);
1561 } else { // partition out of range
1562 printf("Partition %d begins beyond the 32-bit pointer limit of MBR "
1563 "partitions, or is\n too big; omitting it.\n", gptPart + 1);
1564 allOK = 0;
1565 } // if/else
1566 return allOK;
1567} // GPTData::OnePartToMBR()
1568
1569// Convert the GPT to MBR form. This function is necessarily limited; it
1570// handles at most four partitions and creates layouts that ignore CHS
1571// geometries. Returns the number of converted partitions; if this value
1572// is over 0, the calling function should call DestroyGPT() to destroy
1573// the GPT data, and then exit.
1574int GPTData::XFormToMBR(void) {
1575 char line[255];
1576 int i, j, numParts, numConverted = 0;
1577 uint32_t partNums[4];
1578
1579 // Get the numbers of up to four partitions to add to the
1580 // hybrid MBR....
1581 numParts = CountParts();
1582 printf("Counted %d partitions.\n", numParts);
1583
1584 // Prepare the MBR for conversion (empty it of existing partitions).
1585 protectiveMBR.EmptyMBR(0);
1586 protectiveMBR.SetDiskSize(diskSize);
1587
1588 if (numParts > 4) { // Over four partitions; engage in triage
1589 printf("Type from one to four GPT partition numbers, separated by spaces, to be\n"
1590 "used in the MBR, in sequence: ");
1591 fgets(line, 255, stdin);
1592 numParts = sscanf(line, "%d %d %d %d", &partNums[0], &partNums[1],
1593 &partNums[2], &partNums[3]);
1594 } else { // Four or fewer partitions; convert them all
1595 i = j = 0;
1596 while ((j < numParts) && (i < mainHeader.numParts)) {
1597 if (partitions[i].GetFirstLBA() > 0) { // if GPT part. is defined
1598 partNums[j++] = ++i; // flag it for conversion
1599 } else i++;
1600 } // while
1601 } // if/else
1602
1603 for (i = 0; i < numParts; i++) {
1604 j = partNums[i] - 1;
1605 printf("\nCreating entry for partition #%d\n", j + 1);
1606 numConverted += OnePartToMBR(j, i);
1607 } // for
1608 return numConverted;
1609} // GPTData::XFormToMBR()
1610
srs5694e4ac11e2009-08-31 10:13:04 -04001611// Create a hybrid MBR -- an ugly, funky thing that helps GPT work with
1612// OSes that don't understand GPT.
1613void GPTData::MakeHybrid(void) {
1614 uint32_t partNums[3];
1615 char line[255];
srs5694978041c2009-09-21 20:51:47 -04001616 int numParts, numConverted = 0, i, j, typeCode, mbrNum;
srs5694e4ac11e2009-08-31 10:13:04 -04001617 char fillItUp = 'M'; // fill extra partition entries? (Yes/No/Maybe)
srs5694978041c2009-09-21 20:51:47 -04001618 char eeFirst = 'Y'; // Whether EFI GPT (0xEE) partition comes first in table
srs5694e4ac11e2009-08-31 10:13:04 -04001619
1620 printf("\nWARNING! Hybrid MBRs are flaky and potentially dangerous! If you decide not\n"
1621 "to use one, just hit the Enter key at the below prompt and your MBR\n"
1622 "partition table will be untouched.\n\n\a");
1623
1624 // Now get the numbers of up to three partitions to add to the
1625 // hybrid MBR....
1626 printf("Type from one to three GPT partition numbers, separated by spaces, to be\n"
1627 "added to the hybrid MBR, in sequence: ");
1628 fgets(line, 255, stdin);
1629 numParts = sscanf(line, "%d %d %d", &partNums[0], &partNums[1], &partNums[2]);
1630
1631 if (numParts > 0) {
1632 // Blank out the protective MBR, but leave the boot loader code
1633 // alone....
1634 protectiveMBR.EmptyMBR(0);
1635 protectiveMBR.SetDiskSize(diskSize);
1636 printf("Place EFI GPT (0xEE) partition first in MBR (good for GRUB)? ");
1637 eeFirst = GetYN();
1638 } // if
1639
1640 for (i = 0; i < numParts; i++) {
1641 j = partNums[i] - 1;
1642 printf("\nCreating entry for partition #%d\n", j + 1);
srs5694978041c2009-09-21 20:51:47 -04001643 if (eeFirst == 'Y')
1644 mbrNum = i + 1;
1645 else
1646 mbrNum = i;
1647 numConverted += OnePartToMBR(j, mbrNum);
srs5694e4ac11e2009-08-31 10:13:04 -04001648 } // for
1649
srs5694978041c2009-09-21 20:51:47 -04001650 if ((numParts > 0) && (numConverted > 0)) { // User opted to create a hybrid MBR....
srs5694e4ac11e2009-08-31 10:13:04 -04001651 // Create EFI protective partition that covers the start of the disk.
1652 // If this location (covering the main GPT data structures) is omitted,
1653 // Linux won't find any partitions on the disk. Note that this is
1654 // NUMBERED AFTER the hybrid partitions, contrary to what the
1655 // gptsync utility does. This is because Windows seems to choke on
1656 // disks with a 0xEE partition in the first slot and subsequent
1657 // additional partitions, unless it boots from the disk.
1658 if (eeFirst == 'Y')
1659 mbrNum = 0;
1660 else
1661 mbrNum = numParts;
1662 protectiveMBR.MakePart(mbrNum, 1, protectiveMBR.FindLastInFree(1), 0xEE);
srs5694978041c2009-09-21 20:51:47 -04001663 protectiveMBR.SetHybrid();
srs5694e4ac11e2009-08-31 10:13:04 -04001664
1665 // ... and for good measure, if there are any partition spaces left,
1666 // optionally create another protective EFI partition to cover as much
1667 // space as possible....
1668 for (i = 0; i < 4; i++) {
1669 if (protectiveMBR.GetType(i) == 0x00) { // unused entry....
1670 if (fillItUp == 'M') {
1671 printf("\nUnused partition space(s) found. Use one to protect more partitions? ");
1672 fillItUp = GetYN();
1673 typeCode = 0x00; // use this to flag a need to get type code
1674 } // if
1675 if (fillItUp == 'Y') {
1676 while ((typeCode <= 0) || (typeCode > 255)) {
1677 printf("Enter an MBR hex code (EE is EFI GPT, but may confuse MacOS): ");
1678 // Comment on above: Mac OS treats disks with more than one
1679 // 0xEE MBR partition as MBR disks, not as GPT disks.
1680 fgets(line, 255, stdin);
1681 sscanf(line, "%x", &typeCode);
1682 if (line[0] == '\n')
1683 typeCode = 0;
1684 } // while
1685 protectiveMBR.MakeBiggestPart(i, typeCode); // make a partition
1686 } // if (fillItUp == 'Y')
1687 } // if unused entry
1688 } // for (i = 0; i < 4; i++)
1689 } // if (numParts > 0)
1690} // GPTData::MakeHybrid()
1691
1692/**********************************************************************
1693 * *
1694 * Functions that adjust GPT data structures WITHOUT user interaction *
1695 * (they may display information for the user's benefit, though) *
1696 * *
1697 **********************************************************************/
1698
1699// Resizes GPT to specified number of entries. Creates a new table if
1700// necessary, copies data if it already exists.
1701int GPTData::SetGPTSize(uint32_t numEntries) {
1702 struct GPTPart* newParts;
1703 struct GPTPart* trash;
1704 uint32_t i, high, copyNum;
1705 int allOK = 1;
1706
1707 // First, adjust numEntries upward, if necessary, to get a number
1708 // that fills the allocated sectors
1709 i = blockSize / GPT_SIZE;
1710 if ((numEntries % i) != 0) {
1711 printf("Adjusting GPT size from %lu ", (unsigned long) numEntries);
1712 numEntries = ((numEntries / i) + 1) * i;
1713 printf("to %lu to fill the sector\n", (unsigned long) numEntries);
1714 } // if
1715
srs5694247657a2009-11-26 18:36:12 -05001716 // Do the work only if the # of partitions is changing. Along with being
1717 // efficient, this prevents mucking the with location of the secondary
1718 // partition table, which causes problems when loading data from a RAID
1719 // array that's been expanded because this function is called when loading
1720 // data.
1721 if ((numEntries != mainHeader.numParts) || (partitions == NULL)) {
1722 newParts = (GPTPart*) calloc(numEntries, sizeof (GPTPart));
1723 if (newParts != NULL) {
1724 if (partitions != NULL) { // existing partitions; copy them over
1725 GetPartRange(&i, &high);
1726 if (numEntries < (high + 1)) { // Highest entry too high for new #
1727 printf("The highest-numbered partition is %lu, which is greater than the requested\n"
1728 "partition table size of %d; cannot resize. Perhaps sorting will help.\n",
1729 (unsigned long) (high + 1), numEntries);
1730 allOK = 0;
1731 } else { // go ahead with copy
1732 if (numEntries < mainHeader.numParts)
1733 copyNum = numEntries;
1734 else
1735 copyNum = mainHeader.numParts;
1736 for (i = 0; i < copyNum; i++) {
1737 newParts[i] = partitions[i];
1738 } // for
1739 trash = partitions;
1740 partitions = newParts;
1741 free(trash);
1742 } // if
1743 } else { // No existing partition table; just create it
srs5694e4ac11e2009-08-31 10:13:04 -04001744 partitions = newParts;
srs5694247657a2009-11-26 18:36:12 -05001745 } // if/else existing partitions
1746 mainHeader.numParts = numEntries;
1747 secondHeader.numParts = numEntries;
1748 mainHeader.firstUsableLBA = ((numEntries * GPT_SIZE) / blockSize) + 2 ;
1749 secondHeader.firstUsableLBA = mainHeader.firstUsableLBA;
1750 MoveSecondHeaderToEnd();
1751 if (diskSize > 0)
1752 CheckGPTSize();
1753 } else { // Bad memory allocation
1754 fprintf(stderr, "Error allocating memory for partition table!\n");
1755 allOK = 0;
1756 } // if/else
srs5694e4ac11e2009-08-31 10:13:04 -04001757 } // if/else
1758 return (allOK);
1759} // GPTData::SetGPTSize()
1760
1761// Blank the partition array
1762void GPTData::BlankPartitions(void) {
1763 uint32_t i;
1764
1765 for (i = 0; i < mainHeader.numParts; i++) {
1766 partitions[i].BlankPartition();
1767 } // for
1768} // GPTData::BlankPartitions()
1769
1770// Sort the GPT entries, eliminating gaps and making for a logical
1771// ordering. Relies on QuickSortGPT() for the bulk of the work
1772void GPTData::SortGPT(void) {
1773 int i, lastPart = 0;
1774 GPTPart temp;
1775
1776 // First, find the last partition with data, so as not to
1777 // spend needless time sorting empty entries....
1778 for (i = 0; i < mainHeader.numParts; i++) {
1779 if (partitions[i].GetFirstLBA() > 0)
1780 lastPart = i;
1781 } // for
1782
1783 // Now swap empties with the last partitions, to simplify the logic
1784 // in the Quicksort function....
1785 i = 0;
1786 while (i < lastPart) {
1787 if (partitions[i].GetFirstLBA() == 0) {
1788 temp = partitions[i];
1789 partitions[i] = partitions[lastPart];
1790 partitions[lastPart] = temp;
1791 lastPart--;
1792 } // if
1793 i++;
1794 } // while
1795
1796 // Now call the recursive quick sort routine to do the real work....
1797 QuickSortGPT(partitions, 0, lastPart);
1798} // GPTData::SortGPT()
1799
1800// Set up data structures for entirely new set of partitions on the
1801// specified device. Returns 1 if OK, 0 if there were problems.
srs5694e35eb1b2009-09-14 00:29:34 -04001802// Note that this function does NOT clear the protectiveMBR data
1803// structure, since it may hold the original MBR partitions if the
1804// program was launched on an MBR disk, and those may need to be
1805// converted to GPT format.
srs5694e4ac11e2009-08-31 10:13:04 -04001806int GPTData::ClearGPTData(void) {
srs5694e35eb1b2009-09-14 00:29:34 -04001807 int goOn = 1, i;
srs5694e4ac11e2009-08-31 10:13:04 -04001808
1809 // Set up the partition table....
1810 free(partitions);
1811 partitions = NULL;
1812 SetGPTSize(NUM_GPT_ENTRIES);
1813
1814 // Now initialize a bunch of stuff that's static....
1815 mainHeader.signature = GPT_SIGNATURE;
1816 mainHeader.revision = 0x00010000;
srs5694978041c2009-09-21 20:51:47 -04001817 mainHeader.headerSize = HEADER_SIZE;
srs5694e4ac11e2009-08-31 10:13:04 -04001818 mainHeader.reserved = 0;
1819 mainHeader.currentLBA = UINT64_C(1);
1820 mainHeader.partitionEntriesLBA = (uint64_t) 2;
1821 mainHeader.sizeOfPartitionEntries = GPT_SIZE;
1822 for (i = 0; i < GPT_RESERVED; i++) {
1823 mainHeader.reserved2[i] = '\0';
1824 } // for
1825
1826 // Now some semi-static items (computed based on end of disk)
1827 mainHeader.backupLBA = diskSize - UINT64_C(1);
1828 mainHeader.lastUsableLBA = diskSize - mainHeader.firstUsableLBA;
1829
1830 // Set a unique GUID for the disk, based on random numbers
1831 // rand() is only 32 bits, so multiply together to fill a 64-bit value
1832 mainHeader.diskGUID.data1 = (uint64_t) rand() * (uint64_t) rand();
1833 mainHeader.diskGUID.data2 = (uint64_t) rand() * (uint64_t) rand();
1834
1835 // Copy main header to backup header
1836 RebuildSecondHeader();
1837
1838 // Blank out the partitions array....
1839 BlankPartitions();
1840
1841 // Flag all CRCs as being OK....
1842 mainCrcOk = 1;
1843 secondCrcOk = 1;
1844 mainPartsCrcOk = 1;
1845 secondPartsCrcOk = 1;
1846
1847 return (goOn);
1848} // GPTData::ClearGPTData()
1849
srs5694247657a2009-11-26 18:36:12 -05001850// Set the location of the second GPT header data to the end of the disk.
1851// Used internally and called by the 'e' option on the recovery &
1852// transformation menu, to help users of RAID arrays who add disk space
1853// to their arrays.
1854void GPTData::MoveSecondHeaderToEnd() {
srs56948bb78762009-11-24 15:43:49 -05001855 mainHeader.backupLBA = secondHeader.currentLBA = diskSize - UINT64_C(1);
1856 mainHeader.lastUsableLBA = secondHeader.lastUsableLBA = diskSize - mainHeader.firstUsableLBA;
1857 secondHeader.partitionEntriesLBA = secondHeader.lastUsableLBA + UINT64_C(1);
1858} // GPTData::FixSecondHeaderLocation()
1859
srs5694e4ac11e2009-08-31 10:13:04 -04001860void GPTData::SetName(uint32_t partNum, char* theName) {
1861 if ((partNum >= 0) && (partNum < mainHeader.numParts))
1862 if (partitions[partNum].GetFirstLBA() > 0)
1863 partitions[partNum].SetName((unsigned char*) theName);
1864} // GPTData::SetName
1865
1866// Set the disk GUID to the specified value. Note that the header CRCs must
1867// be recomputed after calling this function.
1868void GPTData::SetDiskGUID(GUIDData newGUID) {
1869 mainHeader.diskGUID = newGUID;
1870 secondHeader.diskGUID = newGUID;
1871} // SetDiskGUID()
1872
1873// Set the unique GUID of the specified partition. Returns 1 on
1874// successful completion, 0 if there were problems (invalid
1875// partition number).
1876int GPTData::SetPartitionGUID(uint32_t pn, GUIDData theGUID) {
1877 int retval = 0;
1878
1879 if (pn < mainHeader.numParts) {
1880 if (partitions[pn].GetFirstLBA() != UINT64_C(0)) {
1881 partitions[pn].SetUniqueGUID(theGUID);
1882 retval = 1;
1883 } // if
1884 } // if
1885 return retval;
1886} // GPTData::SetPartitionGUID()
1887
1888/********************************************************
1889 * *
1890 * Functions that return data about GPT data structures *
1891 * (most of these are inline in gpt.h) *
1892 * *
1893 ********************************************************/
1894
1895// Find the low and high used partition numbers (numbered from 0).
1896// Return value is the number of partitions found. Note that the
1897// *low and *high values are both set to 0 when no partitions
1898// are found, as well as when a single partition in the first
1899// position exists. Thus, the return value is the only way to
1900// tell when no partitions exist.
1901int GPTData::GetPartRange(uint32_t *low, uint32_t *high) {
1902 uint32_t i;
1903 int numFound = 0;
1904
1905 *low = mainHeader.numParts + 1; // code for "not found"
1906 *high = 0;
1907 if (mainHeader.numParts > 0) { // only try if partition table exists...
1908 for (i = 0; i < mainHeader.numParts; i++) {
1909 if (partitions[i].GetFirstLBA() != UINT64_C(0)) { // it exists
1910 *high = i; // since we're counting up, set the high value
1911 // Set the low value only if it's not yet found...
1912 if (*low == (mainHeader.numParts + 1)) *low = i;
1913 numFound++;
1914 } // if
1915 } // for
1916 } // if
1917
1918 // Above will leave *low pointing to its "not found" value if no partitions
1919 // are defined, so reset to 0 if this is the case....
1920 if (*low == (mainHeader.numParts + 1))
1921 *low = 0;
1922 return numFound;
1923} // GPTData::GetPartRange()
1924
srs5694978041c2009-09-21 20:51:47 -04001925// Returns the number of defined partitions.
1926uint32_t GPTData::CountParts(void) {
1927 int i, counted = 0;
1928
1929 for (i = 0; i < mainHeader.numParts; i++) {
1930 if (partitions[i].GetFirstLBA() > 0)
1931 counted++;
1932 } // for
1933 return counted;
1934} // GPTData::CountParts()
1935
srs5694e4ac11e2009-08-31 10:13:04 -04001936/****************************************************
1937 * *
1938 * Functions that return data about disk free space *
1939 * *
1940 ****************************************************/
1941
1942// Find the first available block after the starting point; returns 0 if
1943// there are no available blocks left
1944uint64_t GPTData::FindFirstAvailable(uint64_t start) {
1945 uint64_t first;
1946 uint32_t i;
1947 int firstMoved = 0;
1948
1949 // Begin from the specified starting point or from the first usable
1950 // LBA, whichever is greater...
1951 if (start < mainHeader.firstUsableLBA)
1952 first = mainHeader.firstUsableLBA;
1953 else
1954 first = start;
1955
1956 // ...now search through all partitions; if first is within an
1957 // existing partition, move it to the next sector after that
1958 // partition and repeat. If first was moved, set firstMoved
1959 // flag; repeat until firstMoved is not set, so as to catch
1960 // cases where partitions are out of sequential order....
1961 do {
1962 firstMoved = 0;
1963 for (i = 0; i < mainHeader.numParts; i++) {
1964 if ((first >= partitions[i].GetFirstLBA()) &&
1965 (first <= partitions[i].GetLastLBA())) { // in existing part.
1966 first = partitions[i].GetLastLBA() + 1;
1967 firstMoved = 1;
1968 } // if
1969 } // for
1970 } while (firstMoved == 1);
1971 if (first > mainHeader.lastUsableLBA)
1972 first = 0;
1973 return (first);
1974} // GPTData::FindFirstAvailable()
1975
1976// Finds the first available sector in the largest block of unallocated
1977// space on the disk. Returns 0 if there are no available blocks left
1978uint64_t GPTData::FindFirstInLargest(void) {
srs5694e35eb1b2009-09-14 00:29:34 -04001979 uint64_t start, firstBlock, lastBlock, segmentSize, selectedSize = 0, selectedSegment = 0;
srs5694e4ac11e2009-08-31 10:13:04 -04001980
1981 start = 0;
1982 do {
1983 firstBlock = FindFirstAvailable(start);
1984 if (firstBlock != UINT32_C(0)) { // something's free...
1985 lastBlock = FindLastInFree(firstBlock);
1986 segmentSize = lastBlock - firstBlock + UINT32_C(1);
1987 if (segmentSize > selectedSize) {
1988 selectedSize = segmentSize;
1989 selectedSegment = firstBlock;
1990 } // if
1991 start = lastBlock + 1;
1992 } // if
1993 } while (firstBlock != 0);
1994 return selectedSegment;
1995} // GPTData::FindFirstInLargest()
1996
1997// Find the last available block on the disk at or after the start
1998// block. Returns 0 if there are no available partitions after
1999// (or including) start.
2000uint64_t GPTData::FindLastAvailable(uint64_t start) {
2001 uint64_t last;
2002 uint32_t i;
2003 int lastMoved = 0;
2004
2005 // Start by assuming the last usable LBA is available....
2006 last = mainHeader.lastUsableLBA;
2007
2008 // ...now, similar to algorithm in FindFirstAvailable(), search
2009 // through all partitions, moving last when it's in an existing
2010 // partition. Set the lastMoved flag so we repeat to catch cases
2011 // where partitions are out of logical order.
2012 do {
2013 lastMoved = 0;
2014 for (i = 0; i < mainHeader.numParts; i++) {
2015 if ((last >= partitions[i].GetFirstLBA()) &&
2016 (last <= partitions[i].GetLastLBA())) { // in existing part.
2017 last = partitions[i].GetFirstLBA() - 1;
2018 lastMoved = 1;
2019 } // if
2020 } // for
2021 } while (lastMoved == 1);
2022 if (last < mainHeader.firstUsableLBA)
2023 last = 0;
2024 return (last);
2025} // GPTData::FindLastAvailable()
2026
2027// Find the last available block in the free space pointed to by start.
2028uint64_t GPTData::FindLastInFree(uint64_t start) {
2029 uint64_t nearestStart;
2030 uint32_t i;
2031
2032 nearestStart = mainHeader.lastUsableLBA;
2033 for (i = 0; i < mainHeader.numParts; i++) {
2034 if ((nearestStart > partitions[i].GetFirstLBA()) &&
2035 (partitions[i].GetFirstLBA() > start)) {
2036 nearestStart = partitions[i].GetFirstLBA() - 1;
2037 } // if
2038 } // for
2039 return (nearestStart);
2040} // GPTData::FindLastInFree()
2041
2042// Finds the total number of free blocks, the number of segments in which
2043// they reside, and the size of the largest of those segments
2044uint64_t GPTData::FindFreeBlocks(int *numSegments, uint64_t *largestSegment) {
2045 uint64_t start = UINT64_C(0); // starting point for each search
2046 uint64_t totalFound = UINT64_C(0); // running total
2047 uint64_t firstBlock; // first block in a segment
2048 uint64_t lastBlock; // last block in a segment
2049 uint64_t segmentSize; // size of segment in blocks
2050 int num = 0;
2051
2052 *largestSegment = UINT64_C(0);
2053 do {
2054 firstBlock = FindFirstAvailable(start);
2055 if (firstBlock != UINT64_C(0)) { // something's free...
2056 lastBlock = FindLastInFree(firstBlock);
2057 segmentSize = lastBlock - firstBlock + UINT64_C(1);
2058 if (segmentSize > *largestSegment) {
2059 *largestSegment = segmentSize;
2060 } // if
2061 totalFound += segmentSize;
2062 num++;
2063 start = lastBlock + 1;
2064 } // if
2065 } while (firstBlock != 0);
2066 *numSegments = num;
2067 return totalFound;
2068} // GPTData::FindFreeBlocks()
2069
2070// Returns 1 if sector is unallocated, 0 if it's allocated to a partition
2071int GPTData::IsFree(uint64_t sector) {
2072 int isFree = 1;
2073 uint32_t i;
2074
2075 for (i = 0; i < mainHeader.numParts; i++) {
2076 if ((sector >= partitions[i].GetFirstLBA()) &&
2077 (sector <= partitions[i].GetLastLBA())) {
2078 isFree = 0;
2079 } // if
2080 } // for
srs5694e35eb1b2009-09-14 00:29:34 -04002081 if ((sector < mainHeader.firstUsableLBA) ||
srs5694e4ac11e2009-08-31 10:13:04 -04002082 (sector > mainHeader.lastUsableLBA)) {
2083 isFree = 0;
2084 } // if
2085 return (isFree);
2086} // GPTData::IsFree()
2087
2088/********************************
2089 * *
2090 * Endianness support functions *
2091 * *
2092 ********************************/
2093
srs56942a9f5da2009-08-26 00:48:01 -04002094void GPTData::ReverseHeaderBytes(struct GPTHeader* header) {
srs5694221e0872009-08-29 15:00:31 -04002095 ReverseBytes(&header->signature, 8);
2096 ReverseBytes(&header->revision, 4);
2097 ReverseBytes(&header->headerSize, 4);
2098 ReverseBytes(&header->headerCRC, 4);
2099 ReverseBytes(&header->reserved, 4);
2100 ReverseBytes(&header->currentLBA, 8);
2101 ReverseBytes(&header->backupLBA, 8);
2102 ReverseBytes(&header->firstUsableLBA, 8);
2103 ReverseBytes(&header->lastUsableLBA, 8);
2104 ReverseBytes(&header->partitionEntriesLBA, 8);
2105 ReverseBytes(&header->numParts, 4);
2106 ReverseBytes(&header->sizeOfPartitionEntries, 4);
2107 ReverseBytes(&header->partitionEntriesCRC, 4);
2108 ReverseBytes(&header->reserved2, GPT_RESERVED);
2109 ReverseBytes(&header->diskGUID.data1, 8);
2110 ReverseBytes(&header->diskGUID.data2, 8);
srs56942a9f5da2009-08-26 00:48:01 -04002111} // GPTData::ReverseHeaderBytes()
2112
2113// IMPORTANT NOTE: This function requires non-reversed mainHeader
2114// structure!
2115void GPTData::ReversePartitionBytes() {
2116 uint32_t i;
2117
2118 // Check GPT signature on big-endian systems; this will mismatch
2119 // if the function is called out of order. Unfortunately, it'll also
2120 // mismatch if there's data corruption.
2121 if ((mainHeader.signature != GPT_SIGNATURE) && (IsLittleEndian() == 0)) {
2122 printf("GPT signature mismatch in GPTData::ReversePartitionBytes(). This indicates\n"
srs5694e4ac11e2009-08-31 10:13:04 -04002123 "data corruption or a misplaced call to this function.\n");
srs56942a9f5da2009-08-26 00:48:01 -04002124 } // if signature mismatch....
2125 for (i = 0; i < mainHeader.numParts; i++) {
srs5694221e0872009-08-29 15:00:31 -04002126 partitions[i].ReversePartBytes();
srs56942a9f5da2009-08-26 00:48:01 -04002127 } // for
2128} // GPTData::ReversePartitionBytes()
2129
2130/******************************************
2131 * *
2132 * Additional non-class support functions *
2133 * *
2134 ******************************************/
2135
srs5694e7b4ff92009-08-18 13:16:10 -04002136// Check to be sure that data type sizes are correct. The basic types (uint*_t) should
2137// never fail these tests, but the struct types may fail depending on compile options.
2138// Specifically, the -fpack-struct option to gcc may be required to ensure proper structure
2139// sizes.
2140int SizesOK(void) {
2141 int allOK = 1;
srs5694e7b4ff92009-08-18 13:16:10 -04002142
2143 if (sizeof(uint8_t) != 1) {
2144 fprintf(stderr, "uint8_t is %d bytes, should be 1 byte; aborting!\n", sizeof(uint8_t));
2145 allOK = 0;
2146 } // if
2147 if (sizeof(uint16_t) != 2) {
2148 fprintf(stderr, "uint16_t is %d bytes, should be 2 bytes; aborting!\n", sizeof(uint16_t));
2149 allOK = 0;
2150 } // if
2151 if (sizeof(uint32_t) != 4) {
2152 fprintf(stderr, "uint32_t is %d bytes, should be 4 bytes; aborting!\n", sizeof(uint32_t));
2153 allOK = 0;
2154 } // if
2155 if (sizeof(uint64_t) != 8) {
2156 fprintf(stderr, "uint64_t is %d bytes, should be 8 bytes; aborting!\n", sizeof(uint64_t));
2157 allOK = 0;
2158 } // if
2159 if (sizeof(struct MBRRecord) != 16) {
srs5694221e0872009-08-29 15:00:31 -04002160 fprintf(stderr, "MBRRecord is %d bytes, should be 16 bytes; aborting!\n", sizeof(MBRRecord));
srs5694e7b4ff92009-08-18 13:16:10 -04002161 allOK = 0;
2162 } // if
srs5694978041c2009-09-21 20:51:47 -04002163 if (sizeof(struct TempMBR) != 512) {
2164 fprintf(stderr, "TempMBR is %d bytes, should be 512 bytes; aborting!\n", sizeof(TempMBR));
srs5694e7b4ff92009-08-18 13:16:10 -04002165 allOK = 0;
2166 } // if
2167 if (sizeof(struct GPTHeader) != 512) {
srs5694221e0872009-08-29 15:00:31 -04002168 fprintf(stderr, "GPTHeader is %d bytes, should be 512 bytes; aborting!\n", sizeof(GPTHeader));
srs5694e7b4ff92009-08-18 13:16:10 -04002169 allOK = 0;
2170 } // if
srs5694221e0872009-08-29 15:00:31 -04002171 if (sizeof(GPTPart) != 128) {
2172 fprintf(stderr, "GPTPart is %d bytes, should be 128 bytes; aborting!\n", sizeof(GPTPart));
2173 allOK = 0;
2174 } // if
2175// Determine endianness; set allOK = 0 if running on big-endian hardware
srs56942a9f5da2009-08-26 00:48:01 -04002176 if (IsLittleEndian() == 0) {
2177 fprintf(stderr, "\aRunning on big-endian hardware. Big-endian support is new and poorly"
srs5694e4ac11e2009-08-31 10:13:04 -04002178 " tested!\nBeware!\n");
srs56942a9f5da2009-08-26 00:48:01 -04002179 // allOK = 0;
srs5694e7b4ff92009-08-18 13:16:10 -04002180 } // if
2181 return (allOK);
2182} // SizesOK()
srs5694e4ac11e2009-08-31 10:13:04 -04002183