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