blob: 7204f2c0a271e88ad789c4597252ed5d4104d389 [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, "");
srs56945d58fe02010-01-03 20:57:08 -050043 justLooking = 0;
srs5694e7b4ff92009-08-18 13:16:10 -040044 mainCrcOk = 0;
45 secondCrcOk = 0;
46 mainPartsCrcOk = 0;
47 secondPartsCrcOk = 0;
srs5694221e0872009-08-29 15:00:31 -040048 apmFound = 0;
49 bsdFound = 0;
srs56941d1448a2009-12-31 21:20:19 -050050 sectorAlignment = 8; // Align partitions on 4096-byte boundaries by default
srs5694e7b4ff92009-08-18 13:16:10 -040051 srand((unsigned int) time(NULL));
52 SetGPTSize(NUM_GPT_ENTRIES);
53} // GPTData default constructor
54
55// The following constructor loads GPT data from a device file
56GPTData::GPTData(char* filename) {
57 blockSize = SECTOR_SIZE; // set a default
58 diskSize = 0;
59 partitions = NULL;
60 state = gpt_invalid;
61 strcpy(device, "");
srs56945d58fe02010-01-03 20:57:08 -050062 justLooking = 0;
srs5694e7b4ff92009-08-18 13:16:10 -040063 mainCrcOk = 0;
64 secondCrcOk = 0;
65 mainPartsCrcOk = 0;
66 secondPartsCrcOk = 0;
srs5694221e0872009-08-29 15:00:31 -040067 apmFound = 0;
68 bsdFound = 0;
srs56941d1448a2009-12-31 21:20:19 -050069 sectorAlignment = 8; // Align partitions on 4096-byte boundaries by default
srs5694e7b4ff92009-08-18 13:16:10 -040070 srand((unsigned int) time(NULL));
71 LoadPartitions(filename);
72} // GPTData(char* filename) constructor
73
srs5694e4ac11e2009-08-31 10:13:04 -040074// Destructor
srs5694e7b4ff92009-08-18 13:16:10 -040075GPTData::~GPTData(void) {
76 free(partitions);
77} // GPTData destructor
78
srs5694e4ac11e2009-08-31 10:13:04 -040079/*********************************************************************
80 * *
81 * Begin functions that verify data, or that adjust the verification *
82 * information (compute CRCs, rebuild headers) *
83 * *
84 *********************************************************************/
srs5694e7b4ff92009-08-18 13:16:10 -040085
srs5694e4ac11e2009-08-31 10:13:04 -040086// Perform detailed verification, reporting on any problems found, but
87// do *NOT* recover from these problems. Returns the total number of
88// problems identified.
89int GPTData::Verify(void) {
srs56941d1448a2009-12-31 21:20:19 -050090 int problems = 0, numSegments, i;
91 uint64_t totalFree, largestSegment, firstSector;
srs5694e4ac11e2009-08-31 10:13:04 -040092 char tempStr[255], siTotal[255], siLargest[255];
93
94 // First, check for CRC errors in the GPT data....
95 if (!mainCrcOk) {
96 problems++;
97 printf("\nProblem: The CRC for the main GPT header is invalid. The main GPT header may\n"
98 "be corrupt. Consider loading the backup GPT header to rebuild the main GPT\n"
99 "header\n");
100 } // if
101 if (!mainPartsCrcOk) {
102 problems++;
103 printf("\nProblem: The CRC for the main partition table is invalid. This table may be\n"
104 "corrupt. Consider loading the backup partition table.\n");
105 } // if
106 if (!secondCrcOk) {
107 problems++;
108 printf("\nProblem: The CRC for the backup GPT header is invalid. The backup GPT header\n"
109 "may be corrupt. Consider using the main GPT header to rebuild the backup GPT\n"
110 "header.\n");
111 } // if
112 if (!secondPartsCrcOk) {
113 problems++;
114 printf("\nCaution: The CRC for the backup partition table is invalid. This table may\n"
115 "be corrupt. This program will automatically create a new backup partition\n"
116 "table when you save your partitions.\n");
srs5694e7b4ff92009-08-18 13:16:10 -0400117 } // if
118
srs5694978041c2009-09-21 20:51:47 -0400119 // Now check that the main and backup headers both point to themselves....
120 if (mainHeader.currentLBA != 1) {
121 problems++;
122 printf("\nProblem: The main header's self-pointer doesn't point to itself. This problem\n"
123 "is being automatically corrected, but it may be a symptom of more serious\n"
124 "problems. Think carefully before saving changes with 'w' or using this disk.\n");
125 mainHeader.currentLBA = 1;
126 } // if
127 if (secondHeader.currentLBA != (diskSize - UINT64_C(1))) {
128 problems++;
srs56943f2fe992009-11-24 18:28:18 -0500129 printf("\nProblem: The secondary header's self-pointer indicates that it doesn't reside\n"
130 "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 -0500131 "option on the experts' menu to adjust the secondary header's and partition\n"
132 "table's locations.\n");
srs5694978041c2009-09-21 20:51:47 -0400133 } // if
134
135 // Now check that critical main and backup GPT entries match each other
srs5694e4ac11e2009-08-31 10:13:04 -0400136 if (mainHeader.currentLBA != secondHeader.backupLBA) {
137 problems++;
138 printf("\nProblem: main GPT header's current LBA pointer (%llu) doesn't\n"
139 "match the backup GPT header's LBA pointer(%llu)\n",
140 (unsigned long long) mainHeader.currentLBA,
141 (unsigned long long) secondHeader.backupLBA);
142 } // if
143 if (mainHeader.backupLBA != secondHeader.currentLBA) {
144 problems++;
145 printf("\nProblem: main GPT header's backup LBA pointer (%llu) doesn't\n"
146 "match the backup GPT header's current LBA pointer (%llu)\n",
147 (unsigned long long) mainHeader.backupLBA,
148 (unsigned long long) secondHeader.currentLBA);
149 } // if
150 if (mainHeader.firstUsableLBA != secondHeader.firstUsableLBA) {
151 problems++;
152 printf("\nProblem: main GPT header's first usable LBA pointer (%llu) doesn't\n"
153 "match the backup GPT header's first usable LBA pointer (%llu)\n",
154 (unsigned long long) mainHeader.firstUsableLBA,
155 (unsigned long long) secondHeader.firstUsableLBA);
156 } // if
157 if (mainHeader.lastUsableLBA != secondHeader.lastUsableLBA) {
158 problems++;
159 printf("\nProblem: main GPT header's last usable LBA pointer (%llu) doesn't\n"
160 "match the backup GPT header's last usable LBA pointer (%llu)\n",
161 (unsigned long long) mainHeader.lastUsableLBA,
162 (unsigned long long) secondHeader.lastUsableLBA);
163 } // if
164 if ((mainHeader.diskGUID.data1 != secondHeader.diskGUID.data1) ||
165 (mainHeader.diskGUID.data2 != secondHeader.diskGUID.data2)) {
166 problems++;
167 printf("\nProblem: main header's disk GUID (%s) doesn't\n",
168 GUIDToStr(mainHeader.diskGUID, tempStr));
169 printf("match the backup GPT header's disk GUID (%s)\n",
170 GUIDToStr(secondHeader.diskGUID, tempStr));
171 } // if
172 if (mainHeader.numParts != secondHeader.numParts) {
173 problems++;
174 printf("\nProblem: main GPT header's number of partitions (%lu) doesn't\n"
175 "match the backup GPT header's number of partitions (%lu)\n",
176 (unsigned long) mainHeader.numParts,
177 (unsigned long) secondHeader.numParts);
178 } // if
179 if (mainHeader.sizeOfPartitionEntries != secondHeader.sizeOfPartitionEntries) {
180 problems++;
181 printf("\nProblem: main GPT header's size of partition entries (%lu) doesn't\n"
182 "match the backup GPT header's size of partition entries (%lu)\n",
183 (unsigned long) mainHeader.sizeOfPartitionEntries,
184 (unsigned long) secondHeader.sizeOfPartitionEntries);
185 } // if
186
187 // Now check for a few other miscellaneous problems...
188 // Check that the disk size will hold the data...
189 if (mainHeader.backupLBA > diskSize) {
190 problems++;
191 printf("\nProblem: Disk is too small to hold all the data!\n");
192 printf("(Disk size is %llu sectors, needs to be %llu sectors.)\n",
193 (unsigned long long) diskSize,
194 (unsigned long long) mainHeader.backupLBA);
195 } // if
196
197 // Check for overlapping partitions....
198 problems += FindOverlaps();
199
200 // Check for mismatched MBR and GPT partitions...
201 problems += FindHybridMismatches();
202
203 // Verify that partitions don't run into GPT data areas....
204 problems += CheckGPTSize();
205
srs56941d1448a2009-12-31 21:20:19 -0500206 // Check that partitions are aligned on proper boundaries (for WD Advanced
207 // Format and similar disks)....
208 for (i = 0; i < mainHeader.numParts; i++) {
209 if ((partitions[i].GetFirstLBA() % sectorAlignment) != 0) {
210 printf("\nCaution: Partition %d doesn't begin on a %d-sector boundary. This may\n"
211 "result in degraded performance on some modern (2010 and later) hard disks.\n",
212 i + 1, sectorAlignment);
213 } // if
214 } // for
215
srs5694e4ac11e2009-08-31 10:13:04 -0400216 // Now compute available space, but only if no problems found, since
217 // problems could affect the results
218 if (problems == 0) {
219 totalFree = FindFreeBlocks(&numSegments, &largestSegment);
220 BytesToSI(totalFree * (uint64_t) blockSize, siTotal);
221 BytesToSI(largestSegment * (uint64_t) blockSize, siLargest);
222 printf("No problems found. %llu free sectors (%s) available in %u\n"
223 "segments, the largest of which is %llu sectors (%s) in size\n",
224 (unsigned long long) totalFree,
225 siTotal, numSegments, (unsigned long long) largestSegment,
226 siLargest);
227 } else {
228 printf("\nIdentified %d problems!\n", problems);
srs5694e7b4ff92009-08-18 13:16:10 -0400229 } // if/else
srs5694e4ac11e2009-08-31 10:13:04 -0400230
231 return (problems);
232} // GPTData::Verify()
srs5694e7b4ff92009-08-18 13:16:10 -0400233
234// Checks to see if the GPT tables overrun existing partitions; if they
srs5694221e0872009-08-29 15:00:31 -0400235// do, issues a warning but takes no action. Returns number of problems
236// detected (0 if OK, 1 to 2 if problems).
srs5694e7b4ff92009-08-18 13:16:10 -0400237int GPTData::CheckGPTSize(void) {
238 uint64_t overlap, firstUsedBlock, lastUsedBlock;
239 uint32_t i;
srs5694221e0872009-08-29 15:00:31 -0400240 int numProbs = 0;
srs5694e7b4ff92009-08-18 13:16:10 -0400241
242 // first, locate the first & last used blocks
243 firstUsedBlock = UINT64_MAX;
244 lastUsedBlock = 0;
245 for (i = 0; i < mainHeader.numParts; i++) {
srs5694221e0872009-08-29 15:00:31 -0400246 if ((partitions[i].GetFirstLBA() < firstUsedBlock) &&
srs5694e4ac11e2009-08-31 10:13:04 -0400247 (partitions[i].GetFirstLBA() != 0))
srs5694221e0872009-08-29 15:00:31 -0400248 firstUsedBlock = partitions[i].GetFirstLBA();
249 if (partitions[i].GetLastLBA() > lastUsedBlock)
250 lastUsedBlock = partitions[i].GetLastLBA();
srs5694e7b4ff92009-08-18 13:16:10 -0400251 } // for
252
253 // If the disk size is 0 (the default), then it means that various
254 // variables aren't yet set, so the below tests will be useless;
255 // therefore we should skip everything
256 if (diskSize != 0) {
257 if (mainHeader.firstUsableLBA > firstUsedBlock) {
258 overlap = mainHeader.firstUsableLBA - firstUsedBlock;
srs5694221e0872009-08-29 15:00:31 -0400259 printf("Warning! Main partition table overlaps the first partition by %lu blocks!\n",
260 (unsigned long) overlap);
261 if (firstUsedBlock > 2) {
262 printf("Try reducing the partition table size by %lu entries.\n",
263 (unsigned long) (overlap * 4));
264 printf("(Use the 's' item on the experts' menu.)\n");
265 } else {
266 printf("You will need to delete this partition or resize it in another utility.\n");
267 } // if/else
268 numProbs++;
srs5694e7b4ff92009-08-18 13:16:10 -0400269 } // Problem at start of disk
270 if (mainHeader.lastUsableLBA < lastUsedBlock) {
271 overlap = lastUsedBlock - mainHeader.lastUsableLBA;
srs5694221e0872009-08-29 15:00:31 -0400272 printf("Warning! Secondary partition table overlaps the last partition by %lu blocks\n",
273 (unsigned long) overlap);
274 if (lastUsedBlock > (diskSize - 2)) {
275 printf("You will need to delete this partition or resize it in another utility.\n");
276 } else {
277 printf("Try reducing the partition table size by %lu entries.\n",
278 (unsigned long) (overlap * 4));
279 printf("(Use the 's' item on the experts' menu.)\n");
280 } // if/else
281 numProbs++;
srs5694e7b4ff92009-08-18 13:16:10 -0400282 } // Problem at end of disk
283 } // if (diskSize != 0)
srs5694221e0872009-08-29 15:00:31 -0400284 return numProbs;
srs5694e7b4ff92009-08-18 13:16:10 -0400285} // GPTData::CheckGPTSize()
286
srs5694e7b4ff92009-08-18 13:16:10 -0400287// Check the validity of the GPT header. Returns 1 if the main header
288// is valid, 2 if the backup header is valid, 3 if both are valid, and
289// 0 if neither is valid. Note that this function just checks the GPT
290// signature and revision numbers, not CRCs or other data.
291int GPTData::CheckHeaderValidity(void) {
292 int valid = 3;
293
294 if (mainHeader.signature != GPT_SIGNATURE) {
295 valid -= 1;
srs5694221e0872009-08-29 15:00:31 -0400296// printf("Main GPT signature invalid; read 0x%016llX, should be\n0x%016llX\n",
297// (unsigned long long) mainHeader.signature, (unsigned long long) GPT_SIGNATURE);
srs5694e7b4ff92009-08-18 13:16:10 -0400298 } else if ((mainHeader.revision != 0x00010000) && valid) {
299 valid -= 1;
300 printf("Unsupported GPT version in main header; read 0x%08lX, should be\n0x%08lX\n",
srs56945d58fe02010-01-03 20:57:08 -0500301 (unsigned long) mainHeader.revision, (unsigned long) UINT32_C(0x00010000));
srs5694e7b4ff92009-08-18 13:16:10 -0400302 } // if/else/if
303
304 if (secondHeader.signature != GPT_SIGNATURE) {
305 valid -= 2;
srs5694221e0872009-08-29 15:00:31 -0400306// printf("Secondary GPT signature invalid; read 0x%016llX, should be\n0x%016llX\n",
307// (unsigned long long) secondHeader.signature, (unsigned long long) GPT_SIGNATURE);
srs5694e7b4ff92009-08-18 13:16:10 -0400308 } else if ((secondHeader.revision != 0x00010000) && valid) {
309 valid -= 2;
310 printf("Unsupported GPT version in backup header; read 0x%08lX, should be\n0x%08lX\n",
srs56945d58fe02010-01-03 20:57:08 -0500311 (unsigned long) mainHeader.revision, (unsigned long) UINT32_C(0x00010000));
srs5694e7b4ff92009-08-18 13:16:10 -0400312 } // if/else/if
313
srs56942a9f5da2009-08-26 00:48:01 -0400314 // If MBR bad, check for an Apple disk signature
srs5694e35eb1b2009-09-14 00:29:34 -0400315 if ((protectiveMBR.GetValidity() == invalid) &&
srs5694e4ac11e2009-08-31 10:13:04 -0400316 (((mainHeader.signature << 32) == APM_SIGNATURE1) ||
srs56942a9f5da2009-08-26 00:48:01 -0400317 (mainHeader.signature << 32) == APM_SIGNATURE2)) {
srs5694221e0872009-08-29 15:00:31 -0400318 apmFound = 1; // Will display warning message later
srs56943f2fe992009-11-24 18:28:18 -0500319 } // if
srs56942a9f5da2009-08-26 00:48:01 -0400320
srs5694e4ac11e2009-08-31 10:13:04 -0400321 return valid;
srs5694e7b4ff92009-08-18 13:16:10 -0400322} // GPTData::CheckHeaderValidity()
323
324// Check the header CRC to see if it's OK...
srs56942a9f5da2009-08-26 00:48:01 -0400325// Note: Must be called BEFORE byte-order reversal on big-endian
326// systems!
srs5694e7b4ff92009-08-18 13:16:10 -0400327int GPTData::CheckHeaderCRC(struct GPTHeader* header) {
srs5694978041c2009-09-21 20:51:47 -0400328 uint32_t oldCRC, newCRC, hSize;
srs5694e7b4ff92009-08-18 13:16:10 -0400329
srs56942a9f5da2009-08-26 00:48:01 -0400330 // Back up old header CRC and then blank it, since it must be 0 for
srs5694e7b4ff92009-08-18 13:16:10 -0400331 // computation to be valid
332 oldCRC = header->headerCRC;
333 header->headerCRC = UINT32_C(0);
srs5694978041c2009-09-21 20:51:47 -0400334 hSize = header->headerSize;
335
336 // If big-endian system, reverse byte order
337 if (IsLittleEndian() == 0) {
338 ReverseBytes(&oldCRC, 4);
339 } // if
srs5694e7b4ff92009-08-18 13:16:10 -0400340
341 // Initialize CRC functions...
342 chksum_crc32gentab();
343
344 // Compute CRC, restore original, and return result of comparison
345 newCRC = chksum_crc32((unsigned char*) header, HEADER_SIZE);
srs5694978041c2009-09-21 20:51:47 -0400346 header->headerCRC = oldCRC;
srs5694e7b4ff92009-08-18 13:16:10 -0400347 return (oldCRC == newCRC);
348} // GPTData::CheckHeaderCRC()
349
srs56942a9f5da2009-08-26 00:48:01 -0400350// Recompute all the CRCs. Must be called before saving (but after reversing
351// byte order on big-endian systems) if any changes have been made.
srs5694e7b4ff92009-08-18 13:16:10 -0400352void GPTData::RecomputeCRCs(void) {
srs5694978041c2009-09-21 20:51:47 -0400353 uint32_t crc, hSize, trueNumParts;
srs56942a9f5da2009-08-26 00:48:01 -0400354 int littleEndian = 1;
srs5694e7b4ff92009-08-18 13:16:10 -0400355
356 // Initialize CRC functions...
357 chksum_crc32gentab();
358
srs5694978041c2009-09-21 20:51:47 -0400359 hSize = mainHeader.headerSize;
srs56942a9f5da2009-08-26 00:48:01 -0400360 littleEndian = IsLittleEndian();
361
srs5694e7b4ff92009-08-18 13:16:10 -0400362 // Compute CRC of partition tables & store in main and secondary headers
srs56942a9f5da2009-08-26 00:48:01 -0400363 trueNumParts = mainHeader.numParts;
364 if (littleEndian == 0)
srs5694221e0872009-08-29 15:00:31 -0400365 ReverseBytes(&trueNumParts, 4); // unreverse this key piece of data....
srs56942a9f5da2009-08-26 00:48:01 -0400366 crc = chksum_crc32((unsigned char*) partitions, trueNumParts * GPT_SIZE);
srs5694e7b4ff92009-08-18 13:16:10 -0400367 mainHeader.partitionEntriesCRC = crc;
368 secondHeader.partitionEntriesCRC = crc;
srs56942a9f5da2009-08-26 00:48:01 -0400369 if (littleEndian == 0) {
srs5694221e0872009-08-29 15:00:31 -0400370 ReverseBytes(&mainHeader.partitionEntriesCRC, 4);
371 ReverseBytes(&secondHeader.partitionEntriesCRC, 4);
srs56942a9f5da2009-08-26 00:48:01 -0400372 } // if
srs5694e7b4ff92009-08-18 13:16:10 -0400373
374 // Zero out GPT tables' own CRCs (required for correct computation)
375 mainHeader.headerCRC = 0;
376 secondHeader.headerCRC = 0;
377
378 // Compute & store CRCs of main & secondary headers...
srs5694978041c2009-09-21 20:51:47 -0400379 crc = chksum_crc32((unsigned char*) &mainHeader, hSize);
srs56942a9f5da2009-08-26 00:48:01 -0400380 if (littleEndian == 0)
srs5694221e0872009-08-29 15:00:31 -0400381 ReverseBytes(&crc, 4);
srs5694e7b4ff92009-08-18 13:16:10 -0400382 mainHeader.headerCRC = crc;
srs5694978041c2009-09-21 20:51:47 -0400383 crc = chksum_crc32((unsigned char*) &secondHeader, hSize);
srs56942a9f5da2009-08-26 00:48:01 -0400384 if (littleEndian == 0)
srs5694221e0872009-08-29 15:00:31 -0400385 ReverseBytes(&crc, 4);
srs5694e7b4ff92009-08-18 13:16:10 -0400386 secondHeader.headerCRC = crc;
387} // GPTData::RecomputeCRCs()
388
srs5694e7b4ff92009-08-18 13:16:10 -0400389// Rebuild the main GPT header, using the secondary header as a model.
390// Typically called when the main header has been found to be corrupt.
391void GPTData::RebuildMainHeader(void) {
392 int i;
393
394 mainHeader.signature = GPT_SIGNATURE;
395 mainHeader.revision = secondHeader.revision;
srs5694978041c2009-09-21 20:51:47 -0400396 mainHeader.headerSize = secondHeader.headerSize;
srs5694e7b4ff92009-08-18 13:16:10 -0400397 mainHeader.headerCRC = UINT32_C(0);
398 mainHeader.reserved = secondHeader.reserved;
399 mainHeader.currentLBA = secondHeader.backupLBA;
400 mainHeader.backupLBA = secondHeader.currentLBA;
401 mainHeader.firstUsableLBA = secondHeader.firstUsableLBA;
402 mainHeader.lastUsableLBA = secondHeader.lastUsableLBA;
403 mainHeader.diskGUID.data1 = secondHeader.diskGUID.data1;
404 mainHeader.diskGUID.data2 = secondHeader.diskGUID.data2;
405 mainHeader.partitionEntriesLBA = UINT64_C(2);
406 mainHeader.numParts = secondHeader.numParts;
407 mainHeader.sizeOfPartitionEntries = secondHeader.sizeOfPartitionEntries;
408 mainHeader.partitionEntriesCRC = secondHeader.partitionEntriesCRC;
409 for (i = 0 ; i < GPT_RESERVED; i++)
410 mainHeader.reserved2[i] = secondHeader.reserved2[i];
411} // GPTData::RebuildMainHeader()
412
413// Rebuild the secondary GPT header, using the main header as a model.
414void GPTData::RebuildSecondHeader(void) {
415 int i;
416
417 secondHeader.signature = GPT_SIGNATURE;
418 secondHeader.revision = mainHeader.revision;
srs5694978041c2009-09-21 20:51:47 -0400419 secondHeader.headerSize = mainHeader.headerSize;
srs5694e7b4ff92009-08-18 13:16:10 -0400420 secondHeader.headerCRC = UINT32_C(0);
421 secondHeader.reserved = mainHeader.reserved;
422 secondHeader.currentLBA = mainHeader.backupLBA;
423 secondHeader.backupLBA = mainHeader.currentLBA;
424 secondHeader.firstUsableLBA = mainHeader.firstUsableLBA;
425 secondHeader.lastUsableLBA = mainHeader.lastUsableLBA;
426 secondHeader.diskGUID.data1 = mainHeader.diskGUID.data1;
427 secondHeader.diskGUID.data2 = mainHeader.diskGUID.data2;
428 secondHeader.partitionEntriesLBA = secondHeader.lastUsableLBA + UINT64_C(1);
429 secondHeader.numParts = mainHeader.numParts;
430 secondHeader.sizeOfPartitionEntries = mainHeader.sizeOfPartitionEntries;
431 secondHeader.partitionEntriesCRC = mainHeader.partitionEntriesCRC;
432 for (i = 0 ; i < GPT_RESERVED; i++)
433 secondHeader.reserved2[i] = mainHeader.reserved2[i];
srs5694e4ac11e2009-08-31 10:13:04 -0400434} // GPTData::RebuildSecondHeader()
435
436// Search for hybrid MBR entries that have no corresponding GPT partition.
437// Returns number of such mismatches found
438int GPTData::FindHybridMismatches(void) {
439 int i, j, found, numFound = 0;
440 uint64_t mbrFirst, mbrLast;
441
442 for (i = 0; i < 4; i++) {
443 if ((protectiveMBR.GetType(i) != 0xEE) && (protectiveMBR.GetType(i) != 0x00)) {
444 j = 0;
445 found = 0;
446 do {
447 mbrFirst = (uint64_t) protectiveMBR.GetFirstSector(i);
448 mbrLast = mbrFirst + (uint64_t) protectiveMBR.GetLength(i) - UINT64_C(1);
449 if ((partitions[j].GetFirstLBA() == mbrFirst) &&
450 (partitions[j].GetLastLBA() == mbrLast))
451 found = 1;
452 j++;
453 } while ((!found) && (j < mainHeader.numParts));
454 if (!found) {
455 numFound++;
456 printf("\nWarning! Mismatched GPT and MBR partitions! MBR partition "
457 "%d, of type 0x%02X,\nhas no corresponding GPT partition! "
458 "You may continue, but this condition\nmight cause data loss"
459 " in the future!\a\n", i + 1, protectiveMBR.GetType(i));
460 } // if
461 } // if
462 } // for
463 return numFound;
464} // GPTData::FindHybridMismatches
465
466// Find overlapping partitions and warn user about them. Returns number of
467// overlapping partitions.
468int GPTData::FindOverlaps(void) {
469 int i, j, problems = 0;
470
471 for (i = 1; i < mainHeader.numParts; i++) {
472 for (j = 0; j < i; j++) {
473 if (partitions[i].DoTheyOverlap(&partitions[j])) {
474 problems++;
475 printf("\nProblem: partitions %d and %d overlap:\n", i + 1, j + 1);
476 printf(" Partition %d: %llu to %llu\n", i,
477 (unsigned long long) partitions[i].GetFirstLBA(),
478 (unsigned long long) partitions[i].GetLastLBA());
479 printf(" Partition %d: %llu to %llu\n", j,
480 (unsigned long long) partitions[j].GetFirstLBA(),
481 (unsigned long long) partitions[j].GetLastLBA());
482 } // if
483 } // for j...
484 } // for i...
485 return problems;
486} // GPTData::FindOverlaps()
487
488/******************************************************************
489 * *
490 * Begin functions that load data from disk or save data to disk. *
491 * *
492 ******************************************************************/
493
494// Scan for partition data. This function loads the MBR data (regular MBR or
495// protective MBR) and loads BSD disklabel data (which is probably invalid).
496// It also looks for APM data, forces a load of GPT data, and summarizes
497// the results.
498void GPTData::PartitionScan(int fd) {
499 BSDData bsdDisklabel;
500// int bsdFound;
501
502 printf("Partition table scan:\n");
503
504 // Read the MBR & check for BSD disklabel
505 protectiveMBR.ReadMBRData(fd);
506 protectiveMBR.ShowState();
507 bsdDisklabel.ReadBSDData(fd, 0, diskSize - 1);
508 bsdFound = bsdDisklabel.ShowState();
509// bsdDisklabel.DisplayBSDData();
510
511 // Load the GPT data, whether or not it's valid
512 ForceLoadGPTData(fd);
513 ShowAPMState(); // Show whether there's an Apple Partition Map present
514 ShowGPTState(); // Show GPT status
515 printf("\n");
516
517 if (apmFound) {
518 printf("\n*******************************************************************\n");
519 printf("This disk appears to contain an Apple-format (APM) partition table!\n");
srs56945d58fe02010-01-03 20:57:08 -0500520 if (!justLooking) {
521 printf("It will be destroyed if you continue!\n");
522 } // if
srs5694e4ac11e2009-08-31 10:13:04 -0400523 printf("*******************************************************************\n\n\a");
524 } // if
srs5694e4ac11e2009-08-31 10:13:04 -0400525} // GPTData::PartitionScan()
526
527// Read GPT data from a disk.
528int GPTData::LoadPartitions(char* deviceFilename) {
529 int fd, err;
530 int allOK = 1, i;
531 uint64_t firstBlock, lastBlock;
532 BSDData bsdDisklabel;
533
534 // First, do a test to see if writing will be possible later....
535 fd = OpenForWrite(deviceFilename);
srs56945d58fe02010-01-03 20:57:08 -0500536 if ((fd == -1) && (!justLooking)) {
srs5694e4ac11e2009-08-31 10:13:04 -0400537 printf("\aNOTE: Write test failed with error number %d. It will be "
538 "impossible to save\nchanges to this disk's partition table!\n\n",
539 errno);
srs56945d58fe02010-01-03 20:57:08 -0500540 justLooking = 1;
541 } // if
srs5694e4ac11e2009-08-31 10:13:04 -0400542 close(fd);
543
544 if ((fd = open(deviceFilename, O_RDONLY)) != -1) {
545 // store disk information....
546 diskSize = disksize(fd, &err);
547 blockSize = (uint32_t) GetBlockSize(fd);
srs56945d58fe02010-01-03 20:57:08 -0500548 sectorAlignment = FindAlignment(fd);
srs5694e4ac11e2009-08-31 10:13:04 -0400549 strcpy(device, deviceFilename);
550 PartitionScan(fd); // Check for partition types & print summary
551
552 switch (UseWhichPartitions()) {
553 case use_mbr:
554 XFormPartitions();
555 break;
556 case use_bsd:
557 bsdDisklabel.ReadBSDData(fd, 0, diskSize - 1);
558// bsdDisklabel.DisplayBSDData();
559 ClearGPTData();
560 protectiveMBR.MakeProtectiveMBR(1); // clear boot area (option 1)
561 XFormDisklabel(&bsdDisklabel, 0);
562 break;
563 case use_gpt:
564 break;
565 case use_new:
566 ClearGPTData();
567 protectiveMBR.MakeProtectiveMBR();
568 break;
569 } // switch
570
571 // Now find the first and last sectors used by partitions...
572 if (allOK) {
573 firstBlock = mainHeader.backupLBA; // start high
574 lastBlock = 0; // start low
575 for (i = 0; i < mainHeader.numParts; i++) {
576 if ((partitions[i].GetFirstLBA() < firstBlock) &&
577 (partitions[i].GetFirstLBA() > 0))
578 firstBlock = partitions[i].GetFirstLBA();
579 if (partitions[i].GetLastLBA() > lastBlock)
580 lastBlock = partitions[i].GetLastLBA();
581 } // for
582 } // if
583 CheckGPTSize();
584 } else {
585 allOK = 0;
586 fprintf(stderr, "Problem opening %s for reading! Error is %d\n",
587 deviceFilename, errno);
588 if (errno == EACCES) { // User is probably not running as root
589 fprintf(stderr, "You must run this program as root or use sudo!\n");
590 } // if
591 } // if/else
592 return (allOK);
593} // GPTData::LoadPartitions()
594
595// Loads the GPT, as much as possible. Returns 1 if this seems to have
596// succeeded, 0 if there are obvious problems....
597int GPTData::ForceLoadGPTData(int fd) {
598 int allOK = 1, validHeaders;
599 off_t seekTo;
600 char* storage;
601 uint32_t newCRC, sizeOfParts;
602
603 // Seek to and read the main GPT header
604 lseek64(fd, 512, SEEK_SET);
srs56945d58fe02010-01-03 20:57:08 -0500605 if (read(fd, &mainHeader, 512) != 512) { // read main GPT header
606 fprintf(stderr, "Warning! Error %d reading secondary GPT header!", errno);
607 } // if
srs5694e4ac11e2009-08-31 10:13:04 -0400608 mainCrcOk = CheckHeaderCRC(&mainHeader);
609 if (IsLittleEndian() == 0) // big-endian system; adjust header byte order....
610 ReverseHeaderBytes(&mainHeader);
611
srs56943f2fe992009-11-24 18:28:18 -0500612 // Load backup header, check its CRC, and store the results of the
613 // check for future reference. Load backup header using pointer in main
614 // header if possible; but if main header has a CRC error, or if it
615 // points to beyond the end of the disk, load the last sector of the
616 // disk instead.
617 if (mainCrcOk) {
618 if (mainHeader.backupLBA < diskSize) {
619 seekTo = mainHeader.backupLBA * blockSize;
620 } else {
621 seekTo = (diskSize * blockSize) - UINT64_C(512);
622 printf("Warning! Disk size is smaller than the main header indicates! Loading\n"
623 "secondary header from the last sector of the disk! You should use 'v' to\n"
624 "verify disk integrity, and perhaps options on the experts' menu to repair\n"
625 "the disk.\n");
626 } // else
627 } else {
628 seekTo = (diskSize * blockSize) - UINT64_C(512);
629 } // if/else (mainCrcOk)
630
srs5694e4ac11e2009-08-31 10:13:04 -0400631 if (lseek64(fd, seekTo, SEEK_SET) != (off_t) -1) {
srs56945d58fe02010-01-03 20:57:08 -0500632 if (read(fd, &secondHeader, 512) != 512) { // read secondary GPT header
633 fprintf(stderr, "Warning! Error %d reading secondary GPT header!", errno);
634 } // if
srs5694e4ac11e2009-08-31 10:13:04 -0400635 secondCrcOk = CheckHeaderCRC(&secondHeader);
636 if (IsLittleEndian() == 0) // big-endian system; adjust header byte order....
637 ReverseHeaderBytes(&secondHeader);
638 } else {
639 allOK = 0;
640 state = gpt_invalid;
srs56943f2fe992009-11-24 18:28:18 -0500641 fprintf(stderr, "Unable to seek to secondary GPT header at sector %llu!\n",
srs56945d58fe02010-01-03 20:57:08 -0500642 (unsigned long long) (diskSize - (UINT64_C(1))));
srs5694e4ac11e2009-08-31 10:13:04 -0400643 } // if/else lseek
644
645 // Return valid headers code: 0 = both headers bad; 1 = main header
646 // good, backup bad; 2 = backup header good, main header bad;
647 // 3 = both headers good. Note these codes refer to valid GPT
648 // signatures and version numbers; more subtle problems will elude
649 // this check!
650 validHeaders = CheckHeaderValidity();
651
652 // Read partitions (from primary array)
653 if (validHeaders > 0) { // if at least one header is OK....
654 // GPT appears to be valid....
655 state = gpt_valid;
656
657 // We're calling the GPT valid, but there's a possibility that one
658 // of the two headers is corrupt. If so, use the one that seems to
659 // be in better shape to regenerate the bad one
660 if (validHeaders == 2) { // valid backup header, invalid main header
661 printf("Caution: invalid main GPT header, but valid backup; regenerating main header\n"
662 "from backup!\n");
663 RebuildMainHeader();
664 mainCrcOk = secondCrcOk; // Since copied, use CRC validity of backup
665 } else if (validHeaders == 1) { // valid main header, invalid backup
666 printf("Caution: invalid backup GPT header, but valid main header; regenerating\n"
667 "backup header from main header.\n");
668 RebuildSecondHeader();
669 secondCrcOk = mainCrcOk; // Since regenerated, use CRC validity of main
670 } // if/else/if
671
672 // Load the main partition table, including storing results of its
673 // CRC check
674 if (LoadMainTable() == 0)
675 allOK = 0;
676
677 // Load backup partition table into temporary storage to check
678 // its CRC and store the results, then discard this temporary
679 // storage, since we don't use it in any but recovery operations
680 seekTo = secondHeader.partitionEntriesLBA * (off_t) blockSize;
681 if ((lseek64(fd, seekTo, SEEK_SET) != (off_t) -1) && (secondCrcOk)) {
682 sizeOfParts = secondHeader.numParts * secondHeader.sizeOfPartitionEntries;
683 storage = (char*) malloc(sizeOfParts);
srs56945d58fe02010-01-03 20:57:08 -0500684 if (read(fd, storage, sizeOfParts) != sizeOfParts) {
685 fprintf(stderr, "Warning! Error %d reading backup partition table!\n", errno);
686 } // if
srs5694e4ac11e2009-08-31 10:13:04 -0400687 newCRC = chksum_crc32((unsigned char*) storage, sizeOfParts);
688 free(storage);
689 secondPartsCrcOk = (newCRC == secondHeader.partitionEntriesCRC);
690 } // if
691
692 // Check for valid CRCs and warn if there are problems
693 if ((mainCrcOk == 0) || (secondCrcOk == 0) || (mainPartsCrcOk == 0) ||
694 (secondPartsCrcOk == 0)) {
695 printf("Warning! One or more CRCs don't match. You should repair the disk!\n");
696 state = gpt_corrupt;
697 } // if
698 } else {
699 state = gpt_invalid;
700 } // if/else
701 return allOK;
702} // GPTData::ForceLoadGPTData()
703
srs5694247657a2009-11-26 18:36:12 -0500704// Loads the partition table pointed to by the main GPT header. The
srs5694e4ac11e2009-08-31 10:13:04 -0400705// main GPT header in memory MUST be valid for this call to do anything
706// sensible!
707int GPTData::LoadMainTable(void) {
708 int fd, retval = 0;
709 uint32_t newCRC, sizeOfParts;
710
711 if ((fd = open(device, O_RDONLY)) != -1) {
712 // Set internal data structures for number of partitions on the disk
713 SetGPTSize(mainHeader.numParts);
714
715 // Load main partition table, and record whether its CRC
716 // matches the stored value
717 lseek64(fd, mainHeader.partitionEntriesLBA * blockSize, SEEK_SET);
718 sizeOfParts = mainHeader.numParts * mainHeader.sizeOfPartitionEntries;
srs56945d58fe02010-01-03 20:57:08 -0500719 if (read(fd, partitions, sizeOfParts) != sizeOfParts) {
720 fprintf(stderr, "Warning! Error %d when loading the main partition table!\n", errno);
721 } // if
srs5694e4ac11e2009-08-31 10:13:04 -0400722 newCRC = chksum_crc32((unsigned char*) partitions, sizeOfParts);
723 mainPartsCrcOk = (newCRC == mainHeader.partitionEntriesCRC);
724 if (IsLittleEndian() == 0)
725 ReversePartitionBytes();
726 retval = 1;
727 } // if
728 return retval;
729} // GPTData::LoadMainTable()
srs5694e7b4ff92009-08-18 13:16:10 -0400730
731// Load the second (backup) partition table as the primary partition
732// table. Used in repair functions
733void GPTData::LoadSecondTableAsMain(void) {
734 int fd;
735 off_t seekTo;
736 uint32_t sizeOfParts, newCRC;
737
738 if ((fd = open(device, O_RDONLY)) != -1) {
739 seekTo = secondHeader.partitionEntriesLBA * (off_t) blockSize;
740 if (lseek64(fd, seekTo, SEEK_SET) != (off_t) -1) {
741 SetGPTSize(secondHeader.numParts);
742 sizeOfParts = secondHeader.numParts * secondHeader.sizeOfPartitionEntries;
srs56945d58fe02010-01-03 20:57:08 -0500743 if (read(fd, partitions, sizeOfParts) != sizeOfParts) {
744 fprintf(stderr, "Warning! Read error %d! Misbehavior now likely!\n", errno);
745 } // if
srs5694e7b4ff92009-08-18 13:16:10 -0400746 newCRC = chksum_crc32((unsigned char*) partitions, sizeOfParts);
747 secondPartsCrcOk = (newCRC == secondHeader.partitionEntriesCRC);
srs5694e4ac11e2009-08-31 10:13:04 -0400748 mainPartsCrcOk = secondPartsCrcOk;
srs56942a9f5da2009-08-26 00:48:01 -0400749 if (IsLittleEndian() == 0)
750 ReversePartitionBytes();
srs5694e7b4ff92009-08-18 13:16:10 -0400751 if (!secondPartsCrcOk) {
752 printf("Error! After loading backup partitions, the CRC still doesn't check out!\n");
753 } // if
754 } else {
755 printf("Error! Couldn't seek to backup partition table!\n");
756 } // if/else
757 } else {
srs56941d1448a2009-12-31 21:20:19 -0500758 printf("Error! Couldn't open device %s when recovering backup partition table!\n", device);
srs5694e7b4ff92009-08-18 13:16:10 -0400759 } // if/else
760} // GPTData::LoadSecondTableAsMain()
761
srs5694e7b4ff92009-08-18 13:16:10 -0400762// Writes GPT (and protective MBR) to disk. Returns 1 on successful
763// write, 0 if there was a problem.
764int GPTData::SaveGPTData(void) {
srs5694978041c2009-09-21 20:51:47 -0400765 int allOK = 1;
srs5694e7b4ff92009-08-18 13:16:10 -0400766 char answer, line[256];
767 int fd;
768 uint64_t secondTable;
srs56942a9f5da2009-08-26 00:48:01 -0400769 uint32_t numParts;
srs5694e7b4ff92009-08-18 13:16:10 -0400770 off_t offset;
771
772 if (strlen(device) == 0) {
773 printf("Device not defined.\n");
774 } // if
775
776 // First do some final sanity checks....
srs56945d58fe02010-01-03 20:57:08 -0500777
778 // This test should only fail on read-only disks....
779 if (justLooking) {
780 printf("The justLooking flag is set. This probably means you can't write to the disk.\n");
781 allOK = 0;
782 } // if
783
srs5694e7b4ff92009-08-18 13:16:10 -0400784 // Is there enough space to hold the GPT headers and partition tables,
785 // given the partition sizes?
srs5694221e0872009-08-29 15:00:31 -0400786 if (CheckGPTSize() > 0) {
srs5694e7b4ff92009-08-18 13:16:10 -0400787 allOK = 0;
788 } // if
789
790 // Check that disk is really big enough to handle this...
791 if (mainHeader.backupLBA > diskSize) {
srs5694247657a2009-11-26 18:36:12 -0500792 fprintf(stderr, "Error! Disk is too small! The 'e' option on the experts' menu might fix the\n"
793 "problem (or it might not). Aborting!\n");
srs56945d58fe02010-01-03 20:57:08 -0500794 printf("(Disk size is %llu sectors, needs to be %llu sectors.)\n",
795 (unsigned long long) diskSize, (unsigned long long) mainHeader.backupLBA);
srs5694e7b4ff92009-08-18 13:16:10 -0400796 allOK = 0;
797 } // if
srs5694247657a2009-11-26 18:36:12 -0500798 // Check that second header is properly placed. Warn and ask if this should
799 // be corrected if the test fails....
800 if (mainHeader.backupLBA < (diskSize - UINT64_C(1))) {
801 printf("Warning! Secondary header is placed too early on the disk! Do you want to\n"
802 "correct this problem? ");
803 if (GetYN() == 'Y') {
804 MoveSecondHeaderToEnd();
805 printf("Have moved second header and partition table to correct location.\n");
806 } else {
807 printf("Have not corrected the problem. Strange problems may occur in the future!\n");
808 } // if correction requested
809 } // if
srs5694e7b4ff92009-08-18 13:16:10 -0400810
811 // Check for overlapping partitions....
srs5694e4ac11e2009-08-31 10:13:04 -0400812 if (FindOverlaps() > 0) {
813 allOK = 0;
814 fprintf(stderr, "Aborting write operation!\n");
815 } // if
816
817 // Check for mismatched MBR and GPT data, but let it pass if found
818 // (function displays warning message)
819 FindHybridMismatches();
srs5694e7b4ff92009-08-18 13:16:10 -0400820
srs56942a9f5da2009-08-26 00:48:01 -0400821 // Pull out some data that's needed before doing byte-order reversal on
822 // big-endian systems....
823 numParts = mainHeader.numParts;
824 secondTable = secondHeader.partitionEntriesLBA;
825 if (IsLittleEndian() == 0) {
826 // Reverse partition bytes first, since that function requires non-reversed
827 // data from the main header....
828 ReversePartitionBytes();
829 ReverseHeaderBytes(&mainHeader);
830 ReverseHeaderBytes(&secondHeader);
831 } // if
srs5694e7b4ff92009-08-18 13:16:10 -0400832 RecomputeCRCs();
833
834 if (allOK) {
835 printf("\nFinal checks complete. About to write GPT data. THIS WILL OVERWRITE EXISTING\n");
836 printf("MBR PARTITIONS!! THIS PROGRAM IS BETA QUALITY AT BEST. IF YOU LOSE ALL YOUR\n");
837 printf("DATA, YOU HAVE ONLY YOURSELF TO BLAME IF YOU ANSWER 'Y' BELOW!\n\n");
srs56945d58fe02010-01-03 20:57:08 -0500838 printf("Do you want to proceed, possibly destroying your data? ");
839 answer = GetYN();
840 if (answer == 'Y') {
841 printf("OK; writing new GUID partition table (GPT).\n");
srs5694e7b4ff92009-08-18 13:16:10 -0400842 } else {
843 allOK = 0;
844 } // if/else
845 } // if
846
847 // Do it!
848 if (allOK) {
srs5694e4ac11e2009-08-31 10:13:04 -0400849 fd = OpenForWrite(device);
srs5694e7b4ff92009-08-18 13:16:10 -0400850 if (fd != -1) {
851 // First, write the protective MBR...
srs5694e4ac11e2009-08-31 10:13:04 -0400852 protectiveMBR.WriteMBRData(fd);
srs5694e7b4ff92009-08-18 13:16:10 -0400853
854 // Now write the main GPT header...
855 if (allOK)
856 if (write(fd, &mainHeader, 512) == -1)
srs5694e4ac11e2009-08-31 10:13:04 -0400857 allOK = 0;
srs5694e7b4ff92009-08-18 13:16:10 -0400858
859 // Now write the main partition tables...
srs5694e4ac11e2009-08-31 10:13:04 -0400860 if (allOK) {
861 if (write(fd, partitions, GPT_SIZE * numParts) == -1)
srs5694e7b4ff92009-08-18 13:16:10 -0400862 allOK = 0;
863 } // if
864
865 // Now seek to near the end to write the secondary GPT....
srs5694e4ac11e2009-08-31 10:13:04 -0400866 if (allOK) {
srs5694e7b4ff92009-08-18 13:16:10 -0400867 offset = (off_t) secondTable * (off_t) (blockSize);
868 if (lseek64(fd, offset, SEEK_SET) == (off_t) - 1) {
869 allOK = 0;
870 printf("Unable to seek to end of disk!\n");
871 } // if
872 } // if
873
874 // Now write the secondary partition tables....
srs5694e4ac11e2009-08-31 10:13:04 -0400875 if (allOK)
srs56942a9f5da2009-08-26 00:48:01 -0400876 if (write(fd, partitions, GPT_SIZE * numParts) == -1)
srs5694e7b4ff92009-08-18 13:16:10 -0400877 allOK = 0;
878
879 // Now write the secondary GPT header...
srs5694e4ac11e2009-08-31 10:13:04 -0400880 if (allOK)
srs5694e7b4ff92009-08-18 13:16:10 -0400881 if (write(fd, &secondHeader, 512) == -1)
srs5694e4ac11e2009-08-31 10:13:04 -0400882 allOK = 0;
srs5694e7b4ff92009-08-18 13:16:10 -0400883
884 // re-read the partition table
885 if (allOK) {
srs5694e35eb1b2009-09-14 00:29:34 -0400886 DiskSync(fd);
srs5694e7b4ff92009-08-18 13:16:10 -0400887 } // if
888
889 if (allOK) { // writes completed OK
srs5694e4ac11e2009-08-31 10:13:04 -0400890 printf("The operation has completed successfully.\n");
srs5694e7b4ff92009-08-18 13:16:10 -0400891 } else {
srs5694e4ac11e2009-08-31 10:13:04 -0400892 printf("Warning! An error was reported when writing the partition table! This error\n");
893 printf("MIGHT be harmless, but you may have trashed the disk! Use parted and, if\n");
894 printf("necessary, restore your original partition table.\n");
srs5694e7b4ff92009-08-18 13:16:10 -0400895 } // if/else
srs5694e4ac11e2009-08-31 10:13:04 -0400896 close(fd);
srs5694e7b4ff92009-08-18 13:16:10 -0400897 } else {
srs5694e4ac11e2009-08-31 10:13:04 -0400898 fprintf(stderr, "Unable to open device %s for writing! Errno is %d! Aborting write!\n",
899 device, errno);
900 allOK = 0;
srs5694e7b4ff92009-08-18 13:16:10 -0400901 } // if/else
902 } else {
903 printf("Aborting write of new partition table.\n");
904 } // if
905
srs56942a9f5da2009-08-26 00:48:01 -0400906 if (IsLittleEndian() == 0) {
907 // Reverse (normalize) header bytes first, since ReversePartitionBytes()
908 // requires non-reversed data in mainHeader...
909 ReverseHeaderBytes(&mainHeader);
910 ReverseHeaderBytes(&secondHeader);
911 ReversePartitionBytes();
912 } // if
913
srs5694e7b4ff92009-08-18 13:16:10 -0400914 return (allOK);
915} // GPTData::SaveGPTData()
916
917// Save GPT data to a backup file. This function does much less error
918// checking than SaveGPTData(). It can therefore preserve many types of
919// corruption for later analysis; however, it preserves only the MBR,
920// the main GPT header, the backup GPT header, and the main partition
921// table; it discards the backup partition table, since it should be
922// identical to the main partition table on healthy disks.
923int GPTData::SaveGPTBackup(char* filename) {
srs56942a9f5da2009-08-26 00:48:01 -0400924 int fd, allOK = 1;
925 uint32_t numParts;
srs5694e7b4ff92009-08-18 13:16:10 -0400926
927 if ((fd = open(filename, O_WRONLY | O_CREAT, S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH)) != -1) {
srs56942a9f5da2009-08-26 00:48:01 -0400928 // Reverse the byte order, if necessary....
929 numParts = mainHeader.numParts;
930 if (IsLittleEndian() == 0) {
931 ReversePartitionBytes();
932 ReverseHeaderBytes(&mainHeader);
933 ReverseHeaderBytes(&secondHeader);
934 } // if
935
srs5694978041c2009-09-21 20:51:47 -0400936 // Recomputing the CRCs is likely to alter them, which could be bad
937 // if the intent is to save a potentially bad GPT for later analysis;
938 // but if we don't do this, we get bogus errors when we load the
939 // backup. I'm favoring misses over false alarms....
940 RecomputeCRCs();
941
srs56942a9f5da2009-08-26 00:48:01 -0400942 // Now write the protective MBR...
srs5694e7b4ff92009-08-18 13:16:10 -0400943 protectiveMBR.WriteMBRData(fd);
944
945 // Now write the main GPT header...
946 if (allOK)
947 if (write(fd, &mainHeader, 512) == -1)
948 allOK = 0;
949
950 // Now write the secondary GPT header...
951 if (allOK)
952 if (write(fd, &secondHeader, 512) == -1)
srs5694e4ac11e2009-08-31 10:13:04 -0400953 allOK = 0;
srs5694e7b4ff92009-08-18 13:16:10 -0400954
955 // Now write the main partition tables...
956 if (allOK) {
srs56942a9f5da2009-08-26 00:48:01 -0400957 if (write(fd, partitions, GPT_SIZE * numParts) == -1)
srs5694e7b4ff92009-08-18 13:16:10 -0400958 allOK = 0;
959 } // if
960
961 if (allOK) { // writes completed OK
962 printf("The operation has completed successfully.\n");
963 } else {
964 printf("Warning! An error was reported when writing the backup file.\n");
srs56948bb78762009-11-24 15:43:49 -0500965 printf("It may not be usable!\n");
srs5694e7b4ff92009-08-18 13:16:10 -0400966 } // if/else
967 close(fd);
srs56942a9f5da2009-08-26 00:48:01 -0400968
969 // Now reverse the byte-order reversal, if necessary....
970 if (IsLittleEndian() == 0) {
971 ReverseHeaderBytes(&mainHeader);
972 ReverseHeaderBytes(&secondHeader);
973 ReversePartitionBytes();
974 } // if
srs5694e7b4ff92009-08-18 13:16:10 -0400975 } else {
976 fprintf(stderr, "Unable to open file %s for writing! Aborting!\n", filename);
977 allOK = 0;
978 } // if/else
979 return allOK;
980} // GPTData::SaveGPTBackup()
981
982// Load GPT data from a backup file created by SaveGPTBackup(). This function
983// does minimal error checking. It returns 1 if it completed successfully,
984// 0 if there was a problem. In the latter case, it creates a new empty
985// set of partitions.
986int GPTData::LoadGPTBackup(char* filename) {
987 int fd, allOK = 1, val;
988 uint32_t numParts, sizeOfEntries, sizeOfParts, newCRC;
srs56942a9f5da2009-08-26 00:48:01 -0400989 int littleEndian = 1;
srs5694e7b4ff92009-08-18 13:16:10 -0400990
991 if ((fd = open(filename, O_RDONLY)) != -1) {
srs56942a9f5da2009-08-26 00:48:01 -0400992 if (IsLittleEndian() == 0)
993 littleEndian = 0;
994
srs5694e7b4ff92009-08-18 13:16:10 -0400995 // Let the MBRData class load the saved MBR...
srs5694221e0872009-08-29 15:00:31 -0400996 protectiveMBR.ReadMBRData(fd, 0); // 0 = don't check block size
srs5694e7b4ff92009-08-18 13:16:10 -0400997
998 // Load the main GPT header, check its vaility, and set the GPT
999 // size based on the data
srs56945d58fe02010-01-03 20:57:08 -05001000 if (read(fd, &mainHeader, 512)) {
1001 fprintf(stderr, "Warning! Read error %d; strange behavior now likely!\n", errno);
1002 } // if
srs5694e7b4ff92009-08-18 13:16:10 -04001003 mainCrcOk = CheckHeaderCRC(&mainHeader);
1004
srs56942a9f5da2009-08-26 00:48:01 -04001005 // Reverse byte order, if necessary
1006 if (littleEndian == 0) {
1007 ReverseHeaderBytes(&mainHeader);
1008 } // if
1009
srs5694e7b4ff92009-08-18 13:16:10 -04001010 // Load the backup GPT header in much the same way as the main
1011 // GPT header....
srs56945d58fe02010-01-03 20:57:08 -05001012 if (read(fd, &secondHeader, 512) != 512) {
1013 fprintf(stderr, "Warning! Read error %d; strange behavior now likely!\n", errno);
1014 } // if
srs5694e7b4ff92009-08-18 13:16:10 -04001015 secondCrcOk = CheckHeaderCRC(&secondHeader);
1016
srs56942a9f5da2009-08-26 00:48:01 -04001017 // Reverse byte order, if necessary
1018 if (littleEndian == 0) {
1019 ReverseHeaderBytes(&secondHeader);
1020 } // if
1021
srs5694e7b4ff92009-08-18 13:16:10 -04001022 // Return valid headers code: 0 = both headers bad; 1 = main header
1023 // good, backup bad; 2 = backup header good, main header bad;
1024 // 3 = both headers good. Note these codes refer to valid GPT
1025 // signatures and version numbers; more subtle problems will elude
1026 // this check!
1027 if ((val = CheckHeaderValidity()) > 0) {
1028 if (val == 2) { // only backup header seems to be good
1029 numParts = secondHeader.numParts;
srs5694e4ac11e2009-08-31 10:13:04 -04001030 sizeOfEntries = secondHeader.sizeOfPartitionEntries;
srs5694e7b4ff92009-08-18 13:16:10 -04001031 } else { // main header is OK
1032 numParts = mainHeader.numParts;
1033 sizeOfEntries = mainHeader.sizeOfPartitionEntries;
1034 } // if/else
1035
1036 SetGPTSize(numParts);
1037
1038 // If current disk size doesn't match that of backup....
1039 if (secondHeader.currentLBA != diskSize - UINT64_C(1)) {
1040 printf("Warning! Current disk size doesn't match that of the backup!\n"
srs5694e4ac11e2009-08-31 10:13:04 -04001041 "Adjusting sizes to match, but subsequent problems are possible!\n");
srs5694247657a2009-11-26 18:36:12 -05001042 MoveSecondHeaderToEnd();
srs5694e7b4ff92009-08-18 13:16:10 -04001043 } // if
1044
1045 // Load main partition table, and record whether its CRC
1046 // matches the stored value
1047 sizeOfParts = numParts * sizeOfEntries;
srs56945d58fe02010-01-03 20:57:08 -05001048 if (read(fd, partitions, sizeOfParts) != sizeOfParts) {
1049 fprintf(stderr, "Warning! Read error %d; strange behavior now likely!\n", errno);
1050 } // if
srs5694e7b4ff92009-08-18 13:16:10 -04001051
1052 newCRC = chksum_crc32((unsigned char*) partitions, sizeOfParts);
1053 mainPartsCrcOk = (newCRC == mainHeader.partitionEntriesCRC);
1054 secondPartsCrcOk = (newCRC == secondHeader.partitionEntriesCRC);
srs56942a9f5da2009-08-26 00:48:01 -04001055 // Reverse byte order, if necessary
srs5694e4ac11e2009-08-31 10:13:04 -04001056 if (littleEndian == 0) {
1057 ReversePartitionBytes();
1058 } // if
srs56942a9f5da2009-08-26 00:48:01 -04001059
srs5694e7b4ff92009-08-18 13:16:10 -04001060 } else {
1061 allOK = 0;
1062 } // if/else
1063 } else {
1064 allOK = 0;
1065 fprintf(stderr, "Unable to open file %s for reading! Aborting!\n", filename);
1066 } // if/else
1067
1068 // Something went badly wrong, so blank out partitions
1069 if (allOK == 0) {
1070 ClearGPTData();
1071 protectiveMBR.MakeProtectiveMBR();
1072 } // if
1073 return allOK;
1074} // GPTData::LoadGPTBackup()
1075
srs5694e4ac11e2009-08-31 10:13:04 -04001076// Tell user whether Apple Partition Map (APM) was discovered....
1077void GPTData::ShowAPMState(void) {
1078 if (apmFound)
1079 printf(" APM: present\n");
1080 else
1081 printf(" APM: not present\n");
1082} // GPTData::ShowAPMState()
1083
1084// Tell user about the state of the GPT data....
1085void GPTData::ShowGPTState(void) {
1086 switch (state) {
1087 case gpt_invalid:
1088 printf(" GPT: not present\n");
1089 break;
1090 case gpt_valid:
1091 printf(" GPT: present\n");
1092 break;
1093 case gpt_corrupt:
1094 printf(" GPT: damaged\n");
1095 break;
1096 default:
1097 printf("\a GPT: unknown -- bug!\n");
1098 break;
1099 } // switch
1100} // GPTData::ShowGPTState()
1101
1102// Display the basic GPT data
1103void GPTData::DisplayGPTData(void) {
srs5694e35eb1b2009-09-14 00:29:34 -04001104 int i;
srs5694e4ac11e2009-08-31 10:13:04 -04001105 char sizeInSI[255]; // String to hold size of disk in SI units
1106 char tempStr[255];
1107 uint64_t temp, totalFree;
1108
1109 BytesToSI(diskSize * blockSize, sizeInSI);
srs5694978041c2009-09-21 20:51:47 -04001110 printf("Disk %s: %llu sectors, %s\n", device,
1111 (unsigned long long) diskSize, sizeInSI);
srs5694e4ac11e2009-08-31 10:13:04 -04001112 printf("Disk identifier (GUID): %s\n", GUIDToStr(mainHeader.diskGUID, tempStr));
1113 printf("Partition table holds up to %lu entries\n", (unsigned long) mainHeader.numParts);
srs56945d58fe02010-01-03 20:57:08 -05001114 printf("First usable sector is %llu, last usable sector is %llu\n",
1115 (unsigned long long) mainHeader.firstUsableLBA,
1116 (unsigned long long) mainHeader.lastUsableLBA);
srs5694e4ac11e2009-08-31 10:13:04 -04001117 totalFree = FindFreeBlocks(&i, &temp);
srs56945d58fe02010-01-03 20:57:08 -05001118 printf("Total free space is %llu sectors (%s)\n", (unsigned long long) totalFree,
srs5694e4ac11e2009-08-31 10:13:04 -04001119 BytesToSI(totalFree * (uint64_t) blockSize, sizeInSI));
1120 printf("\nNumber Start (sector) End (sector) Size Code Name\n");
1121 for (i = 0; i < mainHeader.numParts; i++) {
srs5694978041c2009-09-21 20:51:47 -04001122 partitions[i].ShowSummary(i, blockSize);
srs5694e4ac11e2009-08-31 10:13:04 -04001123 } // for
1124} // GPTData::DisplayGPTData()
1125
1126// Get partition number from user and then call ShowPartDetails(partNum)
1127// to show its detailed information
1128void GPTData::ShowDetails(void) {
1129 int partNum;
1130 uint32_t low, high;
1131
1132 if (GetPartRange(&low, &high) > 0) {
1133 partNum = GetPartNum();
1134 ShowPartDetails(partNum);
1135 } else {
1136 printf("No partitions\n");
1137 } // if/else
1138} // GPTData::ShowDetails()
1139
1140// Show detailed information on the specified partition
1141void GPTData::ShowPartDetails(uint32_t partNum) {
1142 if (partitions[partNum].GetFirstLBA() != 0) {
1143 partitions[partNum].ShowDetails(blockSize);
1144 } else {
1145 printf("Partition #%d does not exist.", (int) (partNum + 1));
1146 } // if
1147} // GPTData::ShowPartDetails()
1148
1149/*********************************************************************
1150 * *
1151 * Begin functions that obtain information from the users, and often *
1152 * do something with that information (call other functions) *
1153 * *
1154 *********************************************************************/
1155
1156// Prompts user for partition number and returns the result.
1157uint32_t GPTData::GetPartNum(void) {
1158 uint32_t partNum;
1159 uint32_t low, high;
1160 char prompt[255];
1161
1162 if (GetPartRange(&low, &high) > 0) {
1163 sprintf(prompt, "Partition number (%d-%d): ", low + 1, high + 1);
1164 partNum = GetNumber(low + 1, high + 1, low, prompt);
1165 } else partNum = 1;
1166 return (partNum - 1);
1167} // GPTData::GetPartNum()
1168
1169// What it says: Resize the partition table. (Default is 128 entries.)
1170void GPTData::ResizePartitionTable(void) {
1171 int newSize;
1172 char prompt[255];
1173 uint32_t curLow, curHigh;
1174
1175 printf("Current partition table size is %lu.\n",
1176 (unsigned long) mainHeader.numParts);
1177 GetPartRange(&curLow, &curHigh);
1178 curHigh++; // since GetPartRange() returns numbers starting from 0...
1179 // There's no point in having fewer than four partitions....
1180 if (curHigh < 4)
1181 curHigh = 4;
1182 sprintf(prompt, "Enter new size (%d up, default %d): ", (int) curHigh,
1183 (int) NUM_GPT_ENTRIES);
1184 newSize = GetNumber(4, 65535, 128, prompt);
1185 if (newSize < 128) {
1186 printf("Caution: The partition table size should officially be 16KB or larger,\n"
1187 "which works out to 128 entries. In practice, smaller tables seem to\n"
1188 "work with most OSes, but this practice is risky. I'm proceeding with\n"
1189 "the resize, but you may want to reconsider this action and undo it.\n\n");
1190 } // if
1191 SetGPTSize(newSize);
1192} // GPTData::ResizePartitionTable()
1193
1194// Interactively create a partition
1195void GPTData::CreatePartition(void) {
1196 uint64_t firstBlock, firstInLargest, lastBlock, sector;
1197 char prompt[255];
1198 int partNum, firstFreePart = 0;
1199
1200 // Find first free partition...
1201 while (partitions[firstFreePart].GetFirstLBA() != 0) {
1202 firstFreePart++;
1203 } // while
1204
1205 if (((firstBlock = FindFirstAvailable()) != 0) &&
1206 (firstFreePart < mainHeader.numParts)) {
1207 lastBlock = FindLastAvailable(firstBlock);
1208 firstInLargest = FindFirstInLargest();
1209
1210 // Get partition number....
1211 do {
1212 sprintf(prompt, "Partition number (%d-%d, default %d): ", firstFreePart + 1,
1213 mainHeader.numParts, firstFreePart + 1);
1214 partNum = GetNumber(firstFreePart + 1, mainHeader.numParts,
1215 firstFreePart + 1, prompt) - 1;
1216 if (partitions[partNum].GetFirstLBA() != 0)
1217 printf("partition %d is in use.\n", partNum + 1);
1218 } while (partitions[partNum].GetFirstLBA() != 0);
1219
1220 // Get first block for new partition...
srs56945d58fe02010-01-03 20:57:08 -05001221 sprintf(prompt, "First sector (%llu-%llu, default = %llu) or {+-}size{KMGT}: ",
1222 (unsigned long long) firstBlock, (unsigned long long) lastBlock,
1223 (unsigned long long) firstInLargest);
srs5694e4ac11e2009-08-31 10:13:04 -04001224 do {
1225 sector = GetSectorNum(firstBlock, lastBlock, firstInLargest, prompt);
1226 } while (IsFree(sector) == 0);
srs56941d1448a2009-12-31 21:20:19 -05001227 Align(&sector); // Align sector to correct multiple
srs5694e4ac11e2009-08-31 10:13:04 -04001228 firstBlock = sector;
1229
1230 // Get last block for new partitions...
1231 lastBlock = FindLastInFree(firstBlock);
srs56945d58fe02010-01-03 20:57:08 -05001232 sprintf(prompt, "Last sector (%llu-%llu, default = %llu) or {+-}size{KMGT}: ",
1233 (unsigned long long) firstBlock, (unsigned long long) lastBlock,
1234 (unsigned long long) lastBlock);
srs5694e4ac11e2009-08-31 10:13:04 -04001235 do {
1236 sector = GetSectorNum(firstBlock, lastBlock, lastBlock, prompt);
1237 } while (IsFree(sector) == 0);
1238 lastBlock = sector;
1239
1240 partitions[partNum].SetFirstLBA(firstBlock);
1241 partitions[partNum].SetLastLBA(lastBlock);
1242
1243 partitions[partNum].SetUniqueGUID(1);
1244 partitions[partNum].ChangeType();
1245 partitions[partNum].SetName((unsigned char*) partitions[partNum].GetNameType(prompt));
1246 } else {
1247 printf("No free sectors available\n");
1248 } // if/else
1249} // GPTData::CreatePartition()
1250
1251// Interactively delete a partition (duh!)
1252void GPTData::DeletePartition(void) {
1253 int partNum;
1254 uint32_t low, high;
1255 uint64_t startSector, length;
1256 char prompt[255];
1257
1258 if (GetPartRange(&low, &high) > 0) {
1259 sprintf(prompt, "Partition number (%d-%d): ", low + 1, high + 1);
1260 partNum = GetNumber(low + 1, high + 1, low, prompt);
1261
1262 // In case there's a protective MBR, look for & delete matching
1263 // MBR partition....
1264 startSector = partitions[partNum - 1].GetFirstLBA();
1265 length = partitions[partNum - 1].GetLengthLBA();
1266 protectiveMBR.DeleteByLocation(startSector, length);
1267
1268 // Now delete the GPT partition
1269 partitions[partNum - 1].BlankPartition();
1270 } else {
1271 printf("No partitions\n");
1272 } // if/else
1273} // GPTData::DeletePartition()
1274
1275// Prompt user for a partition number, then change its type code
1276// using ChangeGPTType(struct GPTPartition*) function.
1277void GPTData::ChangePartType(void) {
1278 int partNum;
1279 uint32_t low, high;
1280
1281 if (GetPartRange(&low, &high) > 0) {
1282 partNum = GetPartNum();
1283 partitions[partNum].ChangeType();
1284 } else {
1285 printf("No partitions\n");
1286 } // if/else
1287} // GPTData::ChangePartType()
1288
1289// Partition attributes seem to be rarely used, but I want a way to
1290// adjust them for completeness....
1291void GPTData::SetAttributes(uint32_t partNum) {
1292 Attributes theAttr;
1293
1294 theAttr.SetAttributes(partitions[partNum].GetAttributes());
1295 theAttr.DisplayAttributes();
1296 theAttr.ChangeAttributes();
1297 partitions[partNum].SetAttributes(theAttr.GetAttributes());
1298} // GPTData::SetAttributes()
1299
srs5694c0ca8f82009-08-20 21:35:25 -04001300// This function destroys the on-disk GPT structures. Returns 1 if the
1301// user confirms destruction, 0 if the user aborts.
srs5694978041c2009-09-21 20:51:47 -04001302// If prompt == 0, don't ask user about proceeding and do NOT wipe out
1303// MBR. (Set prompt == 0 when doing a GPT-to-MBR conversion.)
1304int GPTData::DestroyGPT(int prompt) {
srs56945d58fe02010-01-03 20:57:08 -05001305 int fd, i, sum;
srs5694978041c2009-09-21 20:51:47 -04001306 char blankSector[512], goOn = 'Y', blank = 'N';
srs5694c0ca8f82009-08-20 21:35:25 -04001307
1308 for (i = 0; i < 512; i++) {
1309 blankSector[i] = '\0';
1310 } // for
1311
srs5694978041c2009-09-21 20:51:47 -04001312 if (((apmFound) || (bsdFound)) && prompt) {
srs5694e35eb1b2009-09-14 00:29:34 -04001313 printf("WARNING: APM or BSD disklabel structures detected! This operation could\n"
1314 "damage any APM or BSD partitions on this disk!\n");
1315 } // if APM or BSD
srs5694978041c2009-09-21 20:51:47 -04001316 if (prompt) {
1317 printf("\a\aAbout to wipe out GPT on %s. Proceed? ", device);
1318 goOn = GetYN();
1319 } // if
srs5694c0ca8f82009-08-20 21:35:25 -04001320 if (goOn == 'Y') {
1321 fd = open(device, O_WRONLY);
1322#ifdef __APPLE__
1323 // MacOS X requires a shared lock under some circumstances....
1324 if (fd < 0) {
1325 fd = open(device, O_WRONLY|O_SHLOCK);
1326 } // if
1327#endif
1328 if (fd != -1) {
1329 lseek64(fd, mainHeader.currentLBA * 512, SEEK_SET); // seek to GPT header
srs56945d58fe02010-01-03 20:57:08 -05001330 if (write(fd, blankSector, 512) != 512) { // blank it out
1331 fprintf(stderr, "Warning! GPT main header not overwritten! Error is %d\n", errno);
1332 } // if
srs5694c0ca8f82009-08-20 21:35:25 -04001333 lseek64(fd, mainHeader.partitionEntriesLBA * 512, SEEK_SET); // seek to partition table
srs56945d58fe02010-01-03 20:57:08 -05001334 sum = 0;
srs5694c0ca8f82009-08-20 21:35:25 -04001335 for (i = 0; i < GetBlocksInPartTable(); i++)
srs56945d58fe02010-01-03 20:57:08 -05001336 sum += write(fd, blankSector, 512);
1337 if (sum != 512 * GetBlocksInPartTable())
1338 fprintf(stderr, "Warning! GPT main partition table not overwritten! Error is %d\n", errno);
srs5694c0ca8f82009-08-20 21:35:25 -04001339 lseek64(fd, secondHeader.partitionEntriesLBA * 512, SEEK_SET); // seek to partition table
srs56945d58fe02010-01-03 20:57:08 -05001340 sum = 0;
srs5694c0ca8f82009-08-20 21:35:25 -04001341 for (i = 0; i < GetBlocksInPartTable(); i++)
srs56945d58fe02010-01-03 20:57:08 -05001342 sum += write(fd, blankSector, 512);
1343 if (sum != 512 * GetBlocksInPartTable())
1344 fprintf(stderr, "Warning! GPT backup partition table not overwritten! Error is %d\n", errno);
srs5694c0ca8f82009-08-20 21:35:25 -04001345 lseek64(fd, secondHeader.currentLBA * 512, SEEK_SET); // seek to GPT header
srs56945d58fe02010-01-03 20:57:08 -05001346 if (write(fd, blankSector, 512) != 512) { // blank it out
1347 fprintf(stderr, "Warning! GPT backup header not overwritten! Error is %d\n", errno);
1348 } // if
srs5694978041c2009-09-21 20:51:47 -04001349 if (prompt) {
1350 printf("Blank out MBR? ");
1351 blank = GetYN();
1352 }// if
1353 // Note on below: Touch the MBR only if the user wants it completely
1354 // blanked out. Version 0.4.2 deleted the 0xEE partition and re-wrote
1355 // the MBR, but this could wipe out a valid MBR that the program
1356 // had subsequently discarded (say, if it conflicted with older GPT
1357 // structures).
1358 if (blank == 'Y') {
srs5694c0ca8f82009-08-20 21:35:25 -04001359 lseek64(fd, 0, SEEK_SET);
srs56945d58fe02010-01-03 20:57:08 -05001360 if (write(fd, blankSector, 512) != 512) { // blank it out
1361 fprintf(stderr, "Warning! MBR not overwritten! Error is %d!\n", errno);
1362 } // if
srs5694978041c2009-09-21 20:51:47 -04001363 } else {
1364 printf("MBR is unchanged. You may need to delete an EFI GPT (0xEE) partition\n"
1365 "with fdisk or another tool.\n");
srs5694e35eb1b2009-09-14 00:29:34 -04001366 } // if/else
1367 DiskSync(fd);
srs5694c0ca8f82009-08-20 21:35:25 -04001368 close(fd);
srs5694e4ac11e2009-08-31 10:13:04 -04001369 printf("GPT data structures destroyed! You may now partition the disk using fdisk or\n"
1370 "other utilities. Program will now terminate.\n");
srs5694c0ca8f82009-08-20 21:35:25 -04001371 } else {
srs56941d1448a2009-12-31 21:20:19 -05001372 printf("Problem opening %s for writing! Program will now terminate.\n", device);
srs5694c0ca8f82009-08-20 21:35:25 -04001373 } // if/else (fd != -1)
1374 } // if (goOn == 'Y')
1375 return (goOn == 'Y');
1376} // GPTData::DestroyGPT()
1377
srs5694e4ac11e2009-08-31 10:13:04 -04001378/**************************************************************************
1379 * *
1380 * Partition table transformation functions (MBR or BSD disklabel to GPT) *
1381 * (some of these functions may require user interaction) *
1382 * *
1383 **************************************************************************/
1384
1385// Examines the MBR & GPT data, and perhaps asks the user questions, to
1386// determine which set of data to use: the MBR (use_mbr), the GPT (use_gpt),
1387// or create a new set of partitions (use_new)
1388WhichToUse GPTData::UseWhichPartitions(void) {
1389 WhichToUse which = use_new;
1390 MBRValidity mbrState;
1391 int answer;
1392
1393 mbrState = protectiveMBR.GetValidity();
1394
1395 if ((state == gpt_invalid) && ((mbrState == mbr) || (mbrState == hybrid))) {
srs56945d58fe02010-01-03 20:57:08 -05001396 printf("\n***************************************************************\n"
1397 "Found invalid GPT and valid MBR; converting MBR to GPT format.\n");
1398 if (!justLooking) {
1399 printf("\aTHIS OPERATON IS POTENTIALLY DESTRUCTIVE! Exit by typing 'q' if\n"
1400 "you don't want to convert your MBR partitions to GPT format!\n");
1401 } // if
1402 printf("***************************************************************\n\n");
srs5694e4ac11e2009-08-31 10:13:04 -04001403 which = use_mbr;
1404 } // if
1405
1406 if ((state == gpt_invalid) && bsdFound) {
srs56945d58fe02010-01-03 20:57:08 -05001407 printf("\n**********************************************************************\n"
srs5694e4ac11e2009-08-31 10:13:04 -04001408 "Found invalid GPT and valid BSD disklabel; converting BSD disklabel\n"
srs56945d58fe02010-01-03 20:57:08 -05001409 "to GPT format.");
1410 if (!justLooking) {
1411 printf("\a THIS OPERATON IS POTENTIALLY DESTRUCTIVE! Your first\n"
srs5694e4ac11e2009-08-31 10:13:04 -04001412 "BSD partition will likely be unusable. Exit by typing 'q' if you don't\n"
srs56945d58fe02010-01-03 20:57:08 -05001413 "want to convert your BSD partitions to GPT format!");
1414 } // if
1415 printf("\n**********************************************************************\n\n");
srs5694e4ac11e2009-08-31 10:13:04 -04001416 which = use_bsd;
1417 } // if
1418
1419 if ((state == gpt_valid) && (mbrState == gpt)) {
1420 printf("Found valid GPT with protective MBR; using GPT.\n");
1421 which = use_gpt;
1422 } // if
1423 if ((state == gpt_valid) && (mbrState == hybrid)) {
1424 printf("Found valid GPT with hybrid MBR; using GPT.\n");
1425 which = use_gpt;
1426 } // if
1427 if ((state == gpt_valid) && (mbrState == invalid)) {
1428 printf("\aFound valid GPT with corrupt MBR; using GPT and will create new\nprotective MBR on save.\n");
1429 which = use_gpt;
1430 protectiveMBR.MakeProtectiveMBR();
1431 } // if
1432 if ((state == gpt_valid) && (mbrState == mbr)) {
1433 printf("Found valid MBR and GPT. Which do you want to use?\n");
1434 answer = GetNumber(1, 3, 2, (char*) " 1 - MBR\n 2 - GPT\n 3 - Create blank GPT\n\nYour answer: ");
1435 if (answer == 1) {
1436 which = use_mbr;
1437 } else if (answer == 2) {
1438 which = use_gpt;
1439 protectiveMBR.MakeProtectiveMBR();
1440 printf("Using GPT and creating fresh protective MBR.\n");
1441 } else which = use_new;
1442 } // if
1443
1444 // Nasty decisions here -- GPT is present, but corrupt (bad CRCs or other
1445 // problems)
1446 if (state == gpt_corrupt) {
1447 if ((mbrState == mbr) || (mbrState == hybrid)) {
1448 printf("Found valid MBR and corrupt GPT. Which do you want to use? (Using the\n"
1449 "GPT MAY permit recovery of GPT data.)\n");
1450 answer = GetNumber(1, 3, 2, (char*) " 1 - MBR\n 2 - GPT\n 3 - Create blank GPT\n\nYour answer: ");
1451 if (answer == 1) {
1452 which = use_mbr;
1453// protectiveMBR.MakeProtectiveMBR();
1454 } else if (answer == 2) {
1455 which = use_gpt;
1456 } else which = use_new;
1457 } else if (mbrState == invalid) {
1458 printf("Found invalid MBR and corrupt GPT. What do you want to do? (Using the\n"
1459 "GPT MAY permit recovery of GPT data.)\n");
1460 answer = GetNumber(1, 2, 1, (char*) " 1 - GPT\n 2 - Create blank GPT\n\nYour answer: ");
1461 if (answer == 1) {
1462 which = use_gpt;
1463 } else which = use_new;
1464 } else { // corrupt GPT, MBR indicates it's a GPT disk....
1465 printf("\a\a****************************************************************************\n"
1466 "Caution: Found protective or hybrid MBR and corrupt GPT. Using GPT, but disk\n"
1467 "verification and recovery are STRONGLY recommended.\n"
1468 "****************************************************************************\n");
srs5694247657a2009-11-26 18:36:12 -05001469 which = use_gpt;
srs5694e4ac11e2009-08-31 10:13:04 -04001470 } // if/else/else
1471 } // if (corrupt GPT)
1472
1473 if (which == use_new)
1474 printf("Creating new GPT entries.\n");
1475
1476 return which;
1477} // UseWhichPartitions()
1478
1479// Convert MBR partition table into GPT form
1480int GPTData::XFormPartitions(void) {
1481 int i, numToConvert;
1482 uint8_t origType;
1483 struct newGUID;
srs5694e4ac11e2009-08-31 10:13:04 -04001484
1485 // Clear out old data & prepare basics....
1486 ClearGPTData();
1487
1488 // Convert the smaller of the # of GPT or MBR partitions
srs5694978041c2009-09-21 20:51:47 -04001489 if (mainHeader.numParts > (MAX_MBR_PARTS))
1490 numToConvert = MAX_MBR_PARTS;
srs5694e4ac11e2009-08-31 10:13:04 -04001491 else
1492 numToConvert = mainHeader.numParts;
1493
1494 for (i = 0; i < numToConvert; i++) {
1495 origType = protectiveMBR.GetType(i);
1496 // don't waste CPU time trying to convert extended, hybrid protective, or
1497 // null (non-existent) partitions
srs5694e35eb1b2009-09-14 00:29:34 -04001498 if ((origType != 0x05) && (origType != 0x0f) && (origType != 0x85) &&
srs5694e4ac11e2009-08-31 10:13:04 -04001499 (origType != 0x00) && (origType != 0xEE))
1500 partitions[i] = protectiveMBR.AsGPT(i);
1501 } // for
1502
1503 // Convert MBR into protective MBR
1504 protectiveMBR.MakeProtectiveMBR();
1505
1506 // Record that all original CRCs were OK so as not to raise flags
1507 // when doing a disk verification
1508 mainCrcOk = secondCrcOk = mainPartsCrcOk = secondPartsCrcOk = 1;
1509
1510 return (1);
1511} // GPTData::XFormPartitions()
1512
1513// Transforms BSD disklabel on the specified partition (numbered from 0).
1514// If an invalid partition number is given, the program prompts for one.
1515// Returns the number of new partitions created.
1516int GPTData::XFormDisklabel(int i) {
1517 uint32_t low, high, partNum, startPart;
1518 uint16_t hexCode;
1519 int goOn = 1, numDone = 0;
1520 BSDData disklabel;
1521
1522 if (GetPartRange(&low, &high) != 0) {
1523 if ((i < low) || (i > high))
1524 partNum = GetPartNum();
1525 else
1526 partNum = (uint32_t) i;
1527
1528 // Find the partition after the last used one
1529 startPart = high + 1;
1530
1531 // Now see if the specified partition has a BSD type code....
1532 hexCode = partitions[partNum].GetHexType();
1533 if ((hexCode != 0xa500) && (hexCode != 0xa900)) {
1534 printf("Specified partition doesn't have a disklabel partition type "
1535 "code.\nContinue anyway?");
1536 goOn = (GetYN() == 'Y');
1537 } // if
1538
1539 // If all is OK, read the disklabel and convert it.
1540 if (goOn) {
1541 goOn = disklabel.ReadBSDData(device, partitions[partNum].GetFirstLBA(),
1542 partitions[partNum].GetLastLBA());
1543 if ((goOn) && (disklabel.IsDisklabel())) {
1544 numDone = XFormDisklabel(&disklabel, startPart);
1545 if (numDone == 1)
1546 printf("Converted %d BSD partition.\n", numDone);
1547 else
1548 printf("Converted %d BSD partitions.\n", numDone);
1549 } else {
1550 printf("Unable to convert partitions! Unrecognized BSD disklabel.\n");
1551 } // if/else
1552 } // if
1553 if (numDone > 0) { // converted partitions; delete carrier
1554 partitions[partNum].BlankPartition();
1555 } // if
1556 } else {
1557 printf("No partitions\n");
1558 } // if/else
1559 return numDone;
1560} // GPTData::XFormDisklable(int i)
1561
1562// Transform the partitions on an already-loaded BSD disklabel...
1563int GPTData::XFormDisklabel(BSDData* disklabel, int startPart) {
1564 int i, numDone = 0;
1565
1566 if ((disklabel->IsDisklabel()) && (startPart >= 0) &&
1567 (startPart < mainHeader.numParts)) {
1568 for (i = 0; i < disklabel->GetNumParts(); i++) {
1569 partitions[i + startPart] = disklabel->AsGPT(i);
1570 if (partitions[i + startPart].GetFirstLBA() != UINT64_C(0))
1571 numDone++;
1572 } // for
1573 } // if
1574
1575 // Record that all original CRCs were OK so as not to raise flags
1576 // when doing a disk verification
1577 mainCrcOk = secondCrcOk = mainPartsCrcOk = secondPartsCrcOk = 1;
1578
1579 return numDone;
1580} // GPTData::XFormDisklabel(BSDData* disklabel)
1581
srs5694978041c2009-09-21 20:51:47 -04001582// Add one GPT partition to MBR. Used by XFormToMBR() and MakeHybrid()
1583// functions. Returns 1 if operation was successful.
1584int GPTData::OnePartToMBR(uint32_t gptPart, int mbrPart) {
1585 int allOK = 1, typeCode, bootable;
1586 uint64_t length;
1587 char line[255];
srs56945d58fe02010-01-03 20:57:08 -05001588 char* junk;
srs5694978041c2009-09-21 20:51:47 -04001589
1590 if ((mbrPart < 0) || (mbrPart > 3)) {
1591 printf("MBR partition %d is out of range; omitting it.\n", mbrPart + 1);
1592 allOK = 0;
1593 } // if
1594 if (gptPart >= mainHeader.numParts) {
1595 printf("GPT partition %d is out of range; omitting it.\n", gptPart + 1);
1596 allOK = 0;
1597 } // if
1598 if (allOK && (partitions[gptPart].GetLastLBA() == UINT64_C(0))) {
1599 printf("GPT partition %d is undefined; omitting it.\n", gptPart + 1);
1600 allOK = 0;
1601 } // if
1602 if (allOK && (partitions[gptPart].GetFirstLBA() <= UINT32_MAX) &&
1603 (partitions[gptPart].GetLengthLBA() <= UINT32_MAX)) {
1604 if (partitions[gptPart].GetLastLBA() > UINT32_MAX) {
1605 printf("Caution: Partition end point past 32-bit pointer boundary;"
1606 " some OSes may\nreact strangely.\n");
1607 } // if partition ends past 32-bit (usually 2TiB) boundary
1608 do {
1609 printf("Enter an MBR hex code (default %02X): ",
1610 typeHelper.GUIDToID(partitions[gptPart].GetType()) / 256);
srs56945d58fe02010-01-03 20:57:08 -05001611 junk = fgets(line, 255, stdin);
srs5694978041c2009-09-21 20:51:47 -04001612 sscanf(line, "%x", &typeCode);
1613 if (line[0] == '\n')
1614 typeCode = partitions[gptPart].GetHexType() / 256;
1615 } while ((typeCode <= 0) || (typeCode > 255));
1616 printf("Set the bootable flag? ");
1617 bootable = (GetYN() == 'Y');
1618 length = partitions[gptPart].GetLengthLBA();
1619 protectiveMBR.MakePart(mbrPart, (uint32_t) partitions[gptPart].GetFirstLBA(),
1620 (uint32_t) length, typeCode, bootable);
1621 } else { // partition out of range
1622 printf("Partition %d begins beyond the 32-bit pointer limit of MBR "
1623 "partitions, or is\n too big; omitting it.\n", gptPart + 1);
1624 allOK = 0;
1625 } // if/else
1626 return allOK;
1627} // GPTData::OnePartToMBR()
1628
1629// Convert the GPT to MBR form. This function is necessarily limited; it
1630// handles at most four partitions and creates layouts that ignore CHS
1631// geometries. Returns the number of converted partitions; if this value
1632// is over 0, the calling function should call DestroyGPT() to destroy
1633// the GPT data, and then exit.
1634int GPTData::XFormToMBR(void) {
1635 char line[255];
srs56945d58fe02010-01-03 20:57:08 -05001636 char* junk;
srs5694978041c2009-09-21 20:51:47 -04001637 int i, j, numParts, numConverted = 0;
1638 uint32_t partNums[4];
1639
1640 // Get the numbers of up to four partitions to add to the
1641 // hybrid MBR....
1642 numParts = CountParts();
1643 printf("Counted %d partitions.\n", numParts);
1644
1645 // Prepare the MBR for conversion (empty it of existing partitions).
1646 protectiveMBR.EmptyMBR(0);
1647 protectiveMBR.SetDiskSize(diskSize);
1648
1649 if (numParts > 4) { // Over four partitions; engage in triage
1650 printf("Type from one to four GPT partition numbers, separated by spaces, to be\n"
1651 "used in the MBR, in sequence: ");
srs56945d58fe02010-01-03 20:57:08 -05001652 junk = fgets(line, 255, stdin);
srs5694978041c2009-09-21 20:51:47 -04001653 numParts = sscanf(line, "%d %d %d %d", &partNums[0], &partNums[1],
1654 &partNums[2], &partNums[3]);
1655 } else { // Four or fewer partitions; convert them all
1656 i = j = 0;
1657 while ((j < numParts) && (i < mainHeader.numParts)) {
1658 if (partitions[i].GetFirstLBA() > 0) { // if GPT part. is defined
1659 partNums[j++] = ++i; // flag it for conversion
1660 } else i++;
1661 } // while
1662 } // if/else
1663
1664 for (i = 0; i < numParts; i++) {
1665 j = partNums[i] - 1;
1666 printf("\nCreating entry for partition #%d\n", j + 1);
1667 numConverted += OnePartToMBR(j, i);
1668 } // for
1669 return numConverted;
1670} // GPTData::XFormToMBR()
1671
srs5694e4ac11e2009-08-31 10:13:04 -04001672// Create a hybrid MBR -- an ugly, funky thing that helps GPT work with
1673// OSes that don't understand GPT.
1674void GPTData::MakeHybrid(void) {
1675 uint32_t partNums[3];
1676 char line[255];
srs56945d58fe02010-01-03 20:57:08 -05001677 char* junk;
srs5694978041c2009-09-21 20:51:47 -04001678 int numParts, numConverted = 0, i, j, typeCode, mbrNum;
srs5694e4ac11e2009-08-31 10:13:04 -04001679 char fillItUp = 'M'; // fill extra partition entries? (Yes/No/Maybe)
srs5694978041c2009-09-21 20:51:47 -04001680 char eeFirst = 'Y'; // Whether EFI GPT (0xEE) partition comes first in table
srs5694e4ac11e2009-08-31 10:13:04 -04001681
1682 printf("\nWARNING! Hybrid MBRs are flaky and potentially dangerous! If you decide not\n"
1683 "to use one, just hit the Enter key at the below prompt and your MBR\n"
1684 "partition table will be untouched.\n\n\a");
1685
1686 // Now get the numbers of up to three partitions to add to the
1687 // hybrid MBR....
1688 printf("Type from one to three GPT partition numbers, separated by spaces, to be\n"
1689 "added to the hybrid MBR, in sequence: ");
srs56945d58fe02010-01-03 20:57:08 -05001690 junk = fgets(line, 255, stdin);
srs5694e4ac11e2009-08-31 10:13:04 -04001691 numParts = sscanf(line, "%d %d %d", &partNums[0], &partNums[1], &partNums[2]);
1692
1693 if (numParts > 0) {
1694 // Blank out the protective MBR, but leave the boot loader code
1695 // alone....
1696 protectiveMBR.EmptyMBR(0);
1697 protectiveMBR.SetDiskSize(diskSize);
1698 printf("Place EFI GPT (0xEE) partition first in MBR (good for GRUB)? ");
1699 eeFirst = GetYN();
1700 } // if
1701
1702 for (i = 0; i < numParts; i++) {
1703 j = partNums[i] - 1;
1704 printf("\nCreating entry for partition #%d\n", j + 1);
srs5694978041c2009-09-21 20:51:47 -04001705 if (eeFirst == 'Y')
1706 mbrNum = i + 1;
1707 else
1708 mbrNum = i;
1709 numConverted += OnePartToMBR(j, mbrNum);
srs5694e4ac11e2009-08-31 10:13:04 -04001710 } // for
1711
srs5694978041c2009-09-21 20:51:47 -04001712 if ((numParts > 0) && (numConverted > 0)) { // User opted to create a hybrid MBR....
srs5694e4ac11e2009-08-31 10:13:04 -04001713 // Create EFI protective partition that covers the start of the disk.
1714 // If this location (covering the main GPT data structures) is omitted,
1715 // Linux won't find any partitions on the disk. Note that this is
1716 // NUMBERED AFTER the hybrid partitions, contrary to what the
1717 // gptsync utility does. This is because Windows seems to choke on
1718 // disks with a 0xEE partition in the first slot and subsequent
1719 // additional partitions, unless it boots from the disk.
1720 if (eeFirst == 'Y')
1721 mbrNum = 0;
1722 else
1723 mbrNum = numParts;
1724 protectiveMBR.MakePart(mbrNum, 1, protectiveMBR.FindLastInFree(1), 0xEE);
srs5694978041c2009-09-21 20:51:47 -04001725 protectiveMBR.SetHybrid();
srs5694e4ac11e2009-08-31 10:13:04 -04001726
1727 // ... and for good measure, if there are any partition spaces left,
1728 // optionally create another protective EFI partition to cover as much
1729 // space as possible....
1730 for (i = 0; i < 4; i++) {
1731 if (protectiveMBR.GetType(i) == 0x00) { // unused entry....
1732 if (fillItUp == 'M') {
1733 printf("\nUnused partition space(s) found. Use one to protect more partitions? ");
1734 fillItUp = GetYN();
1735 typeCode = 0x00; // use this to flag a need to get type code
1736 } // if
1737 if (fillItUp == 'Y') {
1738 while ((typeCode <= 0) || (typeCode > 255)) {
1739 printf("Enter an MBR hex code (EE is EFI GPT, but may confuse MacOS): ");
1740 // Comment on above: Mac OS treats disks with more than one
1741 // 0xEE MBR partition as MBR disks, not as GPT disks.
srs56945d58fe02010-01-03 20:57:08 -05001742 junk = fgets(line, 255, stdin);
srs5694e4ac11e2009-08-31 10:13:04 -04001743 sscanf(line, "%x", &typeCode);
1744 if (line[0] == '\n')
1745 typeCode = 0;
1746 } // while
1747 protectiveMBR.MakeBiggestPart(i, typeCode); // make a partition
1748 } // if (fillItUp == 'Y')
1749 } // if unused entry
1750 } // for (i = 0; i < 4; i++)
1751 } // if (numParts > 0)
1752} // GPTData::MakeHybrid()
1753
1754/**********************************************************************
1755 * *
1756 * Functions that adjust GPT data structures WITHOUT user interaction *
1757 * (they may display information for the user's benefit, though) *
1758 * *
1759 **********************************************************************/
1760
1761// Resizes GPT to specified number of entries. Creates a new table if
1762// necessary, copies data if it already exists.
1763int GPTData::SetGPTSize(uint32_t numEntries) {
1764 struct GPTPart* newParts;
1765 struct GPTPart* trash;
1766 uint32_t i, high, copyNum;
1767 int allOK = 1;
1768
1769 // First, adjust numEntries upward, if necessary, to get a number
1770 // that fills the allocated sectors
1771 i = blockSize / GPT_SIZE;
1772 if ((numEntries % i) != 0) {
1773 printf("Adjusting GPT size from %lu ", (unsigned long) numEntries);
1774 numEntries = ((numEntries / i) + 1) * i;
1775 printf("to %lu to fill the sector\n", (unsigned long) numEntries);
1776 } // if
1777
srs5694247657a2009-11-26 18:36:12 -05001778 // Do the work only if the # of partitions is changing. Along with being
1779 // efficient, this prevents mucking the with location of the secondary
1780 // partition table, which causes problems when loading data from a RAID
1781 // array that's been expanded because this function is called when loading
1782 // data.
1783 if ((numEntries != mainHeader.numParts) || (partitions == NULL)) {
1784 newParts = (GPTPart*) calloc(numEntries, sizeof (GPTPart));
1785 if (newParts != NULL) {
1786 if (partitions != NULL) { // existing partitions; copy them over
1787 GetPartRange(&i, &high);
1788 if (numEntries < (high + 1)) { // Highest entry too high for new #
1789 printf("The highest-numbered partition is %lu, which is greater than the requested\n"
1790 "partition table size of %d; cannot resize. Perhaps sorting will help.\n",
1791 (unsigned long) (high + 1), numEntries);
1792 allOK = 0;
1793 } else { // go ahead with copy
1794 if (numEntries < mainHeader.numParts)
1795 copyNum = numEntries;
1796 else
1797 copyNum = mainHeader.numParts;
1798 for (i = 0; i < copyNum; i++) {
1799 newParts[i] = partitions[i];
1800 } // for
1801 trash = partitions;
1802 partitions = newParts;
1803 free(trash);
1804 } // if
1805 } else { // No existing partition table; just create it
srs5694e4ac11e2009-08-31 10:13:04 -04001806 partitions = newParts;
srs5694247657a2009-11-26 18:36:12 -05001807 } // if/else existing partitions
1808 mainHeader.numParts = numEntries;
1809 secondHeader.numParts = numEntries;
1810 mainHeader.firstUsableLBA = ((numEntries * GPT_SIZE) / blockSize) + 2 ;
1811 secondHeader.firstUsableLBA = mainHeader.firstUsableLBA;
1812 MoveSecondHeaderToEnd();
1813 if (diskSize > 0)
1814 CheckGPTSize();
1815 } else { // Bad memory allocation
1816 fprintf(stderr, "Error allocating memory for partition table!\n");
1817 allOK = 0;
1818 } // if/else
srs5694e4ac11e2009-08-31 10:13:04 -04001819 } // if/else
1820 return (allOK);
1821} // GPTData::SetGPTSize()
1822
1823// Blank the partition array
1824void GPTData::BlankPartitions(void) {
1825 uint32_t i;
1826
1827 for (i = 0; i < mainHeader.numParts; i++) {
1828 partitions[i].BlankPartition();
1829 } // for
1830} // GPTData::BlankPartitions()
1831
1832// Sort the GPT entries, eliminating gaps and making for a logical
1833// ordering. Relies on QuickSortGPT() for the bulk of the work
1834void GPTData::SortGPT(void) {
1835 int i, lastPart = 0;
1836 GPTPart temp;
1837
1838 // First, find the last partition with data, so as not to
1839 // spend needless time sorting empty entries....
1840 for (i = 0; i < mainHeader.numParts; i++) {
1841 if (partitions[i].GetFirstLBA() > 0)
1842 lastPart = i;
1843 } // for
1844
1845 // Now swap empties with the last partitions, to simplify the logic
1846 // in the Quicksort function....
1847 i = 0;
1848 while (i < lastPart) {
1849 if (partitions[i].GetFirstLBA() == 0) {
1850 temp = partitions[i];
1851 partitions[i] = partitions[lastPart];
1852 partitions[lastPart] = temp;
1853 lastPart--;
1854 } // if
1855 i++;
1856 } // while
1857
1858 // Now call the recursive quick sort routine to do the real work....
1859 QuickSortGPT(partitions, 0, lastPart);
1860} // GPTData::SortGPT()
1861
1862// Set up data structures for entirely new set of partitions on the
1863// specified device. Returns 1 if OK, 0 if there were problems.
srs5694e35eb1b2009-09-14 00:29:34 -04001864// Note that this function does NOT clear the protectiveMBR data
1865// structure, since it may hold the original MBR partitions if the
1866// program was launched on an MBR disk, and those may need to be
1867// converted to GPT format.
srs5694e4ac11e2009-08-31 10:13:04 -04001868int GPTData::ClearGPTData(void) {
srs5694e35eb1b2009-09-14 00:29:34 -04001869 int goOn = 1, i;
srs5694e4ac11e2009-08-31 10:13:04 -04001870
1871 // Set up the partition table....
1872 free(partitions);
1873 partitions = NULL;
1874 SetGPTSize(NUM_GPT_ENTRIES);
1875
1876 // Now initialize a bunch of stuff that's static....
1877 mainHeader.signature = GPT_SIGNATURE;
1878 mainHeader.revision = 0x00010000;
srs5694978041c2009-09-21 20:51:47 -04001879 mainHeader.headerSize = HEADER_SIZE;
srs5694e4ac11e2009-08-31 10:13:04 -04001880 mainHeader.reserved = 0;
1881 mainHeader.currentLBA = UINT64_C(1);
1882 mainHeader.partitionEntriesLBA = (uint64_t) 2;
1883 mainHeader.sizeOfPartitionEntries = GPT_SIZE;
1884 for (i = 0; i < GPT_RESERVED; i++) {
1885 mainHeader.reserved2[i] = '\0';
1886 } // for
1887
1888 // Now some semi-static items (computed based on end of disk)
1889 mainHeader.backupLBA = diskSize - UINT64_C(1);
1890 mainHeader.lastUsableLBA = diskSize - mainHeader.firstUsableLBA;
1891
1892 // Set a unique GUID for the disk, based on random numbers
1893 // rand() is only 32 bits, so multiply together to fill a 64-bit value
1894 mainHeader.diskGUID.data1 = (uint64_t) rand() * (uint64_t) rand();
1895 mainHeader.diskGUID.data2 = (uint64_t) rand() * (uint64_t) rand();
1896
1897 // Copy main header to backup header
1898 RebuildSecondHeader();
1899
1900 // Blank out the partitions array....
1901 BlankPartitions();
1902
1903 // Flag all CRCs as being OK....
1904 mainCrcOk = 1;
1905 secondCrcOk = 1;
1906 mainPartsCrcOk = 1;
1907 secondPartsCrcOk = 1;
1908
1909 return (goOn);
1910} // GPTData::ClearGPTData()
1911
srs5694247657a2009-11-26 18:36:12 -05001912// Set the location of the second GPT header data to the end of the disk.
1913// Used internally and called by the 'e' option on the recovery &
1914// transformation menu, to help users of RAID arrays who add disk space
1915// to their arrays.
1916void GPTData::MoveSecondHeaderToEnd() {
srs56948bb78762009-11-24 15:43:49 -05001917 mainHeader.backupLBA = secondHeader.currentLBA = diskSize - UINT64_C(1);
1918 mainHeader.lastUsableLBA = secondHeader.lastUsableLBA = diskSize - mainHeader.firstUsableLBA;
1919 secondHeader.partitionEntriesLBA = secondHeader.lastUsableLBA + UINT64_C(1);
1920} // GPTData::FixSecondHeaderLocation()
1921
srs5694e4ac11e2009-08-31 10:13:04 -04001922void GPTData::SetName(uint32_t partNum, char* theName) {
1923 if ((partNum >= 0) && (partNum < mainHeader.numParts))
1924 if (partitions[partNum].GetFirstLBA() > 0)
1925 partitions[partNum].SetName((unsigned char*) theName);
1926} // GPTData::SetName
1927
1928// Set the disk GUID to the specified value. Note that the header CRCs must
1929// be recomputed after calling this function.
1930void GPTData::SetDiskGUID(GUIDData newGUID) {
1931 mainHeader.diskGUID = newGUID;
1932 secondHeader.diskGUID = newGUID;
1933} // SetDiskGUID()
1934
1935// Set the unique GUID of the specified partition. Returns 1 on
1936// successful completion, 0 if there were problems (invalid
1937// partition number).
1938int GPTData::SetPartitionGUID(uint32_t pn, GUIDData theGUID) {
1939 int retval = 0;
1940
1941 if (pn < mainHeader.numParts) {
1942 if (partitions[pn].GetFirstLBA() != UINT64_C(0)) {
1943 partitions[pn].SetUniqueGUID(theGUID);
1944 retval = 1;
1945 } // if
1946 } // if
1947 return retval;
1948} // GPTData::SetPartitionGUID()
1949
srs56941d1448a2009-12-31 21:20:19 -05001950// Adjust sector number so that it falls on a sector boundary that's a
1951// multiple of sectorAlignment. This is done to improve the performance
1952// of Western Digital Advanced Format disks and disks with similar
1953// technology from other companies, which use 4096-byte sectors
1954// internally although they translate to 512-byte sectors for the
1955// benefit of the OS. If partitions aren't properly aligned on these
1956// disks, some filesystem data structures can span multiple physical
1957// sectors, degrading performance. This function should be called
1958// only on the FIRST sector of the partition, not the last!
1959// This function returns 1 if the alignment was altered, 0 if it
1960// was unchanged.
1961int GPTData::Align(uint64_t* sector) {
1962 int retval = 0, sectorOK = 0;
1963 uint64_t earlier, later, testSector, original;
1964
1965 if ((*sector % sectorAlignment) != 0) {
1966 original = *sector;
1967 retval = 1;
1968 earlier = (*sector / sectorAlignment) * sectorAlignment;
1969 later = earlier + (uint64_t) sectorAlignment;
1970
1971 // Check to see that every sector between the earlier one and the
1972 // requested one is clear, and that it's not too early....
1973 if (earlier >= mainHeader.firstUsableLBA) {
1974// printf("earlier is %llu, first usable is %llu\n", earlier, mainHeader.firstUsableLBA);
1975 sectorOK = 1;
1976 testSector = earlier;
1977 do {
1978 sectorOK = IsFree(testSector++);
1979 } while ((sectorOK == 1) && (testSector < *sector));
1980 if (sectorOK == 1) {
1981 *sector = earlier;
1982// printf("Moved sector earlier.\n");
1983 } // if
1984 } // if firstUsableLBA check
1985
1986 // If couldn't move the sector earlier, try to move it later instead....
1987 if ((sectorOK != 1) && (later <= mainHeader.lastUsableLBA)) {
1988 sectorOK = 1;
1989 testSector = later;
1990 do {
1991 sectorOK = IsFree(testSector--);
1992 } while ((sectorOK == 1) && (testSector > *sector));
1993 if (sectorOK == 1) {
1994 *sector = later;
1995// printf("Moved sector later\n");
1996 } // if
1997 } // if
1998
1999 // If sector was changed successfully, inform the user of this fact.
2000 // Otherwise, notify the user that it couldn't be done....
2001 if (sectorOK == 1) {
2002 printf("Information: Moved requested sector from %llu to %llu for\n"
srs56945d58fe02010-01-03 20:57:08 -05002003 "alignment purposes. Use 'l' on the experts' menu to adjust alignment.\n",
2004 (unsigned long long) original, (unsigned long long) *sector);
srs56941d1448a2009-12-31 21:20:19 -05002005 } else {
2006 printf("Information: Sector not aligned on %d-sector boundary and could not be moved.\n"
2007 "If you're using a Western Digital Advanced Format or similar disk with\n"
2008 "underlying 4096-byte sectors, performance may suffer.\n", sectorAlignment);
2009 retval = 0;
2010 } // if/else
2011 } // if
2012 return retval;
2013} // GPTData::Align()
2014
srs5694e4ac11e2009-08-31 10:13:04 -04002015/********************************************************
2016 * *
2017 * Functions that return data about GPT data structures *
2018 * (most of these are inline in gpt.h) *
2019 * *
2020 ********************************************************/
2021
2022// Find the low and high used partition numbers (numbered from 0).
2023// Return value is the number of partitions found. Note that the
2024// *low and *high values are both set to 0 when no partitions
2025// are found, as well as when a single partition in the first
2026// position exists. Thus, the return value is the only way to
2027// tell when no partitions exist.
2028int GPTData::GetPartRange(uint32_t *low, uint32_t *high) {
2029 uint32_t i;
2030 int numFound = 0;
2031
2032 *low = mainHeader.numParts + 1; // code for "not found"
2033 *high = 0;
2034 if (mainHeader.numParts > 0) { // only try if partition table exists...
2035 for (i = 0; i < mainHeader.numParts; i++) {
2036 if (partitions[i].GetFirstLBA() != UINT64_C(0)) { // it exists
2037 *high = i; // since we're counting up, set the high value
2038 // Set the low value only if it's not yet found...
2039 if (*low == (mainHeader.numParts + 1)) *low = i;
2040 numFound++;
2041 } // if
2042 } // for
2043 } // if
2044
2045 // Above will leave *low pointing to its "not found" value if no partitions
2046 // are defined, so reset to 0 if this is the case....
2047 if (*low == (mainHeader.numParts + 1))
2048 *low = 0;
2049 return numFound;
2050} // GPTData::GetPartRange()
2051
srs5694978041c2009-09-21 20:51:47 -04002052// Returns the number of defined partitions.
2053uint32_t GPTData::CountParts(void) {
2054 int i, counted = 0;
2055
2056 for (i = 0; i < mainHeader.numParts; i++) {
2057 if (partitions[i].GetFirstLBA() > 0)
2058 counted++;
2059 } // for
2060 return counted;
2061} // GPTData::CountParts()
2062
srs5694e4ac11e2009-08-31 10:13:04 -04002063/****************************************************
2064 * *
2065 * Functions that return data about disk free space *
2066 * *
2067 ****************************************************/
2068
2069// Find the first available block after the starting point; returns 0 if
2070// there are no available blocks left
2071uint64_t GPTData::FindFirstAvailable(uint64_t start) {
2072 uint64_t first;
2073 uint32_t i;
2074 int firstMoved = 0;
2075
2076 // Begin from the specified starting point or from the first usable
2077 // LBA, whichever is greater...
2078 if (start < mainHeader.firstUsableLBA)
2079 first = mainHeader.firstUsableLBA;
2080 else
2081 first = start;
2082
2083 // ...now search through all partitions; if first is within an
2084 // existing partition, move it to the next sector after that
2085 // partition and repeat. If first was moved, set firstMoved
2086 // flag; repeat until firstMoved is not set, so as to catch
2087 // cases where partitions are out of sequential order....
2088 do {
2089 firstMoved = 0;
2090 for (i = 0; i < mainHeader.numParts; i++) {
2091 if ((first >= partitions[i].GetFirstLBA()) &&
2092 (first <= partitions[i].GetLastLBA())) { // in existing part.
2093 first = partitions[i].GetLastLBA() + 1;
2094 firstMoved = 1;
2095 } // if
2096 } // for
2097 } while (firstMoved == 1);
2098 if (first > mainHeader.lastUsableLBA)
2099 first = 0;
2100 return (first);
2101} // GPTData::FindFirstAvailable()
2102
2103// Finds the first available sector in the largest block of unallocated
2104// space on the disk. Returns 0 if there are no available blocks left
2105uint64_t GPTData::FindFirstInLargest(void) {
srs5694e35eb1b2009-09-14 00:29:34 -04002106 uint64_t start, firstBlock, lastBlock, segmentSize, selectedSize = 0, selectedSegment = 0;
srs5694e4ac11e2009-08-31 10:13:04 -04002107
2108 start = 0;
2109 do {
2110 firstBlock = FindFirstAvailable(start);
2111 if (firstBlock != UINT32_C(0)) { // something's free...
2112 lastBlock = FindLastInFree(firstBlock);
2113 segmentSize = lastBlock - firstBlock + UINT32_C(1);
2114 if (segmentSize > selectedSize) {
2115 selectedSize = segmentSize;
2116 selectedSegment = firstBlock;
2117 } // if
2118 start = lastBlock + 1;
2119 } // if
2120 } while (firstBlock != 0);
2121 return selectedSegment;
2122} // GPTData::FindFirstInLargest()
2123
2124// Find the last available block on the disk at or after the start
2125// block. Returns 0 if there are no available partitions after
2126// (or including) start.
2127uint64_t GPTData::FindLastAvailable(uint64_t start) {
2128 uint64_t last;
2129 uint32_t i;
2130 int lastMoved = 0;
2131
2132 // Start by assuming the last usable LBA is available....
2133 last = mainHeader.lastUsableLBA;
2134
2135 // ...now, similar to algorithm in FindFirstAvailable(), search
2136 // through all partitions, moving last when it's in an existing
2137 // partition. Set the lastMoved flag so we repeat to catch cases
2138 // where partitions are out of logical order.
2139 do {
2140 lastMoved = 0;
2141 for (i = 0; i < mainHeader.numParts; i++) {
2142 if ((last >= partitions[i].GetFirstLBA()) &&
2143 (last <= partitions[i].GetLastLBA())) { // in existing part.
2144 last = partitions[i].GetFirstLBA() - 1;
2145 lastMoved = 1;
2146 } // if
2147 } // for
2148 } while (lastMoved == 1);
2149 if (last < mainHeader.firstUsableLBA)
2150 last = 0;
2151 return (last);
2152} // GPTData::FindLastAvailable()
2153
2154// Find the last available block in the free space pointed to by start.
2155uint64_t GPTData::FindLastInFree(uint64_t start) {
2156 uint64_t nearestStart;
2157 uint32_t i;
2158
2159 nearestStart = mainHeader.lastUsableLBA;
2160 for (i = 0; i < mainHeader.numParts; i++) {
2161 if ((nearestStart > partitions[i].GetFirstLBA()) &&
2162 (partitions[i].GetFirstLBA() > start)) {
2163 nearestStart = partitions[i].GetFirstLBA() - 1;
2164 } // if
2165 } // for
2166 return (nearestStart);
2167} // GPTData::FindLastInFree()
2168
2169// Finds the total number of free blocks, the number of segments in which
2170// they reside, and the size of the largest of those segments
2171uint64_t GPTData::FindFreeBlocks(int *numSegments, uint64_t *largestSegment) {
2172 uint64_t start = UINT64_C(0); // starting point for each search
2173 uint64_t totalFound = UINT64_C(0); // running total
2174 uint64_t firstBlock; // first block in a segment
2175 uint64_t lastBlock; // last block in a segment
2176 uint64_t segmentSize; // size of segment in blocks
2177 int num = 0;
2178
2179 *largestSegment = UINT64_C(0);
2180 do {
2181 firstBlock = FindFirstAvailable(start);
2182 if (firstBlock != UINT64_C(0)) { // something's free...
2183 lastBlock = FindLastInFree(firstBlock);
2184 segmentSize = lastBlock - firstBlock + UINT64_C(1);
2185 if (segmentSize > *largestSegment) {
2186 *largestSegment = segmentSize;
2187 } // if
2188 totalFound += segmentSize;
2189 num++;
2190 start = lastBlock + 1;
2191 } // if
2192 } while (firstBlock != 0);
2193 *numSegments = num;
2194 return totalFound;
2195} // GPTData::FindFreeBlocks()
2196
2197// Returns 1 if sector is unallocated, 0 if it's allocated to a partition
2198int GPTData::IsFree(uint64_t sector) {
2199 int isFree = 1;
2200 uint32_t i;
2201
2202 for (i = 0; i < mainHeader.numParts; i++) {
2203 if ((sector >= partitions[i].GetFirstLBA()) &&
2204 (sector <= partitions[i].GetLastLBA())) {
2205 isFree = 0;
2206 } // if
2207 } // for
srs5694e35eb1b2009-09-14 00:29:34 -04002208 if ((sector < mainHeader.firstUsableLBA) ||
srs5694e4ac11e2009-08-31 10:13:04 -04002209 (sector > mainHeader.lastUsableLBA)) {
2210 isFree = 0;
2211 } // if
2212 return (isFree);
2213} // GPTData::IsFree()
2214
2215/********************************
2216 * *
2217 * Endianness support functions *
2218 * *
2219 ********************************/
2220
srs56942a9f5da2009-08-26 00:48:01 -04002221void GPTData::ReverseHeaderBytes(struct GPTHeader* header) {
srs5694221e0872009-08-29 15:00:31 -04002222 ReverseBytes(&header->signature, 8);
2223 ReverseBytes(&header->revision, 4);
2224 ReverseBytes(&header->headerSize, 4);
2225 ReverseBytes(&header->headerCRC, 4);
2226 ReverseBytes(&header->reserved, 4);
2227 ReverseBytes(&header->currentLBA, 8);
2228 ReverseBytes(&header->backupLBA, 8);
2229 ReverseBytes(&header->firstUsableLBA, 8);
2230 ReverseBytes(&header->lastUsableLBA, 8);
2231 ReverseBytes(&header->partitionEntriesLBA, 8);
2232 ReverseBytes(&header->numParts, 4);
2233 ReverseBytes(&header->sizeOfPartitionEntries, 4);
2234 ReverseBytes(&header->partitionEntriesCRC, 4);
2235 ReverseBytes(&header->reserved2, GPT_RESERVED);
2236 ReverseBytes(&header->diskGUID.data1, 8);
2237 ReverseBytes(&header->diskGUID.data2, 8);
srs56942a9f5da2009-08-26 00:48:01 -04002238} // GPTData::ReverseHeaderBytes()
2239
2240// IMPORTANT NOTE: This function requires non-reversed mainHeader
2241// structure!
2242void GPTData::ReversePartitionBytes() {
2243 uint32_t i;
2244
2245 // Check GPT signature on big-endian systems; this will mismatch
2246 // if the function is called out of order. Unfortunately, it'll also
2247 // mismatch if there's data corruption.
2248 if ((mainHeader.signature != GPT_SIGNATURE) && (IsLittleEndian() == 0)) {
2249 printf("GPT signature mismatch in GPTData::ReversePartitionBytes(). This indicates\n"
srs5694e4ac11e2009-08-31 10:13:04 -04002250 "data corruption or a misplaced call to this function.\n");
srs56942a9f5da2009-08-26 00:48:01 -04002251 } // if signature mismatch....
2252 for (i = 0; i < mainHeader.numParts; i++) {
srs5694221e0872009-08-29 15:00:31 -04002253 partitions[i].ReversePartBytes();
srs56942a9f5da2009-08-26 00:48:01 -04002254 } // for
2255} // GPTData::ReversePartitionBytes()
2256
2257/******************************************
2258 * *
2259 * Additional non-class support functions *
2260 * *
2261 ******************************************/
2262
srs5694e7b4ff92009-08-18 13:16:10 -04002263// Check to be sure that data type sizes are correct. The basic types (uint*_t) should
2264// never fail these tests, but the struct types may fail depending on compile options.
2265// Specifically, the -fpack-struct option to gcc may be required to ensure proper structure
2266// sizes.
2267int SizesOK(void) {
2268 int allOK = 1;
srs5694e7b4ff92009-08-18 13:16:10 -04002269
2270 if (sizeof(uint8_t) != 1) {
srs56941d1448a2009-12-31 21:20:19 -05002271 fprintf(stderr, "uint8_t is %lu bytes, should be 1 byte; aborting!\n", sizeof(uint8_t));
srs5694e7b4ff92009-08-18 13:16:10 -04002272 allOK = 0;
2273 } // if
2274 if (sizeof(uint16_t) != 2) {
srs56941d1448a2009-12-31 21:20:19 -05002275 fprintf(stderr, "uint16_t is %lu bytes, should be 2 bytes; aborting!\n", sizeof(uint16_t));
srs5694e7b4ff92009-08-18 13:16:10 -04002276 allOK = 0;
2277 } // if
2278 if (sizeof(uint32_t) != 4) {
srs56941d1448a2009-12-31 21:20:19 -05002279 fprintf(stderr, "uint32_t is %lu bytes, should be 4 bytes; aborting!\n", sizeof(uint32_t));
srs5694e7b4ff92009-08-18 13:16:10 -04002280 allOK = 0;
2281 } // if
2282 if (sizeof(uint64_t) != 8) {
srs56941d1448a2009-12-31 21:20:19 -05002283 fprintf(stderr, "uint64_t is %lu bytes, should be 8 bytes; aborting!\n", sizeof(uint64_t));
srs5694e7b4ff92009-08-18 13:16:10 -04002284 allOK = 0;
2285 } // if
2286 if (sizeof(struct MBRRecord) != 16) {
srs56941d1448a2009-12-31 21:20:19 -05002287 fprintf(stderr, "MBRRecord is %lu bytes, should be 16 bytes; aborting!\n", sizeof(MBRRecord));
srs5694e7b4ff92009-08-18 13:16:10 -04002288 allOK = 0;
2289 } // if
srs5694978041c2009-09-21 20:51:47 -04002290 if (sizeof(struct TempMBR) != 512) {
srs56941d1448a2009-12-31 21:20:19 -05002291 fprintf(stderr, "TempMBR is %lu bytes, should be 512 bytes; aborting!\n", sizeof(TempMBR));
srs5694e7b4ff92009-08-18 13:16:10 -04002292 allOK = 0;
2293 } // if
2294 if (sizeof(struct GPTHeader) != 512) {
srs56941d1448a2009-12-31 21:20:19 -05002295 fprintf(stderr, "GPTHeader is %lu bytes, should be 512 bytes; aborting!\n", sizeof(GPTHeader));
srs5694e7b4ff92009-08-18 13:16:10 -04002296 allOK = 0;
2297 } // if
srs5694221e0872009-08-29 15:00:31 -04002298 if (sizeof(GPTPart) != 128) {
srs56941d1448a2009-12-31 21:20:19 -05002299 fprintf(stderr, "GPTPart is %lu bytes, should be 128 bytes; aborting!\n", sizeof(GPTPart));
srs5694221e0872009-08-29 15:00:31 -04002300 allOK = 0;
2301 } // if
2302// Determine endianness; set allOK = 0 if running on big-endian hardware
srs56942a9f5da2009-08-26 00:48:01 -04002303 if (IsLittleEndian() == 0) {
2304 fprintf(stderr, "\aRunning on big-endian hardware. Big-endian support is new and poorly"
srs5694e4ac11e2009-08-31 10:13:04 -04002305 " tested!\nBeware!\n");
srs56942a9f5da2009-08-26 00:48:01 -04002306 // allOK = 0;
srs5694e7b4ff92009-08-18 13:16:10 -04002307 } // if
2308 return (allOK);
2309} // SizesOK()
srs5694e4ac11e2009-08-31 10:13:04 -04002310