blob: 867c3ff6da7c095fa35d7bb87d0cfc13a98485e1 [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"
srs5694546a9c72010-01-26 16:00:26 -050027#include "diskio.h"
srs5694e7b4ff92009-08-18 13:16:10 -040028
29using namespace std;
30
31/****************************************
32 * *
33 * GPTData class and related structures *
34 * *
35 ****************************************/
36
srs5694e4ac11e2009-08-31 10:13:04 -040037// Default constructor
srs5694e7b4ff92009-08-18 13:16:10 -040038GPTData::GPTData(void) {
39 blockSize = SECTOR_SIZE; // set a default
40 diskSize = 0;
41 partitions = NULL;
42 state = gpt_valid;
43 strcpy(device, "");
srs56945d58fe02010-01-03 20:57:08 -050044 justLooking = 0;
srs5694e7b4ff92009-08-18 13:16:10 -040045 mainCrcOk = 0;
46 secondCrcOk = 0;
47 mainPartsCrcOk = 0;
48 secondPartsCrcOk = 0;
srs5694221e0872009-08-29 15:00:31 -040049 apmFound = 0;
50 bsdFound = 0;
srs56941d1448a2009-12-31 21:20:19 -050051 sectorAlignment = 8; // Align partitions on 4096-byte boundaries by default
srs5694ba00fed2010-01-12 18:18:36 -050052 beQuiet = 0;
53 whichWasUsed = use_new;
srs5694e7b4ff92009-08-18 13:16:10 -040054 srand((unsigned int) time(NULL));
srs56941e093722010-01-05 00:14:19 -050055 mainHeader.numParts = 0;
srs5694e7b4ff92009-08-18 13:16:10 -040056 SetGPTSize(NUM_GPT_ENTRIES);
57} // GPTData default constructor
58
59// The following constructor loads GPT data from a device file
60GPTData::GPTData(char* filename) {
61 blockSize = SECTOR_SIZE; // set a default
62 diskSize = 0;
63 partitions = NULL;
64 state = gpt_invalid;
65 strcpy(device, "");
srs56945d58fe02010-01-03 20:57:08 -050066 justLooking = 0;
srs5694e7b4ff92009-08-18 13:16:10 -040067 mainCrcOk = 0;
68 secondCrcOk = 0;
69 mainPartsCrcOk = 0;
70 secondPartsCrcOk = 0;
srs5694221e0872009-08-29 15:00:31 -040071 apmFound = 0;
72 bsdFound = 0;
srs56941d1448a2009-12-31 21:20:19 -050073 sectorAlignment = 8; // Align partitions on 4096-byte boundaries by default
srs5694ba00fed2010-01-12 18:18:36 -050074 beQuiet = 0;
75 whichWasUsed = use_new;
srs5694e7b4ff92009-08-18 13:16:10 -040076 srand((unsigned int) time(NULL));
srs56941e093722010-01-05 00:14:19 -050077 mainHeader.numParts = 0;
srs56943c0af382010-01-15 19:19:18 -050078 if (!LoadPartitions(filename))
79 exit(2);
srs5694e7b4ff92009-08-18 13:16:10 -040080} // GPTData(char* filename) constructor
81
srs5694e4ac11e2009-08-31 10:13:04 -040082// Destructor
srs5694e7b4ff92009-08-18 13:16:10 -040083GPTData::~GPTData(void) {
84 free(partitions);
85} // GPTData destructor
86
srs5694e4ac11e2009-08-31 10:13:04 -040087/*********************************************************************
88 * *
89 * Begin functions that verify data, or that adjust the verification *
90 * information (compute CRCs, rebuild headers) *
91 * *
92 *********************************************************************/
srs5694e7b4ff92009-08-18 13:16:10 -040093
srs5694e4ac11e2009-08-31 10:13:04 -040094// Perform detailed verification, reporting on any problems found, but
95// do *NOT* recover from these problems. Returns the total number of
96// problems identified.
97int GPTData::Verify(void) {
srs56941d1448a2009-12-31 21:20:19 -050098 int problems = 0, numSegments, i;
99 uint64_t totalFree, largestSegment, firstSector;
srs5694e4ac11e2009-08-31 10:13:04 -0400100 char tempStr[255], siTotal[255], siLargest[255];
101
102 // First, check for CRC errors in the GPT data....
103 if (!mainCrcOk) {
104 problems++;
105 printf("\nProblem: The CRC for the main GPT header is invalid. The main GPT header may\n"
srs5694546a9c72010-01-26 16:00:26 -0500106 "be corrupt. Consider loading the backup GPT header to rebuild the main GPT\n"
107 "header ('b' on the recovery & transformation menu). This report may be a false\n"
108 "alarm if you've already corrected other problems.\n");
srs5694e4ac11e2009-08-31 10:13:04 -0400109 } // if
110 if (!mainPartsCrcOk) {
111 problems++;
112 printf("\nProblem: The CRC for the main partition table is invalid. This table may be\n"
srs5694546a9c72010-01-26 16:00:26 -0500113 "corrupt. Consider loading the backup partition table ('c' on the recovery &\n"
114 "transformation menu). This report may be a false alarm if you've already\n"
115 "corrected other problems.\n");
srs5694e4ac11e2009-08-31 10:13:04 -0400116 } // if
117 if (!secondCrcOk) {
118 problems++;
119 printf("\nProblem: The CRC for the backup GPT header is invalid. The backup GPT header\n"
120 "may be corrupt. Consider using the main GPT header to rebuild the backup GPT\n"
srs5694546a9c72010-01-26 16:00:26 -0500121 "header ('d' on the recovery & transformation menu). This report may be a false\n"
122 "alarm if you've already corrected other problems.\n");
srs5694e4ac11e2009-08-31 10:13:04 -0400123 } // if
124 if (!secondPartsCrcOk) {
125 problems++;
126 printf("\nCaution: The CRC for the backup partition table is invalid. This table may\n"
srs5694546a9c72010-01-26 16:00:26 -0500127 "be corrupt. This program will automatically create a new backup partition\n"
128 "table when you save your partitions.\n");
srs5694e7b4ff92009-08-18 13:16:10 -0400129 } // if
130
srs5694978041c2009-09-21 20:51:47 -0400131 // Now check that the main and backup headers both point to themselves....
132 if (mainHeader.currentLBA != 1) {
133 problems++;
134 printf("\nProblem: The main header's self-pointer doesn't point to itself. This problem\n"
135 "is being automatically corrected, but it may be a symptom of more serious\n"
136 "problems. Think carefully before saving changes with 'w' or using this disk.\n");
137 mainHeader.currentLBA = 1;
138 } // if
139 if (secondHeader.currentLBA != (diskSize - UINT64_C(1))) {
140 problems++;
srs56943f2fe992009-11-24 18:28:18 -0500141 printf("\nProblem: The secondary header's self-pointer indicates that it doesn't reside\n"
142 "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 -0500143 "option on the experts' menu to adjust the secondary header's and partition\n"
144 "table's locations.\n");
srs5694978041c2009-09-21 20:51:47 -0400145 } // if
146
147 // Now check that critical main and backup GPT entries match each other
srs5694e4ac11e2009-08-31 10:13:04 -0400148 if (mainHeader.currentLBA != secondHeader.backupLBA) {
149 problems++;
150 printf("\nProblem: main GPT header's current LBA pointer (%llu) doesn't\n"
srs5694546a9c72010-01-26 16:00:26 -0500151 "match the backup GPT header's LBA pointer(%llu).\n",
srs5694e4ac11e2009-08-31 10:13:04 -0400152 (unsigned long long) mainHeader.currentLBA,
153 (unsigned long long) secondHeader.backupLBA);
154 } // if
155 if (mainHeader.backupLBA != secondHeader.currentLBA) {
156 problems++;
157 printf("\nProblem: main GPT header's backup LBA pointer (%llu) doesn't\n"
srs5694546a9c72010-01-26 16:00:26 -0500158 "match the backup GPT header's current LBA pointer (%llu).\n"
159 "The 'e' option on the experts' menu may fix this problem.\n",
160 (unsigned long long) mainHeader.backupLBA,
srs5694e4ac11e2009-08-31 10:13:04 -0400161 (unsigned long long) secondHeader.currentLBA);
162 } // if
163 if (mainHeader.firstUsableLBA != secondHeader.firstUsableLBA) {
164 problems++;
165 printf("\nProblem: main GPT header's first usable LBA pointer (%llu) doesn't\n"
srs5694546a9c72010-01-26 16:00:26 -0500166 "match the backup GPT header's first usable LBA pointer (%llu)\n",
167 (unsigned long long) mainHeader.firstUsableLBA,
srs5694e4ac11e2009-08-31 10:13:04 -0400168 (unsigned long long) secondHeader.firstUsableLBA);
169 } // if
170 if (mainHeader.lastUsableLBA != secondHeader.lastUsableLBA) {
171 problems++;
172 printf("\nProblem: main GPT header's last usable LBA pointer (%llu) doesn't\n"
srs5694546a9c72010-01-26 16:00:26 -0500173 "match the backup GPT header's last usable LBA pointer (%llu)\n",
174 (unsigned long long) mainHeader.lastUsableLBA,
srs5694e4ac11e2009-08-31 10:13:04 -0400175 (unsigned long long) secondHeader.lastUsableLBA);
srs5694546a9c72010-01-26 16:00:26 -0500176 printf("The 'e' option on the experts' menu can probably fix this problem.\n");
srs5694e4ac11e2009-08-31 10:13:04 -0400177 } // if
178 if ((mainHeader.diskGUID.data1 != secondHeader.diskGUID.data1) ||
179 (mainHeader.diskGUID.data2 != secondHeader.diskGUID.data2)) {
180 problems++;
181 printf("\nProblem: main header's disk GUID (%s) doesn't\n",
182 GUIDToStr(mainHeader.diskGUID, tempStr));
183 printf("match the backup GPT header's disk GUID (%s)\n",
184 GUIDToStr(secondHeader.diskGUID, tempStr));
srs5694546a9c72010-01-26 16:00:26 -0500185 printf("You should use the 'b' or 'd' option on the recovery & transformation menu to\n"
186 "select one or the other header.\n");
srs5694e4ac11e2009-08-31 10:13:04 -0400187 } // if
188 if (mainHeader.numParts != secondHeader.numParts) {
189 problems++;
190 printf("\nProblem: main GPT header's number of partitions (%lu) doesn't\n"
191 "match the backup GPT header's number of partitions (%lu)\n",
192 (unsigned long) mainHeader.numParts,
193 (unsigned long) secondHeader.numParts);
srs5694546a9c72010-01-26 16:00:26 -0500194 printf("Resizing the partition table ('s' on the experts' menu) may help.\n");
srs5694e4ac11e2009-08-31 10:13:04 -0400195 } // if
196 if (mainHeader.sizeOfPartitionEntries != secondHeader.sizeOfPartitionEntries) {
197 problems++;
198 printf("\nProblem: main GPT header's size of partition entries (%lu) doesn't\n"
199 "match the backup GPT header's size of partition entries (%lu)\n",
200 (unsigned long) mainHeader.sizeOfPartitionEntries,
201 (unsigned long) secondHeader.sizeOfPartitionEntries);
srs5694546a9c72010-01-26 16:00:26 -0500202 printf("You should use the 'b' or 'd' option on the recovery & transformation menu to\n"
203 "select one or the other header.\n");
srs5694e4ac11e2009-08-31 10:13:04 -0400204 } // if
205
206 // Now check for a few other miscellaneous problems...
207 // Check that the disk size will hold the data...
208 if (mainHeader.backupLBA > diskSize) {
209 problems++;
210 printf("\nProblem: Disk is too small to hold all the data!\n");
211 printf("(Disk size is %llu sectors, needs to be %llu sectors.)\n",
srs5694546a9c72010-01-26 16:00:26 -0500212 (unsigned long long) diskSize,
213 (unsigned long long) mainHeader.backupLBA);
214 printf("The 'e' option on the experts' menu may fix this problem.\n");
srs5694e4ac11e2009-08-31 10:13:04 -0400215 } // if
216
217 // Check for overlapping partitions....
218 problems += FindOverlaps();
219
220 // Check for mismatched MBR and GPT partitions...
221 problems += FindHybridMismatches();
222
223 // Verify that partitions don't run into GPT data areas....
224 problems += CheckGPTSize();
225
srs56941d1448a2009-12-31 21:20:19 -0500226 // Check that partitions are aligned on proper boundaries (for WD Advanced
227 // Format and similar disks)....
228 for (i = 0; i < mainHeader.numParts; i++) {
229 if ((partitions[i].GetFirstLBA() % sectorAlignment) != 0) {
230 printf("\nCaution: Partition %d doesn't begin on a %d-sector boundary. This may\n"
231 "result in degraded performance on some modern (2010 and later) hard disks.\n",
232 i + 1, sectorAlignment);
233 } // if
234 } // for
235
srs5694e4ac11e2009-08-31 10:13:04 -0400236 // Now compute available space, but only if no problems found, since
237 // problems could affect the results
238 if (problems == 0) {
239 totalFree = FindFreeBlocks(&numSegments, &largestSegment);
240 BytesToSI(totalFree * (uint64_t) blockSize, siTotal);
241 BytesToSI(largestSegment * (uint64_t) blockSize, siLargest);
srs5694546a9c72010-01-26 16:00:26 -0500242 printf("No problems found. %llu free sectors ", (unsigned long long) totalFree);
243 printf("(%s) available in %u\n", siTotal, numSegments);
244 printf("segments, the largest of which is %llu sectors ", (unsigned long long) largestSegment);
245 printf("(%s) in size\n", siLargest);
srs5694e4ac11e2009-08-31 10:13:04 -0400246 } else {
247 printf("\nIdentified %d problems!\n", problems);
srs5694e7b4ff92009-08-18 13:16:10 -0400248 } // if/else
srs5694e4ac11e2009-08-31 10:13:04 -0400249
250 return (problems);
251} // GPTData::Verify()
srs5694e7b4ff92009-08-18 13:16:10 -0400252
253// Checks to see if the GPT tables overrun existing partitions; if they
srs5694221e0872009-08-29 15:00:31 -0400254// do, issues a warning but takes no action. Returns number of problems
255// detected (0 if OK, 1 to 2 if problems).
srs5694e7b4ff92009-08-18 13:16:10 -0400256int GPTData::CheckGPTSize(void) {
257 uint64_t overlap, firstUsedBlock, lastUsedBlock;
258 uint32_t i;
srs5694221e0872009-08-29 15:00:31 -0400259 int numProbs = 0;
srs5694e7b4ff92009-08-18 13:16:10 -0400260
261 // first, locate the first & last used blocks
262 firstUsedBlock = UINT64_MAX;
263 lastUsedBlock = 0;
264 for (i = 0; i < mainHeader.numParts; i++) {
srs5694221e0872009-08-29 15:00:31 -0400265 if ((partitions[i].GetFirstLBA() < firstUsedBlock) &&
srs5694e4ac11e2009-08-31 10:13:04 -0400266 (partitions[i].GetFirstLBA() != 0))
srs5694221e0872009-08-29 15:00:31 -0400267 firstUsedBlock = partitions[i].GetFirstLBA();
268 if (partitions[i].GetLastLBA() > lastUsedBlock)
269 lastUsedBlock = partitions[i].GetLastLBA();
srs5694e7b4ff92009-08-18 13:16:10 -0400270 } // for
271
272 // If the disk size is 0 (the default), then it means that various
273 // variables aren't yet set, so the below tests will be useless;
274 // therefore we should skip everything
275 if (diskSize != 0) {
276 if (mainHeader.firstUsableLBA > firstUsedBlock) {
277 overlap = mainHeader.firstUsableLBA - firstUsedBlock;
srs5694221e0872009-08-29 15:00:31 -0400278 printf("Warning! Main partition table overlaps the first partition by %lu blocks!\n",
279 (unsigned long) overlap);
280 if (firstUsedBlock > 2) {
281 printf("Try reducing the partition table size by %lu entries.\n",
282 (unsigned long) (overlap * 4));
283 printf("(Use the 's' item on the experts' menu.)\n");
284 } else {
285 printf("You will need to delete this partition or resize it in another utility.\n");
286 } // if/else
287 numProbs++;
srs5694e7b4ff92009-08-18 13:16:10 -0400288 } // Problem at start of disk
289 if (mainHeader.lastUsableLBA < lastUsedBlock) {
290 overlap = lastUsedBlock - mainHeader.lastUsableLBA;
srs5694546a9c72010-01-26 16:00:26 -0500291 printf("Warning! Secondary partition table overlaps the last partition by %lu blocks!\n",
srs5694221e0872009-08-29 15:00:31 -0400292 (unsigned long) overlap);
293 if (lastUsedBlock > (diskSize - 2)) {
294 printf("You will need to delete this partition or resize it in another utility.\n");
295 } else {
296 printf("Try reducing the partition table size by %lu entries.\n",
297 (unsigned long) (overlap * 4));
298 printf("(Use the 's' item on the experts' menu.)\n");
299 } // if/else
300 numProbs++;
srs5694e7b4ff92009-08-18 13:16:10 -0400301 } // Problem at end of disk
302 } // if (diskSize != 0)
srs5694221e0872009-08-29 15:00:31 -0400303 return numProbs;
srs5694e7b4ff92009-08-18 13:16:10 -0400304} // GPTData::CheckGPTSize()
305
srs5694e7b4ff92009-08-18 13:16:10 -0400306// Check the validity of the GPT header. Returns 1 if the main header
307// is valid, 2 if the backup header is valid, 3 if both are valid, and
308// 0 if neither is valid. Note that this function just checks the GPT
309// signature and revision numbers, not CRCs or other data.
310int GPTData::CheckHeaderValidity(void) {
311 int valid = 3;
312
313 if (mainHeader.signature != GPT_SIGNATURE) {
314 valid -= 1;
srs5694221e0872009-08-29 15:00:31 -0400315// printf("Main GPT signature invalid; read 0x%016llX, should be\n0x%016llX\n",
316// (unsigned long long) mainHeader.signature, (unsigned long long) GPT_SIGNATURE);
srs5694e7b4ff92009-08-18 13:16:10 -0400317 } else if ((mainHeader.revision != 0x00010000) && valid) {
318 valid -= 1;
319 printf("Unsupported GPT version in main header; read 0x%08lX, should be\n0x%08lX\n",
srs56945d58fe02010-01-03 20:57:08 -0500320 (unsigned long) mainHeader.revision, (unsigned long) UINT32_C(0x00010000));
srs5694e7b4ff92009-08-18 13:16:10 -0400321 } // if/else/if
322
323 if (secondHeader.signature != GPT_SIGNATURE) {
324 valid -= 2;
srs5694221e0872009-08-29 15:00:31 -0400325// printf("Secondary GPT signature invalid; read 0x%016llX, should be\n0x%016llX\n",
326// (unsigned long long) secondHeader.signature, (unsigned long long) GPT_SIGNATURE);
srs5694e7b4ff92009-08-18 13:16:10 -0400327 } else if ((secondHeader.revision != 0x00010000) && valid) {
328 valid -= 2;
329 printf("Unsupported GPT version in backup header; read 0x%08lX, should be\n0x%08lX\n",
srs56945d58fe02010-01-03 20:57:08 -0500330 (unsigned long) mainHeader.revision, (unsigned long) UINT32_C(0x00010000));
srs5694e7b4ff92009-08-18 13:16:10 -0400331 } // if/else/if
332
srs56942a9f5da2009-08-26 00:48:01 -0400333 // If MBR bad, check for an Apple disk signature
srs5694e35eb1b2009-09-14 00:29:34 -0400334 if ((protectiveMBR.GetValidity() == invalid) &&
srs5694e4ac11e2009-08-31 10:13:04 -0400335 (((mainHeader.signature << 32) == APM_SIGNATURE1) ||
srs56942a9f5da2009-08-26 00:48:01 -0400336 (mainHeader.signature << 32) == APM_SIGNATURE2)) {
srs5694221e0872009-08-29 15:00:31 -0400337 apmFound = 1; // Will display warning message later
srs56943f2fe992009-11-24 18:28:18 -0500338 } // if
srs56942a9f5da2009-08-26 00:48:01 -0400339
srs5694e4ac11e2009-08-31 10:13:04 -0400340 return valid;
srs5694e7b4ff92009-08-18 13:16:10 -0400341} // GPTData::CheckHeaderValidity()
342
343// Check the header CRC to see if it's OK...
srs56942a9f5da2009-08-26 00:48:01 -0400344// Note: Must be called BEFORE byte-order reversal on big-endian
345// systems!
srs5694e7b4ff92009-08-18 13:16:10 -0400346int GPTData::CheckHeaderCRC(struct GPTHeader* header) {
srs5694978041c2009-09-21 20:51:47 -0400347 uint32_t oldCRC, newCRC, hSize;
srs5694e7b4ff92009-08-18 13:16:10 -0400348
srs56942a9f5da2009-08-26 00:48:01 -0400349 // Back up old header CRC and then blank it, since it must be 0 for
srs5694e7b4ff92009-08-18 13:16:10 -0400350 // computation to be valid
351 oldCRC = header->headerCRC;
352 header->headerCRC = UINT32_C(0);
srs5694978041c2009-09-21 20:51:47 -0400353 hSize = header->headerSize;
354
355 // If big-endian system, reverse byte order
356 if (IsLittleEndian() == 0) {
357 ReverseBytes(&oldCRC, 4);
358 } // if
srs5694e7b4ff92009-08-18 13:16:10 -0400359
360 // Initialize CRC functions...
361 chksum_crc32gentab();
362
363 // Compute CRC, restore original, and return result of comparison
364 newCRC = chksum_crc32((unsigned char*) header, HEADER_SIZE);
srs5694978041c2009-09-21 20:51:47 -0400365 header->headerCRC = oldCRC;
srs5694e7b4ff92009-08-18 13:16:10 -0400366 return (oldCRC == newCRC);
367} // GPTData::CheckHeaderCRC()
368
srs56942a9f5da2009-08-26 00:48:01 -0400369// Recompute all the CRCs. Must be called before saving (but after reversing
370// byte order on big-endian systems) if any changes have been made.
srs5694e7b4ff92009-08-18 13:16:10 -0400371void GPTData::RecomputeCRCs(void) {
srs5694978041c2009-09-21 20:51:47 -0400372 uint32_t crc, hSize, trueNumParts;
srs56942a9f5da2009-08-26 00:48:01 -0400373 int littleEndian = 1;
srs5694e7b4ff92009-08-18 13:16:10 -0400374
375 // Initialize CRC functions...
376 chksum_crc32gentab();
377
srs5694978041c2009-09-21 20:51:47 -0400378 hSize = mainHeader.headerSize;
srs56942a9f5da2009-08-26 00:48:01 -0400379 littleEndian = IsLittleEndian();
380
srs5694e7b4ff92009-08-18 13:16:10 -0400381 // Compute CRC of partition tables & store in main and secondary headers
srs56942a9f5da2009-08-26 00:48:01 -0400382 trueNumParts = mainHeader.numParts;
383 if (littleEndian == 0)
srs5694221e0872009-08-29 15:00:31 -0400384 ReverseBytes(&trueNumParts, 4); // unreverse this key piece of data....
srs56942a9f5da2009-08-26 00:48:01 -0400385 crc = chksum_crc32((unsigned char*) partitions, trueNumParts * GPT_SIZE);
srs5694e7b4ff92009-08-18 13:16:10 -0400386 mainHeader.partitionEntriesCRC = crc;
387 secondHeader.partitionEntriesCRC = crc;
srs56942a9f5da2009-08-26 00:48:01 -0400388 if (littleEndian == 0) {
srs5694221e0872009-08-29 15:00:31 -0400389 ReverseBytes(&mainHeader.partitionEntriesCRC, 4);
390 ReverseBytes(&secondHeader.partitionEntriesCRC, 4);
srs56942a9f5da2009-08-26 00:48:01 -0400391 } // if
srs5694e7b4ff92009-08-18 13:16:10 -0400392
393 // Zero out GPT tables' own CRCs (required for correct computation)
394 mainHeader.headerCRC = 0;
395 secondHeader.headerCRC = 0;
396
397 // Compute & store CRCs of main & secondary headers...
srs5694978041c2009-09-21 20:51:47 -0400398 crc = chksum_crc32((unsigned char*) &mainHeader, hSize);
srs56942a9f5da2009-08-26 00:48:01 -0400399 if (littleEndian == 0)
srs5694221e0872009-08-29 15:00:31 -0400400 ReverseBytes(&crc, 4);
srs5694e7b4ff92009-08-18 13:16:10 -0400401 mainHeader.headerCRC = crc;
srs5694978041c2009-09-21 20:51:47 -0400402 crc = chksum_crc32((unsigned char*) &secondHeader, hSize);
srs56942a9f5da2009-08-26 00:48:01 -0400403 if (littleEndian == 0)
srs5694221e0872009-08-29 15:00:31 -0400404 ReverseBytes(&crc, 4);
srs5694e7b4ff92009-08-18 13:16:10 -0400405 secondHeader.headerCRC = crc;
406} // GPTData::RecomputeCRCs()
407
srs5694e7b4ff92009-08-18 13:16:10 -0400408// Rebuild the main GPT header, using the secondary header as a model.
409// Typically called when the main header has been found to be corrupt.
410void GPTData::RebuildMainHeader(void) {
411 int i;
412
413 mainHeader.signature = GPT_SIGNATURE;
414 mainHeader.revision = secondHeader.revision;
srs5694978041c2009-09-21 20:51:47 -0400415 mainHeader.headerSize = secondHeader.headerSize;
srs5694e7b4ff92009-08-18 13:16:10 -0400416 mainHeader.headerCRC = UINT32_C(0);
417 mainHeader.reserved = secondHeader.reserved;
418 mainHeader.currentLBA = secondHeader.backupLBA;
419 mainHeader.backupLBA = secondHeader.currentLBA;
420 mainHeader.firstUsableLBA = secondHeader.firstUsableLBA;
421 mainHeader.lastUsableLBA = secondHeader.lastUsableLBA;
422 mainHeader.diskGUID.data1 = secondHeader.diskGUID.data1;
423 mainHeader.diskGUID.data2 = secondHeader.diskGUID.data2;
424 mainHeader.partitionEntriesLBA = UINT64_C(2);
425 mainHeader.numParts = secondHeader.numParts;
426 mainHeader.sizeOfPartitionEntries = secondHeader.sizeOfPartitionEntries;
427 mainHeader.partitionEntriesCRC = secondHeader.partitionEntriesCRC;
428 for (i = 0 ; i < GPT_RESERVED; i++)
429 mainHeader.reserved2[i] = secondHeader.reserved2[i];
srs5694546a9c72010-01-26 16:00:26 -0500430 mainCrcOk = secondCrcOk;
srs5694e7b4ff92009-08-18 13:16:10 -0400431} // GPTData::RebuildMainHeader()
432
433// Rebuild the secondary GPT header, using the main header as a model.
434void GPTData::RebuildSecondHeader(void) {
435 int i;
436
437 secondHeader.signature = GPT_SIGNATURE;
438 secondHeader.revision = mainHeader.revision;
srs5694978041c2009-09-21 20:51:47 -0400439 secondHeader.headerSize = mainHeader.headerSize;
srs5694e7b4ff92009-08-18 13:16:10 -0400440 secondHeader.headerCRC = UINT32_C(0);
441 secondHeader.reserved = mainHeader.reserved;
442 secondHeader.currentLBA = mainHeader.backupLBA;
443 secondHeader.backupLBA = mainHeader.currentLBA;
444 secondHeader.firstUsableLBA = mainHeader.firstUsableLBA;
445 secondHeader.lastUsableLBA = mainHeader.lastUsableLBA;
446 secondHeader.diskGUID.data1 = mainHeader.diskGUID.data1;
447 secondHeader.diskGUID.data2 = mainHeader.diskGUID.data2;
448 secondHeader.partitionEntriesLBA = secondHeader.lastUsableLBA + UINT64_C(1);
449 secondHeader.numParts = mainHeader.numParts;
450 secondHeader.sizeOfPartitionEntries = mainHeader.sizeOfPartitionEntries;
451 secondHeader.partitionEntriesCRC = mainHeader.partitionEntriesCRC;
452 for (i = 0 ; i < GPT_RESERVED; i++)
453 secondHeader.reserved2[i] = mainHeader.reserved2[i];
srs5694546a9c72010-01-26 16:00:26 -0500454 secondCrcOk = mainCrcOk;
srs5694e4ac11e2009-08-31 10:13:04 -0400455} // GPTData::RebuildSecondHeader()
456
457// Search for hybrid MBR entries that have no corresponding GPT partition.
458// Returns number of such mismatches found
459int GPTData::FindHybridMismatches(void) {
460 int i, j, found, numFound = 0;
461 uint64_t mbrFirst, mbrLast;
462
463 for (i = 0; i < 4; i++) {
464 if ((protectiveMBR.GetType(i) != 0xEE) && (protectiveMBR.GetType(i) != 0x00)) {
465 j = 0;
466 found = 0;
467 do {
468 mbrFirst = (uint64_t) protectiveMBR.GetFirstSector(i);
469 mbrLast = mbrFirst + (uint64_t) protectiveMBR.GetLength(i) - UINT64_C(1);
470 if ((partitions[j].GetFirstLBA() == mbrFirst) &&
471 (partitions[j].GetLastLBA() == mbrLast))
472 found = 1;
473 j++;
474 } while ((!found) && (j < mainHeader.numParts));
475 if (!found) {
476 numFound++;
477 printf("\nWarning! Mismatched GPT and MBR partitions! MBR partition "
478 "%d, of type 0x%02X,\nhas no corresponding GPT partition! "
479 "You may continue, but this condition\nmight cause data loss"
480 " in the future!\a\n", i + 1, protectiveMBR.GetType(i));
481 } // if
482 } // if
483 } // for
484 return numFound;
485} // GPTData::FindHybridMismatches
486
487// Find overlapping partitions and warn user about them. Returns number of
488// overlapping partitions.
489int GPTData::FindOverlaps(void) {
490 int i, j, problems = 0;
491
492 for (i = 1; i < mainHeader.numParts; i++) {
493 for (j = 0; j < i; j++) {
494 if (partitions[i].DoTheyOverlap(&partitions[j])) {
495 problems++;
496 printf("\nProblem: partitions %d and %d overlap:\n", i + 1, j + 1);
497 printf(" Partition %d: %llu to %llu\n", i,
498 (unsigned long long) partitions[i].GetFirstLBA(),
499 (unsigned long long) partitions[i].GetLastLBA());
500 printf(" Partition %d: %llu to %llu\n", j,
501 (unsigned long long) partitions[j].GetFirstLBA(),
502 (unsigned long long) partitions[j].GetLastLBA());
503 } // if
504 } // for j...
505 } // for i...
506 return problems;
507} // GPTData::FindOverlaps()
508
509/******************************************************************
510 * *
511 * Begin functions that load data from disk or save data to disk. *
512 * *
513 ******************************************************************/
514
515// Scan for partition data. This function loads the MBR data (regular MBR or
516// protective MBR) and loads BSD disklabel data (which is probably invalid).
517// It also looks for APM data, forces a load of GPT data, and summarizes
518// the results.
srs5694546a9c72010-01-26 16:00:26 -0500519void GPTData::PartitionScan(void) {
srs5694e4ac11e2009-08-31 10:13:04 -0400520 BSDData bsdDisklabel;
srs5694e4ac11e2009-08-31 10:13:04 -0400521
522 // Read the MBR & check for BSD disklabel
srs5694546a9c72010-01-26 16:00:26 -0500523 protectiveMBR.ReadMBRData(&myDisk);
524 bsdDisklabel.ReadBSDData(&myDisk, 0, diskSize - 1);
srs5694e4ac11e2009-08-31 10:13:04 -0400525
526 // Load the GPT data, whether or not it's valid
srs5694546a9c72010-01-26 16:00:26 -0500527 ForceLoadGPTData();
srs5694ba00fed2010-01-12 18:18:36 -0500528
529 if (!beQuiet) {
530 printf("Partition table scan:\n");
531 protectiveMBR.ShowState();
532 bsdDisklabel.ShowState();
533 ShowAPMState(); // Show whether there's an Apple Partition Map present
534 ShowGPTState(); // Show GPT status
535 printf("\n");
536 } // if
srs5694e4ac11e2009-08-31 10:13:04 -0400537
538 if (apmFound) {
539 printf("\n*******************************************************************\n");
540 printf("This disk appears to contain an Apple-format (APM) partition table!\n");
srs56945d58fe02010-01-03 20:57:08 -0500541 if (!justLooking) {
542 printf("It will be destroyed if you continue!\n");
543 } // if
srs5694e4ac11e2009-08-31 10:13:04 -0400544 printf("*******************************************************************\n\n\a");
545 } // if
srs5694e4ac11e2009-08-31 10:13:04 -0400546} // GPTData::PartitionScan()
547
548// Read GPT data from a disk.
549int GPTData::LoadPartitions(char* deviceFilename) {
550 int fd, err;
551 int allOK = 1, i;
552 uint64_t firstBlock, lastBlock;
553 BSDData bsdDisklabel;
554
555 // First, do a test to see if writing will be possible later....
srs5694546a9c72010-01-26 16:00:26 -0500556 fd = myDisk.OpenForWrite(deviceFilename);
srs56945d58fe02010-01-03 20:57:08 -0500557 if ((fd == -1) && (!justLooking)) {
srs5694e4ac11e2009-08-31 10:13:04 -0400558 printf("\aNOTE: Write test failed with error number %d. It will be "
srs56947dbb9322010-01-20 16:56:30 -0500559 "impossible to save\nchanges to this disk's partition table!\n",
srs5694e4ac11e2009-08-31 10:13:04 -0400560 errno);
srs56947dbb9322010-01-20 16:56:30 -0500561#ifdef __FreeBSD__
562 printf("You may be able to enable writes by exiting this program, typing\n"
563 "'sysctl kern.geom.debugflags=16' at a shell prompt, and re-running this\n"
564 "program.\n");
565#endif
566 printf("\n");
srs56945d58fe02010-01-03 20:57:08 -0500567 justLooking = 1;
568 } // if
srs5694546a9c72010-01-26 16:00:26 -0500569 myDisk.Close();
srs5694e4ac11e2009-08-31 10:13:04 -0400570
srs5694546a9c72010-01-26 16:00:26 -0500571// if ((fd = open(deviceFilename, O_RDONLY)) != -1) {
572 if (myDisk.OpenForRead(deviceFilename)) {
srs5694e4ac11e2009-08-31 10:13:04 -0400573 // store disk information....
srs5694546a9c72010-01-26 16:00:26 -0500574 diskSize = myDisk.DiskSize(&err);
575 blockSize = (uint32_t) myDisk.GetBlockSize();
576 sectorAlignment = myDisk.FindAlignment();
srs5694e4ac11e2009-08-31 10:13:04 -0400577 strcpy(device, deviceFilename);
srs5694546a9c72010-01-26 16:00:26 -0500578 PartitionScan(); // Check for partition types, load GPT, & print summary
srs5694e4ac11e2009-08-31 10:13:04 -0400579
srs5694ba00fed2010-01-12 18:18:36 -0500580 whichWasUsed = UseWhichPartitions();
581 switch (whichWasUsed) {
srs5694e4ac11e2009-08-31 10:13:04 -0400582 case use_mbr:
583 XFormPartitions();
584 break;
585 case use_bsd:
srs5694546a9c72010-01-26 16:00:26 -0500586 bsdDisklabel.ReadBSDData(&myDisk, 0, diskSize - 1);
srs5694e4ac11e2009-08-31 10:13:04 -0400587// bsdDisklabel.DisplayBSDData();
588 ClearGPTData();
589 protectiveMBR.MakeProtectiveMBR(1); // clear boot area (option 1)
590 XFormDisklabel(&bsdDisklabel, 0);
591 break;
592 case use_gpt:
593 break;
594 case use_new:
595 ClearGPTData();
596 protectiveMBR.MakeProtectiveMBR();
597 break;
srs56943c0af382010-01-15 19:19:18 -0500598 case use_abort:
599 allOK = 0;
600 printf("Aborting because of invalid partition data!\n");
601 break;
srs5694e4ac11e2009-08-31 10:13:04 -0400602 } // switch
603
604 // Now find the first and last sectors used by partitions...
605 if (allOK) {
606 firstBlock = mainHeader.backupLBA; // start high
607 lastBlock = 0; // start low
608 for (i = 0; i < mainHeader.numParts; i++) {
609 if ((partitions[i].GetFirstLBA() < firstBlock) &&
610 (partitions[i].GetFirstLBA() > 0))
611 firstBlock = partitions[i].GetFirstLBA();
612 if (partitions[i].GetLastLBA() > lastBlock)
613 lastBlock = partitions[i].GetLastLBA();
614 } // for
srs56943c0af382010-01-15 19:19:18 -0500615 CheckGPTSize();
srs5694e4ac11e2009-08-31 10:13:04 -0400616 } // if
srs5694e4ac11e2009-08-31 10:13:04 -0400617 } else {
618 allOK = 0;
619 fprintf(stderr, "Problem opening %s for reading! Error is %d\n",
620 deviceFilename, errno);
621 if (errno == EACCES) { // User is probably not running as root
622 fprintf(stderr, "You must run this program as root or use sudo!\n");
623 } // if
624 } // if/else
625 return (allOK);
626} // GPTData::LoadPartitions()
627
628// Loads the GPT, as much as possible. Returns 1 if this seems to have
629// succeeded, 0 if there are obvious problems....
srs5694546a9c72010-01-26 16:00:26 -0500630int GPTData::ForceLoadGPTData(void) {
srs5694e4ac11e2009-08-31 10:13:04 -0400631 int allOK = 1, validHeaders;
632 off_t seekTo;
633 char* storage;
634 uint32_t newCRC, sizeOfParts;
635
636 // Seek to and read the main GPT header
srs5694546a9c72010-01-26 16:00:26 -0500637 if (myDisk.Seek(1)) {
638 if (myDisk.Read(&mainHeader, 512) != 512) { // read main GPT header
639 fprintf(stderr, "Warning! Error %d reading main GPT header!\n", errno);
640 } // if read not OK
641 } else allOK = 0; // if/else seek OK
srs5694e4ac11e2009-08-31 10:13:04 -0400642 mainCrcOk = CheckHeaderCRC(&mainHeader);
643 if (IsLittleEndian() == 0) // big-endian system; adjust header byte order....
644 ReverseHeaderBytes(&mainHeader);
645
srs56943f2fe992009-11-24 18:28:18 -0500646 // Load backup header, check its CRC, and store the results of the
647 // check for future reference. Load backup header using pointer in main
648 // header if possible; but if main header has a CRC error, or if it
649 // points to beyond the end of the disk, load the last sector of the
650 // disk instead.
651 if (mainCrcOk) {
652 if (mainHeader.backupLBA < diskSize) {
srs5694546a9c72010-01-26 16:00:26 -0500653 seekTo = mainHeader.backupLBA;
srs56943f2fe992009-11-24 18:28:18 -0500654 } else {
srs5694546a9c72010-01-26 16:00:26 -0500655 seekTo = diskSize - UINT64_C(1);
srs56943f2fe992009-11-24 18:28:18 -0500656 printf("Warning! Disk size is smaller than the main header indicates! Loading\n"
657 "secondary header from the last sector of the disk! You should use 'v' to\n"
658 "verify disk integrity, and perhaps options on the experts' menu to repair\n"
659 "the disk.\n");
660 } // else
661 } else {
srs5694546a9c72010-01-26 16:00:26 -0500662 seekTo = diskSize - UINT64_C(1);
srs56943f2fe992009-11-24 18:28:18 -0500663 } // if/else (mainCrcOk)
664
srs5694546a9c72010-01-26 16:00:26 -0500665 if (myDisk.Seek(seekTo)) {
666 if (myDisk.Read(&secondHeader, 512) != 512) { // read secondary GPT header
srs56941e093722010-01-05 00:14:19 -0500667 fprintf(stderr, "Warning! Error %d reading secondary GPT header!\n", errno);
srs56945d58fe02010-01-03 20:57:08 -0500668 } // if
srs5694e4ac11e2009-08-31 10:13:04 -0400669 secondCrcOk = CheckHeaderCRC(&secondHeader);
670 if (IsLittleEndian() == 0) // big-endian system; adjust header byte order....
671 ReverseHeaderBytes(&secondHeader);
672 } else {
673 allOK = 0;
674 state = gpt_invalid;
srs56943f2fe992009-11-24 18:28:18 -0500675 fprintf(stderr, "Unable to seek to secondary GPT header at sector %llu!\n",
srs56945d58fe02010-01-03 20:57:08 -0500676 (unsigned long long) (diskSize - (UINT64_C(1))));
srs5694e4ac11e2009-08-31 10:13:04 -0400677 } // if/else lseek
678
679 // Return valid headers code: 0 = both headers bad; 1 = main header
680 // good, backup bad; 2 = backup header good, main header bad;
681 // 3 = both headers good. Note these codes refer to valid GPT
682 // signatures and version numbers; more subtle problems will elude
683 // this check!
684 validHeaders = CheckHeaderValidity();
685
686 // Read partitions (from primary array)
687 if (validHeaders > 0) { // if at least one header is OK....
688 // GPT appears to be valid....
689 state = gpt_valid;
690
691 // We're calling the GPT valid, but there's a possibility that one
692 // of the two headers is corrupt. If so, use the one that seems to
693 // be in better shape to regenerate the bad one
srs5694546a9c72010-01-26 16:00:26 -0500694 if (validHeaders == 1) { // valid main header, invalid backup header
695 fprintf(stderr, "\aCaution: invalid backup GPT header, but valid main header; regenerating\n"
696 "backup header from main header.\n\n");
srs5694e4ac11e2009-08-31 10:13:04 -0400697 RebuildSecondHeader();
srs5694546a9c72010-01-26 16:00:26 -0500698 state = gpt_corrupt;
srs5694e4ac11e2009-08-31 10:13:04 -0400699 secondCrcOk = mainCrcOk; // Since regenerated, use CRC validity of main
srs5694546a9c72010-01-26 16:00:26 -0500700 } else if (validHeaders == 2) { // valid backup header, invalid main header
701 fprintf(stderr, "\aCaution: invalid main GPT header, but valid backup; regenerating main header\n"
702 "from backup!\n\n");
703 RebuildMainHeader();
704 state = gpt_corrupt;
705 mainCrcOk = secondCrcOk; // Since copied, use CRC validity of backup
srs5694e4ac11e2009-08-31 10:13:04 -0400706 } // if/else/if
707
srs5694546a9c72010-01-26 16:00:26 -0500708 // Figure out which partition table to load....
709 // Load the main partition table, since either its header's CRC is OK or the
710 // backup header's CRC is not OK....
711 if (mainCrcOk || !secondCrcOk) {
712 if (LoadMainTable() == 0)
713 allOK = 0;
714 } else { // bad main header CRC and backup header CRC is OK
715 state = gpt_corrupt;
716 if (LoadSecondTableAsMain()) {
717 fprintf(stderr, "\aWarning: Invalid CRC on main header data; loaded backup partition table.\n");
718 } else { // backup table bad, bad main header CRC, but try main table in desperation....
719 if (LoadMainTable() == 0) {
720 allOK = 0;
721 fprintf(stderr, "\a\aWarning! Unable to load either main or backup partition table!\n");
722 } // if
723 } // if/else (LoadSecondTableAsMain())
724 } // if/else (load partition table)
srs5694e4ac11e2009-08-31 10:13:04 -0400725
726 // Load backup partition table into temporary storage to check
727 // its CRC and store the results, then discard this temporary
728 // storage, since we don't use it in any but recovery operations
srs5694546a9c72010-01-26 16:00:26 -0500729 seekTo = secondHeader.partitionEntriesLBA;
730 if ((myDisk.Seek(seekTo)) && (secondCrcOk)) {
srs5694e4ac11e2009-08-31 10:13:04 -0400731 sizeOfParts = secondHeader.numParts * secondHeader.sizeOfPartitionEntries;
732 storage = (char*) malloc(sizeOfParts);
srs5694546a9c72010-01-26 16:00:26 -0500733 if (myDisk.Read(storage, sizeOfParts) != sizeOfParts) {
srs56945d58fe02010-01-03 20:57:08 -0500734 fprintf(stderr, "Warning! Error %d reading backup partition table!\n", errno);
735 } // if
srs5694e4ac11e2009-08-31 10:13:04 -0400736 newCRC = chksum_crc32((unsigned char*) storage, sizeOfParts);
737 free(storage);
738 secondPartsCrcOk = (newCRC == secondHeader.partitionEntriesCRC);
739 } // if
740
srs5694546a9c72010-01-26 16:00:26 -0500741 // Problem with main partition table; if backup is OK, use it instead....
742 if (secondPartsCrcOk && secondCrcOk && !mainPartsCrcOk) {
743 state = gpt_corrupt;
744 allOK = allOK && LoadSecondTableAsMain();
745 fprintf(stderr, "\aWarning! Main partition table CRC mismatch! Loaded backup "
746 "partition table\ninstead of main partition table!\n\n");
747 } // if
748
srs5694e4ac11e2009-08-31 10:13:04 -0400749 // Check for valid CRCs and warn if there are problems
750 if ((mainCrcOk == 0) || (secondCrcOk == 0) || (mainPartsCrcOk == 0) ||
751 (secondPartsCrcOk == 0)) {
srs5694546a9c72010-01-26 16:00:26 -0500752 printf("Warning! One or more CRCs don't match. You should repair the disk!\n\n");
srs5694e4ac11e2009-08-31 10:13:04 -0400753 state = gpt_corrupt;
srs5694ba00fed2010-01-12 18:18:36 -0500754 } // if
srs5694e4ac11e2009-08-31 10:13:04 -0400755 } else {
756 state = gpt_invalid;
757 } // if/else
758 return allOK;
759} // GPTData::ForceLoadGPTData()
760
srs5694247657a2009-11-26 18:36:12 -0500761// Loads the partition table pointed to by the main GPT header. The
srs5694e4ac11e2009-08-31 10:13:04 -0400762// main GPT header in memory MUST be valid for this call to do anything
763// sensible!
srs5694546a9c72010-01-26 16:00:26 -0500764// Returns 1 on success, 0 on failure. CRC errors do NOT count as failure.
srs5694e4ac11e2009-08-31 10:13:04 -0400765int GPTData::LoadMainTable(void) {
srs5694546a9c72010-01-26 16:00:26 -0500766 int fd, retval = 1;
srs5694e4ac11e2009-08-31 10:13:04 -0400767 uint32_t newCRC, sizeOfParts;
768
srs5694546a9c72010-01-26 16:00:26 -0500769 if (myDisk.OpenForRead(device)) {
srs5694e4ac11e2009-08-31 10:13:04 -0400770 // Set internal data structures for number of partitions on the disk
771 SetGPTSize(mainHeader.numParts);
772
773 // Load main partition table, and record whether its CRC
774 // matches the stored value
srs5694546a9c72010-01-26 16:00:26 -0500775 if (!myDisk.Seek(mainHeader.partitionEntriesLBA))
776 retval = 0;
srs5694e4ac11e2009-08-31 10:13:04 -0400777 sizeOfParts = mainHeader.numParts * mainHeader.sizeOfPartitionEntries;
srs5694546a9c72010-01-26 16:00:26 -0500778 if (myDisk.Read(partitions, sizeOfParts) != sizeOfParts) {
srs56945d58fe02010-01-03 20:57:08 -0500779 fprintf(stderr, "Warning! Error %d when loading the main partition table!\n", errno);
srs5694546a9c72010-01-26 16:00:26 -0500780 retval = 0;
srs56945d58fe02010-01-03 20:57:08 -0500781 } // if
srs5694e4ac11e2009-08-31 10:13:04 -0400782 newCRC = chksum_crc32((unsigned char*) partitions, sizeOfParts);
783 mainPartsCrcOk = (newCRC == mainHeader.partitionEntriesCRC);
784 if (IsLittleEndian() == 0)
785 ReversePartitionBytes();
srs5694546a9c72010-01-26 16:00:26 -0500786 } else retval = 0; // if open for read....
srs5694e4ac11e2009-08-31 10:13:04 -0400787 return retval;
788} // GPTData::LoadMainTable()
srs5694e7b4ff92009-08-18 13:16:10 -0400789
790// Load the second (backup) partition table as the primary partition
srs5694546a9c72010-01-26 16:00:26 -0500791// table. Used in repair functions, and when starting up if the main
792// partition table is damaged.
793// Returns 1 on success, 0 on failure. CRC errors do NOT count as failure.
794int GPTData::LoadSecondTableAsMain(void) {
795 uint64_t seekTo;
srs5694e7b4ff92009-08-18 13:16:10 -0400796 uint32_t sizeOfParts, newCRC;
srs5694546a9c72010-01-26 16:00:26 -0500797 int retval = 1;
srs5694e7b4ff92009-08-18 13:16:10 -0400798
srs5694546a9c72010-01-26 16:00:26 -0500799 if (myDisk.OpenForRead(device)) {
800 seekTo = secondHeader.partitionEntriesLBA;
801 retval = myDisk.Seek(seekTo);
802 if (retval == 1) {
srs5694e7b4ff92009-08-18 13:16:10 -0400803 SetGPTSize(secondHeader.numParts);
804 sizeOfParts = secondHeader.numParts * secondHeader.sizeOfPartitionEntries;
srs5694546a9c72010-01-26 16:00:26 -0500805 if (myDisk.Read(partitions, sizeOfParts) != sizeOfParts) {
srs56945d58fe02010-01-03 20:57:08 -0500806 fprintf(stderr, "Warning! Read error %d! Misbehavior now likely!\n", errno);
srs5694546a9c72010-01-26 16:00:26 -0500807 retval = 0;
srs56945d58fe02010-01-03 20:57:08 -0500808 } // if
srs5694e7b4ff92009-08-18 13:16:10 -0400809 newCRC = chksum_crc32((unsigned char*) partitions, sizeOfParts);
810 secondPartsCrcOk = (newCRC == secondHeader.partitionEntriesCRC);
srs5694e4ac11e2009-08-31 10:13:04 -0400811 mainPartsCrcOk = secondPartsCrcOk;
srs56942a9f5da2009-08-26 00:48:01 -0400812 if (IsLittleEndian() == 0)
813 ReversePartitionBytes();
srs5694e7b4ff92009-08-18 13:16:10 -0400814 if (!secondPartsCrcOk) {
815 printf("Error! After loading backup partitions, the CRC still doesn't check out!\n");
816 } // if
817 } else {
srs5694546a9c72010-01-26 16:00:26 -0500818 printf("Error! Couldn't seek to backup partition table!\n", retval);
srs5694e7b4ff92009-08-18 13:16:10 -0400819 } // if/else
820 } else {
srs56941d1448a2009-12-31 21:20:19 -0500821 printf("Error! Couldn't open device %s when recovering backup partition table!\n", device);
srs5694546a9c72010-01-26 16:00:26 -0500822 retval = 0;
srs5694e7b4ff92009-08-18 13:16:10 -0400823 } // if/else
srs5694546a9c72010-01-26 16:00:26 -0500824 return retval;
srs5694e7b4ff92009-08-18 13:16:10 -0400825} // GPTData::LoadSecondTableAsMain()
826
srs5694e7b4ff92009-08-18 13:16:10 -0400827// Writes GPT (and protective MBR) to disk. Returns 1 on successful
828// write, 0 if there was a problem.
srs5694ba00fed2010-01-12 18:18:36 -0500829int GPTData::SaveGPTData(int quiet) {
srs5694978041c2009-09-21 20:51:47 -0400830 int allOK = 1;
srs5694e7b4ff92009-08-18 13:16:10 -0400831 char answer, line[256];
srs5694e7b4ff92009-08-18 13:16:10 -0400832 uint64_t secondTable;
srs56942a9f5da2009-08-26 00:48:01 -0400833 uint32_t numParts;
srs5694e7b4ff92009-08-18 13:16:10 -0400834 off_t offset;
835
836 if (strlen(device) == 0) {
837 printf("Device not defined.\n");
838 } // if
839
840 // First do some final sanity checks....
srs56945d58fe02010-01-03 20:57:08 -0500841
842 // This test should only fail on read-only disks....
843 if (justLooking) {
844 printf("The justLooking flag is set. This probably means you can't write to the disk.\n");
845 allOK = 0;
846 } // if
847
srs5694e7b4ff92009-08-18 13:16:10 -0400848 // Is there enough space to hold the GPT headers and partition tables,
849 // given the partition sizes?
srs5694221e0872009-08-29 15:00:31 -0400850 if (CheckGPTSize() > 0) {
srs5694e7b4ff92009-08-18 13:16:10 -0400851 allOK = 0;
852 } // if
853
854 // Check that disk is really big enough to handle this...
855 if (mainHeader.backupLBA > diskSize) {
srs5694247657a2009-11-26 18:36:12 -0500856 fprintf(stderr, "Error! Disk is too small! The 'e' option on the experts' menu might fix the\n"
857 "problem (or it might not). Aborting!\n");
srs56945d58fe02010-01-03 20:57:08 -0500858 printf("(Disk size is %llu sectors, needs to be %llu sectors.)\n",
859 (unsigned long long) diskSize, (unsigned long long) mainHeader.backupLBA);
srs5694e7b4ff92009-08-18 13:16:10 -0400860 allOK = 0;
861 } // if
srs5694247657a2009-11-26 18:36:12 -0500862 // Check that second header is properly placed. Warn and ask if this should
863 // be corrected if the test fails....
srs5694ba00fed2010-01-12 18:18:36 -0500864 if ((mainHeader.backupLBA < (diskSize - UINT64_C(1))) && (quiet == 0)) {
srs5694247657a2009-11-26 18:36:12 -0500865 printf("Warning! Secondary header is placed too early on the disk! Do you want to\n"
866 "correct this problem? ");
867 if (GetYN() == 'Y') {
868 MoveSecondHeaderToEnd();
869 printf("Have moved second header and partition table to correct location.\n");
870 } else {
871 printf("Have not corrected the problem. Strange problems may occur in the future!\n");
872 } // if correction requested
873 } // if
srs5694e7b4ff92009-08-18 13:16:10 -0400874
875 // Check for overlapping partitions....
srs5694e4ac11e2009-08-31 10:13:04 -0400876 if (FindOverlaps() > 0) {
877 allOK = 0;
878 fprintf(stderr, "Aborting write operation!\n");
879 } // if
880
881 // Check for mismatched MBR and GPT data, but let it pass if found
882 // (function displays warning message)
883 FindHybridMismatches();
srs5694e7b4ff92009-08-18 13:16:10 -0400884
srs56942a9f5da2009-08-26 00:48:01 -0400885 // Pull out some data that's needed before doing byte-order reversal on
886 // big-endian systems....
887 numParts = mainHeader.numParts;
888 secondTable = secondHeader.partitionEntriesLBA;
889 if (IsLittleEndian() == 0) {
890 // Reverse partition bytes first, since that function requires non-reversed
891 // data from the main header....
892 ReversePartitionBytes();
893 ReverseHeaderBytes(&mainHeader);
894 ReverseHeaderBytes(&secondHeader);
895 } // if
srs5694e7b4ff92009-08-18 13:16:10 -0400896 RecomputeCRCs();
897
srs5694ba00fed2010-01-12 18:18:36 -0500898 if ((allOK) && (!quiet)) {
srs5694e7b4ff92009-08-18 13:16:10 -0400899 printf("\nFinal checks complete. About to write GPT data. THIS WILL OVERWRITE EXISTING\n");
srs5694546a9c72010-01-26 16:00:26 -0500900 printf("MBR PARTITIONS!!\n\n");
srs56945d58fe02010-01-03 20:57:08 -0500901 printf("Do you want to proceed, possibly destroying your data? ");
902 answer = GetYN();
903 if (answer == 'Y') {
904 printf("OK; writing new GUID partition table (GPT).\n");
srs5694e7b4ff92009-08-18 13:16:10 -0400905 } else {
906 allOK = 0;
907 } // if/else
908 } // if
909
910 // Do it!
911 if (allOK) {
srs5694546a9c72010-01-26 16:00:26 -0500912 // First, write the protective MBR...
913 allOK = protectiveMBR.WriteMBRData(&myDisk);
srs5694e7b4ff92009-08-18 13:16:10 -0400914
srs5694546a9c72010-01-26 16:00:26 -0500915 if (allOK && myDisk.OpenForWrite(device)) {
srs5694e7b4ff92009-08-18 13:16:10 -0400916 // Now write the main GPT header...
srs5694546a9c72010-01-26 16:00:26 -0500917 if (myDisk.Seek(1) == 1) {
918 if (myDisk.Write(&mainHeader, 512) != 512)
919 allOK = 0;
920 } else allOK = 0; // if (myDisk.Seek()...)
srs5694e7b4ff92009-08-18 13:16:10 -0400921
922 // Now write the main partition tables...
srs5694e4ac11e2009-08-31 10:13:04 -0400923 if (allOK) {
srs5694546a9c72010-01-26 16:00:26 -0500924 offset = mainHeader.partitionEntriesLBA;
925 if (myDisk.Seek(offset)) {
926 if (myDisk.Write(partitions, GPT_SIZE * numParts) == -1)
927 allOK = 0;
928 } else allOK = 0; // if (myDisk.Seek()...)
srs56941e093722010-01-05 00:14:19 -0500929 } // if (allOK)
srs5694e7b4ff92009-08-18 13:16:10 -0400930
931 // Now seek to near the end to write the secondary GPT....
srs5694e4ac11e2009-08-31 10:13:04 -0400932 if (allOK) {
srs5694546a9c72010-01-26 16:00:26 -0500933 offset = (off_t) secondTable;
934 if (myDisk.Seek(offset) != 1) {
srs5694e7b4ff92009-08-18 13:16:10 -0400935 allOK = 0;
srs5694546a9c72010-01-26 16:00:26 -0500936 printf("Unable to seek to end of disk! Perhaps the 'e' option on the experts' menu\n"
937 "will resolve this problem.\n");
srs5694e7b4ff92009-08-18 13:16:10 -0400938 } // if
939 } // if
940
941 // Now write the secondary partition tables....
srs56941e093722010-01-05 00:14:19 -0500942 if (allOK) {
srs5694546a9c72010-01-26 16:00:26 -0500943 if (myDisk.Write(partitions, GPT_SIZE * numParts) == -1)
srs5694e7b4ff92009-08-18 13:16:10 -0400944 allOK = 0;
srs56941e093722010-01-05 00:14:19 -0500945 } // if (allOK)
srs5694e7b4ff92009-08-18 13:16:10 -0400946
947 // Now write the secondary GPT header...
srs56941e093722010-01-05 00:14:19 -0500948 if (allOK) {
srs5694546a9c72010-01-26 16:00:26 -0500949 offset = mainHeader.backupLBA;
950 if (myDisk.Seek(offset)) {
951 if (myDisk.Write(&secondHeader, 512) == -1)
srs56941e093722010-01-05 00:14:19 -0500952 allOK = 0;
srs5694546a9c72010-01-26 16:00:26 -0500953 } else allOK = 0; // if (myDisk.Seek()...)
srs56941e093722010-01-05 00:14:19 -0500954 } // if (allOK)
srs5694e7b4ff92009-08-18 13:16:10 -0400955
956 // re-read the partition table
957 if (allOK) {
srs5694546a9c72010-01-26 16:00:26 -0500958 myDisk.DiskSync();
srs5694e7b4ff92009-08-18 13:16:10 -0400959 } // if
960
961 if (allOK) { // writes completed OK
srs5694e4ac11e2009-08-31 10:13:04 -0400962 printf("The operation has completed successfully.\n");
srs5694e7b4ff92009-08-18 13:16:10 -0400963 } else {
srs5694e4ac11e2009-08-31 10:13:04 -0400964 printf("Warning! An error was reported when writing the partition table! This error\n");
965 printf("MIGHT be harmless, but you may have trashed the disk! Use parted and, if\n");
966 printf("necessary, restore your original partition table.\n");
srs5694e7b4ff92009-08-18 13:16:10 -0400967 } // if/else
srs5694546a9c72010-01-26 16:00:26 -0500968 myDisk.Close();
srs5694e7b4ff92009-08-18 13:16:10 -0400969 } else {
srs5694e4ac11e2009-08-31 10:13:04 -0400970 fprintf(stderr, "Unable to open device %s for writing! Errno is %d! Aborting write!\n",
971 device, errno);
972 allOK = 0;
srs5694e7b4ff92009-08-18 13:16:10 -0400973 } // if/else
974 } else {
975 printf("Aborting write of new partition table.\n");
976 } // if
977
srs56942a9f5da2009-08-26 00:48:01 -0400978 if (IsLittleEndian() == 0) {
979 // Reverse (normalize) header bytes first, since ReversePartitionBytes()
980 // requires non-reversed data in mainHeader...
981 ReverseHeaderBytes(&mainHeader);
982 ReverseHeaderBytes(&secondHeader);
983 ReversePartitionBytes();
984 } // if
985
srs5694e7b4ff92009-08-18 13:16:10 -0400986 return (allOK);
987} // GPTData::SaveGPTData()
988
989// Save GPT data to a backup file. This function does much less error
990// checking than SaveGPTData(). It can therefore preserve many types of
991// corruption for later analysis; however, it preserves only the MBR,
992// the main GPT header, the backup GPT header, and the main partition
993// table; it discards the backup partition table, since it should be
994// identical to the main partition table on healthy disks.
995int GPTData::SaveGPTBackup(char* filename) {
srs56942a9f5da2009-08-26 00:48:01 -0400996 int fd, allOK = 1;
997 uint32_t numParts;
srs5694546a9c72010-01-26 16:00:26 -0500998 DiskIO backupFile;
srs5694e7b4ff92009-08-18 13:16:10 -0400999
srs5694546a9c72010-01-26 16:00:26 -05001000// if ((fd = open(filename, O_WRONLY | O_CREAT, S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH)) != -1) {
1001 if (backupFile.OpenForWrite(filename)) {
srs56942a9f5da2009-08-26 00:48:01 -04001002 // Reverse the byte order, if necessary....
1003 numParts = mainHeader.numParts;
1004 if (IsLittleEndian() == 0) {
1005 ReversePartitionBytes();
1006 ReverseHeaderBytes(&mainHeader);
1007 ReverseHeaderBytes(&secondHeader);
1008 } // if
1009
srs5694978041c2009-09-21 20:51:47 -04001010 // Recomputing the CRCs is likely to alter them, which could be bad
1011 // if the intent is to save a potentially bad GPT for later analysis;
1012 // but if we don't do this, we get bogus errors when we load the
1013 // backup. I'm favoring misses over false alarms....
1014 RecomputeCRCs();
1015
srs56942a9f5da2009-08-26 00:48:01 -04001016 // Now write the protective MBR...
srs5694546a9c72010-01-26 16:00:26 -05001017 protectiveMBR.WriteMBRData(&backupFile);
srs5694e7b4ff92009-08-18 13:16:10 -04001018
1019 // Now write the main GPT header...
1020 if (allOK)
srs5694546a9c72010-01-26 16:00:26 -05001021 // MBR write closed disk, so re-open and seek to end....
1022 backupFile.OpenForWrite();
1023 backupFile.Seek(1);
1024 if (backupFile.Write(&mainHeader, 512) == -1)
srs5694e7b4ff92009-08-18 13:16:10 -04001025 allOK = 0;
1026
1027 // Now write the secondary GPT header...
1028 if (allOK)
srs5694546a9c72010-01-26 16:00:26 -05001029 if (backupFile.Write(&secondHeader, 512) == -1)
srs5694e4ac11e2009-08-31 10:13:04 -04001030 allOK = 0;
srs5694e7b4ff92009-08-18 13:16:10 -04001031
1032 // Now write the main partition tables...
1033 if (allOK) {
srs5694546a9c72010-01-26 16:00:26 -05001034 if (backupFile.Write(partitions, GPT_SIZE * numParts) == -1)
srs5694e7b4ff92009-08-18 13:16:10 -04001035 allOK = 0;
1036 } // if
1037
1038 if (allOK) { // writes completed OK
1039 printf("The operation has completed successfully.\n");
1040 } else {
1041 printf("Warning! An error was reported when writing the backup file.\n");
srs56948bb78762009-11-24 15:43:49 -05001042 printf("It may not be usable!\n");
srs5694e7b4ff92009-08-18 13:16:10 -04001043 } // if/else
srs5694546a9c72010-01-26 16:00:26 -05001044 backupFile.Close();
srs56942a9f5da2009-08-26 00:48:01 -04001045
1046 // Now reverse the byte-order reversal, if necessary....
1047 if (IsLittleEndian() == 0) {
1048 ReverseHeaderBytes(&mainHeader);
1049 ReverseHeaderBytes(&secondHeader);
1050 ReversePartitionBytes();
1051 } // if
srs5694e7b4ff92009-08-18 13:16:10 -04001052 } else {
1053 fprintf(stderr, "Unable to open file %s for writing! Aborting!\n", filename);
1054 allOK = 0;
1055 } // if/else
1056 return allOK;
1057} // GPTData::SaveGPTBackup()
1058
1059// Load GPT data from a backup file created by SaveGPTBackup(). This function
1060// does minimal error checking. It returns 1 if it completed successfully,
1061// 0 if there was a problem. In the latter case, it creates a new empty
1062// set of partitions.
1063int GPTData::LoadGPTBackup(char* filename) {
1064 int fd, allOK = 1, val;
1065 uint32_t numParts, sizeOfEntries, sizeOfParts, newCRC;
srs56942a9f5da2009-08-26 00:48:01 -04001066 int littleEndian = 1;
srs5694546a9c72010-01-26 16:00:26 -05001067 DiskIO backupFile;
srs5694e7b4ff92009-08-18 13:16:10 -04001068
srs5694546a9c72010-01-26 16:00:26 -05001069// if ((fd = open(filename, O_RDONLY)) != -1) {
1070 if (backupFile.OpenForRead(filename)) {
srs56942a9f5da2009-08-26 00:48:01 -04001071 if (IsLittleEndian() == 0)
1072 littleEndian = 0;
1073
srs5694e7b4ff92009-08-18 13:16:10 -04001074 // Let the MBRData class load the saved MBR...
srs5694546a9c72010-01-26 16:00:26 -05001075 protectiveMBR.ReadMBRData(&backupFile, 0); // 0 = don't check block size
srs5694e7b4ff92009-08-18 13:16:10 -04001076
1077 // Load the main GPT header, check its vaility, and set the GPT
1078 // size based on the data
srs5694546a9c72010-01-26 16:00:26 -05001079 if (backupFile.Read(&mainHeader, 512) != 512) {
srs56945d58fe02010-01-03 20:57:08 -05001080 fprintf(stderr, "Warning! Read error %d; strange behavior now likely!\n", errno);
1081 } // if
srs5694e7b4ff92009-08-18 13:16:10 -04001082 mainCrcOk = CheckHeaderCRC(&mainHeader);
1083
srs56942a9f5da2009-08-26 00:48:01 -04001084 // Reverse byte order, if necessary
1085 if (littleEndian == 0) {
1086 ReverseHeaderBytes(&mainHeader);
1087 } // if
1088
srs5694e7b4ff92009-08-18 13:16:10 -04001089 // Load the backup GPT header in much the same way as the main
1090 // GPT header....
srs5694546a9c72010-01-26 16:00:26 -05001091 if (backupFile.Read(&secondHeader, 512) != 512) {
srs56945d58fe02010-01-03 20:57:08 -05001092 fprintf(stderr, "Warning! Read error %d; strange behavior now likely!\n", errno);
1093 } // if
srs5694e7b4ff92009-08-18 13:16:10 -04001094 secondCrcOk = CheckHeaderCRC(&secondHeader);
1095
srs56942a9f5da2009-08-26 00:48:01 -04001096 // Reverse byte order, if necessary
1097 if (littleEndian == 0) {
1098 ReverseHeaderBytes(&secondHeader);
1099 } // if
1100
srs5694e7b4ff92009-08-18 13:16:10 -04001101 // Return valid headers code: 0 = both headers bad; 1 = main header
1102 // good, backup bad; 2 = backup header good, main header bad;
1103 // 3 = both headers good. Note these codes refer to valid GPT
1104 // signatures and version numbers; more subtle problems will elude
1105 // this check!
1106 if ((val = CheckHeaderValidity()) > 0) {
1107 if (val == 2) { // only backup header seems to be good
1108 numParts = secondHeader.numParts;
srs5694e4ac11e2009-08-31 10:13:04 -04001109 sizeOfEntries = secondHeader.sizeOfPartitionEntries;
srs5694e7b4ff92009-08-18 13:16:10 -04001110 } else { // main header is OK
1111 numParts = mainHeader.numParts;
1112 sizeOfEntries = mainHeader.sizeOfPartitionEntries;
1113 } // if/else
1114
1115 SetGPTSize(numParts);
1116
1117 // If current disk size doesn't match that of backup....
1118 if (secondHeader.currentLBA != diskSize - UINT64_C(1)) {
1119 printf("Warning! Current disk size doesn't match that of the backup!\n"
srs5694e4ac11e2009-08-31 10:13:04 -04001120 "Adjusting sizes to match, but subsequent problems are possible!\n");
srs5694247657a2009-11-26 18:36:12 -05001121 MoveSecondHeaderToEnd();
srs5694e7b4ff92009-08-18 13:16:10 -04001122 } // if
1123
1124 // Load main partition table, and record whether its CRC
1125 // matches the stored value
1126 sizeOfParts = numParts * sizeOfEntries;
srs5694546a9c72010-01-26 16:00:26 -05001127 if (backupFile.Read(partitions, sizeOfParts) != sizeOfParts) {
srs56945d58fe02010-01-03 20:57:08 -05001128 fprintf(stderr, "Warning! Read error %d; strange behavior now likely!\n", errno);
1129 } // if
srs5694e7b4ff92009-08-18 13:16:10 -04001130
1131 newCRC = chksum_crc32((unsigned char*) partitions, sizeOfParts);
1132 mainPartsCrcOk = (newCRC == mainHeader.partitionEntriesCRC);
1133 secondPartsCrcOk = (newCRC == secondHeader.partitionEntriesCRC);
srs56942a9f5da2009-08-26 00:48:01 -04001134 // Reverse byte order, if necessary
srs5694e4ac11e2009-08-31 10:13:04 -04001135 if (littleEndian == 0) {
1136 ReversePartitionBytes();
1137 } // if
srs56942a9f5da2009-08-26 00:48:01 -04001138
srs5694e7b4ff92009-08-18 13:16:10 -04001139 } else {
1140 allOK = 0;
1141 } // if/else
1142 } else {
1143 allOK = 0;
1144 fprintf(stderr, "Unable to open file %s for reading! Aborting!\n", filename);
1145 } // if/else
1146
1147 // Something went badly wrong, so blank out partitions
1148 if (allOK == 0) {
1149 ClearGPTData();
1150 protectiveMBR.MakeProtectiveMBR();
1151 } // if
1152 return allOK;
1153} // GPTData::LoadGPTBackup()
1154
srs5694e4ac11e2009-08-31 10:13:04 -04001155// Tell user whether Apple Partition Map (APM) was discovered....
1156void GPTData::ShowAPMState(void) {
1157 if (apmFound)
1158 printf(" APM: present\n");
1159 else
1160 printf(" APM: not present\n");
1161} // GPTData::ShowAPMState()
1162
1163// Tell user about the state of the GPT data....
1164void GPTData::ShowGPTState(void) {
1165 switch (state) {
1166 case gpt_invalid:
1167 printf(" GPT: not present\n");
1168 break;
1169 case gpt_valid:
1170 printf(" GPT: present\n");
1171 break;
1172 case gpt_corrupt:
1173 printf(" GPT: damaged\n");
1174 break;
1175 default:
1176 printf("\a GPT: unknown -- bug!\n");
1177 break;
1178 } // switch
1179} // GPTData::ShowGPTState()
1180
1181// Display the basic GPT data
1182void GPTData::DisplayGPTData(void) {
srs5694e35eb1b2009-09-14 00:29:34 -04001183 int i;
srs5694e4ac11e2009-08-31 10:13:04 -04001184 char sizeInSI[255]; // String to hold size of disk in SI units
1185 char tempStr[255];
1186 uint64_t temp, totalFree;
1187
1188 BytesToSI(diskSize * blockSize, sizeInSI);
srs5694546a9c72010-01-26 16:00:26 -05001189 printf("Disk %s: %llu sectors, ", device,
1190 (unsigned long long) diskSize);
1191 printf("%s\n", sizeInSI);
srs56941e093722010-01-05 00:14:19 -05001192 printf("Logical sector size: %d bytes\n", blockSize);
srs5694e4ac11e2009-08-31 10:13:04 -04001193 printf("Disk identifier (GUID): %s\n", GUIDToStr(mainHeader.diskGUID, tempStr));
1194 printf("Partition table holds up to %lu entries\n", (unsigned long) mainHeader.numParts);
srs56945d58fe02010-01-03 20:57:08 -05001195 printf("First usable sector is %llu, last usable sector is %llu\n",
1196 (unsigned long long) mainHeader.firstUsableLBA,
1197 (unsigned long long) mainHeader.lastUsableLBA);
srs5694e4ac11e2009-08-31 10:13:04 -04001198 totalFree = FindFreeBlocks(&i, &temp);
srs5694546a9c72010-01-26 16:00:26 -05001199 printf("Total free space is %llu sectors ", (unsigned long long) totalFree);
1200 printf("(%s)\n", BytesToSI(totalFree * (uint64_t) blockSize, sizeInSI));
srs5694e4ac11e2009-08-31 10:13:04 -04001201 printf("\nNumber Start (sector) End (sector) Size Code Name\n");
1202 for (i = 0; i < mainHeader.numParts; i++) {
srs5694978041c2009-09-21 20:51:47 -04001203 partitions[i].ShowSummary(i, blockSize);
srs5694e4ac11e2009-08-31 10:13:04 -04001204 } // for
1205} // GPTData::DisplayGPTData()
1206
1207// Get partition number from user and then call ShowPartDetails(partNum)
1208// to show its detailed information
1209void GPTData::ShowDetails(void) {
1210 int partNum;
1211 uint32_t low, high;
1212
1213 if (GetPartRange(&low, &high) > 0) {
1214 partNum = GetPartNum();
1215 ShowPartDetails(partNum);
1216 } else {
1217 printf("No partitions\n");
1218 } // if/else
1219} // GPTData::ShowDetails()
1220
1221// Show detailed information on the specified partition
1222void GPTData::ShowPartDetails(uint32_t partNum) {
1223 if (partitions[partNum].GetFirstLBA() != 0) {
1224 partitions[partNum].ShowDetails(blockSize);
1225 } else {
1226 printf("Partition #%d does not exist.", (int) (partNum + 1));
1227 } // if
1228} // GPTData::ShowPartDetails()
1229
1230/*********************************************************************
1231 * *
1232 * Begin functions that obtain information from the users, and often *
1233 * do something with that information (call other functions) *
1234 * *
1235 *********************************************************************/
1236
1237// Prompts user for partition number and returns the result.
1238uint32_t GPTData::GetPartNum(void) {
1239 uint32_t partNum;
1240 uint32_t low, high;
1241 char prompt[255];
1242
1243 if (GetPartRange(&low, &high) > 0) {
1244 sprintf(prompt, "Partition number (%d-%d): ", low + 1, high + 1);
1245 partNum = GetNumber(low + 1, high + 1, low, prompt);
1246 } else partNum = 1;
1247 return (partNum - 1);
1248} // GPTData::GetPartNum()
1249
1250// What it says: Resize the partition table. (Default is 128 entries.)
1251void GPTData::ResizePartitionTable(void) {
1252 int newSize;
1253 char prompt[255];
1254 uint32_t curLow, curHigh;
1255
1256 printf("Current partition table size is %lu.\n",
1257 (unsigned long) mainHeader.numParts);
1258 GetPartRange(&curLow, &curHigh);
1259 curHigh++; // since GetPartRange() returns numbers starting from 0...
1260 // There's no point in having fewer than four partitions....
1261 if (curHigh < 4)
1262 curHigh = 4;
1263 sprintf(prompt, "Enter new size (%d up, default %d): ", (int) curHigh,
1264 (int) NUM_GPT_ENTRIES);
1265 newSize = GetNumber(4, 65535, 128, prompt);
1266 if (newSize < 128) {
1267 printf("Caution: The partition table size should officially be 16KB or larger,\n"
1268 "which works out to 128 entries. In practice, smaller tables seem to\n"
1269 "work with most OSes, but this practice is risky. I'm proceeding with\n"
1270 "the resize, but you may want to reconsider this action and undo it.\n\n");
1271 } // if
1272 SetGPTSize(newSize);
1273} // GPTData::ResizePartitionTable()
1274
1275// Interactively create a partition
1276void GPTData::CreatePartition(void) {
1277 uint64_t firstBlock, firstInLargest, lastBlock, sector;
1278 char prompt[255];
1279 int partNum, firstFreePart = 0;
1280
1281 // Find first free partition...
1282 while (partitions[firstFreePart].GetFirstLBA() != 0) {
1283 firstFreePart++;
1284 } // while
1285
1286 if (((firstBlock = FindFirstAvailable()) != 0) &&
1287 (firstFreePart < mainHeader.numParts)) {
1288 lastBlock = FindLastAvailable(firstBlock);
1289 firstInLargest = FindFirstInLargest();
1290
1291 // Get partition number....
1292 do {
1293 sprintf(prompt, "Partition number (%d-%d, default %d): ", firstFreePart + 1,
1294 mainHeader.numParts, firstFreePart + 1);
1295 partNum = GetNumber(firstFreePart + 1, mainHeader.numParts,
1296 firstFreePart + 1, prompt) - 1;
1297 if (partitions[partNum].GetFirstLBA() != 0)
1298 printf("partition %d is in use.\n", partNum + 1);
1299 } while (partitions[partNum].GetFirstLBA() != 0);
1300
1301 // Get first block for new partition...
srs56945d58fe02010-01-03 20:57:08 -05001302 sprintf(prompt, "First sector (%llu-%llu, default = %llu) or {+-}size{KMGT}: ",
1303 (unsigned long long) firstBlock, (unsigned long long) lastBlock,
1304 (unsigned long long) firstInLargest);
srs5694e4ac11e2009-08-31 10:13:04 -04001305 do {
1306 sector = GetSectorNum(firstBlock, lastBlock, firstInLargest, prompt);
1307 } while (IsFree(sector) == 0);
srs56941d1448a2009-12-31 21:20:19 -05001308 Align(&sector); // Align sector to correct multiple
srs5694e4ac11e2009-08-31 10:13:04 -04001309 firstBlock = sector;
1310
1311 // Get last block for new partitions...
1312 lastBlock = FindLastInFree(firstBlock);
srs56945d58fe02010-01-03 20:57:08 -05001313 sprintf(prompt, "Last sector (%llu-%llu, default = %llu) or {+-}size{KMGT}: ",
1314 (unsigned long long) firstBlock, (unsigned long long) lastBlock,
1315 (unsigned long long) lastBlock);
srs5694e4ac11e2009-08-31 10:13:04 -04001316 do {
1317 sector = GetSectorNum(firstBlock, lastBlock, lastBlock, prompt);
1318 } while (IsFree(sector) == 0);
1319 lastBlock = sector;
1320
srs5694ba00fed2010-01-12 18:18:36 -05001321 firstFreePart = CreatePartition(partNum, firstBlock, lastBlock);
srs5694e4ac11e2009-08-31 10:13:04 -04001322 partitions[partNum].ChangeType();
srs5694546a9c72010-01-26 16:00:26 -05001323 partitions[partNum].SetName((unsigned char*) partitions[partNum].GetNameType().c_str());
srs5694ba00fed2010-01-12 18:18:36 -05001324 } else {
1325 printf("No free sectors available\n");
1326 } // if/else
srs5694e4ac11e2009-08-31 10:13:04 -04001327} // GPTData::CreatePartition()
1328
1329// Interactively delete a partition (duh!)
1330void GPTData::DeletePartition(void) {
1331 int partNum;
1332 uint32_t low, high;
srs5694e4ac11e2009-08-31 10:13:04 -04001333 char prompt[255];
1334
1335 if (GetPartRange(&low, &high) > 0) {
1336 sprintf(prompt, "Partition number (%d-%d): ", low + 1, high + 1);
1337 partNum = GetNumber(low + 1, high + 1, low, prompt);
srs5694ba00fed2010-01-12 18:18:36 -05001338 DeletePartition(partNum - 1);
srs5694e4ac11e2009-08-31 10:13:04 -04001339 } else {
1340 printf("No partitions\n");
1341 } // if/else
1342} // GPTData::DeletePartition()
1343
1344// Prompt user for a partition number, then change its type code
1345// using ChangeGPTType(struct GPTPartition*) function.
1346void GPTData::ChangePartType(void) {
1347 int partNum;
1348 uint32_t low, high;
1349
1350 if (GetPartRange(&low, &high) > 0) {
1351 partNum = GetPartNum();
1352 partitions[partNum].ChangeType();
1353 } else {
1354 printf("No partitions\n");
1355 } // if/else
1356} // GPTData::ChangePartType()
1357
1358// Partition attributes seem to be rarely used, but I want a way to
1359// adjust them for completeness....
1360void GPTData::SetAttributes(uint32_t partNum) {
1361 Attributes theAttr;
1362
1363 theAttr.SetAttributes(partitions[partNum].GetAttributes());
1364 theAttr.DisplayAttributes();
1365 theAttr.ChangeAttributes();
1366 partitions[partNum].SetAttributes(theAttr.GetAttributes());
1367} // GPTData::SetAttributes()
1368
srs5694c0ca8f82009-08-20 21:35:25 -04001369// This function destroys the on-disk GPT structures. Returns 1 if the
1370// user confirms destruction, 0 if the user aborts.
srs5694978041c2009-09-21 20:51:47 -04001371// If prompt == 0, don't ask user about proceeding and do NOT wipe out
1372// MBR. (Set prompt == 0 when doing a GPT-to-MBR conversion.)
srs5694ba00fed2010-01-12 18:18:36 -05001373// If prompt == -1, don't ask user about proceeding and DO wipe out
1374// MBR.
srs5694978041c2009-09-21 20:51:47 -04001375int GPTData::DestroyGPT(int prompt) {
srs56941e093722010-01-05 00:14:19 -05001376 int fd, i, sum, tableSize;
srs5694978041c2009-09-21 20:51:47 -04001377 char blankSector[512], goOn = 'Y', blank = 'N';
srs56941e093722010-01-05 00:14:19 -05001378 char* emptyTable;
srs5694c0ca8f82009-08-20 21:35:25 -04001379
1380 for (i = 0; i < 512; i++) {
1381 blankSector[i] = '\0';
1382 } // for
1383
srs5694ba00fed2010-01-12 18:18:36 -05001384 if (((apmFound) || (bsdFound)) && (prompt > 0)) {
srs5694e35eb1b2009-09-14 00:29:34 -04001385 printf("WARNING: APM or BSD disklabel structures detected! This operation could\n"
1386 "damage any APM or BSD partitions on this disk!\n");
1387 } // if APM or BSD
srs5694ba00fed2010-01-12 18:18:36 -05001388 if (prompt > 0) {
srs5694978041c2009-09-21 20:51:47 -04001389 printf("\a\aAbout to wipe out GPT on %s. Proceed? ", device);
1390 goOn = GetYN();
1391 } // if
srs5694c0ca8f82009-08-20 21:35:25 -04001392 if (goOn == 'Y') {
srs5694546a9c72010-01-26 16:00:26 -05001393/* OpenForWrite(device);
srs5694c0ca8f82009-08-20 21:35:25 -04001394#ifdef __APPLE__
1395 // MacOS X requires a shared lock under some circumstances....
1396 if (fd < 0) {
1397 fd = open(device, O_WRONLY|O_SHLOCK);
1398 } // if
srs5694546a9c72010-01-26 16:00:26 -05001399#endif */
1400 if (myDisk.OpenForWrite(device)) {
1401 myDisk.Seek(mainHeader.currentLBA); // seek to GPT header
1402 if (myDisk.Write(blankSector, 512) != 512) { // blank it out
srs56945d58fe02010-01-03 20:57:08 -05001403 fprintf(stderr, "Warning! GPT main header not overwritten! Error is %d\n", errno);
1404 } // if
srs5694546a9c72010-01-26 16:00:26 -05001405 myDisk.Seek(mainHeader.partitionEntriesLBA); // seek to partition table
srs56941e093722010-01-05 00:14:19 -05001406 tableSize = mainHeader.numParts * mainHeader.sizeOfPartitionEntries;
1407 emptyTable = (char*) malloc(tableSize);
1408 for (i = 0; i < tableSize; i++)
1409 emptyTable[i] = (char) 0;
srs5694546a9c72010-01-26 16:00:26 -05001410 sum = myDisk.Write(emptyTable, tableSize);
srs56941e093722010-01-05 00:14:19 -05001411 if (sum != tableSize)
srs56945d58fe02010-01-03 20:57:08 -05001412 fprintf(stderr, "Warning! GPT main partition table not overwritten! Error is %d\n", errno);
srs5694546a9c72010-01-26 16:00:26 -05001413 myDisk.Seek(secondHeader.partitionEntriesLBA); // seek to partition table
1414 sum = myDisk.Write(emptyTable, tableSize);
srs56941e093722010-01-05 00:14:19 -05001415 if (sum != tableSize)
srs56945d58fe02010-01-03 20:57:08 -05001416 fprintf(stderr, "Warning! GPT backup partition table not overwritten! Error is %d\n", errno);
srs5694546a9c72010-01-26 16:00:26 -05001417 myDisk.Seek(secondHeader.currentLBA); // seek to GPT header
1418 if (myDisk.Write(blankSector, 512) != 512) { // blank it out
srs56945d58fe02010-01-03 20:57:08 -05001419 fprintf(stderr, "Warning! GPT backup header not overwritten! Error is %d\n", errno);
1420 } // if
srs5694ba00fed2010-01-12 18:18:36 -05001421 if (prompt > 0) {
srs5694978041c2009-09-21 20:51:47 -04001422 printf("Blank out MBR? ");
1423 blank = GetYN();
srs5694ba00fed2010-01-12 18:18:36 -05001424 } // if
srs5694978041c2009-09-21 20:51:47 -04001425 // Note on below: Touch the MBR only if the user wants it completely
1426 // blanked out. Version 0.4.2 deleted the 0xEE partition and re-wrote
1427 // the MBR, but this could wipe out a valid MBR that the program
1428 // had subsequently discarded (say, if it conflicted with older GPT
1429 // structures).
srs5694ba00fed2010-01-12 18:18:36 -05001430 if ((blank == 'Y') || (prompt < 0)) {
srs5694546a9c72010-01-26 16:00:26 -05001431 myDisk.Seek(0);
1432 if (myDisk.Write(blankSector, 512) != 512) { // blank it out
srs56945d58fe02010-01-03 20:57:08 -05001433 fprintf(stderr, "Warning! MBR not overwritten! Error is %d!\n", errno);
1434 } // if
srs5694978041c2009-09-21 20:51:47 -04001435 } else {
1436 printf("MBR is unchanged. You may need to delete an EFI GPT (0xEE) partition\n"
1437 "with fdisk or another tool.\n");
srs5694e35eb1b2009-09-14 00:29:34 -04001438 } // if/else
srs5694546a9c72010-01-26 16:00:26 -05001439 myDisk.DiskSync();
1440 myDisk.Close();
srs5694e4ac11e2009-08-31 10:13:04 -04001441 printf("GPT data structures destroyed! You may now partition the disk using fdisk or\n"
1442 "other utilities. Program will now terminate.\n");
srs5694c0ca8f82009-08-20 21:35:25 -04001443 } else {
srs56941d1448a2009-12-31 21:20:19 -05001444 printf("Problem opening %s for writing! Program will now terminate.\n", device);
srs5694c0ca8f82009-08-20 21:35:25 -04001445 } // if/else (fd != -1)
1446 } // if (goOn == 'Y')
1447 return (goOn == 'Y');
1448} // GPTData::DestroyGPT()
1449
srs5694e4ac11e2009-08-31 10:13:04 -04001450/**************************************************************************
1451 * *
1452 * Partition table transformation functions (MBR or BSD disklabel to GPT) *
1453 * (some of these functions may require user interaction) *
1454 * *
1455 **************************************************************************/
1456
1457// Examines the MBR & GPT data, and perhaps asks the user questions, to
1458// determine which set of data to use: the MBR (use_mbr), the GPT (use_gpt),
1459// or create a new set of partitions (use_new)
1460WhichToUse GPTData::UseWhichPartitions(void) {
1461 WhichToUse which = use_new;
1462 MBRValidity mbrState;
1463 int answer;
1464
1465 mbrState = protectiveMBR.GetValidity();
1466
1467 if ((state == gpt_invalid) && ((mbrState == mbr) || (mbrState == hybrid))) {
srs56945d58fe02010-01-03 20:57:08 -05001468 printf("\n***************************************************************\n"
1469 "Found invalid GPT and valid MBR; converting MBR to GPT format.\n");
1470 if (!justLooking) {
1471 printf("\aTHIS OPERATON IS POTENTIALLY DESTRUCTIVE! Exit by typing 'q' if\n"
1472 "you don't want to convert your MBR partitions to GPT format!\n");
1473 } // if
1474 printf("***************************************************************\n\n");
srs5694e4ac11e2009-08-31 10:13:04 -04001475 which = use_mbr;
1476 } // if
1477
1478 if ((state == gpt_invalid) && bsdFound) {
srs56945d58fe02010-01-03 20:57:08 -05001479 printf("\n**********************************************************************\n"
srs5694e4ac11e2009-08-31 10:13:04 -04001480 "Found invalid GPT and valid BSD disklabel; converting BSD disklabel\n"
srs56945d58fe02010-01-03 20:57:08 -05001481 "to GPT format.");
1482 if (!justLooking) {
1483 printf("\a THIS OPERATON IS POTENTIALLY DESTRUCTIVE! Your first\n"
srs5694e4ac11e2009-08-31 10:13:04 -04001484 "BSD partition will likely be unusable. Exit by typing 'q' if you don't\n"
srs56945d58fe02010-01-03 20:57:08 -05001485 "want to convert your BSD partitions to GPT format!");
1486 } // if
1487 printf("\n**********************************************************************\n\n");
srs5694e4ac11e2009-08-31 10:13:04 -04001488 which = use_bsd;
1489 } // if
1490
1491 if ((state == gpt_valid) && (mbrState == gpt)) {
srs5694e4ac11e2009-08-31 10:13:04 -04001492 which = use_gpt;
srs56943c0af382010-01-15 19:19:18 -05001493 if (!beQuiet)
1494 printf("Found valid GPT with protective MBR; using GPT.\n");
srs5694e4ac11e2009-08-31 10:13:04 -04001495 } // if
1496 if ((state == gpt_valid) && (mbrState == hybrid)) {
srs5694e4ac11e2009-08-31 10:13:04 -04001497 which = use_gpt;
srs56943c0af382010-01-15 19:19:18 -05001498 if (!beQuiet)
1499 printf("Found valid GPT with hybrid MBR; using GPT.\n");
srs5694e4ac11e2009-08-31 10:13:04 -04001500 } // if
1501 if ((state == gpt_valid) && (mbrState == invalid)) {
1502 printf("\aFound valid GPT with corrupt MBR; using GPT and will create new\nprotective MBR on save.\n");
1503 which = use_gpt;
1504 protectiveMBR.MakeProtectiveMBR();
1505 } // if
1506 if ((state == gpt_valid) && (mbrState == mbr)) {
srs56943c0af382010-01-15 19:19:18 -05001507 if (!beQuiet) {
1508 printf("Found valid MBR and GPT. Which do you want to use?\n");
1509 answer = GetNumber(1, 3, 2, (char*) " 1 - MBR\n 2 - GPT\n 3 - Create blank GPT\n\nYour answer: ");
1510 if (answer == 1) {
1511 which = use_mbr;
1512 } else if (answer == 2) {
1513 which = use_gpt;
1514 protectiveMBR.MakeProtectiveMBR();
1515 printf("Using GPT and creating fresh protective MBR.\n");
1516 } else which = use_new;
1517 } else which = use_abort;
srs5694e4ac11e2009-08-31 10:13:04 -04001518 } // if
1519
1520 // Nasty decisions here -- GPT is present, but corrupt (bad CRCs or other
1521 // problems)
1522 if (state == gpt_corrupt) {
srs56943c0af382010-01-15 19:19:18 -05001523 if (beQuiet) {
1524 which = use_abort;
1525 } else {
1526 if ((mbrState == mbr) || (mbrState == hybrid)) {
1527 printf("Found valid MBR and corrupt GPT. Which do you want to use? (Using the\n"
1528 "GPT MAY permit recovery of GPT data.)\n");
1529 answer = GetNumber(1, 3, 2, (char*) " 1 - MBR\n 2 - GPT\n 3 - Create blank GPT\n\nYour answer: ");
1530 if (answer == 1) {
1531 which = use_mbr;
1532 // protectiveMBR.MakeProtectiveMBR();
1533 } else if (answer == 2) {
1534 which = use_gpt;
1535 } else which = use_new;
1536 } else if (mbrState == invalid) {
1537 printf("Found invalid MBR and corrupt GPT. What do you want to do? (Using the\n"
1538 "GPT MAY permit recovery of GPT data.)\n");
1539 answer = GetNumber(1, 2, 1, (char*) " 1 - GPT\n 2 - Create blank GPT\n\nYour answer: ");
1540 if (answer == 1) {
1541 which = use_gpt;
1542 } else which = use_new;
1543 } else { // corrupt GPT, MBR indicates it's a GPT disk....
1544 printf("\a\a****************************************************************************\n"
1545 "Caution: Found protective or hybrid MBR and corrupt GPT. Using GPT, but disk\n"
1546 "verification and recovery are STRONGLY recommended.\n"
1547 "****************************************************************************\n");
srs5694e4ac11e2009-08-31 10:13:04 -04001548 which = use_gpt;
srs56943c0af382010-01-15 19:19:18 -05001549 } // if/else/else
1550 } // else (beQuiet)
srs5694e4ac11e2009-08-31 10:13:04 -04001551 } // if (corrupt GPT)
1552
1553 if (which == use_new)
1554 printf("Creating new GPT entries.\n");
1555
1556 return which;
1557} // UseWhichPartitions()
1558
1559// Convert MBR partition table into GPT form
1560int GPTData::XFormPartitions(void) {
1561 int i, numToConvert;
1562 uint8_t origType;
1563 struct newGUID;
srs5694e4ac11e2009-08-31 10:13:04 -04001564
1565 // Clear out old data & prepare basics....
1566 ClearGPTData();
1567
1568 // Convert the smaller of the # of GPT or MBR partitions
srs5694978041c2009-09-21 20:51:47 -04001569 if (mainHeader.numParts > (MAX_MBR_PARTS))
1570 numToConvert = MAX_MBR_PARTS;
srs5694e4ac11e2009-08-31 10:13:04 -04001571 else
1572 numToConvert = mainHeader.numParts;
1573
1574 for (i = 0; i < numToConvert; i++) {
1575 origType = protectiveMBR.GetType(i);
1576 // don't waste CPU time trying to convert extended, hybrid protective, or
1577 // null (non-existent) partitions
srs5694e35eb1b2009-09-14 00:29:34 -04001578 if ((origType != 0x05) && (origType != 0x0f) && (origType != 0x85) &&
srs5694e4ac11e2009-08-31 10:13:04 -04001579 (origType != 0x00) && (origType != 0xEE))
1580 partitions[i] = protectiveMBR.AsGPT(i);
1581 } // for
1582
1583 // Convert MBR into protective MBR
1584 protectiveMBR.MakeProtectiveMBR();
1585
1586 // Record that all original CRCs were OK so as not to raise flags
1587 // when doing a disk verification
1588 mainCrcOk = secondCrcOk = mainPartsCrcOk = secondPartsCrcOk = 1;
1589
1590 return (1);
1591} // GPTData::XFormPartitions()
1592
1593// Transforms BSD disklabel on the specified partition (numbered from 0).
1594// If an invalid partition number is given, the program prompts for one.
1595// Returns the number of new partitions created.
1596int GPTData::XFormDisklabel(int i) {
1597 uint32_t low, high, partNum, startPart;
1598 uint16_t hexCode;
1599 int goOn = 1, numDone = 0;
1600 BSDData disklabel;
1601
1602 if (GetPartRange(&low, &high) != 0) {
1603 if ((i < low) || (i > high))
1604 partNum = GetPartNum();
1605 else
1606 partNum = (uint32_t) i;
1607
1608 // Find the partition after the last used one
1609 startPart = high + 1;
1610
1611 // Now see if the specified partition has a BSD type code....
1612 hexCode = partitions[partNum].GetHexType();
1613 if ((hexCode != 0xa500) && (hexCode != 0xa900)) {
1614 printf("Specified partition doesn't have a disklabel partition type "
1615 "code.\nContinue anyway?");
1616 goOn = (GetYN() == 'Y');
1617 } // if
1618
1619 // If all is OK, read the disklabel and convert it.
1620 if (goOn) {
1621 goOn = disklabel.ReadBSDData(device, partitions[partNum].GetFirstLBA(),
1622 partitions[partNum].GetLastLBA());
1623 if ((goOn) && (disklabel.IsDisklabel())) {
1624 numDone = XFormDisklabel(&disklabel, startPart);
1625 if (numDone == 1)
1626 printf("Converted %d BSD partition.\n", numDone);
1627 else
1628 printf("Converted %d BSD partitions.\n", numDone);
1629 } else {
1630 printf("Unable to convert partitions! Unrecognized BSD disklabel.\n");
1631 } // if/else
1632 } // if
1633 if (numDone > 0) { // converted partitions; delete carrier
1634 partitions[partNum].BlankPartition();
1635 } // if
1636 } else {
1637 printf("No partitions\n");
1638 } // if/else
1639 return numDone;
1640} // GPTData::XFormDisklable(int i)
1641
1642// Transform the partitions on an already-loaded BSD disklabel...
1643int GPTData::XFormDisklabel(BSDData* disklabel, int startPart) {
1644 int i, numDone = 0;
1645
1646 if ((disklabel->IsDisklabel()) && (startPart >= 0) &&
1647 (startPart < mainHeader.numParts)) {
1648 for (i = 0; i < disklabel->GetNumParts(); i++) {
1649 partitions[i + startPart] = disklabel->AsGPT(i);
1650 if (partitions[i + startPart].GetFirstLBA() != UINT64_C(0))
1651 numDone++;
1652 } // for
1653 } // if
1654
1655 // Record that all original CRCs were OK so as not to raise flags
1656 // when doing a disk verification
1657 mainCrcOk = secondCrcOk = mainPartsCrcOk = secondPartsCrcOk = 1;
1658
1659 return numDone;
1660} // GPTData::XFormDisklabel(BSDData* disklabel)
1661
srs5694978041c2009-09-21 20:51:47 -04001662// Add one GPT partition to MBR. Used by XFormToMBR() and MakeHybrid()
1663// functions. Returns 1 if operation was successful.
1664int GPTData::OnePartToMBR(uint32_t gptPart, int mbrPart) {
1665 int allOK = 1, typeCode, bootable;
1666 uint64_t length;
1667 char line[255];
srs56945d58fe02010-01-03 20:57:08 -05001668 char* junk;
srs5694978041c2009-09-21 20:51:47 -04001669
1670 if ((mbrPart < 0) || (mbrPart > 3)) {
1671 printf("MBR partition %d is out of range; omitting it.\n", mbrPart + 1);
1672 allOK = 0;
1673 } // if
1674 if (gptPart >= mainHeader.numParts) {
1675 printf("GPT partition %d is out of range; omitting it.\n", gptPart + 1);
1676 allOK = 0;
1677 } // if
1678 if (allOK && (partitions[gptPart].GetLastLBA() == UINT64_C(0))) {
1679 printf("GPT partition %d is undefined; omitting it.\n", gptPart + 1);
1680 allOK = 0;
1681 } // if
1682 if (allOK && (partitions[gptPart].GetFirstLBA() <= UINT32_MAX) &&
1683 (partitions[gptPart].GetLengthLBA() <= UINT32_MAX)) {
1684 if (partitions[gptPart].GetLastLBA() > UINT32_MAX) {
1685 printf("Caution: Partition end point past 32-bit pointer boundary;"
1686 " some OSes may\nreact strangely.\n");
1687 } // if partition ends past 32-bit (usually 2TiB) boundary
1688 do {
1689 printf("Enter an MBR hex code (default %02X): ",
1690 typeHelper.GUIDToID(partitions[gptPart].GetType()) / 256);
srs56945d58fe02010-01-03 20:57:08 -05001691 junk = fgets(line, 255, stdin);
srs5694978041c2009-09-21 20:51:47 -04001692 sscanf(line, "%x", &typeCode);
1693 if (line[0] == '\n')
1694 typeCode = partitions[gptPart].GetHexType() / 256;
1695 } while ((typeCode <= 0) || (typeCode > 255));
1696 printf("Set the bootable flag? ");
1697 bootable = (GetYN() == 'Y');
1698 length = partitions[gptPart].GetLengthLBA();
1699 protectiveMBR.MakePart(mbrPart, (uint32_t) partitions[gptPart].GetFirstLBA(),
1700 (uint32_t) length, typeCode, bootable);
1701 } else { // partition out of range
1702 printf("Partition %d begins beyond the 32-bit pointer limit of MBR "
1703 "partitions, or is\n too big; omitting it.\n", gptPart + 1);
1704 allOK = 0;
1705 } // if/else
1706 return allOK;
1707} // GPTData::OnePartToMBR()
1708
1709// Convert the GPT to MBR form. This function is necessarily limited; it
1710// handles at most four partitions and creates layouts that ignore CHS
1711// geometries. Returns the number of converted partitions; if this value
1712// is over 0, the calling function should call DestroyGPT() to destroy
1713// the GPT data, and then exit.
1714int GPTData::XFormToMBR(void) {
1715 char line[255];
srs56945d58fe02010-01-03 20:57:08 -05001716 char* junk;
srs5694978041c2009-09-21 20:51:47 -04001717 int i, j, numParts, numConverted = 0;
1718 uint32_t partNums[4];
1719
1720 // Get the numbers of up to four partitions to add to the
1721 // hybrid MBR....
1722 numParts = CountParts();
1723 printf("Counted %d partitions.\n", numParts);
1724
1725 // Prepare the MBR for conversion (empty it of existing partitions).
1726 protectiveMBR.EmptyMBR(0);
1727 protectiveMBR.SetDiskSize(diskSize);
1728
1729 if (numParts > 4) { // Over four partitions; engage in triage
1730 printf("Type from one to four GPT partition numbers, separated by spaces, to be\n"
1731 "used in the MBR, in sequence: ");
srs56945d58fe02010-01-03 20:57:08 -05001732 junk = fgets(line, 255, stdin);
srs5694978041c2009-09-21 20:51:47 -04001733 numParts = sscanf(line, "%d %d %d %d", &partNums[0], &partNums[1],
1734 &partNums[2], &partNums[3]);
1735 } else { // Four or fewer partitions; convert them all
1736 i = j = 0;
1737 while ((j < numParts) && (i < mainHeader.numParts)) {
1738 if (partitions[i].GetFirstLBA() > 0) { // if GPT part. is defined
1739 partNums[j++] = ++i; // flag it for conversion
1740 } else i++;
1741 } // while
1742 } // if/else
1743
1744 for (i = 0; i < numParts; i++) {
1745 j = partNums[i] - 1;
1746 printf("\nCreating entry for partition #%d\n", j + 1);
1747 numConverted += OnePartToMBR(j, i);
1748 } // for
srs5694546a9c72010-01-26 16:00:26 -05001749 printf("MBR writing returned %d\n", protectiveMBR.WriteMBRData(&myDisk));
srs5694978041c2009-09-21 20:51:47 -04001750 return numConverted;
1751} // GPTData::XFormToMBR()
1752
srs5694e4ac11e2009-08-31 10:13:04 -04001753// Create a hybrid MBR -- an ugly, funky thing that helps GPT work with
1754// OSes that don't understand GPT.
1755void GPTData::MakeHybrid(void) {
1756 uint32_t partNums[3];
1757 char line[255];
srs56945d58fe02010-01-03 20:57:08 -05001758 char* junk;
srs5694978041c2009-09-21 20:51:47 -04001759 int numParts, numConverted = 0, i, j, typeCode, mbrNum;
srs5694e4ac11e2009-08-31 10:13:04 -04001760 char fillItUp = 'M'; // fill extra partition entries? (Yes/No/Maybe)
srs5694978041c2009-09-21 20:51:47 -04001761 char eeFirst = 'Y'; // Whether EFI GPT (0xEE) partition comes first in table
srs5694e4ac11e2009-08-31 10:13:04 -04001762
1763 printf("\nWARNING! Hybrid MBRs are flaky and potentially dangerous! If you decide not\n"
1764 "to use one, just hit the Enter key at the below prompt and your MBR\n"
1765 "partition table will be untouched.\n\n\a");
1766
1767 // Now get the numbers of up to three partitions to add to the
1768 // hybrid MBR....
1769 printf("Type from one to three GPT partition numbers, separated by spaces, to be\n"
1770 "added to the hybrid MBR, in sequence: ");
srs56945d58fe02010-01-03 20:57:08 -05001771 junk = fgets(line, 255, stdin);
srs5694e4ac11e2009-08-31 10:13:04 -04001772 numParts = sscanf(line, "%d %d %d", &partNums[0], &partNums[1], &partNums[2]);
1773
1774 if (numParts > 0) {
1775 // Blank out the protective MBR, but leave the boot loader code
1776 // alone....
1777 protectiveMBR.EmptyMBR(0);
1778 protectiveMBR.SetDiskSize(diskSize);
1779 printf("Place EFI GPT (0xEE) partition first in MBR (good for GRUB)? ");
1780 eeFirst = GetYN();
1781 } // if
1782
1783 for (i = 0; i < numParts; i++) {
1784 j = partNums[i] - 1;
1785 printf("\nCreating entry for partition #%d\n", j + 1);
srs5694978041c2009-09-21 20:51:47 -04001786 if (eeFirst == 'Y')
1787 mbrNum = i + 1;
1788 else
1789 mbrNum = i;
1790 numConverted += OnePartToMBR(j, mbrNum);
srs5694e4ac11e2009-08-31 10:13:04 -04001791 } // for
1792
srs5694978041c2009-09-21 20:51:47 -04001793 if ((numParts > 0) && (numConverted > 0)) { // User opted to create a hybrid MBR....
srs5694e4ac11e2009-08-31 10:13:04 -04001794 // Create EFI protective partition that covers the start of the disk.
1795 // If this location (covering the main GPT data structures) is omitted,
1796 // Linux won't find any partitions on the disk. Note that this is
1797 // NUMBERED AFTER the hybrid partitions, contrary to what the
1798 // gptsync utility does. This is because Windows seems to choke on
1799 // disks with a 0xEE partition in the first slot and subsequent
1800 // additional partitions, unless it boots from the disk.
1801 if (eeFirst == 'Y')
1802 mbrNum = 0;
1803 else
1804 mbrNum = numParts;
1805 protectiveMBR.MakePart(mbrNum, 1, protectiveMBR.FindLastInFree(1), 0xEE);
srs5694978041c2009-09-21 20:51:47 -04001806 protectiveMBR.SetHybrid();
srs5694e4ac11e2009-08-31 10:13:04 -04001807
1808 // ... and for good measure, if there are any partition spaces left,
1809 // optionally create another protective EFI partition to cover as much
1810 // space as possible....
1811 for (i = 0; i < 4; i++) {
1812 if (protectiveMBR.GetType(i) == 0x00) { // unused entry....
1813 if (fillItUp == 'M') {
1814 printf("\nUnused partition space(s) found. Use one to protect more partitions? ");
1815 fillItUp = GetYN();
1816 typeCode = 0x00; // use this to flag a need to get type code
1817 } // if
1818 if (fillItUp == 'Y') {
1819 while ((typeCode <= 0) || (typeCode > 255)) {
1820 printf("Enter an MBR hex code (EE is EFI GPT, but may confuse MacOS): ");
1821 // Comment on above: Mac OS treats disks with more than one
1822 // 0xEE MBR partition as MBR disks, not as GPT disks.
srs56945d58fe02010-01-03 20:57:08 -05001823 junk = fgets(line, 255, stdin);
srs5694e4ac11e2009-08-31 10:13:04 -04001824 sscanf(line, "%x", &typeCode);
1825 if (line[0] == '\n')
1826 typeCode = 0;
1827 } // while
1828 protectiveMBR.MakeBiggestPart(i, typeCode); // make a partition
1829 } // if (fillItUp == 'Y')
1830 } // if unused entry
1831 } // for (i = 0; i < 4; i++)
1832 } // if (numParts > 0)
1833} // GPTData::MakeHybrid()
1834
1835/**********************************************************************
1836 * *
1837 * Functions that adjust GPT data structures WITHOUT user interaction *
1838 * (they may display information for the user's benefit, though) *
1839 * *
1840 **********************************************************************/
1841
1842// Resizes GPT to specified number of entries. Creates a new table if
srs5694ba00fed2010-01-12 18:18:36 -05001843// necessary, copies data if it already exists. Returns 1 if all goes
1844// well, 0 if an error is encountered.
srs5694e4ac11e2009-08-31 10:13:04 -04001845int GPTData::SetGPTSize(uint32_t numEntries) {
1846 struct GPTPart* newParts;
1847 struct GPTPart* trash;
1848 uint32_t i, high, copyNum;
1849 int allOK = 1;
1850
1851 // First, adjust numEntries upward, if necessary, to get a number
1852 // that fills the allocated sectors
1853 i = blockSize / GPT_SIZE;
1854 if ((numEntries % i) != 0) {
1855 printf("Adjusting GPT size from %lu ", (unsigned long) numEntries);
1856 numEntries = ((numEntries / i) + 1) * i;
1857 printf("to %lu to fill the sector\n", (unsigned long) numEntries);
1858 } // if
1859
srs5694247657a2009-11-26 18:36:12 -05001860 // Do the work only if the # of partitions is changing. Along with being
1861 // efficient, this prevents mucking the with location of the secondary
1862 // partition table, which causes problems when loading data from a RAID
1863 // array that's been expanded because this function is called when loading
1864 // data.
srs5694546a9c72010-01-26 16:00:26 -05001865 if ((numEntries != mainHeader.numParts) || (numEntries != secondHeader.numParts)
1866 || (partitions == NULL)) {
srs5694247657a2009-11-26 18:36:12 -05001867 newParts = (GPTPart*) calloc(numEntries, sizeof (GPTPart));
1868 if (newParts != NULL) {
1869 if (partitions != NULL) { // existing partitions; copy them over
1870 GetPartRange(&i, &high);
1871 if (numEntries < (high + 1)) { // Highest entry too high for new #
1872 printf("The highest-numbered partition is %lu, which is greater than the requested\n"
1873 "partition table size of %d; cannot resize. Perhaps sorting will help.\n",
1874 (unsigned long) (high + 1), numEntries);
1875 allOK = 0;
1876 } else { // go ahead with copy
1877 if (numEntries < mainHeader.numParts)
1878 copyNum = numEntries;
1879 else
1880 copyNum = mainHeader.numParts;
1881 for (i = 0; i < copyNum; i++) {
1882 newParts[i] = partitions[i];
1883 } // for
1884 trash = partitions;
1885 partitions = newParts;
1886 free(trash);
1887 } // if
1888 } else { // No existing partition table; just create it
srs5694e4ac11e2009-08-31 10:13:04 -04001889 partitions = newParts;
srs5694247657a2009-11-26 18:36:12 -05001890 } // if/else existing partitions
1891 mainHeader.numParts = numEntries;
1892 secondHeader.numParts = numEntries;
1893 mainHeader.firstUsableLBA = ((numEntries * GPT_SIZE) / blockSize) + 2 ;
1894 secondHeader.firstUsableLBA = mainHeader.firstUsableLBA;
1895 MoveSecondHeaderToEnd();
1896 if (diskSize > 0)
1897 CheckGPTSize();
1898 } else { // Bad memory allocation
1899 fprintf(stderr, "Error allocating memory for partition table!\n");
1900 allOK = 0;
1901 } // if/else
srs5694e4ac11e2009-08-31 10:13:04 -04001902 } // if/else
1903 return (allOK);
1904} // GPTData::SetGPTSize()
1905
1906// Blank the partition array
1907void GPTData::BlankPartitions(void) {
1908 uint32_t i;
1909
1910 for (i = 0; i < mainHeader.numParts; i++) {
1911 partitions[i].BlankPartition();
1912 } // for
1913} // GPTData::BlankPartitions()
1914
srs5694ba00fed2010-01-12 18:18:36 -05001915// Delete a partition by number. Returns 1 if successful,
1916// 0 if there was a problem. Returns 1 if partition was in
1917// range, 0 if it was out of range.
1918int GPTData::DeletePartition(uint32_t partNum) {
1919 uint64_t startSector, length;
1920 uint32_t low, high, numParts, retval = 1;;
1921
1922 numParts = GetPartRange(&low, &high);
1923 if ((numParts > 0) && (partNum >= low) && (partNum <= high)) {
1924 // In case there's a protective MBR, look for & delete matching
1925 // MBR partition....
1926 startSector = partitions[partNum].GetFirstLBA();
1927 length = partitions[partNum].GetLengthLBA();
1928 protectiveMBR.DeleteByLocation(startSector, length);
1929
1930 // Now delete the GPT partition
1931 partitions[partNum].BlankPartition();
1932 } else {
1933 fprintf(stderr, "Partition number %d out of range!\n", partNum + 1);
1934 retval = 0;
1935 } // if/else
1936 return retval;
1937} // GPTData::DeletePartition(uint32_t partNum)
1938
1939// Non-interactively create a partition. Note that this function is overloaded
1940// with another of the same name but different parameters; that one prompts
1941// the user for data. This one returns 1 if the operation was successful, 0
1942// if a problem was discovered.
1943int GPTData::CreatePartition(uint32_t partNum, uint64_t startSector, uint64_t endSector) {
1944 int retval = 1; // assume there'll be no problems
1945
1946 if (IsFreePartNum(partNum)) {
1947 Align(&startSector); // Align sector to correct multiple
1948 if (IsFree(startSector) && (startSector <= endSector)) {
1949 if (FindLastInFree(startSector) >= endSector) {
1950 partitions[partNum].SetFirstLBA(startSector);
1951 partitions[partNum].SetLastLBA(endSector);
1952 partitions[partNum].SetType(0x0700);
1953 partitions[partNum].SetUniqueGUID(1);
1954 } else retval = 0; // if free space until endSector
1955 } else retval = 0; // if startSector is free
1956 } else retval = 0; // if legal partition number
1957 return retval;
1958} // GPTData::CreatePartition(partNum, startSector, endSector)
1959
srs5694e4ac11e2009-08-31 10:13:04 -04001960// Sort the GPT entries, eliminating gaps and making for a logical
1961// ordering. Relies on QuickSortGPT() for the bulk of the work
1962void GPTData::SortGPT(void) {
srs5694e4ac11e2009-08-31 10:13:04 -04001963 GPTPart temp;
srs5694546a9c72010-01-26 16:00:26 -05001964 uint32_t i, numFound, firstPart, lastPart;
srs5694e4ac11e2009-08-31 10:13:04 -04001965
1966 // First, find the last partition with data, so as not to
1967 // spend needless time sorting empty entries....
srs5694546a9c72010-01-26 16:00:26 -05001968 numFound = GetPartRange(&firstPart, &lastPart);
srs5694e4ac11e2009-08-31 10:13:04 -04001969
1970 // Now swap empties with the last partitions, to simplify the logic
1971 // in the Quicksort function....
1972 i = 0;
1973 while (i < lastPart) {
1974 if (partitions[i].GetFirstLBA() == 0) {
1975 temp = partitions[i];
1976 partitions[i] = partitions[lastPart];
1977 partitions[lastPart] = temp;
srs5694546a9c72010-01-26 16:00:26 -05001978 do {
1979 lastPart--;
1980 } while ((lastPart > 0) && (partitions[lastPart].GetFirstLBA() == 0));
srs5694e4ac11e2009-08-31 10:13:04 -04001981 } // if
1982 i++;
1983 } // while
1984
srs5694546a9c72010-01-26 16:00:26 -05001985 // If there are more empties than partitions in the range from 0 to lastPart,
1986 // the above leaves lastPart set too high, so we've got to adjust it to
1987 // prevent empties from migrating to the top of the list....
1988 GetPartRange(&firstPart, &lastPart);
1989
srs5694e4ac11e2009-08-31 10:13:04 -04001990 // Now call the recursive quick sort routine to do the real work....
1991 QuickSortGPT(partitions, 0, lastPart);
1992} // GPTData::SortGPT()
1993
1994// Set up data structures for entirely new set of partitions on the
1995// specified device. Returns 1 if OK, 0 if there were problems.
srs5694e35eb1b2009-09-14 00:29:34 -04001996// Note that this function does NOT clear the protectiveMBR data
1997// structure, since it may hold the original MBR partitions if the
1998// program was launched on an MBR disk, and those may need to be
1999// converted to GPT format.
srs5694e4ac11e2009-08-31 10:13:04 -04002000int GPTData::ClearGPTData(void) {
srs5694e35eb1b2009-09-14 00:29:34 -04002001 int goOn = 1, i;
srs5694e4ac11e2009-08-31 10:13:04 -04002002
2003 // Set up the partition table....
2004 free(partitions);
2005 partitions = NULL;
2006 SetGPTSize(NUM_GPT_ENTRIES);
2007
2008 // Now initialize a bunch of stuff that's static....
2009 mainHeader.signature = GPT_SIGNATURE;
2010 mainHeader.revision = 0x00010000;
srs5694978041c2009-09-21 20:51:47 -04002011 mainHeader.headerSize = HEADER_SIZE;
srs5694e4ac11e2009-08-31 10:13:04 -04002012 mainHeader.reserved = 0;
2013 mainHeader.currentLBA = UINT64_C(1);
2014 mainHeader.partitionEntriesLBA = (uint64_t) 2;
2015 mainHeader.sizeOfPartitionEntries = GPT_SIZE;
2016 for (i = 0; i < GPT_RESERVED; i++) {
2017 mainHeader.reserved2[i] = '\0';
2018 } // for
2019
2020 // Now some semi-static items (computed based on end of disk)
2021 mainHeader.backupLBA = diskSize - UINT64_C(1);
2022 mainHeader.lastUsableLBA = diskSize - mainHeader.firstUsableLBA;
2023
2024 // Set a unique GUID for the disk, based on random numbers
2025 // rand() is only 32 bits, so multiply together to fill a 64-bit value
2026 mainHeader.diskGUID.data1 = (uint64_t) rand() * (uint64_t) rand();
2027 mainHeader.diskGUID.data2 = (uint64_t) rand() * (uint64_t) rand();
2028
2029 // Copy main header to backup header
2030 RebuildSecondHeader();
2031
2032 // Blank out the partitions array....
2033 BlankPartitions();
2034
2035 // Flag all CRCs as being OK....
2036 mainCrcOk = 1;
2037 secondCrcOk = 1;
2038 mainPartsCrcOk = 1;
2039 secondPartsCrcOk = 1;
2040
2041 return (goOn);
2042} // GPTData::ClearGPTData()
2043
srs5694247657a2009-11-26 18:36:12 -05002044// Set the location of the second GPT header data to the end of the disk.
2045// Used internally and called by the 'e' option on the recovery &
2046// transformation menu, to help users of RAID arrays who add disk space
2047// to their arrays.
2048void GPTData::MoveSecondHeaderToEnd() {
srs56948bb78762009-11-24 15:43:49 -05002049 mainHeader.backupLBA = secondHeader.currentLBA = diskSize - UINT64_C(1);
2050 mainHeader.lastUsableLBA = secondHeader.lastUsableLBA = diskSize - mainHeader.firstUsableLBA;
2051 secondHeader.partitionEntriesLBA = secondHeader.lastUsableLBA + UINT64_C(1);
2052} // GPTData::FixSecondHeaderLocation()
2053
srs5694ba00fed2010-01-12 18:18:36 -05002054int GPTData::SetName(uint32_t partNum, char* theName) {
2055 int retval = 1;
2056 if (!IsFreePartNum(partNum))
2057 partitions[partNum].SetName((unsigned char*) theName);
2058 else retval = 0;
2059
2060 return retval;
srs5694e4ac11e2009-08-31 10:13:04 -04002061} // GPTData::SetName
2062
2063// Set the disk GUID to the specified value. Note that the header CRCs must
2064// be recomputed after calling this function.
2065void GPTData::SetDiskGUID(GUIDData newGUID) {
2066 mainHeader.diskGUID = newGUID;
2067 secondHeader.diskGUID = newGUID;
2068} // SetDiskGUID()
2069
2070// Set the unique GUID of the specified partition. Returns 1 on
2071// successful completion, 0 if there were problems (invalid
2072// partition number).
2073int GPTData::SetPartitionGUID(uint32_t pn, GUIDData theGUID) {
2074 int retval = 0;
2075
2076 if (pn < mainHeader.numParts) {
2077 if (partitions[pn].GetFirstLBA() != UINT64_C(0)) {
2078 partitions[pn].SetUniqueGUID(theGUID);
2079 retval = 1;
2080 } // if
2081 } // if
2082 return retval;
2083} // GPTData::SetPartitionGUID()
2084
srs5694ba00fed2010-01-12 18:18:36 -05002085// Change partition type code non-interactively. Returns 1 if
2086// successful, 0 if not....
2087int GPTData::ChangePartType(uint32_t partNum, uint16_t hexCode) {
2088 int retval = 1;
2089
2090 if (!IsFreePartNum(partNum)) {
2091 partitions[partNum].SetType(hexCode);
2092 } else retval = 0;
2093 return retval;
2094} // GPTData::ChangePartType()
2095
srs56941d1448a2009-12-31 21:20:19 -05002096// Adjust sector number so that it falls on a sector boundary that's a
2097// multiple of sectorAlignment. This is done to improve the performance
2098// of Western Digital Advanced Format disks and disks with similar
2099// technology from other companies, which use 4096-byte sectors
2100// internally although they translate to 512-byte sectors for the
2101// benefit of the OS. If partitions aren't properly aligned on these
2102// disks, some filesystem data structures can span multiple physical
2103// sectors, degrading performance. This function should be called
2104// only on the FIRST sector of the partition, not the last!
2105// This function returns 1 if the alignment was altered, 0 if it
2106// was unchanged.
2107int GPTData::Align(uint64_t* sector) {
2108 int retval = 0, sectorOK = 0;
2109 uint64_t earlier, later, testSector, original;
2110
2111 if ((*sector % sectorAlignment) != 0) {
2112 original = *sector;
2113 retval = 1;
2114 earlier = (*sector / sectorAlignment) * sectorAlignment;
2115 later = earlier + (uint64_t) sectorAlignment;
2116
2117 // Check to see that every sector between the earlier one and the
2118 // requested one is clear, and that it's not too early....
2119 if (earlier >= mainHeader.firstUsableLBA) {
2120// printf("earlier is %llu, first usable is %llu\n", earlier, mainHeader.firstUsableLBA);
2121 sectorOK = 1;
2122 testSector = earlier;
2123 do {
2124 sectorOK = IsFree(testSector++);
2125 } while ((sectorOK == 1) && (testSector < *sector));
2126 if (sectorOK == 1) {
2127 *sector = earlier;
2128// printf("Moved sector earlier.\n");
2129 } // if
2130 } // if firstUsableLBA check
2131
2132 // If couldn't move the sector earlier, try to move it later instead....
2133 if ((sectorOK != 1) && (later <= mainHeader.lastUsableLBA)) {
2134 sectorOK = 1;
2135 testSector = later;
2136 do {
2137 sectorOK = IsFree(testSector--);
2138 } while ((sectorOK == 1) && (testSector > *sector));
2139 if (sectorOK == 1) {
2140 *sector = later;
2141// printf("Moved sector later\n");
2142 } // if
2143 } // if
2144
2145 // If sector was changed successfully, inform the user of this fact.
2146 // Otherwise, notify the user that it couldn't be done....
2147 if (sectorOK == 1) {
2148 printf("Information: Moved requested sector from %llu to %llu for\n"
srs5694ba00fed2010-01-12 18:18:36 -05002149 "alignment purposes.\n",
srs56945d58fe02010-01-03 20:57:08 -05002150 (unsigned long long) original, (unsigned long long) *sector);
srs5694ba00fed2010-01-12 18:18:36 -05002151 if (!beQuiet)
2152 printf("Use 'l' on the experts' menu to adjust alignment\n");
srs56941d1448a2009-12-31 21:20:19 -05002153 } else {
2154 printf("Information: Sector not aligned on %d-sector boundary and could not be moved.\n"
2155 "If you're using a Western Digital Advanced Format or similar disk with\n"
2156 "underlying 4096-byte sectors, performance may suffer.\n", sectorAlignment);
2157 retval = 0;
2158 } // if/else
2159 } // if
2160 return retval;
2161} // GPTData::Align()
2162
srs5694e4ac11e2009-08-31 10:13:04 -04002163/********************************************************
2164 * *
2165 * Functions that return data about GPT data structures *
2166 * (most of these are inline in gpt.h) *
2167 * *
2168 ********************************************************/
2169
2170// Find the low and high used partition numbers (numbered from 0).
2171// Return value is the number of partitions found. Note that the
2172// *low and *high values are both set to 0 when no partitions
2173// are found, as well as when a single partition in the first
2174// position exists. Thus, the return value is the only way to
2175// tell when no partitions exist.
2176int GPTData::GetPartRange(uint32_t *low, uint32_t *high) {
2177 uint32_t i;
2178 int numFound = 0;
2179
2180 *low = mainHeader.numParts + 1; // code for "not found"
2181 *high = 0;
2182 if (mainHeader.numParts > 0) { // only try if partition table exists...
2183 for (i = 0; i < mainHeader.numParts; i++) {
2184 if (partitions[i].GetFirstLBA() != UINT64_C(0)) { // it exists
2185 *high = i; // since we're counting up, set the high value
2186 // Set the low value only if it's not yet found...
2187 if (*low == (mainHeader.numParts + 1)) *low = i;
2188 numFound++;
2189 } // if
2190 } // for
2191 } // if
2192
2193 // Above will leave *low pointing to its "not found" value if no partitions
2194 // are defined, so reset to 0 if this is the case....
2195 if (*low == (mainHeader.numParts + 1))
2196 *low = 0;
2197 return numFound;
2198} // GPTData::GetPartRange()
2199
srs5694978041c2009-09-21 20:51:47 -04002200// Returns the number of defined partitions.
2201uint32_t GPTData::CountParts(void) {
2202 int i, counted = 0;
2203
2204 for (i = 0; i < mainHeader.numParts; i++) {
2205 if (partitions[i].GetFirstLBA() > 0)
2206 counted++;
2207 } // for
2208 return counted;
2209} // GPTData::CountParts()
2210
srs5694e4ac11e2009-08-31 10:13:04 -04002211/****************************************************
2212 * *
2213 * Functions that return data about disk free space *
2214 * *
2215 ****************************************************/
2216
2217// Find the first available block after the starting point; returns 0 if
2218// there are no available blocks left
2219uint64_t GPTData::FindFirstAvailable(uint64_t start) {
2220 uint64_t first;
2221 uint32_t i;
2222 int firstMoved = 0;
2223
2224 // Begin from the specified starting point or from the first usable
2225 // LBA, whichever is greater...
2226 if (start < mainHeader.firstUsableLBA)
2227 first = mainHeader.firstUsableLBA;
2228 else
2229 first = start;
2230
2231 // ...now search through all partitions; if first is within an
2232 // existing partition, move it to the next sector after that
2233 // partition and repeat. If first was moved, set firstMoved
2234 // flag; repeat until firstMoved is not set, so as to catch
2235 // cases where partitions are out of sequential order....
2236 do {
2237 firstMoved = 0;
2238 for (i = 0; i < mainHeader.numParts; i++) {
2239 if ((first >= partitions[i].GetFirstLBA()) &&
2240 (first <= partitions[i].GetLastLBA())) { // in existing part.
2241 first = partitions[i].GetLastLBA() + 1;
2242 firstMoved = 1;
2243 } // if
2244 } // for
2245 } while (firstMoved == 1);
2246 if (first > mainHeader.lastUsableLBA)
2247 first = 0;
2248 return (first);
2249} // GPTData::FindFirstAvailable()
2250
2251// Finds the first available sector in the largest block of unallocated
2252// space on the disk. Returns 0 if there are no available blocks left
2253uint64_t GPTData::FindFirstInLargest(void) {
srs5694e35eb1b2009-09-14 00:29:34 -04002254 uint64_t start, firstBlock, lastBlock, segmentSize, selectedSize = 0, selectedSegment = 0;
srs5694e4ac11e2009-08-31 10:13:04 -04002255
2256 start = 0;
2257 do {
2258 firstBlock = FindFirstAvailable(start);
2259 if (firstBlock != UINT32_C(0)) { // something's free...
2260 lastBlock = FindLastInFree(firstBlock);
2261 segmentSize = lastBlock - firstBlock + UINT32_C(1);
2262 if (segmentSize > selectedSize) {
2263 selectedSize = segmentSize;
2264 selectedSegment = firstBlock;
2265 } // if
2266 start = lastBlock + 1;
2267 } // if
2268 } while (firstBlock != 0);
2269 return selectedSegment;
2270} // GPTData::FindFirstInLargest()
2271
2272// Find the last available block on the disk at or after the start
2273// block. Returns 0 if there are no available partitions after
2274// (or including) start.
2275uint64_t GPTData::FindLastAvailable(uint64_t start) {
2276 uint64_t last;
2277 uint32_t i;
2278 int lastMoved = 0;
2279
2280 // Start by assuming the last usable LBA is available....
2281 last = mainHeader.lastUsableLBA;
2282
2283 // ...now, similar to algorithm in FindFirstAvailable(), search
2284 // through all partitions, moving last when it's in an existing
2285 // partition. Set the lastMoved flag so we repeat to catch cases
2286 // where partitions are out of logical order.
2287 do {
2288 lastMoved = 0;
2289 for (i = 0; i < mainHeader.numParts; i++) {
2290 if ((last >= partitions[i].GetFirstLBA()) &&
2291 (last <= partitions[i].GetLastLBA())) { // in existing part.
2292 last = partitions[i].GetFirstLBA() - 1;
2293 lastMoved = 1;
2294 } // if
2295 } // for
2296 } while (lastMoved == 1);
2297 if (last < mainHeader.firstUsableLBA)
2298 last = 0;
2299 return (last);
2300} // GPTData::FindLastAvailable()
2301
2302// Find the last available block in the free space pointed to by start.
2303uint64_t GPTData::FindLastInFree(uint64_t start) {
2304 uint64_t nearestStart;
2305 uint32_t i;
2306
2307 nearestStart = mainHeader.lastUsableLBA;
2308 for (i = 0; i < mainHeader.numParts; i++) {
2309 if ((nearestStart > partitions[i].GetFirstLBA()) &&
2310 (partitions[i].GetFirstLBA() > start)) {
2311 nearestStart = partitions[i].GetFirstLBA() - 1;
2312 } // if
2313 } // for
2314 return (nearestStart);
2315} // GPTData::FindLastInFree()
2316
2317// Finds the total number of free blocks, the number of segments in which
2318// they reside, and the size of the largest of those segments
2319uint64_t GPTData::FindFreeBlocks(int *numSegments, uint64_t *largestSegment) {
2320 uint64_t start = UINT64_C(0); // starting point for each search
2321 uint64_t totalFound = UINT64_C(0); // running total
2322 uint64_t firstBlock; // first block in a segment
2323 uint64_t lastBlock; // last block in a segment
2324 uint64_t segmentSize; // size of segment in blocks
2325 int num = 0;
2326
2327 *largestSegment = UINT64_C(0);
2328 do {
2329 firstBlock = FindFirstAvailable(start);
2330 if (firstBlock != UINT64_C(0)) { // something's free...
2331 lastBlock = FindLastInFree(firstBlock);
2332 segmentSize = lastBlock - firstBlock + UINT64_C(1);
2333 if (segmentSize > *largestSegment) {
2334 *largestSegment = segmentSize;
2335 } // if
2336 totalFound += segmentSize;
2337 num++;
2338 start = lastBlock + 1;
2339 } // if
2340 } while (firstBlock != 0);
2341 *numSegments = num;
2342 return totalFound;
2343} // GPTData::FindFreeBlocks()
2344
2345// Returns 1 if sector is unallocated, 0 if it's allocated to a partition
2346int GPTData::IsFree(uint64_t sector) {
2347 int isFree = 1;
2348 uint32_t i;
2349
2350 for (i = 0; i < mainHeader.numParts; i++) {
2351 if ((sector >= partitions[i].GetFirstLBA()) &&
2352 (sector <= partitions[i].GetLastLBA())) {
2353 isFree = 0;
2354 } // if
2355 } // for
srs5694e35eb1b2009-09-14 00:29:34 -04002356 if ((sector < mainHeader.firstUsableLBA) ||
srs5694e4ac11e2009-08-31 10:13:04 -04002357 (sector > mainHeader.lastUsableLBA)) {
2358 isFree = 0;
2359 } // if
2360 return (isFree);
2361} // GPTData::IsFree()
2362
srs5694ba00fed2010-01-12 18:18:36 -05002363// Returns 1 if partNum is unused.
2364int GPTData::IsFreePartNum(uint32_t partNum) {
2365 int retval = 1;
2366
2367 if ((partNum >= 0) && (partNum < mainHeader.numParts)) {
2368 if ((partitions[partNum].GetFirstLBA() != UINT64_C(0)) ||
2369 (partitions[partNum].GetLastLBA() != UINT64_C(0))) {
2370 retval = 0;
2371 } // if partition is in use
2372 } else retval = 0;
2373
2374 return retval;
2375} // GPTData::IsFreePartNum()
2376
srs5694e4ac11e2009-08-31 10:13:04 -04002377/********************************
2378 * *
2379 * Endianness support functions *
2380 * *
2381 ********************************/
2382
srs56942a9f5da2009-08-26 00:48:01 -04002383void GPTData::ReverseHeaderBytes(struct GPTHeader* header) {
srs5694221e0872009-08-29 15:00:31 -04002384 ReverseBytes(&header->signature, 8);
2385 ReverseBytes(&header->revision, 4);
2386 ReverseBytes(&header->headerSize, 4);
2387 ReverseBytes(&header->headerCRC, 4);
2388 ReverseBytes(&header->reserved, 4);
2389 ReverseBytes(&header->currentLBA, 8);
2390 ReverseBytes(&header->backupLBA, 8);
2391 ReverseBytes(&header->firstUsableLBA, 8);
2392 ReverseBytes(&header->lastUsableLBA, 8);
2393 ReverseBytes(&header->partitionEntriesLBA, 8);
2394 ReverseBytes(&header->numParts, 4);
2395 ReverseBytes(&header->sizeOfPartitionEntries, 4);
2396 ReverseBytes(&header->partitionEntriesCRC, 4);
2397 ReverseBytes(&header->reserved2, GPT_RESERVED);
2398 ReverseBytes(&header->diskGUID.data1, 8);
2399 ReverseBytes(&header->diskGUID.data2, 8);
srs56942a9f5da2009-08-26 00:48:01 -04002400} // GPTData::ReverseHeaderBytes()
2401
2402// IMPORTANT NOTE: This function requires non-reversed mainHeader
2403// structure!
2404void GPTData::ReversePartitionBytes() {
2405 uint32_t i;
2406
2407 // Check GPT signature on big-endian systems; this will mismatch
2408 // if the function is called out of order. Unfortunately, it'll also
2409 // mismatch if there's data corruption.
2410 if ((mainHeader.signature != GPT_SIGNATURE) && (IsLittleEndian() == 0)) {
2411 printf("GPT signature mismatch in GPTData::ReversePartitionBytes(). This indicates\n"
srs5694e4ac11e2009-08-31 10:13:04 -04002412 "data corruption or a misplaced call to this function.\n");
srs56942a9f5da2009-08-26 00:48:01 -04002413 } // if signature mismatch....
2414 for (i = 0; i < mainHeader.numParts; i++) {
srs5694221e0872009-08-29 15:00:31 -04002415 partitions[i].ReversePartBytes();
srs56942a9f5da2009-08-26 00:48:01 -04002416 } // for
2417} // GPTData::ReversePartitionBytes()
2418
2419/******************************************
2420 * *
2421 * Additional non-class support functions *
2422 * *
2423 ******************************************/
2424
srs5694e7b4ff92009-08-18 13:16:10 -04002425// Check to be sure that data type sizes are correct. The basic types (uint*_t) should
2426// never fail these tests, but the struct types may fail depending on compile options.
2427// Specifically, the -fpack-struct option to gcc may be required to ensure proper structure
2428// sizes.
2429int SizesOK(void) {
2430 int allOK = 1;
srs5694e7b4ff92009-08-18 13:16:10 -04002431
2432 if (sizeof(uint8_t) != 1) {
srs56941d1448a2009-12-31 21:20:19 -05002433 fprintf(stderr, "uint8_t is %lu bytes, should be 1 byte; aborting!\n", sizeof(uint8_t));
srs5694e7b4ff92009-08-18 13:16:10 -04002434 allOK = 0;
2435 } // if
2436 if (sizeof(uint16_t) != 2) {
srs56941d1448a2009-12-31 21:20:19 -05002437 fprintf(stderr, "uint16_t is %lu bytes, should be 2 bytes; aborting!\n", sizeof(uint16_t));
srs5694e7b4ff92009-08-18 13:16:10 -04002438 allOK = 0;
2439 } // if
2440 if (sizeof(uint32_t) != 4) {
srs56941d1448a2009-12-31 21:20:19 -05002441 fprintf(stderr, "uint32_t is %lu bytes, should be 4 bytes; aborting!\n", sizeof(uint32_t));
srs5694e7b4ff92009-08-18 13:16:10 -04002442 allOK = 0;
2443 } // if
2444 if (sizeof(uint64_t) != 8) {
srs56941d1448a2009-12-31 21:20:19 -05002445 fprintf(stderr, "uint64_t is %lu bytes, should be 8 bytes; aborting!\n", sizeof(uint64_t));
srs5694e7b4ff92009-08-18 13:16:10 -04002446 allOK = 0;
2447 } // if
2448 if (sizeof(struct MBRRecord) != 16) {
srs56941d1448a2009-12-31 21:20:19 -05002449 fprintf(stderr, "MBRRecord is %lu bytes, should be 16 bytes; aborting!\n", sizeof(MBRRecord));
srs5694e7b4ff92009-08-18 13:16:10 -04002450 allOK = 0;
2451 } // if
srs5694978041c2009-09-21 20:51:47 -04002452 if (sizeof(struct TempMBR) != 512) {
srs56941d1448a2009-12-31 21:20:19 -05002453 fprintf(stderr, "TempMBR is %lu bytes, should be 512 bytes; aborting!\n", sizeof(TempMBR));
srs5694e7b4ff92009-08-18 13:16:10 -04002454 allOK = 0;
2455 } // if
2456 if (sizeof(struct GPTHeader) != 512) {
srs56941d1448a2009-12-31 21:20:19 -05002457 fprintf(stderr, "GPTHeader is %lu bytes, should be 512 bytes; aborting!\n", sizeof(GPTHeader));
srs5694e7b4ff92009-08-18 13:16:10 -04002458 allOK = 0;
2459 } // if
srs5694221e0872009-08-29 15:00:31 -04002460 if (sizeof(GPTPart) != 128) {
srs56941d1448a2009-12-31 21:20:19 -05002461 fprintf(stderr, "GPTPart is %lu bytes, should be 128 bytes; aborting!\n", sizeof(GPTPart));
srs5694221e0872009-08-29 15:00:31 -04002462 allOK = 0;
2463 } // if
2464// Determine endianness; set allOK = 0 if running on big-endian hardware
srs56942a9f5da2009-08-26 00:48:01 -04002465 if (IsLittleEndian() == 0) {
2466 fprintf(stderr, "\aRunning on big-endian hardware. Big-endian support is new and poorly"
srs5694e4ac11e2009-08-31 10:13:04 -04002467 " tested!\nBeware!\n");
srs56942a9f5da2009-08-26 00:48:01 -04002468 // allOK = 0;
srs5694e7b4ff92009-08-18 13:16:10 -04002469 } // if
2470 return (allOK);
2471} // SizesOK()
srs5694e4ac11e2009-08-31 10:13:04 -04002472