blob: c34c8623ef50647e1df73523007267e3cc030ddf [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;
srs56941d1448a2009-12-31 21:20:19 -050049 sectorAlignment = 8; // Align partitions on 4096-byte boundaries by default
srs5694e7b4ff92009-08-18 13:16:10 -040050 srand((unsigned int) time(NULL));
51 SetGPTSize(NUM_GPT_ENTRIES);
52} // GPTData default constructor
53
54// The following constructor loads GPT data from a device file
55GPTData::GPTData(char* filename) {
56 blockSize = SECTOR_SIZE; // set a default
57 diskSize = 0;
58 partitions = NULL;
59 state = gpt_invalid;
60 strcpy(device, "");
61 mainCrcOk = 0;
62 secondCrcOk = 0;
63 mainPartsCrcOk = 0;
64 secondPartsCrcOk = 0;
srs5694221e0872009-08-29 15:00:31 -040065 apmFound = 0;
66 bsdFound = 0;
srs56941d1448a2009-12-31 21:20:19 -050067 sectorAlignment = 8; // Align partitions on 4096-byte boundaries by default
srs5694e7b4ff92009-08-18 13:16:10 -040068 srand((unsigned int) time(NULL));
69 LoadPartitions(filename);
70} // GPTData(char* filename) constructor
71
srs5694e4ac11e2009-08-31 10:13:04 -040072// Destructor
srs5694e7b4ff92009-08-18 13:16:10 -040073GPTData::~GPTData(void) {
74 free(partitions);
75} // GPTData destructor
76
srs5694e4ac11e2009-08-31 10:13:04 -040077/*********************************************************************
78 * *
79 * Begin functions that verify data, or that adjust the verification *
80 * information (compute CRCs, rebuild headers) *
81 * *
82 *********************************************************************/
srs5694e7b4ff92009-08-18 13:16:10 -040083
srs5694e4ac11e2009-08-31 10:13:04 -040084// Perform detailed verification, reporting on any problems found, but
85// do *NOT* recover from these problems. Returns the total number of
86// problems identified.
87int GPTData::Verify(void) {
srs56941d1448a2009-12-31 21:20:19 -050088 int problems = 0, numSegments, i;
89 uint64_t totalFree, largestSegment, firstSector;
srs5694e4ac11e2009-08-31 10:13:04 -040090 char tempStr[255], siTotal[255], siLargest[255];
91
92 // First, check for CRC errors in the GPT data....
93 if (!mainCrcOk) {
94 problems++;
95 printf("\nProblem: The CRC for the main GPT header is invalid. The main GPT header may\n"
96 "be corrupt. Consider loading the backup GPT header to rebuild the main GPT\n"
97 "header\n");
98 } // if
99 if (!mainPartsCrcOk) {
100 problems++;
101 printf("\nProblem: The CRC for the main partition table is invalid. This table may be\n"
102 "corrupt. Consider loading the backup partition table.\n");
103 } // if
104 if (!secondCrcOk) {
105 problems++;
106 printf("\nProblem: The CRC for the backup GPT header is invalid. The backup GPT header\n"
107 "may be corrupt. Consider using the main GPT header to rebuild the backup GPT\n"
108 "header.\n");
109 } // if
110 if (!secondPartsCrcOk) {
111 problems++;
112 printf("\nCaution: The CRC for the backup partition table is invalid. This table may\n"
113 "be corrupt. This program will automatically create a new backup partition\n"
114 "table when you save your partitions.\n");
srs5694e7b4ff92009-08-18 13:16:10 -0400115 } // if
116
srs5694978041c2009-09-21 20:51:47 -0400117 // Now check that the main and backup headers both point to themselves....
118 if (mainHeader.currentLBA != 1) {
119 problems++;
120 printf("\nProblem: The main header's self-pointer doesn't point to itself. This problem\n"
121 "is being automatically corrected, but it may be a symptom of more serious\n"
122 "problems. Think carefully before saving changes with 'w' or using this disk.\n");
123 mainHeader.currentLBA = 1;
124 } // if
125 if (secondHeader.currentLBA != (diskSize - UINT64_C(1))) {
126 problems++;
srs56943f2fe992009-11-24 18:28:18 -0500127 printf("\nProblem: The secondary header's self-pointer indicates that it doesn't reside\n"
128 "at the end of the disk. If you've added a disk to a RAID array, use the 'e'\n"
srs5694247657a2009-11-26 18:36:12 -0500129 "option on the experts' menu to adjust the secondary header's and partition\n"
130 "table's locations.\n");
srs5694978041c2009-09-21 20:51:47 -0400131 } // if
132
133 // Now check that critical main and backup GPT entries match each other
srs5694e4ac11e2009-08-31 10:13:04 -0400134 if (mainHeader.currentLBA != secondHeader.backupLBA) {
135 problems++;
136 printf("\nProblem: main GPT header's current LBA pointer (%llu) doesn't\n"
137 "match the backup GPT header's LBA pointer(%llu)\n",
138 (unsigned long long) mainHeader.currentLBA,
139 (unsigned long long) secondHeader.backupLBA);
140 } // if
141 if (mainHeader.backupLBA != secondHeader.currentLBA) {
142 problems++;
143 printf("\nProblem: main GPT header's backup LBA pointer (%llu) doesn't\n"
144 "match the backup GPT header's current LBA pointer (%llu)\n",
145 (unsigned long long) mainHeader.backupLBA,
146 (unsigned long long) secondHeader.currentLBA);
147 } // if
148 if (mainHeader.firstUsableLBA != secondHeader.firstUsableLBA) {
149 problems++;
150 printf("\nProblem: main GPT header's first usable LBA pointer (%llu) doesn't\n"
151 "match the backup GPT header's first usable LBA pointer (%llu)\n",
152 (unsigned long long) mainHeader.firstUsableLBA,
153 (unsigned long long) secondHeader.firstUsableLBA);
154 } // if
155 if (mainHeader.lastUsableLBA != secondHeader.lastUsableLBA) {
156 problems++;
157 printf("\nProblem: main GPT header's last usable LBA pointer (%llu) doesn't\n"
158 "match the backup GPT header's last usable LBA pointer (%llu)\n",
159 (unsigned long long) mainHeader.lastUsableLBA,
160 (unsigned long long) secondHeader.lastUsableLBA);
161 } // if
162 if ((mainHeader.diskGUID.data1 != secondHeader.diskGUID.data1) ||
163 (mainHeader.diskGUID.data2 != secondHeader.diskGUID.data2)) {
164 problems++;
165 printf("\nProblem: main header's disk GUID (%s) doesn't\n",
166 GUIDToStr(mainHeader.diskGUID, tempStr));
167 printf("match the backup GPT header's disk GUID (%s)\n",
168 GUIDToStr(secondHeader.diskGUID, tempStr));
169 } // if
170 if (mainHeader.numParts != secondHeader.numParts) {
171 problems++;
172 printf("\nProblem: main GPT header's number of partitions (%lu) doesn't\n"
173 "match the backup GPT header's number of partitions (%lu)\n",
174 (unsigned long) mainHeader.numParts,
175 (unsigned long) secondHeader.numParts);
176 } // if
177 if (mainHeader.sizeOfPartitionEntries != secondHeader.sizeOfPartitionEntries) {
178 problems++;
179 printf("\nProblem: main GPT header's size of partition entries (%lu) doesn't\n"
180 "match the backup GPT header's size of partition entries (%lu)\n",
181 (unsigned long) mainHeader.sizeOfPartitionEntries,
182 (unsigned long) secondHeader.sizeOfPartitionEntries);
183 } // if
184
185 // Now check for a few other miscellaneous problems...
186 // Check that the disk size will hold the data...
187 if (mainHeader.backupLBA > diskSize) {
188 problems++;
189 printf("\nProblem: Disk is too small to hold all the data!\n");
190 printf("(Disk size is %llu sectors, needs to be %llu sectors.)\n",
191 (unsigned long long) diskSize,
192 (unsigned long long) mainHeader.backupLBA);
193 } // if
194
195 // Check for overlapping partitions....
196 problems += FindOverlaps();
197
198 // Check for mismatched MBR and GPT partitions...
199 problems += FindHybridMismatches();
200
201 // Verify that partitions don't run into GPT data areas....
202 problems += CheckGPTSize();
203
srs56941d1448a2009-12-31 21:20:19 -0500204 // Check that partitions are aligned on proper boundaries (for WD Advanced
205 // Format and similar disks)....
206 for (i = 0; i < mainHeader.numParts; i++) {
207 if ((partitions[i].GetFirstLBA() % sectorAlignment) != 0) {
208 printf("\nCaution: Partition %d doesn't begin on a %d-sector boundary. This may\n"
209 "result in degraded performance on some modern (2010 and later) hard disks.\n",
210 i + 1, sectorAlignment);
211 } // if
212 } // for
213
srs5694e4ac11e2009-08-31 10:13:04 -0400214 // Now compute available space, but only if no problems found, since
215 // problems could affect the results
216 if (problems == 0) {
217 totalFree = FindFreeBlocks(&numSegments, &largestSegment);
218 BytesToSI(totalFree * (uint64_t) blockSize, siTotal);
219 BytesToSI(largestSegment * (uint64_t) blockSize, siLargest);
220 printf("No problems found. %llu free sectors (%s) available in %u\n"
221 "segments, the largest of which is %llu sectors (%s) in size\n",
222 (unsigned long long) totalFree,
223 siTotal, numSegments, (unsigned long long) largestSegment,
224 siLargest);
225 } else {
226 printf("\nIdentified %d problems!\n", problems);
srs5694e7b4ff92009-08-18 13:16:10 -0400227 } // if/else
srs5694e4ac11e2009-08-31 10:13:04 -0400228
229 return (problems);
230} // GPTData::Verify()
srs5694e7b4ff92009-08-18 13:16:10 -0400231
232// Checks to see if the GPT tables overrun existing partitions; if they
srs5694221e0872009-08-29 15:00:31 -0400233// do, issues a warning but takes no action. Returns number of problems
234// detected (0 if OK, 1 to 2 if problems).
srs5694e7b4ff92009-08-18 13:16:10 -0400235int GPTData::CheckGPTSize(void) {
236 uint64_t overlap, firstUsedBlock, lastUsedBlock;
237 uint32_t i;
srs5694221e0872009-08-29 15:00:31 -0400238 int numProbs = 0;
srs5694e7b4ff92009-08-18 13:16:10 -0400239
240 // first, locate the first & last used blocks
241 firstUsedBlock = UINT64_MAX;
242 lastUsedBlock = 0;
243 for (i = 0; i < mainHeader.numParts; i++) {
srs5694221e0872009-08-29 15:00:31 -0400244 if ((partitions[i].GetFirstLBA() < firstUsedBlock) &&
srs5694e4ac11e2009-08-31 10:13:04 -0400245 (partitions[i].GetFirstLBA() != 0))
srs5694221e0872009-08-29 15:00:31 -0400246 firstUsedBlock = partitions[i].GetFirstLBA();
247 if (partitions[i].GetLastLBA() > lastUsedBlock)
248 lastUsedBlock = partitions[i].GetLastLBA();
srs5694e7b4ff92009-08-18 13:16:10 -0400249 } // for
250
251 // If the disk size is 0 (the default), then it means that various
252 // variables aren't yet set, so the below tests will be useless;
253 // therefore we should skip everything
254 if (diskSize != 0) {
255 if (mainHeader.firstUsableLBA > firstUsedBlock) {
256 overlap = mainHeader.firstUsableLBA - firstUsedBlock;
srs5694221e0872009-08-29 15:00:31 -0400257 printf("Warning! Main partition table overlaps the first partition by %lu blocks!\n",
258 (unsigned long) overlap);
259 if (firstUsedBlock > 2) {
260 printf("Try reducing the partition table size by %lu entries.\n",
261 (unsigned long) (overlap * 4));
262 printf("(Use the 's' item on the experts' menu.)\n");
263 } else {
264 printf("You will need to delete this partition or resize it in another utility.\n");
265 } // if/else
266 numProbs++;
srs5694e7b4ff92009-08-18 13:16:10 -0400267 } // Problem at start of disk
268 if (mainHeader.lastUsableLBA < lastUsedBlock) {
269 overlap = lastUsedBlock - mainHeader.lastUsableLBA;
srs5694221e0872009-08-29 15:00:31 -0400270 printf("Warning! Secondary partition table overlaps the last partition by %lu blocks\n",
271 (unsigned long) overlap);
272 if (lastUsedBlock > (diskSize - 2)) {
273 printf("You will need to delete this partition or resize it in another utility.\n");
274 } else {
275 printf("Try reducing the partition table size by %lu entries.\n",
276 (unsigned long) (overlap * 4));
277 printf("(Use the 's' item on the experts' menu.)\n");
278 } // if/else
279 numProbs++;
srs5694e7b4ff92009-08-18 13:16:10 -0400280 } // Problem at end of disk
281 } // if (diskSize != 0)
srs5694221e0872009-08-29 15:00:31 -0400282 return numProbs;
srs5694e7b4ff92009-08-18 13:16:10 -0400283} // GPTData::CheckGPTSize()
284
srs5694e7b4ff92009-08-18 13:16:10 -0400285// Check the validity of the GPT header. Returns 1 if the main header
286// is valid, 2 if the backup header is valid, 3 if both are valid, and
287// 0 if neither is valid. Note that this function just checks the GPT
288// signature and revision numbers, not CRCs or other data.
289int GPTData::CheckHeaderValidity(void) {
290 int valid = 3;
291
292 if (mainHeader.signature != GPT_SIGNATURE) {
293 valid -= 1;
srs5694221e0872009-08-29 15:00:31 -0400294// printf("Main GPT signature invalid; read 0x%016llX, should be\n0x%016llX\n",
295// (unsigned long long) mainHeader.signature, (unsigned long long) GPT_SIGNATURE);
srs5694e7b4ff92009-08-18 13:16:10 -0400296 } else if ((mainHeader.revision != 0x00010000) && valid) {
297 valid -= 1;
298 printf("Unsupported GPT version in main header; read 0x%08lX, should be\n0x%08lX\n",
299 (unsigned long) mainHeader.revision, UINT32_C(0x00010000));
300 } // if/else/if
301
302 if (secondHeader.signature != GPT_SIGNATURE) {
303 valid -= 2;
srs5694221e0872009-08-29 15:00:31 -0400304// printf("Secondary GPT signature invalid; read 0x%016llX, should be\n0x%016llX\n",
305// (unsigned long long) secondHeader.signature, (unsigned long long) GPT_SIGNATURE);
srs5694e7b4ff92009-08-18 13:16:10 -0400306 } else if ((secondHeader.revision != 0x00010000) && valid) {
307 valid -= 2;
308 printf("Unsupported GPT version in backup header; read 0x%08lX, should be\n0x%08lX\n",
309 (unsigned long) mainHeader.revision, UINT32_C(0x00010000));
310 } // if/else/if
311
srs56942a9f5da2009-08-26 00:48:01 -0400312 // If MBR bad, check for an Apple disk signature
srs5694e35eb1b2009-09-14 00:29:34 -0400313 if ((protectiveMBR.GetValidity() == invalid) &&
srs5694e4ac11e2009-08-31 10:13:04 -0400314 (((mainHeader.signature << 32) == APM_SIGNATURE1) ||
srs56942a9f5da2009-08-26 00:48:01 -0400315 (mainHeader.signature << 32) == APM_SIGNATURE2)) {
srs5694221e0872009-08-29 15:00:31 -0400316 apmFound = 1; // Will display warning message later
srs56943f2fe992009-11-24 18:28:18 -0500317 } // if
srs56942a9f5da2009-08-26 00:48:01 -0400318
srs5694e4ac11e2009-08-31 10:13:04 -0400319 return valid;
srs5694e7b4ff92009-08-18 13:16:10 -0400320} // GPTData::CheckHeaderValidity()
321
322// Check the header CRC to see if it's OK...
srs56942a9f5da2009-08-26 00:48:01 -0400323// Note: Must be called BEFORE byte-order reversal on big-endian
324// systems!
srs5694e7b4ff92009-08-18 13:16:10 -0400325int GPTData::CheckHeaderCRC(struct GPTHeader* header) {
srs5694978041c2009-09-21 20:51:47 -0400326 uint32_t oldCRC, newCRC, hSize;
srs5694e7b4ff92009-08-18 13:16:10 -0400327
srs56942a9f5da2009-08-26 00:48:01 -0400328 // Back up old header CRC and then blank it, since it must be 0 for
srs5694e7b4ff92009-08-18 13:16:10 -0400329 // computation to be valid
330 oldCRC = header->headerCRC;
331 header->headerCRC = UINT32_C(0);
srs5694978041c2009-09-21 20:51:47 -0400332 hSize = header->headerSize;
333
334 // If big-endian system, reverse byte order
335 if (IsLittleEndian() == 0) {
336 ReverseBytes(&oldCRC, 4);
337 } // if
srs5694e7b4ff92009-08-18 13:16:10 -0400338
339 // Initialize CRC functions...
340 chksum_crc32gentab();
341
342 // Compute CRC, restore original, and return result of comparison
343 newCRC = chksum_crc32((unsigned char*) header, HEADER_SIZE);
srs5694978041c2009-09-21 20:51:47 -0400344 header->headerCRC = oldCRC;
srs5694e7b4ff92009-08-18 13:16:10 -0400345 return (oldCRC == newCRC);
346} // GPTData::CheckHeaderCRC()
347
srs56942a9f5da2009-08-26 00:48:01 -0400348// Recompute all the CRCs. Must be called before saving (but after reversing
349// byte order on big-endian systems) if any changes have been made.
srs5694e7b4ff92009-08-18 13:16:10 -0400350void GPTData::RecomputeCRCs(void) {
srs5694978041c2009-09-21 20:51:47 -0400351 uint32_t crc, hSize, trueNumParts;
srs56942a9f5da2009-08-26 00:48:01 -0400352 int littleEndian = 1;
srs5694e7b4ff92009-08-18 13:16:10 -0400353
354 // Initialize CRC functions...
355 chksum_crc32gentab();
356
srs5694978041c2009-09-21 20:51:47 -0400357 hSize = mainHeader.headerSize;
srs56942a9f5da2009-08-26 00:48:01 -0400358 littleEndian = IsLittleEndian();
359
srs5694e7b4ff92009-08-18 13:16:10 -0400360 // Compute CRC of partition tables & store in main and secondary headers
srs56942a9f5da2009-08-26 00:48:01 -0400361 trueNumParts = mainHeader.numParts;
362 if (littleEndian == 0)
srs5694221e0872009-08-29 15:00:31 -0400363 ReverseBytes(&trueNumParts, 4); // unreverse this key piece of data....
srs56942a9f5da2009-08-26 00:48:01 -0400364 crc = chksum_crc32((unsigned char*) partitions, trueNumParts * GPT_SIZE);
srs5694e7b4ff92009-08-18 13:16:10 -0400365 mainHeader.partitionEntriesCRC = crc;
366 secondHeader.partitionEntriesCRC = crc;
srs56942a9f5da2009-08-26 00:48:01 -0400367 if (littleEndian == 0) {
srs5694221e0872009-08-29 15:00:31 -0400368 ReverseBytes(&mainHeader.partitionEntriesCRC, 4);
369 ReverseBytes(&secondHeader.partitionEntriesCRC, 4);
srs56942a9f5da2009-08-26 00:48:01 -0400370 } // if
srs5694e7b4ff92009-08-18 13:16:10 -0400371
372 // Zero out GPT tables' own CRCs (required for correct computation)
373 mainHeader.headerCRC = 0;
374 secondHeader.headerCRC = 0;
375
376 // Compute & store CRCs of main & secondary headers...
srs5694978041c2009-09-21 20:51:47 -0400377 crc = chksum_crc32((unsigned char*) &mainHeader, hSize);
srs56942a9f5da2009-08-26 00:48:01 -0400378 if (littleEndian == 0)
srs5694221e0872009-08-29 15:00:31 -0400379 ReverseBytes(&crc, 4);
srs5694e7b4ff92009-08-18 13:16:10 -0400380 mainHeader.headerCRC = crc;
srs5694978041c2009-09-21 20:51:47 -0400381 crc = chksum_crc32((unsigned char*) &secondHeader, hSize);
srs56942a9f5da2009-08-26 00:48:01 -0400382 if (littleEndian == 0)
srs5694221e0872009-08-29 15:00:31 -0400383 ReverseBytes(&crc, 4);
srs5694e7b4ff92009-08-18 13:16:10 -0400384 secondHeader.headerCRC = crc;
385} // GPTData::RecomputeCRCs()
386
srs5694e7b4ff92009-08-18 13:16:10 -0400387// Rebuild the main GPT header, using the secondary header as a model.
388// Typically called when the main header has been found to be corrupt.
389void GPTData::RebuildMainHeader(void) {
390 int i;
391
392 mainHeader.signature = GPT_SIGNATURE;
393 mainHeader.revision = secondHeader.revision;
srs5694978041c2009-09-21 20:51:47 -0400394 mainHeader.headerSize = secondHeader.headerSize;
srs5694e7b4ff92009-08-18 13:16:10 -0400395 mainHeader.headerCRC = UINT32_C(0);
396 mainHeader.reserved = secondHeader.reserved;
397 mainHeader.currentLBA = secondHeader.backupLBA;
398 mainHeader.backupLBA = secondHeader.currentLBA;
399 mainHeader.firstUsableLBA = secondHeader.firstUsableLBA;
400 mainHeader.lastUsableLBA = secondHeader.lastUsableLBA;
401 mainHeader.diskGUID.data1 = secondHeader.diskGUID.data1;
402 mainHeader.diskGUID.data2 = secondHeader.diskGUID.data2;
403 mainHeader.partitionEntriesLBA = UINT64_C(2);
404 mainHeader.numParts = secondHeader.numParts;
405 mainHeader.sizeOfPartitionEntries = secondHeader.sizeOfPartitionEntries;
406 mainHeader.partitionEntriesCRC = secondHeader.partitionEntriesCRC;
407 for (i = 0 ; i < GPT_RESERVED; i++)
408 mainHeader.reserved2[i] = secondHeader.reserved2[i];
409} // GPTData::RebuildMainHeader()
410
411// Rebuild the secondary GPT header, using the main header as a model.
412void GPTData::RebuildSecondHeader(void) {
413 int i;
414
415 secondHeader.signature = GPT_SIGNATURE;
416 secondHeader.revision = mainHeader.revision;
srs5694978041c2009-09-21 20:51:47 -0400417 secondHeader.headerSize = mainHeader.headerSize;
srs5694e7b4ff92009-08-18 13:16:10 -0400418 secondHeader.headerCRC = UINT32_C(0);
419 secondHeader.reserved = mainHeader.reserved;
420 secondHeader.currentLBA = mainHeader.backupLBA;
421 secondHeader.backupLBA = mainHeader.currentLBA;
422 secondHeader.firstUsableLBA = mainHeader.firstUsableLBA;
423 secondHeader.lastUsableLBA = mainHeader.lastUsableLBA;
424 secondHeader.diskGUID.data1 = mainHeader.diskGUID.data1;
425 secondHeader.diskGUID.data2 = mainHeader.diskGUID.data2;
426 secondHeader.partitionEntriesLBA = secondHeader.lastUsableLBA + UINT64_C(1);
427 secondHeader.numParts = mainHeader.numParts;
428 secondHeader.sizeOfPartitionEntries = mainHeader.sizeOfPartitionEntries;
429 secondHeader.partitionEntriesCRC = mainHeader.partitionEntriesCRC;
430 for (i = 0 ; i < GPT_RESERVED; i++)
431 secondHeader.reserved2[i] = mainHeader.reserved2[i];
srs5694e4ac11e2009-08-31 10:13:04 -0400432} // GPTData::RebuildSecondHeader()
433
434// Search for hybrid MBR entries that have no corresponding GPT partition.
435// Returns number of such mismatches found
436int GPTData::FindHybridMismatches(void) {
437 int i, j, found, numFound = 0;
438 uint64_t mbrFirst, mbrLast;
439
440 for (i = 0; i < 4; i++) {
441 if ((protectiveMBR.GetType(i) != 0xEE) && (protectiveMBR.GetType(i) != 0x00)) {
442 j = 0;
443 found = 0;
444 do {
445 mbrFirst = (uint64_t) protectiveMBR.GetFirstSector(i);
446 mbrLast = mbrFirst + (uint64_t) protectiveMBR.GetLength(i) - UINT64_C(1);
447 if ((partitions[j].GetFirstLBA() == mbrFirst) &&
448 (partitions[j].GetLastLBA() == mbrLast))
449 found = 1;
450 j++;
451 } while ((!found) && (j < mainHeader.numParts));
452 if (!found) {
453 numFound++;
454 printf("\nWarning! Mismatched GPT and MBR partitions! MBR partition "
455 "%d, of type 0x%02X,\nhas no corresponding GPT partition! "
456 "You may continue, but this condition\nmight cause data loss"
457 " in the future!\a\n", i + 1, protectiveMBR.GetType(i));
458 } // if
459 } // if
460 } // for
461 return numFound;
462} // GPTData::FindHybridMismatches
463
464// Find overlapping partitions and warn user about them. Returns number of
465// overlapping partitions.
466int GPTData::FindOverlaps(void) {
467 int i, j, problems = 0;
468
469 for (i = 1; i < mainHeader.numParts; i++) {
470 for (j = 0; j < i; j++) {
471 if (partitions[i].DoTheyOverlap(&partitions[j])) {
472 problems++;
473 printf("\nProblem: partitions %d and %d overlap:\n", i + 1, j + 1);
474 printf(" Partition %d: %llu to %llu\n", i,
475 (unsigned long long) partitions[i].GetFirstLBA(),
476 (unsigned long long) partitions[i].GetLastLBA());
477 printf(" Partition %d: %llu to %llu\n", j,
478 (unsigned long long) partitions[j].GetFirstLBA(),
479 (unsigned long long) partitions[j].GetLastLBA());
480 } // if
481 } // for j...
482 } // for i...
483 return problems;
484} // GPTData::FindOverlaps()
485
486/******************************************************************
487 * *
488 * Begin functions that load data from disk or save data to disk. *
489 * *
490 ******************************************************************/
491
492// Scan for partition data. This function loads the MBR data (regular MBR or
493// protective MBR) and loads BSD disklabel data (which is probably invalid).
494// It also looks for APM data, forces a load of GPT data, and summarizes
495// the results.
496void GPTData::PartitionScan(int fd) {
497 BSDData bsdDisklabel;
498// int bsdFound;
499
500 printf("Partition table scan:\n");
501
502 // Read the MBR & check for BSD disklabel
503 protectiveMBR.ReadMBRData(fd);
504 protectiveMBR.ShowState();
505 bsdDisklabel.ReadBSDData(fd, 0, diskSize - 1);
506 bsdFound = bsdDisklabel.ShowState();
507// bsdDisklabel.DisplayBSDData();
508
509 // Load the GPT data, whether or not it's valid
510 ForceLoadGPTData(fd);
511 ShowAPMState(); // Show whether there's an Apple Partition Map present
512 ShowGPTState(); // Show GPT status
513 printf("\n");
514
515 if (apmFound) {
516 printf("\n*******************************************************************\n");
517 printf("This disk appears to contain an Apple-format (APM) partition table!\n");
518 printf("It will be destroyed if you continue!\n");
519 printf("*******************************************************************\n\n\a");
520 } // if
srs5694e4ac11e2009-08-31 10:13:04 -0400521} // GPTData::PartitionScan()
522
523// Read GPT data from a disk.
524int GPTData::LoadPartitions(char* deviceFilename) {
525 int fd, err;
526 int allOK = 1, i;
527 uint64_t firstBlock, lastBlock;
528 BSDData bsdDisklabel;
529
530 // First, do a test to see if writing will be possible later....
531 fd = OpenForWrite(deviceFilename);
532 if (fd == -1)
533 printf("\aNOTE: Write test failed with error number %d. It will be "
534 "impossible to save\nchanges to this disk's partition table!\n\n",
535 errno);
536 close(fd);
537
538 if ((fd = open(deviceFilename, O_RDONLY)) != -1) {
539 // store disk information....
540 diskSize = disksize(fd, &err);
541 blockSize = (uint32_t) GetBlockSize(fd);
542 strcpy(device, deviceFilename);
543 PartitionScan(fd); // Check for partition types & print summary
544
545 switch (UseWhichPartitions()) {
546 case use_mbr:
547 XFormPartitions();
548 break;
549 case use_bsd:
550 bsdDisklabel.ReadBSDData(fd, 0, diskSize - 1);
551// bsdDisklabel.DisplayBSDData();
552 ClearGPTData();
553 protectiveMBR.MakeProtectiveMBR(1); // clear boot area (option 1)
554 XFormDisklabel(&bsdDisklabel, 0);
555 break;
556 case use_gpt:
557 break;
558 case use_new:
559 ClearGPTData();
560 protectiveMBR.MakeProtectiveMBR();
561 break;
562 } // switch
563
564 // Now find the first and last sectors used by partitions...
565 if (allOK) {
566 firstBlock = mainHeader.backupLBA; // start high
567 lastBlock = 0; // start low
568 for (i = 0; i < mainHeader.numParts; i++) {
569 if ((partitions[i].GetFirstLBA() < firstBlock) &&
570 (partitions[i].GetFirstLBA() > 0))
571 firstBlock = partitions[i].GetFirstLBA();
572 if (partitions[i].GetLastLBA() > lastBlock)
573 lastBlock = partitions[i].GetLastLBA();
574 } // for
575 } // if
576 CheckGPTSize();
577 } else {
578 allOK = 0;
579 fprintf(stderr, "Problem opening %s for reading! Error is %d\n",
580 deviceFilename, errno);
581 if (errno == EACCES) { // User is probably not running as root
582 fprintf(stderr, "You must run this program as root or use sudo!\n");
583 } // if
584 } // if/else
585 return (allOK);
586} // GPTData::LoadPartitions()
587
588// Loads the GPT, as much as possible. Returns 1 if this seems to have
589// succeeded, 0 if there are obvious problems....
590int GPTData::ForceLoadGPTData(int fd) {
591 int allOK = 1, validHeaders;
592 off_t seekTo;
593 char* storage;
594 uint32_t newCRC, sizeOfParts;
595
596 // Seek to and read the main GPT header
597 lseek64(fd, 512, SEEK_SET);
598 read(fd, &mainHeader, 512); // read main GPT header
599 mainCrcOk = CheckHeaderCRC(&mainHeader);
600 if (IsLittleEndian() == 0) // big-endian system; adjust header byte order....
601 ReverseHeaderBytes(&mainHeader);
602
srs56943f2fe992009-11-24 18:28:18 -0500603 // Load backup header, check its CRC, and store the results of the
604 // check for future reference. Load backup header using pointer in main
605 // header if possible; but if main header has a CRC error, or if it
606 // points to beyond the end of the disk, load the last sector of the
607 // disk instead.
608 if (mainCrcOk) {
609 if (mainHeader.backupLBA < diskSize) {
610 seekTo = mainHeader.backupLBA * blockSize;
611 } else {
612 seekTo = (diskSize * blockSize) - UINT64_C(512);
613 printf("Warning! Disk size is smaller than the main header indicates! Loading\n"
614 "secondary header from the last sector of the disk! You should use 'v' to\n"
615 "verify disk integrity, and perhaps options on the experts' menu to repair\n"
616 "the disk.\n");
617 } // else
618 } else {
619 seekTo = (diskSize * blockSize) - UINT64_C(512);
620 } // if/else (mainCrcOk)
621
srs5694e4ac11e2009-08-31 10:13:04 -0400622 if (lseek64(fd, seekTo, SEEK_SET) != (off_t) -1) {
623 read(fd, &secondHeader, 512); // read secondary GPT header
624 secondCrcOk = CheckHeaderCRC(&secondHeader);
625 if (IsLittleEndian() == 0) // big-endian system; adjust header byte order....
626 ReverseHeaderBytes(&secondHeader);
627 } else {
628 allOK = 0;
629 state = gpt_invalid;
srs56943f2fe992009-11-24 18:28:18 -0500630 fprintf(stderr, "Unable to seek to secondary GPT header at sector %llu!\n",
srs5694e4ac11e2009-08-31 10:13:04 -0400631 diskSize - (UINT64_C(1)));
632 } // if/else lseek
633
634 // Return valid headers code: 0 = both headers bad; 1 = main header
635 // good, backup bad; 2 = backup header good, main header bad;
636 // 3 = both headers good. Note these codes refer to valid GPT
637 // signatures and version numbers; more subtle problems will elude
638 // this check!
639 validHeaders = CheckHeaderValidity();
640
641 // Read partitions (from primary array)
642 if (validHeaders > 0) { // if at least one header is OK....
643 // GPT appears to be valid....
644 state = gpt_valid;
645
646 // We're calling the GPT valid, but there's a possibility that one
647 // of the two headers is corrupt. If so, use the one that seems to
648 // be in better shape to regenerate the bad one
649 if (validHeaders == 2) { // valid backup header, invalid main header
650 printf("Caution: invalid main GPT header, but valid backup; regenerating main header\n"
651 "from backup!\n");
652 RebuildMainHeader();
653 mainCrcOk = secondCrcOk; // Since copied, use CRC validity of backup
654 } else if (validHeaders == 1) { // valid main header, invalid backup
655 printf("Caution: invalid backup GPT header, but valid main header; regenerating\n"
656 "backup header from main header.\n");
657 RebuildSecondHeader();
658 secondCrcOk = mainCrcOk; // Since regenerated, use CRC validity of main
659 } // if/else/if
660
661 // Load the main partition table, including storing results of its
662 // CRC check
663 if (LoadMainTable() == 0)
664 allOK = 0;
665
666 // Load backup partition table into temporary storage to check
667 // its CRC and store the results, then discard this temporary
668 // storage, since we don't use it in any but recovery operations
669 seekTo = secondHeader.partitionEntriesLBA * (off_t) blockSize;
670 if ((lseek64(fd, seekTo, SEEK_SET) != (off_t) -1) && (secondCrcOk)) {
671 sizeOfParts = secondHeader.numParts * secondHeader.sizeOfPartitionEntries;
672 storage = (char*) malloc(sizeOfParts);
673 read(fd, storage, sizeOfParts);
674 newCRC = chksum_crc32((unsigned char*) storage, sizeOfParts);
675 free(storage);
676 secondPartsCrcOk = (newCRC == secondHeader.partitionEntriesCRC);
677 } // if
678
679 // Check for valid CRCs and warn if there are problems
680 if ((mainCrcOk == 0) || (secondCrcOk == 0) || (mainPartsCrcOk == 0) ||
681 (secondPartsCrcOk == 0)) {
682 printf("Warning! One or more CRCs don't match. You should repair the disk!\n");
683 state = gpt_corrupt;
684 } // if
685 } else {
686 state = gpt_invalid;
687 } // if/else
688 return allOK;
689} // GPTData::ForceLoadGPTData()
690
srs5694247657a2009-11-26 18:36:12 -0500691// Loads the partition table pointed to by the main GPT header. The
srs5694e4ac11e2009-08-31 10:13:04 -0400692// main GPT header in memory MUST be valid for this call to do anything
693// sensible!
694int GPTData::LoadMainTable(void) {
695 int fd, retval = 0;
696 uint32_t newCRC, sizeOfParts;
697
698 if ((fd = open(device, O_RDONLY)) != -1) {
699 // Set internal data structures for number of partitions on the disk
700 SetGPTSize(mainHeader.numParts);
701
702 // Load main partition table, and record whether its CRC
703 // matches the stored value
704 lseek64(fd, mainHeader.partitionEntriesLBA * blockSize, SEEK_SET);
705 sizeOfParts = mainHeader.numParts * mainHeader.sizeOfPartitionEntries;
706 read(fd, partitions, sizeOfParts);
707 newCRC = chksum_crc32((unsigned char*) partitions, sizeOfParts);
708 mainPartsCrcOk = (newCRC == mainHeader.partitionEntriesCRC);
709 if (IsLittleEndian() == 0)
710 ReversePartitionBytes();
711 retval = 1;
712 } // if
713 return retval;
714} // GPTData::LoadMainTable()
srs5694e7b4ff92009-08-18 13:16:10 -0400715
716// Load the second (backup) partition table as the primary partition
717// table. Used in repair functions
718void GPTData::LoadSecondTableAsMain(void) {
719 int fd;
720 off_t seekTo;
721 uint32_t sizeOfParts, newCRC;
722
723 if ((fd = open(device, O_RDONLY)) != -1) {
724 seekTo = secondHeader.partitionEntriesLBA * (off_t) blockSize;
725 if (lseek64(fd, seekTo, SEEK_SET) != (off_t) -1) {
726 SetGPTSize(secondHeader.numParts);
727 sizeOfParts = secondHeader.numParts * secondHeader.sizeOfPartitionEntries;
728 read(fd, partitions, sizeOfParts);
729 newCRC = chksum_crc32((unsigned char*) partitions, sizeOfParts);
730 secondPartsCrcOk = (newCRC == secondHeader.partitionEntriesCRC);
srs5694e4ac11e2009-08-31 10:13:04 -0400731 mainPartsCrcOk = secondPartsCrcOk;
srs56942a9f5da2009-08-26 00:48:01 -0400732 if (IsLittleEndian() == 0)
733 ReversePartitionBytes();
srs5694e7b4ff92009-08-18 13:16:10 -0400734 if (!secondPartsCrcOk) {
735 printf("Error! After loading backup partitions, the CRC still doesn't check out!\n");
736 } // if
737 } else {
738 printf("Error! Couldn't seek to backup partition table!\n");
739 } // if/else
740 } else {
srs56941d1448a2009-12-31 21:20:19 -0500741 printf("Error! Couldn't open device %s when recovering backup partition table!\n", device);
srs5694e7b4ff92009-08-18 13:16:10 -0400742 } // if/else
743} // GPTData::LoadSecondTableAsMain()
744
srs5694e7b4ff92009-08-18 13:16:10 -0400745// Writes GPT (and protective MBR) to disk. Returns 1 on successful
746// write, 0 if there was a problem.
747int GPTData::SaveGPTData(void) {
srs5694978041c2009-09-21 20:51:47 -0400748 int allOK = 1;
srs5694e7b4ff92009-08-18 13:16:10 -0400749 char answer, line[256];
750 int fd;
751 uint64_t secondTable;
srs56942a9f5da2009-08-26 00:48:01 -0400752 uint32_t numParts;
srs5694e7b4ff92009-08-18 13:16:10 -0400753 off_t offset;
754
755 if (strlen(device) == 0) {
756 printf("Device not defined.\n");
757 } // if
758
759 // First do some final sanity checks....
760 // Is there enough space to hold the GPT headers and partition tables,
761 // given the partition sizes?
srs5694221e0872009-08-29 15:00:31 -0400762 if (CheckGPTSize() > 0) {
srs5694e7b4ff92009-08-18 13:16:10 -0400763 allOK = 0;
764 } // if
765
766 // Check that disk is really big enough to handle this...
767 if (mainHeader.backupLBA > diskSize) {
srs5694247657a2009-11-26 18:36:12 -0500768 fprintf(stderr, "Error! Disk is too small! The 'e' option on the experts' menu might fix the\n"
769 "problem (or it might not). Aborting!\n");
srs56941d1448a2009-12-31 21:20:19 -0500770 printf("(Disk size is %llu sectors, needs to be %llu sectors.)\n", diskSize,
srs5694e7b4ff92009-08-18 13:16:10 -0400771 mainHeader.backupLBA);
772 allOK = 0;
773 } // if
srs5694247657a2009-11-26 18:36:12 -0500774 // Check that second header is properly placed. Warn and ask if this should
775 // be corrected if the test fails....
776 if (mainHeader.backupLBA < (diskSize - UINT64_C(1))) {
777 printf("Warning! Secondary header is placed too early on the disk! Do you want to\n"
778 "correct this problem? ");
779 if (GetYN() == 'Y') {
780 MoveSecondHeaderToEnd();
781 printf("Have moved second header and partition table to correct location.\n");
782 } else {
783 printf("Have not corrected the problem. Strange problems may occur in the future!\n");
784 } // if correction requested
785 } // if
srs5694e7b4ff92009-08-18 13:16:10 -0400786
787 // Check for overlapping partitions....
srs5694e4ac11e2009-08-31 10:13:04 -0400788 if (FindOverlaps() > 0) {
789 allOK = 0;
790 fprintf(stderr, "Aborting write operation!\n");
791 } // if
792
793 // Check for mismatched MBR and GPT data, but let it pass if found
794 // (function displays warning message)
795 FindHybridMismatches();
srs5694e7b4ff92009-08-18 13:16:10 -0400796
srs56942a9f5da2009-08-26 00:48:01 -0400797 // Pull out some data that's needed before doing byte-order reversal on
798 // big-endian systems....
799 numParts = mainHeader.numParts;
800 secondTable = secondHeader.partitionEntriesLBA;
801 if (IsLittleEndian() == 0) {
802 // Reverse partition bytes first, since that function requires non-reversed
803 // data from the main header....
804 ReversePartitionBytes();
805 ReverseHeaderBytes(&mainHeader);
806 ReverseHeaderBytes(&secondHeader);
807 } // if
srs5694e7b4ff92009-08-18 13:16:10 -0400808 RecomputeCRCs();
809
810 if (allOK) {
811 printf("\nFinal checks complete. About to write GPT data. THIS WILL OVERWRITE EXISTING\n");
812 printf("MBR PARTITIONS!! THIS PROGRAM IS BETA QUALITY AT BEST. IF YOU LOSE ALL YOUR\n");
813 printf("DATA, YOU HAVE ONLY YOURSELF TO BLAME IF YOU ANSWER 'Y' BELOW!\n\n");
814 printf("Do you want to proceed, possibly destroying your data? (Y/N) ");
815 fgets(line, 255, stdin);
816 sscanf(line, "%c", &answer);
817 if ((answer == 'Y') || (answer == 'y')) {
818 printf("OK; writing new GPT partition table.\n");
819 } else {
820 allOK = 0;
821 } // if/else
822 } // if
823
824 // Do it!
825 if (allOK) {
srs5694e4ac11e2009-08-31 10:13:04 -0400826 fd = OpenForWrite(device);
srs5694e7b4ff92009-08-18 13:16:10 -0400827 if (fd != -1) {
828 // First, write the protective MBR...
srs5694e4ac11e2009-08-31 10:13:04 -0400829 protectiveMBR.WriteMBRData(fd);
srs5694e7b4ff92009-08-18 13:16:10 -0400830
831 // Now write the main GPT header...
832 if (allOK)
833 if (write(fd, &mainHeader, 512) == -1)
srs5694e4ac11e2009-08-31 10:13:04 -0400834 allOK = 0;
srs5694e7b4ff92009-08-18 13:16:10 -0400835
836 // Now write the main partition tables...
srs5694e4ac11e2009-08-31 10:13:04 -0400837 if (allOK) {
838 if (write(fd, partitions, GPT_SIZE * numParts) == -1)
srs5694e7b4ff92009-08-18 13:16:10 -0400839 allOK = 0;
840 } // if
841
842 // Now seek to near the end to write the secondary GPT....
srs5694e4ac11e2009-08-31 10:13:04 -0400843 if (allOK) {
srs5694e7b4ff92009-08-18 13:16:10 -0400844 offset = (off_t) secondTable * (off_t) (blockSize);
845 if (lseek64(fd, offset, SEEK_SET) == (off_t) - 1) {
846 allOK = 0;
847 printf("Unable to seek to end of disk!\n");
848 } // if
849 } // if
850
851 // Now write the secondary partition tables....
srs5694e4ac11e2009-08-31 10:13:04 -0400852 if (allOK)
srs56942a9f5da2009-08-26 00:48:01 -0400853 if (write(fd, partitions, GPT_SIZE * numParts) == -1)
srs5694e7b4ff92009-08-18 13:16:10 -0400854 allOK = 0;
855
856 // Now write the secondary GPT header...
srs5694e4ac11e2009-08-31 10:13:04 -0400857 if (allOK)
srs5694e7b4ff92009-08-18 13:16:10 -0400858 if (write(fd, &secondHeader, 512) == -1)
srs5694e4ac11e2009-08-31 10:13:04 -0400859 allOK = 0;
srs5694e7b4ff92009-08-18 13:16:10 -0400860
861 // re-read the partition table
862 if (allOK) {
srs5694e35eb1b2009-09-14 00:29:34 -0400863 DiskSync(fd);
srs5694e7b4ff92009-08-18 13:16:10 -0400864 } // if
865
866 if (allOK) { // writes completed OK
srs5694e4ac11e2009-08-31 10:13:04 -0400867 printf("The operation has completed successfully.\n");
srs5694e7b4ff92009-08-18 13:16:10 -0400868 } else {
srs5694e4ac11e2009-08-31 10:13:04 -0400869 printf("Warning! An error was reported when writing the partition table! This error\n");
870 printf("MIGHT be harmless, but you may have trashed the disk! Use parted and, if\n");
871 printf("necessary, restore your original partition table.\n");
srs5694e7b4ff92009-08-18 13:16:10 -0400872 } // if/else
srs5694e4ac11e2009-08-31 10:13:04 -0400873 close(fd);
srs5694e7b4ff92009-08-18 13:16:10 -0400874 } else {
srs5694e4ac11e2009-08-31 10:13:04 -0400875 fprintf(stderr, "Unable to open device %s for writing! Errno is %d! Aborting write!\n",
876 device, errno);
877 allOK = 0;
srs5694e7b4ff92009-08-18 13:16:10 -0400878 } // if/else
879 } else {
880 printf("Aborting write of new partition table.\n");
881 } // if
882
srs56942a9f5da2009-08-26 00:48:01 -0400883 if (IsLittleEndian() == 0) {
884 // Reverse (normalize) header bytes first, since ReversePartitionBytes()
885 // requires non-reversed data in mainHeader...
886 ReverseHeaderBytes(&mainHeader);
887 ReverseHeaderBytes(&secondHeader);
888 ReversePartitionBytes();
889 } // if
890
srs5694e7b4ff92009-08-18 13:16:10 -0400891 return (allOK);
892} // GPTData::SaveGPTData()
893
894// Save GPT data to a backup file. This function does much less error
895// checking than SaveGPTData(). It can therefore preserve many types of
896// corruption for later analysis; however, it preserves only the MBR,
897// the main GPT header, the backup GPT header, and the main partition
898// table; it discards the backup partition table, since it should be
899// identical to the main partition table on healthy disks.
900int GPTData::SaveGPTBackup(char* filename) {
srs56942a9f5da2009-08-26 00:48:01 -0400901 int fd, allOK = 1;
902 uint32_t numParts;
srs5694e7b4ff92009-08-18 13:16:10 -0400903
904 if ((fd = open(filename, O_WRONLY | O_CREAT, S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH)) != -1) {
srs56942a9f5da2009-08-26 00:48:01 -0400905 // Reverse the byte order, if necessary....
906 numParts = mainHeader.numParts;
907 if (IsLittleEndian() == 0) {
908 ReversePartitionBytes();
909 ReverseHeaderBytes(&mainHeader);
910 ReverseHeaderBytes(&secondHeader);
911 } // if
912
srs5694978041c2009-09-21 20:51:47 -0400913 // Recomputing the CRCs is likely to alter them, which could be bad
914 // if the intent is to save a potentially bad GPT for later analysis;
915 // but if we don't do this, we get bogus errors when we load the
916 // backup. I'm favoring misses over false alarms....
917 RecomputeCRCs();
918
srs56942a9f5da2009-08-26 00:48:01 -0400919 // Now write the protective MBR...
srs5694e7b4ff92009-08-18 13:16:10 -0400920 protectiveMBR.WriteMBRData(fd);
921
922 // Now write the main GPT header...
923 if (allOK)
924 if (write(fd, &mainHeader, 512) == -1)
925 allOK = 0;
926
927 // Now write the secondary GPT header...
928 if (allOK)
929 if (write(fd, &secondHeader, 512) == -1)
srs5694e4ac11e2009-08-31 10:13:04 -0400930 allOK = 0;
srs5694e7b4ff92009-08-18 13:16:10 -0400931
932 // Now write the main partition tables...
933 if (allOK) {
srs56942a9f5da2009-08-26 00:48:01 -0400934 if (write(fd, partitions, GPT_SIZE * numParts) == -1)
srs5694e7b4ff92009-08-18 13:16:10 -0400935 allOK = 0;
936 } // if
937
938 if (allOK) { // writes completed OK
939 printf("The operation has completed successfully.\n");
940 } else {
941 printf("Warning! An error was reported when writing the backup file.\n");
srs56948bb78762009-11-24 15:43:49 -0500942 printf("It may not be usable!\n");
srs5694e7b4ff92009-08-18 13:16:10 -0400943 } // if/else
944 close(fd);
srs56942a9f5da2009-08-26 00:48:01 -0400945
946 // Now reverse the byte-order reversal, if necessary....
947 if (IsLittleEndian() == 0) {
948 ReverseHeaderBytes(&mainHeader);
949 ReverseHeaderBytes(&secondHeader);
950 ReversePartitionBytes();
951 } // if
srs5694e7b4ff92009-08-18 13:16:10 -0400952 } else {
953 fprintf(stderr, "Unable to open file %s for writing! Aborting!\n", filename);
954 allOK = 0;
955 } // if/else
956 return allOK;
957} // GPTData::SaveGPTBackup()
958
959// Load GPT data from a backup file created by SaveGPTBackup(). This function
960// does minimal error checking. It returns 1 if it completed successfully,
961// 0 if there was a problem. In the latter case, it creates a new empty
962// set of partitions.
963int GPTData::LoadGPTBackup(char* filename) {
964 int fd, allOK = 1, val;
965 uint32_t numParts, sizeOfEntries, sizeOfParts, newCRC;
srs56942a9f5da2009-08-26 00:48:01 -0400966 int littleEndian = 1;
srs5694e7b4ff92009-08-18 13:16:10 -0400967
968 if ((fd = open(filename, O_RDONLY)) != -1) {
srs56942a9f5da2009-08-26 00:48:01 -0400969 if (IsLittleEndian() == 0)
970 littleEndian = 0;
971
srs5694e7b4ff92009-08-18 13:16:10 -0400972 // Let the MBRData class load the saved MBR...
srs5694221e0872009-08-29 15:00:31 -0400973 protectiveMBR.ReadMBRData(fd, 0); // 0 = don't check block size
srs5694e7b4ff92009-08-18 13:16:10 -0400974
975 // Load the main GPT header, check its vaility, and set the GPT
976 // size based on the data
977 read(fd, &mainHeader, 512);
978 mainCrcOk = CheckHeaderCRC(&mainHeader);
979
srs56942a9f5da2009-08-26 00:48:01 -0400980 // Reverse byte order, if necessary
981 if (littleEndian == 0) {
982 ReverseHeaderBytes(&mainHeader);
983 } // if
984
srs5694e7b4ff92009-08-18 13:16:10 -0400985 // Load the backup GPT header in much the same way as the main
986 // GPT header....
987 read(fd, &secondHeader, 512);
988 secondCrcOk = CheckHeaderCRC(&secondHeader);
989
srs56942a9f5da2009-08-26 00:48:01 -0400990 // Reverse byte order, if necessary
991 if (littleEndian == 0) {
992 ReverseHeaderBytes(&secondHeader);
993 } // if
994
srs5694e7b4ff92009-08-18 13:16:10 -0400995 // Return valid headers code: 0 = both headers bad; 1 = main header
996 // good, backup bad; 2 = backup header good, main header bad;
997 // 3 = both headers good. Note these codes refer to valid GPT
998 // signatures and version numbers; more subtle problems will elude
999 // this check!
1000 if ((val = CheckHeaderValidity()) > 0) {
1001 if (val == 2) { // only backup header seems to be good
1002 numParts = secondHeader.numParts;
srs5694e4ac11e2009-08-31 10:13:04 -04001003 sizeOfEntries = secondHeader.sizeOfPartitionEntries;
srs5694e7b4ff92009-08-18 13:16:10 -04001004 } else { // main header is OK
1005 numParts = mainHeader.numParts;
1006 sizeOfEntries = mainHeader.sizeOfPartitionEntries;
1007 } // if/else
1008
1009 SetGPTSize(numParts);
1010
1011 // If current disk size doesn't match that of backup....
1012 if (secondHeader.currentLBA != diskSize - UINT64_C(1)) {
1013 printf("Warning! Current disk size doesn't match that of the backup!\n"
srs5694e4ac11e2009-08-31 10:13:04 -04001014 "Adjusting sizes to match, but subsequent problems are possible!\n");
srs5694247657a2009-11-26 18:36:12 -05001015 MoveSecondHeaderToEnd();
srs5694e7b4ff92009-08-18 13:16:10 -04001016 } // if
1017
1018 // Load main partition table, and record whether its CRC
1019 // matches the stored value
1020 sizeOfParts = numParts * sizeOfEntries;
1021 read(fd, partitions, sizeOfParts);
1022
1023 newCRC = chksum_crc32((unsigned char*) partitions, sizeOfParts);
1024 mainPartsCrcOk = (newCRC == mainHeader.partitionEntriesCRC);
1025 secondPartsCrcOk = (newCRC == secondHeader.partitionEntriesCRC);
srs56942a9f5da2009-08-26 00:48:01 -04001026 // Reverse byte order, if necessary
srs5694e4ac11e2009-08-31 10:13:04 -04001027 if (littleEndian == 0) {
1028 ReversePartitionBytes();
1029 } // if
srs56942a9f5da2009-08-26 00:48:01 -04001030
srs5694e7b4ff92009-08-18 13:16:10 -04001031 } else {
1032 allOK = 0;
1033 } // if/else
1034 } else {
1035 allOK = 0;
1036 fprintf(stderr, "Unable to open file %s for reading! Aborting!\n", filename);
1037 } // if/else
1038
1039 // Something went badly wrong, so blank out partitions
1040 if (allOK == 0) {
1041 ClearGPTData();
1042 protectiveMBR.MakeProtectiveMBR();
1043 } // if
1044 return allOK;
1045} // GPTData::LoadGPTBackup()
1046
srs5694e4ac11e2009-08-31 10:13:04 -04001047// Tell user whether Apple Partition Map (APM) was discovered....
1048void GPTData::ShowAPMState(void) {
1049 if (apmFound)
1050 printf(" APM: present\n");
1051 else
1052 printf(" APM: not present\n");
1053} // GPTData::ShowAPMState()
1054
1055// Tell user about the state of the GPT data....
1056void GPTData::ShowGPTState(void) {
1057 switch (state) {
1058 case gpt_invalid:
1059 printf(" GPT: not present\n");
1060 break;
1061 case gpt_valid:
1062 printf(" GPT: present\n");
1063 break;
1064 case gpt_corrupt:
1065 printf(" GPT: damaged\n");
1066 break;
1067 default:
1068 printf("\a GPT: unknown -- bug!\n");
1069 break;
1070 } // switch
1071} // GPTData::ShowGPTState()
1072
1073// Display the basic GPT data
1074void GPTData::DisplayGPTData(void) {
srs5694e35eb1b2009-09-14 00:29:34 -04001075 int i;
srs5694e4ac11e2009-08-31 10:13:04 -04001076 char sizeInSI[255]; // String to hold size of disk in SI units
1077 char tempStr[255];
1078 uint64_t temp, totalFree;
1079
1080 BytesToSI(diskSize * blockSize, sizeInSI);
srs5694978041c2009-09-21 20:51:47 -04001081 printf("Disk %s: %llu sectors, %s\n", device,
1082 (unsigned long long) diskSize, sizeInSI);
srs5694e4ac11e2009-08-31 10:13:04 -04001083 printf("Disk identifier (GUID): %s\n", GUIDToStr(mainHeader.diskGUID, tempStr));
1084 printf("Partition table holds up to %lu entries\n", (unsigned long) mainHeader.numParts);
1085 printf("First usable sector is %lu, last usable sector is %lu\n",
1086 (unsigned long) mainHeader.firstUsableLBA,
1087 (unsigned long) mainHeader.lastUsableLBA);
1088 totalFree = FindFreeBlocks(&i, &temp);
1089 printf("Total free space is %llu sectors (%s)\n", totalFree,
1090 BytesToSI(totalFree * (uint64_t) blockSize, sizeInSI));
1091 printf("\nNumber Start (sector) End (sector) Size Code Name\n");
1092 for (i = 0; i < mainHeader.numParts; i++) {
srs5694978041c2009-09-21 20:51:47 -04001093 partitions[i].ShowSummary(i, blockSize);
srs5694e4ac11e2009-08-31 10:13:04 -04001094 } // for
1095} // GPTData::DisplayGPTData()
1096
1097// Get partition number from user and then call ShowPartDetails(partNum)
1098// to show its detailed information
1099void GPTData::ShowDetails(void) {
1100 int partNum;
1101 uint32_t low, high;
1102
1103 if (GetPartRange(&low, &high) > 0) {
1104 partNum = GetPartNum();
1105 ShowPartDetails(partNum);
1106 } else {
1107 printf("No partitions\n");
1108 } // if/else
1109} // GPTData::ShowDetails()
1110
1111// Show detailed information on the specified partition
1112void GPTData::ShowPartDetails(uint32_t partNum) {
1113 if (partitions[partNum].GetFirstLBA() != 0) {
1114 partitions[partNum].ShowDetails(blockSize);
1115 } else {
1116 printf("Partition #%d does not exist.", (int) (partNum + 1));
1117 } // if
1118} // GPTData::ShowPartDetails()
1119
1120/*********************************************************************
1121 * *
1122 * Begin functions that obtain information from the users, and often *
1123 * do something with that information (call other functions) *
1124 * *
1125 *********************************************************************/
1126
1127// Prompts user for partition number and returns the result.
1128uint32_t GPTData::GetPartNum(void) {
1129 uint32_t partNum;
1130 uint32_t low, high;
1131 char prompt[255];
1132
1133 if (GetPartRange(&low, &high) > 0) {
1134 sprintf(prompt, "Partition number (%d-%d): ", low + 1, high + 1);
1135 partNum = GetNumber(low + 1, high + 1, low, prompt);
1136 } else partNum = 1;
1137 return (partNum - 1);
1138} // GPTData::GetPartNum()
1139
1140// What it says: Resize the partition table. (Default is 128 entries.)
1141void GPTData::ResizePartitionTable(void) {
1142 int newSize;
1143 char prompt[255];
1144 uint32_t curLow, curHigh;
1145
1146 printf("Current partition table size is %lu.\n",
1147 (unsigned long) mainHeader.numParts);
1148 GetPartRange(&curLow, &curHigh);
1149 curHigh++; // since GetPartRange() returns numbers starting from 0...
1150 // There's no point in having fewer than four partitions....
1151 if (curHigh < 4)
1152 curHigh = 4;
1153 sprintf(prompt, "Enter new size (%d up, default %d): ", (int) curHigh,
1154 (int) NUM_GPT_ENTRIES);
1155 newSize = GetNumber(4, 65535, 128, prompt);
1156 if (newSize < 128) {
1157 printf("Caution: The partition table size should officially be 16KB or larger,\n"
1158 "which works out to 128 entries. In practice, smaller tables seem to\n"
1159 "work with most OSes, but this practice is risky. I'm proceeding with\n"
1160 "the resize, but you may want to reconsider this action and undo it.\n\n");
1161 } // if
1162 SetGPTSize(newSize);
1163} // GPTData::ResizePartitionTable()
1164
1165// Interactively create a partition
1166void GPTData::CreatePartition(void) {
1167 uint64_t firstBlock, firstInLargest, lastBlock, sector;
1168 char prompt[255];
1169 int partNum, firstFreePart = 0;
1170
1171 // Find first free partition...
1172 while (partitions[firstFreePart].GetFirstLBA() != 0) {
1173 firstFreePart++;
1174 } // while
1175
1176 if (((firstBlock = FindFirstAvailable()) != 0) &&
1177 (firstFreePart < mainHeader.numParts)) {
1178 lastBlock = FindLastAvailable(firstBlock);
1179 firstInLargest = FindFirstInLargest();
1180
1181 // Get partition number....
1182 do {
1183 sprintf(prompt, "Partition number (%d-%d, default %d): ", firstFreePart + 1,
1184 mainHeader.numParts, firstFreePart + 1);
1185 partNum = GetNumber(firstFreePart + 1, mainHeader.numParts,
1186 firstFreePart + 1, prompt) - 1;
1187 if (partitions[partNum].GetFirstLBA() != 0)
1188 printf("partition %d is in use.\n", partNum + 1);
1189 } while (partitions[partNum].GetFirstLBA() != 0);
1190
1191 // Get first block for new partition...
1192 sprintf(prompt,
1193 "First sector (%llu-%llu, default = %llu) or {+-}size{KMGT}: ",
1194 firstBlock, lastBlock, firstInLargest);
1195 do {
1196 sector = GetSectorNum(firstBlock, lastBlock, firstInLargest, prompt);
1197 } while (IsFree(sector) == 0);
srs56941d1448a2009-12-31 21:20:19 -05001198 Align(&sector); // Align sector to correct multiple
srs5694e4ac11e2009-08-31 10:13:04 -04001199 firstBlock = sector;
1200
1201 // Get last block for new partitions...
1202 lastBlock = FindLastInFree(firstBlock);
1203 sprintf(prompt,
1204 "Last sector (%llu-%llu, default = %llu) or {+-}size{KMGT}: ",
1205 firstBlock, lastBlock, lastBlock);
1206 do {
1207 sector = GetSectorNum(firstBlock, lastBlock, lastBlock, prompt);
1208 } while (IsFree(sector) == 0);
1209 lastBlock = sector;
1210
1211 partitions[partNum].SetFirstLBA(firstBlock);
1212 partitions[partNum].SetLastLBA(lastBlock);
1213
1214 partitions[partNum].SetUniqueGUID(1);
1215 partitions[partNum].ChangeType();
1216 partitions[partNum].SetName((unsigned char*) partitions[partNum].GetNameType(prompt));
1217 } else {
1218 printf("No free sectors available\n");
1219 } // if/else
1220} // GPTData::CreatePartition()
1221
1222// Interactively delete a partition (duh!)
1223void GPTData::DeletePartition(void) {
1224 int partNum;
1225 uint32_t low, high;
1226 uint64_t startSector, length;
1227 char prompt[255];
1228
1229 if (GetPartRange(&low, &high) > 0) {
1230 sprintf(prompt, "Partition number (%d-%d): ", low + 1, high + 1);
1231 partNum = GetNumber(low + 1, high + 1, low, prompt);
1232
1233 // In case there's a protective MBR, look for & delete matching
1234 // MBR partition....
1235 startSector = partitions[partNum - 1].GetFirstLBA();
1236 length = partitions[partNum - 1].GetLengthLBA();
1237 protectiveMBR.DeleteByLocation(startSector, length);
1238
1239 // Now delete the GPT partition
1240 partitions[partNum - 1].BlankPartition();
1241 } else {
1242 printf("No partitions\n");
1243 } // if/else
1244} // GPTData::DeletePartition()
1245
1246// Prompt user for a partition number, then change its type code
1247// using ChangeGPTType(struct GPTPartition*) function.
1248void GPTData::ChangePartType(void) {
1249 int partNum;
1250 uint32_t low, high;
1251
1252 if (GetPartRange(&low, &high) > 0) {
1253 partNum = GetPartNum();
1254 partitions[partNum].ChangeType();
1255 } else {
1256 printf("No partitions\n");
1257 } // if/else
1258} // GPTData::ChangePartType()
1259
1260// Partition attributes seem to be rarely used, but I want a way to
1261// adjust them for completeness....
1262void GPTData::SetAttributes(uint32_t partNum) {
1263 Attributes theAttr;
1264
1265 theAttr.SetAttributes(partitions[partNum].GetAttributes());
1266 theAttr.DisplayAttributes();
1267 theAttr.ChangeAttributes();
1268 partitions[partNum].SetAttributes(theAttr.GetAttributes());
1269} // GPTData::SetAttributes()
1270
srs5694c0ca8f82009-08-20 21:35:25 -04001271// This function destroys the on-disk GPT structures. Returns 1 if the
1272// user confirms destruction, 0 if the user aborts.
srs5694978041c2009-09-21 20:51:47 -04001273// If prompt == 0, don't ask user about proceeding and do NOT wipe out
1274// MBR. (Set prompt == 0 when doing a GPT-to-MBR conversion.)
1275int GPTData::DestroyGPT(int prompt) {
srs5694e35eb1b2009-09-14 00:29:34 -04001276 int fd, i;
srs5694978041c2009-09-21 20:51:47 -04001277 char blankSector[512], goOn = 'Y', blank = 'N';
srs5694c0ca8f82009-08-20 21:35:25 -04001278
1279 for (i = 0; i < 512; i++) {
1280 blankSector[i] = '\0';
1281 } // for
1282
srs5694978041c2009-09-21 20:51:47 -04001283 if (((apmFound) || (bsdFound)) && prompt) {
srs5694e35eb1b2009-09-14 00:29:34 -04001284 printf("WARNING: APM or BSD disklabel structures detected! This operation could\n"
1285 "damage any APM or BSD partitions on this disk!\n");
1286 } // if APM or BSD
srs5694978041c2009-09-21 20:51:47 -04001287 if (prompt) {
1288 printf("\a\aAbout to wipe out GPT on %s. Proceed? ", device);
1289 goOn = GetYN();
1290 } // if
srs5694c0ca8f82009-08-20 21:35:25 -04001291 if (goOn == 'Y') {
1292 fd = open(device, O_WRONLY);
1293#ifdef __APPLE__
1294 // MacOS X requires a shared lock under some circumstances....
1295 if (fd < 0) {
1296 fd = open(device, O_WRONLY|O_SHLOCK);
1297 } // if
1298#endif
1299 if (fd != -1) {
1300 lseek64(fd, mainHeader.currentLBA * 512, SEEK_SET); // seek to GPT header
1301 write(fd, blankSector, 512); // blank it out
1302 lseek64(fd, mainHeader.partitionEntriesLBA * 512, SEEK_SET); // seek to partition table
1303 for (i = 0; i < GetBlocksInPartTable(); i++)
1304 write(fd, blankSector, 512);
1305 lseek64(fd, secondHeader.partitionEntriesLBA * 512, SEEK_SET); // seek to partition table
1306 for (i = 0; i < GetBlocksInPartTable(); i++)
1307 write(fd, blankSector, 512);
1308 lseek64(fd, secondHeader.currentLBA * 512, SEEK_SET); // seek to GPT header
1309 write(fd, blankSector, 512); // blank it out
srs5694978041c2009-09-21 20:51:47 -04001310 if (prompt) {
1311 printf("Blank out MBR? ");
1312 blank = GetYN();
1313 }// if
1314 // Note on below: Touch the MBR only if the user wants it completely
1315 // blanked out. Version 0.4.2 deleted the 0xEE partition and re-wrote
1316 // the MBR, but this could wipe out a valid MBR that the program
1317 // had subsequently discarded (say, if it conflicted with older GPT
1318 // structures).
1319 if (blank == 'Y') {
srs5694c0ca8f82009-08-20 21:35:25 -04001320 lseek64(fd, 0, SEEK_SET);
1321 write(fd, blankSector, 512); // blank it out
srs5694978041c2009-09-21 20:51:47 -04001322 } else {
1323 printf("MBR is unchanged. You may need to delete an EFI GPT (0xEE) partition\n"
1324 "with fdisk or another tool.\n");
srs5694e35eb1b2009-09-14 00:29:34 -04001325 } // if/else
1326 DiskSync(fd);
srs5694c0ca8f82009-08-20 21:35:25 -04001327 close(fd);
srs5694e4ac11e2009-08-31 10:13:04 -04001328 printf("GPT data structures destroyed! You may now partition the disk using fdisk or\n"
1329 "other utilities. Program will now terminate.\n");
srs5694c0ca8f82009-08-20 21:35:25 -04001330 } else {
srs56941d1448a2009-12-31 21:20:19 -05001331 printf("Problem opening %s for writing! Program will now terminate.\n", device);
srs5694c0ca8f82009-08-20 21:35:25 -04001332 } // if/else (fd != -1)
1333 } // if (goOn == 'Y')
1334 return (goOn == 'Y');
1335} // GPTData::DestroyGPT()
1336
srs5694e4ac11e2009-08-31 10:13:04 -04001337/**************************************************************************
1338 * *
1339 * Partition table transformation functions (MBR or BSD disklabel to GPT) *
1340 * (some of these functions may require user interaction) *
1341 * *
1342 **************************************************************************/
1343
1344// Examines the MBR & GPT data, and perhaps asks the user questions, to
1345// determine which set of data to use: the MBR (use_mbr), the GPT (use_gpt),
1346// or create a new set of partitions (use_new)
1347WhichToUse GPTData::UseWhichPartitions(void) {
1348 WhichToUse which = use_new;
1349 MBRValidity mbrState;
1350 int answer;
1351
1352 mbrState = protectiveMBR.GetValidity();
1353
1354 if ((state == gpt_invalid) && ((mbrState == mbr) || (mbrState == hybrid))) {
1355 printf("\n\a***************************************************************\n"
1356 "Found invalid GPT and valid MBR; converting MBR to GPT format.\n"
1357 "THIS OPERATON IS POTENTIALLY DESTRUCTIVE! Exit by typing 'q' if\n"
1358 "you don't want to convert your MBR partitions to GPT format!\n"
1359 "***************************************************************\n\n");
1360 which = use_mbr;
1361 } // if
1362
1363 if ((state == gpt_invalid) && bsdFound) {
1364 printf("\n\a**********************************************************************\n"
1365 "Found invalid GPT and valid BSD disklabel; converting BSD disklabel\n"
1366 "to GPT format. THIS OPERATON IS POTENTIALLY DESTRUCTIVE! Your first\n"
1367 "BSD partition will likely be unusable. Exit by typing 'q' if you don't\n"
1368 "want to convert your BSD partitions to GPT format!\n"
1369 "**********************************************************************\n\n");
1370 which = use_bsd;
1371 } // if
1372
1373 if ((state == gpt_valid) && (mbrState == gpt)) {
1374 printf("Found valid GPT with protective MBR; using GPT.\n");
1375 which = use_gpt;
1376 } // if
1377 if ((state == gpt_valid) && (mbrState == hybrid)) {
1378 printf("Found valid GPT with hybrid MBR; using GPT.\n");
1379 which = use_gpt;
1380 } // if
1381 if ((state == gpt_valid) && (mbrState == invalid)) {
1382 printf("\aFound valid GPT with corrupt MBR; using GPT and will create new\nprotective MBR on save.\n");
1383 which = use_gpt;
1384 protectiveMBR.MakeProtectiveMBR();
1385 } // if
1386 if ((state == gpt_valid) && (mbrState == mbr)) {
1387 printf("Found valid MBR and GPT. Which do you want to use?\n");
1388 answer = GetNumber(1, 3, 2, (char*) " 1 - MBR\n 2 - GPT\n 3 - Create blank GPT\n\nYour answer: ");
1389 if (answer == 1) {
1390 which = use_mbr;
1391 } else if (answer == 2) {
1392 which = use_gpt;
1393 protectiveMBR.MakeProtectiveMBR();
1394 printf("Using GPT and creating fresh protective MBR.\n");
1395 } else which = use_new;
1396 } // if
1397
1398 // Nasty decisions here -- GPT is present, but corrupt (bad CRCs or other
1399 // problems)
1400 if (state == gpt_corrupt) {
1401 if ((mbrState == mbr) || (mbrState == hybrid)) {
1402 printf("Found valid MBR and corrupt GPT. Which do you want to use? (Using the\n"
1403 "GPT MAY permit recovery of GPT data.)\n");
1404 answer = GetNumber(1, 3, 2, (char*) " 1 - MBR\n 2 - GPT\n 3 - Create blank GPT\n\nYour answer: ");
1405 if (answer == 1) {
1406 which = use_mbr;
1407// protectiveMBR.MakeProtectiveMBR();
1408 } else if (answer == 2) {
1409 which = use_gpt;
1410 } else which = use_new;
1411 } else if (mbrState == invalid) {
1412 printf("Found invalid MBR and corrupt GPT. What do you want to do? (Using the\n"
1413 "GPT MAY permit recovery of GPT data.)\n");
1414 answer = GetNumber(1, 2, 1, (char*) " 1 - GPT\n 2 - Create blank GPT\n\nYour answer: ");
1415 if (answer == 1) {
1416 which = use_gpt;
1417 } else which = use_new;
1418 } else { // corrupt GPT, MBR indicates it's a GPT disk....
1419 printf("\a\a****************************************************************************\n"
1420 "Caution: Found protective or hybrid MBR and corrupt GPT. Using GPT, but disk\n"
1421 "verification and recovery are STRONGLY recommended.\n"
1422 "****************************************************************************\n");
srs5694247657a2009-11-26 18:36:12 -05001423 which = use_gpt;
srs5694e4ac11e2009-08-31 10:13:04 -04001424 } // if/else/else
1425 } // if (corrupt GPT)
1426
1427 if (which == use_new)
1428 printf("Creating new GPT entries.\n");
1429
1430 return which;
1431} // UseWhichPartitions()
1432
1433// Convert MBR partition table into GPT form
1434int GPTData::XFormPartitions(void) {
1435 int i, numToConvert;
1436 uint8_t origType;
1437 struct newGUID;
srs5694e4ac11e2009-08-31 10:13:04 -04001438
1439 // Clear out old data & prepare basics....
1440 ClearGPTData();
1441
1442 // Convert the smaller of the # of GPT or MBR partitions
srs5694978041c2009-09-21 20:51:47 -04001443 if (mainHeader.numParts > (MAX_MBR_PARTS))
1444 numToConvert = MAX_MBR_PARTS;
srs5694e4ac11e2009-08-31 10:13:04 -04001445 else
1446 numToConvert = mainHeader.numParts;
1447
1448 for (i = 0; i < numToConvert; i++) {
1449 origType = protectiveMBR.GetType(i);
1450 // don't waste CPU time trying to convert extended, hybrid protective, or
1451 // null (non-existent) partitions
srs5694e35eb1b2009-09-14 00:29:34 -04001452 if ((origType != 0x05) && (origType != 0x0f) && (origType != 0x85) &&
srs5694e4ac11e2009-08-31 10:13:04 -04001453 (origType != 0x00) && (origType != 0xEE))
1454 partitions[i] = protectiveMBR.AsGPT(i);
1455 } // for
1456
1457 // Convert MBR into protective MBR
1458 protectiveMBR.MakeProtectiveMBR();
1459
1460 // Record that all original CRCs were OK so as not to raise flags
1461 // when doing a disk verification
1462 mainCrcOk = secondCrcOk = mainPartsCrcOk = secondPartsCrcOk = 1;
1463
1464 return (1);
1465} // GPTData::XFormPartitions()
1466
1467// Transforms BSD disklabel on the specified partition (numbered from 0).
1468// If an invalid partition number is given, the program prompts for one.
1469// Returns the number of new partitions created.
1470int GPTData::XFormDisklabel(int i) {
1471 uint32_t low, high, partNum, startPart;
1472 uint16_t hexCode;
1473 int goOn = 1, numDone = 0;
1474 BSDData disklabel;
1475
1476 if (GetPartRange(&low, &high) != 0) {
1477 if ((i < low) || (i > high))
1478 partNum = GetPartNum();
1479 else
1480 partNum = (uint32_t) i;
1481
1482 // Find the partition after the last used one
1483 startPart = high + 1;
1484
1485 // Now see if the specified partition has a BSD type code....
1486 hexCode = partitions[partNum].GetHexType();
1487 if ((hexCode != 0xa500) && (hexCode != 0xa900)) {
1488 printf("Specified partition doesn't have a disklabel partition type "
1489 "code.\nContinue anyway?");
1490 goOn = (GetYN() == 'Y');
1491 } // if
1492
1493 // If all is OK, read the disklabel and convert it.
1494 if (goOn) {
1495 goOn = disklabel.ReadBSDData(device, partitions[partNum].GetFirstLBA(),
1496 partitions[partNum].GetLastLBA());
1497 if ((goOn) && (disklabel.IsDisklabel())) {
1498 numDone = XFormDisklabel(&disklabel, startPart);
1499 if (numDone == 1)
1500 printf("Converted %d BSD partition.\n", numDone);
1501 else
1502 printf("Converted %d BSD partitions.\n", numDone);
1503 } else {
1504 printf("Unable to convert partitions! Unrecognized BSD disklabel.\n");
1505 } // if/else
1506 } // if
1507 if (numDone > 0) { // converted partitions; delete carrier
1508 partitions[partNum].BlankPartition();
1509 } // if
1510 } else {
1511 printf("No partitions\n");
1512 } // if/else
1513 return numDone;
1514} // GPTData::XFormDisklable(int i)
1515
1516// Transform the partitions on an already-loaded BSD disklabel...
1517int GPTData::XFormDisklabel(BSDData* disklabel, int startPart) {
1518 int i, numDone = 0;
1519
1520 if ((disklabel->IsDisklabel()) && (startPart >= 0) &&
1521 (startPart < mainHeader.numParts)) {
1522 for (i = 0; i < disklabel->GetNumParts(); i++) {
1523 partitions[i + startPart] = disklabel->AsGPT(i);
1524 if (partitions[i + startPart].GetFirstLBA() != UINT64_C(0))
1525 numDone++;
1526 } // for
1527 } // if
1528
1529 // Record that all original CRCs were OK so as not to raise flags
1530 // when doing a disk verification
1531 mainCrcOk = secondCrcOk = mainPartsCrcOk = secondPartsCrcOk = 1;
1532
1533 return numDone;
1534} // GPTData::XFormDisklabel(BSDData* disklabel)
1535
srs5694978041c2009-09-21 20:51:47 -04001536// Add one GPT partition to MBR. Used by XFormToMBR() and MakeHybrid()
1537// functions. Returns 1 if operation was successful.
1538int GPTData::OnePartToMBR(uint32_t gptPart, int mbrPart) {
1539 int allOK = 1, typeCode, bootable;
1540 uint64_t length;
1541 char line[255];
1542
1543 if ((mbrPart < 0) || (mbrPart > 3)) {
1544 printf("MBR partition %d is out of range; omitting it.\n", mbrPart + 1);
1545 allOK = 0;
1546 } // if
1547 if (gptPart >= mainHeader.numParts) {
1548 printf("GPT partition %d is out of range; omitting it.\n", gptPart + 1);
1549 allOK = 0;
1550 } // if
1551 if (allOK && (partitions[gptPart].GetLastLBA() == UINT64_C(0))) {
1552 printf("GPT partition %d is undefined; omitting it.\n", gptPart + 1);
1553 allOK = 0;
1554 } // if
1555 if (allOK && (partitions[gptPart].GetFirstLBA() <= UINT32_MAX) &&
1556 (partitions[gptPart].GetLengthLBA() <= UINT32_MAX)) {
1557 if (partitions[gptPart].GetLastLBA() > UINT32_MAX) {
1558 printf("Caution: Partition end point past 32-bit pointer boundary;"
1559 " some OSes may\nreact strangely.\n");
1560 } // if partition ends past 32-bit (usually 2TiB) boundary
1561 do {
1562 printf("Enter an MBR hex code (default %02X): ",
1563 typeHelper.GUIDToID(partitions[gptPart].GetType()) / 256);
1564 fgets(line, 255, stdin);
1565 sscanf(line, "%x", &typeCode);
1566 if (line[0] == '\n')
1567 typeCode = partitions[gptPart].GetHexType() / 256;
1568 } while ((typeCode <= 0) || (typeCode > 255));
1569 printf("Set the bootable flag? ");
1570 bootable = (GetYN() == 'Y');
1571 length = partitions[gptPart].GetLengthLBA();
1572 protectiveMBR.MakePart(mbrPart, (uint32_t) partitions[gptPart].GetFirstLBA(),
1573 (uint32_t) length, typeCode, bootable);
1574 } else { // partition out of range
1575 printf("Partition %d begins beyond the 32-bit pointer limit of MBR "
1576 "partitions, or is\n too big; omitting it.\n", gptPart + 1);
1577 allOK = 0;
1578 } // if/else
1579 return allOK;
1580} // GPTData::OnePartToMBR()
1581
1582// Convert the GPT to MBR form. This function is necessarily limited; it
1583// handles at most four partitions and creates layouts that ignore CHS
1584// geometries. Returns the number of converted partitions; if this value
1585// is over 0, the calling function should call DestroyGPT() to destroy
1586// the GPT data, and then exit.
1587int GPTData::XFormToMBR(void) {
1588 char line[255];
1589 int i, j, numParts, numConverted = 0;
1590 uint32_t partNums[4];
1591
1592 // Get the numbers of up to four partitions to add to the
1593 // hybrid MBR....
1594 numParts = CountParts();
1595 printf("Counted %d partitions.\n", numParts);
1596
1597 // Prepare the MBR for conversion (empty it of existing partitions).
1598 protectiveMBR.EmptyMBR(0);
1599 protectiveMBR.SetDiskSize(diskSize);
1600
1601 if (numParts > 4) { // Over four partitions; engage in triage
1602 printf("Type from one to four GPT partition numbers, separated by spaces, to be\n"
1603 "used in the MBR, in sequence: ");
1604 fgets(line, 255, stdin);
1605 numParts = sscanf(line, "%d %d %d %d", &partNums[0], &partNums[1],
1606 &partNums[2], &partNums[3]);
1607 } else { // Four or fewer partitions; convert them all
1608 i = j = 0;
1609 while ((j < numParts) && (i < mainHeader.numParts)) {
1610 if (partitions[i].GetFirstLBA() > 0) { // if GPT part. is defined
1611 partNums[j++] = ++i; // flag it for conversion
1612 } else i++;
1613 } // while
1614 } // if/else
1615
1616 for (i = 0; i < numParts; i++) {
1617 j = partNums[i] - 1;
1618 printf("\nCreating entry for partition #%d\n", j + 1);
1619 numConverted += OnePartToMBR(j, i);
1620 } // for
1621 return numConverted;
1622} // GPTData::XFormToMBR()
1623
srs5694e4ac11e2009-08-31 10:13:04 -04001624// Create a hybrid MBR -- an ugly, funky thing that helps GPT work with
1625// OSes that don't understand GPT.
1626void GPTData::MakeHybrid(void) {
1627 uint32_t partNums[3];
1628 char line[255];
srs5694978041c2009-09-21 20:51:47 -04001629 int numParts, numConverted = 0, i, j, typeCode, mbrNum;
srs5694e4ac11e2009-08-31 10:13:04 -04001630 char fillItUp = 'M'; // fill extra partition entries? (Yes/No/Maybe)
srs5694978041c2009-09-21 20:51:47 -04001631 char eeFirst = 'Y'; // Whether EFI GPT (0xEE) partition comes first in table
srs5694e4ac11e2009-08-31 10:13:04 -04001632
1633 printf("\nWARNING! Hybrid MBRs are flaky and potentially dangerous! If you decide not\n"
1634 "to use one, just hit the Enter key at the below prompt and your MBR\n"
1635 "partition table will be untouched.\n\n\a");
1636
1637 // Now get the numbers of up to three partitions to add to the
1638 // hybrid MBR....
1639 printf("Type from one to three GPT partition numbers, separated by spaces, to be\n"
1640 "added to the hybrid MBR, in sequence: ");
1641 fgets(line, 255, stdin);
1642 numParts = sscanf(line, "%d %d %d", &partNums[0], &partNums[1], &partNums[2]);
1643
1644 if (numParts > 0) {
1645 // Blank out the protective MBR, but leave the boot loader code
1646 // alone....
1647 protectiveMBR.EmptyMBR(0);
1648 protectiveMBR.SetDiskSize(diskSize);
1649 printf("Place EFI GPT (0xEE) partition first in MBR (good for GRUB)? ");
1650 eeFirst = GetYN();
1651 } // if
1652
1653 for (i = 0; i < numParts; i++) {
1654 j = partNums[i] - 1;
1655 printf("\nCreating entry for partition #%d\n", j + 1);
srs5694978041c2009-09-21 20:51:47 -04001656 if (eeFirst == 'Y')
1657 mbrNum = i + 1;
1658 else
1659 mbrNum = i;
1660 numConverted += OnePartToMBR(j, mbrNum);
srs5694e4ac11e2009-08-31 10:13:04 -04001661 } // for
1662
srs5694978041c2009-09-21 20:51:47 -04001663 if ((numParts > 0) && (numConverted > 0)) { // User opted to create a hybrid MBR....
srs5694e4ac11e2009-08-31 10:13:04 -04001664 // Create EFI protective partition that covers the start of the disk.
1665 // If this location (covering the main GPT data structures) is omitted,
1666 // Linux won't find any partitions on the disk. Note that this is
1667 // NUMBERED AFTER the hybrid partitions, contrary to what the
1668 // gptsync utility does. This is because Windows seems to choke on
1669 // disks with a 0xEE partition in the first slot and subsequent
1670 // additional partitions, unless it boots from the disk.
1671 if (eeFirst == 'Y')
1672 mbrNum = 0;
1673 else
1674 mbrNum = numParts;
1675 protectiveMBR.MakePart(mbrNum, 1, protectiveMBR.FindLastInFree(1), 0xEE);
srs5694978041c2009-09-21 20:51:47 -04001676 protectiveMBR.SetHybrid();
srs5694e4ac11e2009-08-31 10:13:04 -04001677
1678 // ... and for good measure, if there are any partition spaces left,
1679 // optionally create another protective EFI partition to cover as much
1680 // space as possible....
1681 for (i = 0; i < 4; i++) {
1682 if (protectiveMBR.GetType(i) == 0x00) { // unused entry....
1683 if (fillItUp == 'M') {
1684 printf("\nUnused partition space(s) found. Use one to protect more partitions? ");
1685 fillItUp = GetYN();
1686 typeCode = 0x00; // use this to flag a need to get type code
1687 } // if
1688 if (fillItUp == 'Y') {
1689 while ((typeCode <= 0) || (typeCode > 255)) {
1690 printf("Enter an MBR hex code (EE is EFI GPT, but may confuse MacOS): ");
1691 // Comment on above: Mac OS treats disks with more than one
1692 // 0xEE MBR partition as MBR disks, not as GPT disks.
1693 fgets(line, 255, stdin);
1694 sscanf(line, "%x", &typeCode);
1695 if (line[0] == '\n')
1696 typeCode = 0;
1697 } // while
1698 protectiveMBR.MakeBiggestPart(i, typeCode); // make a partition
1699 } // if (fillItUp == 'Y')
1700 } // if unused entry
1701 } // for (i = 0; i < 4; i++)
1702 } // if (numParts > 0)
1703} // GPTData::MakeHybrid()
1704
1705/**********************************************************************
1706 * *
1707 * Functions that adjust GPT data structures WITHOUT user interaction *
1708 * (they may display information for the user's benefit, though) *
1709 * *
1710 **********************************************************************/
1711
1712// Resizes GPT to specified number of entries. Creates a new table if
1713// necessary, copies data if it already exists.
1714int GPTData::SetGPTSize(uint32_t numEntries) {
1715 struct GPTPart* newParts;
1716 struct GPTPart* trash;
1717 uint32_t i, high, copyNum;
1718 int allOK = 1;
1719
1720 // First, adjust numEntries upward, if necessary, to get a number
1721 // that fills the allocated sectors
1722 i = blockSize / GPT_SIZE;
1723 if ((numEntries % i) != 0) {
1724 printf("Adjusting GPT size from %lu ", (unsigned long) numEntries);
1725 numEntries = ((numEntries / i) + 1) * i;
1726 printf("to %lu to fill the sector\n", (unsigned long) numEntries);
1727 } // if
1728
srs5694247657a2009-11-26 18:36:12 -05001729 // Do the work only if the # of partitions is changing. Along with being
1730 // efficient, this prevents mucking the with location of the secondary
1731 // partition table, which causes problems when loading data from a RAID
1732 // array that's been expanded because this function is called when loading
1733 // data.
1734 if ((numEntries != mainHeader.numParts) || (partitions == NULL)) {
1735 newParts = (GPTPart*) calloc(numEntries, sizeof (GPTPart));
1736 if (newParts != NULL) {
1737 if (partitions != NULL) { // existing partitions; copy them over
1738 GetPartRange(&i, &high);
1739 if (numEntries < (high + 1)) { // Highest entry too high for new #
1740 printf("The highest-numbered partition is %lu, which is greater than the requested\n"
1741 "partition table size of %d; cannot resize. Perhaps sorting will help.\n",
1742 (unsigned long) (high + 1), numEntries);
1743 allOK = 0;
1744 } else { // go ahead with copy
1745 if (numEntries < mainHeader.numParts)
1746 copyNum = numEntries;
1747 else
1748 copyNum = mainHeader.numParts;
1749 for (i = 0; i < copyNum; i++) {
1750 newParts[i] = partitions[i];
1751 } // for
1752 trash = partitions;
1753 partitions = newParts;
1754 free(trash);
1755 } // if
1756 } else { // No existing partition table; just create it
srs5694e4ac11e2009-08-31 10:13:04 -04001757 partitions = newParts;
srs5694247657a2009-11-26 18:36:12 -05001758 } // if/else existing partitions
1759 mainHeader.numParts = numEntries;
1760 secondHeader.numParts = numEntries;
1761 mainHeader.firstUsableLBA = ((numEntries * GPT_SIZE) / blockSize) + 2 ;
1762 secondHeader.firstUsableLBA = mainHeader.firstUsableLBA;
1763 MoveSecondHeaderToEnd();
1764 if (diskSize > 0)
1765 CheckGPTSize();
1766 } else { // Bad memory allocation
1767 fprintf(stderr, "Error allocating memory for partition table!\n");
1768 allOK = 0;
1769 } // if/else
srs5694e4ac11e2009-08-31 10:13:04 -04001770 } // if/else
1771 return (allOK);
1772} // GPTData::SetGPTSize()
1773
1774// Blank the partition array
1775void GPTData::BlankPartitions(void) {
1776 uint32_t i;
1777
1778 for (i = 0; i < mainHeader.numParts; i++) {
1779 partitions[i].BlankPartition();
1780 } // for
1781} // GPTData::BlankPartitions()
1782
1783// Sort the GPT entries, eliminating gaps and making for a logical
1784// ordering. Relies on QuickSortGPT() for the bulk of the work
1785void GPTData::SortGPT(void) {
1786 int i, lastPart = 0;
1787 GPTPart temp;
1788
1789 // First, find the last partition with data, so as not to
1790 // spend needless time sorting empty entries....
1791 for (i = 0; i < mainHeader.numParts; i++) {
1792 if (partitions[i].GetFirstLBA() > 0)
1793 lastPart = i;
1794 } // for
1795
1796 // Now swap empties with the last partitions, to simplify the logic
1797 // in the Quicksort function....
1798 i = 0;
1799 while (i < lastPart) {
1800 if (partitions[i].GetFirstLBA() == 0) {
1801 temp = partitions[i];
1802 partitions[i] = partitions[lastPart];
1803 partitions[lastPart] = temp;
1804 lastPart--;
1805 } // if
1806 i++;
1807 } // while
1808
1809 // Now call the recursive quick sort routine to do the real work....
1810 QuickSortGPT(partitions, 0, lastPart);
1811} // GPTData::SortGPT()
1812
1813// Set up data structures for entirely new set of partitions on the
1814// specified device. Returns 1 if OK, 0 if there were problems.
srs5694e35eb1b2009-09-14 00:29:34 -04001815// Note that this function does NOT clear the protectiveMBR data
1816// structure, since it may hold the original MBR partitions if the
1817// program was launched on an MBR disk, and those may need to be
1818// converted to GPT format.
srs5694e4ac11e2009-08-31 10:13:04 -04001819int GPTData::ClearGPTData(void) {
srs5694e35eb1b2009-09-14 00:29:34 -04001820 int goOn = 1, i;
srs5694e4ac11e2009-08-31 10:13:04 -04001821
1822 // Set up the partition table....
1823 free(partitions);
1824 partitions = NULL;
1825 SetGPTSize(NUM_GPT_ENTRIES);
1826
1827 // Now initialize a bunch of stuff that's static....
1828 mainHeader.signature = GPT_SIGNATURE;
1829 mainHeader.revision = 0x00010000;
srs5694978041c2009-09-21 20:51:47 -04001830 mainHeader.headerSize = HEADER_SIZE;
srs5694e4ac11e2009-08-31 10:13:04 -04001831 mainHeader.reserved = 0;
1832 mainHeader.currentLBA = UINT64_C(1);
1833 mainHeader.partitionEntriesLBA = (uint64_t) 2;
1834 mainHeader.sizeOfPartitionEntries = GPT_SIZE;
1835 for (i = 0; i < GPT_RESERVED; i++) {
1836 mainHeader.reserved2[i] = '\0';
1837 } // for
1838
1839 // Now some semi-static items (computed based on end of disk)
1840 mainHeader.backupLBA = diskSize - UINT64_C(1);
1841 mainHeader.lastUsableLBA = diskSize - mainHeader.firstUsableLBA;
1842
1843 // Set a unique GUID for the disk, based on random numbers
1844 // rand() is only 32 bits, so multiply together to fill a 64-bit value
1845 mainHeader.diskGUID.data1 = (uint64_t) rand() * (uint64_t) rand();
1846 mainHeader.diskGUID.data2 = (uint64_t) rand() * (uint64_t) rand();
1847
1848 // Copy main header to backup header
1849 RebuildSecondHeader();
1850
1851 // Blank out the partitions array....
1852 BlankPartitions();
1853
1854 // Flag all CRCs as being OK....
1855 mainCrcOk = 1;
1856 secondCrcOk = 1;
1857 mainPartsCrcOk = 1;
1858 secondPartsCrcOk = 1;
1859
1860 return (goOn);
1861} // GPTData::ClearGPTData()
1862
srs5694247657a2009-11-26 18:36:12 -05001863// Set the location of the second GPT header data to the end of the disk.
1864// Used internally and called by the 'e' option on the recovery &
1865// transformation menu, to help users of RAID arrays who add disk space
1866// to their arrays.
1867void GPTData::MoveSecondHeaderToEnd() {
srs56948bb78762009-11-24 15:43:49 -05001868 mainHeader.backupLBA = secondHeader.currentLBA = diskSize - UINT64_C(1);
1869 mainHeader.lastUsableLBA = secondHeader.lastUsableLBA = diskSize - mainHeader.firstUsableLBA;
1870 secondHeader.partitionEntriesLBA = secondHeader.lastUsableLBA + UINT64_C(1);
1871} // GPTData::FixSecondHeaderLocation()
1872
srs5694e4ac11e2009-08-31 10:13:04 -04001873void GPTData::SetName(uint32_t partNum, char* theName) {
1874 if ((partNum >= 0) && (partNum < mainHeader.numParts))
1875 if (partitions[partNum].GetFirstLBA() > 0)
1876 partitions[partNum].SetName((unsigned char*) theName);
1877} // GPTData::SetName
1878
1879// Set the disk GUID to the specified value. Note that the header CRCs must
1880// be recomputed after calling this function.
1881void GPTData::SetDiskGUID(GUIDData newGUID) {
1882 mainHeader.diskGUID = newGUID;
1883 secondHeader.diskGUID = newGUID;
1884} // SetDiskGUID()
1885
1886// Set the unique GUID of the specified partition. Returns 1 on
1887// successful completion, 0 if there were problems (invalid
1888// partition number).
1889int GPTData::SetPartitionGUID(uint32_t pn, GUIDData theGUID) {
1890 int retval = 0;
1891
1892 if (pn < mainHeader.numParts) {
1893 if (partitions[pn].GetFirstLBA() != UINT64_C(0)) {
1894 partitions[pn].SetUniqueGUID(theGUID);
1895 retval = 1;
1896 } // if
1897 } // if
1898 return retval;
1899} // GPTData::SetPartitionGUID()
1900
srs56941d1448a2009-12-31 21:20:19 -05001901// Adjust sector number so that it falls on a sector boundary that's a
1902// multiple of sectorAlignment. This is done to improve the performance
1903// of Western Digital Advanced Format disks and disks with similar
1904// technology from other companies, which use 4096-byte sectors
1905// internally although they translate to 512-byte sectors for the
1906// benefit of the OS. If partitions aren't properly aligned on these
1907// disks, some filesystem data structures can span multiple physical
1908// sectors, degrading performance. This function should be called
1909// only on the FIRST sector of the partition, not the last!
1910// This function returns 1 if the alignment was altered, 0 if it
1911// was unchanged.
1912int GPTData::Align(uint64_t* sector) {
1913 int retval = 0, sectorOK = 0;
1914 uint64_t earlier, later, testSector, original;
1915
1916 if ((*sector % sectorAlignment) != 0) {
1917 original = *sector;
1918 retval = 1;
1919 earlier = (*sector / sectorAlignment) * sectorAlignment;
1920 later = earlier + (uint64_t) sectorAlignment;
1921
1922 // Check to see that every sector between the earlier one and the
1923 // requested one is clear, and that it's not too early....
1924 if (earlier >= mainHeader.firstUsableLBA) {
1925// printf("earlier is %llu, first usable is %llu\n", earlier, mainHeader.firstUsableLBA);
1926 sectorOK = 1;
1927 testSector = earlier;
1928 do {
1929 sectorOK = IsFree(testSector++);
1930 } while ((sectorOK == 1) && (testSector < *sector));
1931 if (sectorOK == 1) {
1932 *sector = earlier;
1933// printf("Moved sector earlier.\n");
1934 } // if
1935 } // if firstUsableLBA check
1936
1937 // If couldn't move the sector earlier, try to move it later instead....
1938 if ((sectorOK != 1) && (later <= mainHeader.lastUsableLBA)) {
1939 sectorOK = 1;
1940 testSector = later;
1941 do {
1942 sectorOK = IsFree(testSector--);
1943 } while ((sectorOK == 1) && (testSector > *sector));
1944 if (sectorOK == 1) {
1945 *sector = later;
1946// printf("Moved sector later\n");
1947 } // if
1948 } // if
1949
1950 // If sector was changed successfully, inform the user of this fact.
1951 // Otherwise, notify the user that it couldn't be done....
1952 if (sectorOK == 1) {
1953 printf("Information: Moved requested sector from %llu to %llu for\n"
1954 "alignment purposes. Use 'l' on the experts' menu to adjust alignment.\n",
1955 original, *sector);
1956 } else {
1957 printf("Information: Sector not aligned on %d-sector boundary and could not be moved.\n"
1958 "If you're using a Western Digital Advanced Format or similar disk with\n"
1959 "underlying 4096-byte sectors, performance may suffer.\n", sectorAlignment);
1960 retval = 0;
1961 } // if/else
1962 } // if
1963 return retval;
1964} // GPTData::Align()
1965
srs5694e4ac11e2009-08-31 10:13:04 -04001966/********************************************************
1967 * *
1968 * Functions that return data about GPT data structures *
1969 * (most of these are inline in gpt.h) *
1970 * *
1971 ********************************************************/
1972
1973// Find the low and high used partition numbers (numbered from 0).
1974// Return value is the number of partitions found. Note that the
1975// *low and *high values are both set to 0 when no partitions
1976// are found, as well as when a single partition in the first
1977// position exists. Thus, the return value is the only way to
1978// tell when no partitions exist.
1979int GPTData::GetPartRange(uint32_t *low, uint32_t *high) {
1980 uint32_t i;
1981 int numFound = 0;
1982
1983 *low = mainHeader.numParts + 1; // code for "not found"
1984 *high = 0;
1985 if (mainHeader.numParts > 0) { // only try if partition table exists...
1986 for (i = 0; i < mainHeader.numParts; i++) {
1987 if (partitions[i].GetFirstLBA() != UINT64_C(0)) { // it exists
1988 *high = i; // since we're counting up, set the high value
1989 // Set the low value only if it's not yet found...
1990 if (*low == (mainHeader.numParts + 1)) *low = i;
1991 numFound++;
1992 } // if
1993 } // for
1994 } // if
1995
1996 // Above will leave *low pointing to its "not found" value if no partitions
1997 // are defined, so reset to 0 if this is the case....
1998 if (*low == (mainHeader.numParts + 1))
1999 *low = 0;
2000 return numFound;
2001} // GPTData::GetPartRange()
2002
srs5694978041c2009-09-21 20:51:47 -04002003// Returns the number of defined partitions.
2004uint32_t GPTData::CountParts(void) {
2005 int i, counted = 0;
2006
2007 for (i = 0; i < mainHeader.numParts; i++) {
2008 if (partitions[i].GetFirstLBA() > 0)
2009 counted++;
2010 } // for
2011 return counted;
2012} // GPTData::CountParts()
2013
srs5694e4ac11e2009-08-31 10:13:04 -04002014/****************************************************
2015 * *
2016 * Functions that return data about disk free space *
2017 * *
2018 ****************************************************/
2019
2020// Find the first available block after the starting point; returns 0 if
2021// there are no available blocks left
2022uint64_t GPTData::FindFirstAvailable(uint64_t start) {
2023 uint64_t first;
2024 uint32_t i;
2025 int firstMoved = 0;
2026
2027 // Begin from the specified starting point or from the first usable
2028 // LBA, whichever is greater...
2029 if (start < mainHeader.firstUsableLBA)
2030 first = mainHeader.firstUsableLBA;
2031 else
2032 first = start;
2033
2034 // ...now search through all partitions; if first is within an
2035 // existing partition, move it to the next sector after that
2036 // partition and repeat. If first was moved, set firstMoved
2037 // flag; repeat until firstMoved is not set, so as to catch
2038 // cases where partitions are out of sequential order....
2039 do {
2040 firstMoved = 0;
2041 for (i = 0; i < mainHeader.numParts; i++) {
2042 if ((first >= partitions[i].GetFirstLBA()) &&
2043 (first <= partitions[i].GetLastLBA())) { // in existing part.
2044 first = partitions[i].GetLastLBA() + 1;
2045 firstMoved = 1;
2046 } // if
2047 } // for
2048 } while (firstMoved == 1);
2049 if (first > mainHeader.lastUsableLBA)
2050 first = 0;
2051 return (first);
2052} // GPTData::FindFirstAvailable()
2053
2054// Finds the first available sector in the largest block of unallocated
2055// space on the disk. Returns 0 if there are no available blocks left
2056uint64_t GPTData::FindFirstInLargest(void) {
srs5694e35eb1b2009-09-14 00:29:34 -04002057 uint64_t start, firstBlock, lastBlock, segmentSize, selectedSize = 0, selectedSegment = 0;
srs5694e4ac11e2009-08-31 10:13:04 -04002058
2059 start = 0;
2060 do {
2061 firstBlock = FindFirstAvailable(start);
2062 if (firstBlock != UINT32_C(0)) { // something's free...
2063 lastBlock = FindLastInFree(firstBlock);
2064 segmentSize = lastBlock - firstBlock + UINT32_C(1);
2065 if (segmentSize > selectedSize) {
2066 selectedSize = segmentSize;
2067 selectedSegment = firstBlock;
2068 } // if
2069 start = lastBlock + 1;
2070 } // if
2071 } while (firstBlock != 0);
2072 return selectedSegment;
2073} // GPTData::FindFirstInLargest()
2074
2075// Find the last available block on the disk at or after the start
2076// block. Returns 0 if there are no available partitions after
2077// (or including) start.
2078uint64_t GPTData::FindLastAvailable(uint64_t start) {
2079 uint64_t last;
2080 uint32_t i;
2081 int lastMoved = 0;
2082
2083 // Start by assuming the last usable LBA is available....
2084 last = mainHeader.lastUsableLBA;
2085
2086 // ...now, similar to algorithm in FindFirstAvailable(), search
2087 // through all partitions, moving last when it's in an existing
2088 // partition. Set the lastMoved flag so we repeat to catch cases
2089 // where partitions are out of logical order.
2090 do {
2091 lastMoved = 0;
2092 for (i = 0; i < mainHeader.numParts; i++) {
2093 if ((last >= partitions[i].GetFirstLBA()) &&
2094 (last <= partitions[i].GetLastLBA())) { // in existing part.
2095 last = partitions[i].GetFirstLBA() - 1;
2096 lastMoved = 1;
2097 } // if
2098 } // for
2099 } while (lastMoved == 1);
2100 if (last < mainHeader.firstUsableLBA)
2101 last = 0;
2102 return (last);
2103} // GPTData::FindLastAvailable()
2104
2105// Find the last available block in the free space pointed to by start.
2106uint64_t GPTData::FindLastInFree(uint64_t start) {
2107 uint64_t nearestStart;
2108 uint32_t i;
2109
2110 nearestStart = mainHeader.lastUsableLBA;
2111 for (i = 0; i < mainHeader.numParts; i++) {
2112 if ((nearestStart > partitions[i].GetFirstLBA()) &&
2113 (partitions[i].GetFirstLBA() > start)) {
2114 nearestStart = partitions[i].GetFirstLBA() - 1;
2115 } // if
2116 } // for
2117 return (nearestStart);
2118} // GPTData::FindLastInFree()
2119
2120// Finds the total number of free blocks, the number of segments in which
2121// they reside, and the size of the largest of those segments
2122uint64_t GPTData::FindFreeBlocks(int *numSegments, uint64_t *largestSegment) {
2123 uint64_t start = UINT64_C(0); // starting point for each search
2124 uint64_t totalFound = UINT64_C(0); // running total
2125 uint64_t firstBlock; // first block in a segment
2126 uint64_t lastBlock; // last block in a segment
2127 uint64_t segmentSize; // size of segment in blocks
2128 int num = 0;
2129
2130 *largestSegment = UINT64_C(0);
2131 do {
2132 firstBlock = FindFirstAvailable(start);
2133 if (firstBlock != UINT64_C(0)) { // something's free...
2134 lastBlock = FindLastInFree(firstBlock);
2135 segmentSize = lastBlock - firstBlock + UINT64_C(1);
2136 if (segmentSize > *largestSegment) {
2137 *largestSegment = segmentSize;
2138 } // if
2139 totalFound += segmentSize;
2140 num++;
2141 start = lastBlock + 1;
2142 } // if
2143 } while (firstBlock != 0);
2144 *numSegments = num;
2145 return totalFound;
2146} // GPTData::FindFreeBlocks()
2147
2148// Returns 1 if sector is unallocated, 0 if it's allocated to a partition
2149int GPTData::IsFree(uint64_t sector) {
2150 int isFree = 1;
2151 uint32_t i;
2152
2153 for (i = 0; i < mainHeader.numParts; i++) {
2154 if ((sector >= partitions[i].GetFirstLBA()) &&
2155 (sector <= partitions[i].GetLastLBA())) {
2156 isFree = 0;
2157 } // if
2158 } // for
srs5694e35eb1b2009-09-14 00:29:34 -04002159 if ((sector < mainHeader.firstUsableLBA) ||
srs5694e4ac11e2009-08-31 10:13:04 -04002160 (sector > mainHeader.lastUsableLBA)) {
2161 isFree = 0;
2162 } // if
2163 return (isFree);
2164} // GPTData::IsFree()
2165
2166/********************************
2167 * *
2168 * Endianness support functions *
2169 * *
2170 ********************************/
2171
srs56942a9f5da2009-08-26 00:48:01 -04002172void GPTData::ReverseHeaderBytes(struct GPTHeader* header) {
srs5694221e0872009-08-29 15:00:31 -04002173 ReverseBytes(&header->signature, 8);
2174 ReverseBytes(&header->revision, 4);
2175 ReverseBytes(&header->headerSize, 4);
2176 ReverseBytes(&header->headerCRC, 4);
2177 ReverseBytes(&header->reserved, 4);
2178 ReverseBytes(&header->currentLBA, 8);
2179 ReverseBytes(&header->backupLBA, 8);
2180 ReverseBytes(&header->firstUsableLBA, 8);
2181 ReverseBytes(&header->lastUsableLBA, 8);
2182 ReverseBytes(&header->partitionEntriesLBA, 8);
2183 ReverseBytes(&header->numParts, 4);
2184 ReverseBytes(&header->sizeOfPartitionEntries, 4);
2185 ReverseBytes(&header->partitionEntriesCRC, 4);
2186 ReverseBytes(&header->reserved2, GPT_RESERVED);
2187 ReverseBytes(&header->diskGUID.data1, 8);
2188 ReverseBytes(&header->diskGUID.data2, 8);
srs56942a9f5da2009-08-26 00:48:01 -04002189} // GPTData::ReverseHeaderBytes()
2190
2191// IMPORTANT NOTE: This function requires non-reversed mainHeader
2192// structure!
2193void GPTData::ReversePartitionBytes() {
2194 uint32_t i;
2195
2196 // Check GPT signature on big-endian systems; this will mismatch
2197 // if the function is called out of order. Unfortunately, it'll also
2198 // mismatch if there's data corruption.
2199 if ((mainHeader.signature != GPT_SIGNATURE) && (IsLittleEndian() == 0)) {
2200 printf("GPT signature mismatch in GPTData::ReversePartitionBytes(). This indicates\n"
srs5694e4ac11e2009-08-31 10:13:04 -04002201 "data corruption or a misplaced call to this function.\n");
srs56942a9f5da2009-08-26 00:48:01 -04002202 } // if signature mismatch....
2203 for (i = 0; i < mainHeader.numParts; i++) {
srs5694221e0872009-08-29 15:00:31 -04002204 partitions[i].ReversePartBytes();
srs56942a9f5da2009-08-26 00:48:01 -04002205 } // for
2206} // GPTData::ReversePartitionBytes()
2207
2208/******************************************
2209 * *
2210 * Additional non-class support functions *
2211 * *
2212 ******************************************/
2213
srs5694e7b4ff92009-08-18 13:16:10 -04002214// Check to be sure that data type sizes are correct. The basic types (uint*_t) should
2215// never fail these tests, but the struct types may fail depending on compile options.
2216// Specifically, the -fpack-struct option to gcc may be required to ensure proper structure
2217// sizes.
2218int SizesOK(void) {
2219 int allOK = 1;
srs5694e7b4ff92009-08-18 13:16:10 -04002220
2221 if (sizeof(uint8_t) != 1) {
srs56941d1448a2009-12-31 21:20:19 -05002222 fprintf(stderr, "uint8_t is %lu bytes, should be 1 byte; aborting!\n", sizeof(uint8_t));
srs5694e7b4ff92009-08-18 13:16:10 -04002223 allOK = 0;
2224 } // if
2225 if (sizeof(uint16_t) != 2) {
srs56941d1448a2009-12-31 21:20:19 -05002226 fprintf(stderr, "uint16_t is %lu bytes, should be 2 bytes; aborting!\n", sizeof(uint16_t));
srs5694e7b4ff92009-08-18 13:16:10 -04002227 allOK = 0;
2228 } // if
2229 if (sizeof(uint32_t) != 4) {
srs56941d1448a2009-12-31 21:20:19 -05002230 fprintf(stderr, "uint32_t is %lu bytes, should be 4 bytes; aborting!\n", sizeof(uint32_t));
srs5694e7b4ff92009-08-18 13:16:10 -04002231 allOK = 0;
2232 } // if
2233 if (sizeof(uint64_t) != 8) {
srs56941d1448a2009-12-31 21:20:19 -05002234 fprintf(stderr, "uint64_t is %lu bytes, should be 8 bytes; aborting!\n", sizeof(uint64_t));
srs5694e7b4ff92009-08-18 13:16:10 -04002235 allOK = 0;
2236 } // if
2237 if (sizeof(struct MBRRecord) != 16) {
srs56941d1448a2009-12-31 21:20:19 -05002238 fprintf(stderr, "MBRRecord is %lu bytes, should be 16 bytes; aborting!\n", sizeof(MBRRecord));
srs5694e7b4ff92009-08-18 13:16:10 -04002239 allOK = 0;
2240 } // if
srs5694978041c2009-09-21 20:51:47 -04002241 if (sizeof(struct TempMBR) != 512) {
srs56941d1448a2009-12-31 21:20:19 -05002242 fprintf(stderr, "TempMBR is %lu bytes, should be 512 bytes; aborting!\n", sizeof(TempMBR));
srs5694e7b4ff92009-08-18 13:16:10 -04002243 allOK = 0;
2244 } // if
2245 if (sizeof(struct GPTHeader) != 512) {
srs56941d1448a2009-12-31 21:20:19 -05002246 fprintf(stderr, "GPTHeader is %lu bytes, should be 512 bytes; aborting!\n", sizeof(GPTHeader));
srs5694e7b4ff92009-08-18 13:16:10 -04002247 allOK = 0;
2248 } // if
srs5694221e0872009-08-29 15:00:31 -04002249 if (sizeof(GPTPart) != 128) {
srs56941d1448a2009-12-31 21:20:19 -05002250 fprintf(stderr, "GPTPart is %lu bytes, should be 128 bytes; aborting!\n", sizeof(GPTPart));
srs5694221e0872009-08-29 15:00:31 -04002251 allOK = 0;
2252 } // if
2253// Determine endianness; set allOK = 0 if running on big-endian hardware
srs56942a9f5da2009-08-26 00:48:01 -04002254 if (IsLittleEndian() == 0) {
2255 fprintf(stderr, "\aRunning on big-endian hardware. Big-endian support is new and poorly"
srs5694e4ac11e2009-08-31 10:13:04 -04002256 " tested!\nBeware!\n");
srs56942a9f5da2009-08-26 00:48:01 -04002257 // allOK = 0;
srs5694e7b4ff92009-08-18 13:16:10 -04002258 } // if
2259 return (allOK);
2260} // SizesOK()
srs5694e4ac11e2009-08-31 10:13:04 -04002261