blob: fafadeeffe75e859cd276a6089ce60a71641b341 [file] [log] [blame]
srs5694e7b4ff92009-08-18 13:16:10 -04001/* gpt.cc -- Functions for loading, saving, and manipulating legacy MBR and GPT partition
2 data. */
3
srs5694e4ac11e2009-08-31 10:13:04 -04004/* By Rod Smith, initial coding January to February, 2009 */
srs5694e7b4ff92009-08-18 13:16:10 -04005
srs5694221e0872009-08-29 15:00:31 -04006/* This program is copyright (c) 2009 by Roderick W. Smith. It is distributed
7 under the terms of the GNU GPL version 2, as detailed in the COPYING file. */
8
srs5694e7b4ff92009-08-18 13:16:10 -04009#define __STDC_LIMIT_MACROS
10#define __STDC_CONSTANT_MACROS
11
12#include <stdio.h>
13#include <unistd.h>
14#include <stdlib.h>
15#include <stdint.h>
16#include <fcntl.h>
17#include <string.h>
18#include <time.h>
19#include <sys/stat.h>
20#include <errno.h>
21#include "crc32.h"
22#include "gpt.h"
srs5694221e0872009-08-29 15:00:31 -040023#include "bsd.h"
srs5694e7b4ff92009-08-18 13:16:10 -040024#include "support.h"
25#include "parttypes.h"
26#include "attributes.h"
27
28using namespace std;
29
30/****************************************
31 * *
32 * GPTData class and related structures *
33 * *
34 ****************************************/
35
srs5694e4ac11e2009-08-31 10:13:04 -040036// Default constructor
srs5694e7b4ff92009-08-18 13:16:10 -040037GPTData::GPTData(void) {
38 blockSize = SECTOR_SIZE; // set a default
39 diskSize = 0;
40 partitions = NULL;
41 state = gpt_valid;
42 strcpy(device, "");
srs56945d58fe02010-01-03 20:57:08 -050043 justLooking = 0;
srs5694e7b4ff92009-08-18 13:16:10 -040044 mainCrcOk = 0;
45 secondCrcOk = 0;
46 mainPartsCrcOk = 0;
47 secondPartsCrcOk = 0;
srs5694221e0872009-08-29 15:00:31 -040048 apmFound = 0;
49 bsdFound = 0;
srs56941d1448a2009-12-31 21:20:19 -050050 sectorAlignment = 8; // Align partitions on 4096-byte boundaries by default
srs5694ba00fed2010-01-12 18:18:36 -050051 beQuiet = 0;
52 whichWasUsed = use_new;
srs5694e7b4ff92009-08-18 13:16:10 -040053 srand((unsigned int) time(NULL));
srs56941e093722010-01-05 00:14:19 -050054 mainHeader.numParts = 0;
srs5694e7b4ff92009-08-18 13:16:10 -040055 SetGPTSize(NUM_GPT_ENTRIES);
56} // GPTData default constructor
57
58// The following constructor loads GPT data from a device file
59GPTData::GPTData(char* filename) {
60 blockSize = SECTOR_SIZE; // set a default
61 diskSize = 0;
62 partitions = NULL;
63 state = gpt_invalid;
64 strcpy(device, "");
srs56945d58fe02010-01-03 20:57:08 -050065 justLooking = 0;
srs5694e7b4ff92009-08-18 13:16:10 -040066 mainCrcOk = 0;
67 secondCrcOk = 0;
68 mainPartsCrcOk = 0;
69 secondPartsCrcOk = 0;
srs5694221e0872009-08-29 15:00:31 -040070 apmFound = 0;
71 bsdFound = 0;
srs56941d1448a2009-12-31 21:20:19 -050072 sectorAlignment = 8; // Align partitions on 4096-byte boundaries by default
srs5694ba00fed2010-01-12 18:18:36 -050073 beQuiet = 0;
74 whichWasUsed = use_new;
srs5694e7b4ff92009-08-18 13:16:10 -040075 srand((unsigned int) time(NULL));
srs56941e093722010-01-05 00:14:19 -050076 mainHeader.numParts = 0;
srs56943c0af382010-01-15 19:19:18 -050077 if (!LoadPartitions(filename))
78 exit(2);
srs5694e7b4ff92009-08-18 13:16:10 -040079} // GPTData(char* filename) constructor
80
srs5694e4ac11e2009-08-31 10:13:04 -040081// Destructor
srs5694e7b4ff92009-08-18 13:16:10 -040082GPTData::~GPTData(void) {
83 free(partitions);
84} // GPTData destructor
85
srs5694e4ac11e2009-08-31 10:13:04 -040086/*********************************************************************
87 * *
88 * Begin functions that verify data, or that adjust the verification *
89 * information (compute CRCs, rebuild headers) *
90 * *
91 *********************************************************************/
srs5694e7b4ff92009-08-18 13:16:10 -040092
srs5694e4ac11e2009-08-31 10:13:04 -040093// Perform detailed verification, reporting on any problems found, but
94// do *NOT* recover from these problems. Returns the total number of
95// problems identified.
96int GPTData::Verify(void) {
srs56941d1448a2009-12-31 21:20:19 -050097 int problems = 0, numSegments, i;
98 uint64_t totalFree, largestSegment, firstSector;
srs5694e4ac11e2009-08-31 10:13:04 -040099 char tempStr[255], siTotal[255], siLargest[255];
100
101 // First, check for CRC errors in the GPT data....
102 if (!mainCrcOk) {
103 problems++;
104 printf("\nProblem: The CRC for the main GPT header is invalid. The main GPT header may\n"
105 "be corrupt. Consider loading the backup GPT header to rebuild the main GPT\n"
106 "header\n");
107 } // if
108 if (!mainPartsCrcOk) {
109 problems++;
110 printf("\nProblem: The CRC for the main partition table is invalid. This table may be\n"
111 "corrupt. Consider loading the backup partition table.\n");
112 } // if
113 if (!secondCrcOk) {
114 problems++;
115 printf("\nProblem: The CRC for the backup GPT header is invalid. The backup GPT header\n"
116 "may be corrupt. Consider using the main GPT header to rebuild the backup GPT\n"
117 "header.\n");
118 } // if
119 if (!secondPartsCrcOk) {
120 problems++;
121 printf("\nCaution: The CRC for the backup partition table is invalid. This table may\n"
122 "be corrupt. This program will automatically create a new backup partition\n"
123 "table when you save your partitions.\n");
srs5694e7b4ff92009-08-18 13:16:10 -0400124 } // if
125
srs5694978041c2009-09-21 20:51:47 -0400126 // Now check that the main and backup headers both point to themselves....
127 if (mainHeader.currentLBA != 1) {
128 problems++;
129 printf("\nProblem: The main header's self-pointer doesn't point to itself. This problem\n"
130 "is being automatically corrected, but it may be a symptom of more serious\n"
131 "problems. Think carefully before saving changes with 'w' or using this disk.\n");
132 mainHeader.currentLBA = 1;
133 } // if
134 if (secondHeader.currentLBA != (diskSize - UINT64_C(1))) {
135 problems++;
srs56943f2fe992009-11-24 18:28:18 -0500136 printf("\nProblem: The secondary header's self-pointer indicates that it doesn't reside\n"
137 "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 -0500138 "option on the experts' menu to adjust the secondary header's and partition\n"
139 "table's locations.\n");
srs5694978041c2009-09-21 20:51:47 -0400140 } // if
141
142 // Now check that critical main and backup GPT entries match each other
srs5694e4ac11e2009-08-31 10:13:04 -0400143 if (mainHeader.currentLBA != secondHeader.backupLBA) {
144 problems++;
145 printf("\nProblem: main GPT header's current LBA pointer (%llu) doesn't\n"
146 "match the backup GPT header's LBA pointer(%llu)\n",
147 (unsigned long long) mainHeader.currentLBA,
148 (unsigned long long) secondHeader.backupLBA);
149 } // if
150 if (mainHeader.backupLBA != secondHeader.currentLBA) {
151 problems++;
152 printf("\nProblem: main GPT header's backup LBA pointer (%llu) doesn't\n"
153 "match the backup GPT header's current LBA pointer (%llu)\n",
154 (unsigned long long) mainHeader.backupLBA,
155 (unsigned long long) secondHeader.currentLBA);
156 } // if
157 if (mainHeader.firstUsableLBA != secondHeader.firstUsableLBA) {
158 problems++;
159 printf("\nProblem: main GPT header's first usable LBA pointer (%llu) doesn't\n"
160 "match the backup GPT header's first usable LBA pointer (%llu)\n",
161 (unsigned long long) mainHeader.firstUsableLBA,
162 (unsigned long long) secondHeader.firstUsableLBA);
163 } // if
164 if (mainHeader.lastUsableLBA != secondHeader.lastUsableLBA) {
165 problems++;
166 printf("\nProblem: main GPT header's last usable LBA pointer (%llu) doesn't\n"
167 "match the backup GPT header's last usable LBA pointer (%llu)\n",
168 (unsigned long long) mainHeader.lastUsableLBA,
169 (unsigned long long) secondHeader.lastUsableLBA);
170 } // if
171 if ((mainHeader.diskGUID.data1 != secondHeader.diskGUID.data1) ||
172 (mainHeader.diskGUID.data2 != secondHeader.diskGUID.data2)) {
173 problems++;
174 printf("\nProblem: main header's disk GUID (%s) doesn't\n",
175 GUIDToStr(mainHeader.diskGUID, tempStr));
176 printf("match the backup GPT header's disk GUID (%s)\n",
177 GUIDToStr(secondHeader.diskGUID, tempStr));
178 } // if
179 if (mainHeader.numParts != secondHeader.numParts) {
180 problems++;
181 printf("\nProblem: main GPT header's number of partitions (%lu) doesn't\n"
182 "match the backup GPT header's number of partitions (%lu)\n",
183 (unsigned long) mainHeader.numParts,
184 (unsigned long) secondHeader.numParts);
185 } // if
186 if (mainHeader.sizeOfPartitionEntries != secondHeader.sizeOfPartitionEntries) {
187 problems++;
188 printf("\nProblem: main GPT header's size of partition entries (%lu) doesn't\n"
189 "match the backup GPT header's size of partition entries (%lu)\n",
190 (unsigned long) mainHeader.sizeOfPartitionEntries,
191 (unsigned long) secondHeader.sizeOfPartitionEntries);
192 } // if
193
194 // Now check for a few other miscellaneous problems...
195 // Check that the disk size will hold the data...
196 if (mainHeader.backupLBA > diskSize) {
197 problems++;
198 printf("\nProblem: Disk is too small to hold all the data!\n");
199 printf("(Disk size is %llu sectors, needs to be %llu sectors.)\n",
200 (unsigned long long) diskSize,
201 (unsigned long long) mainHeader.backupLBA);
202 } // if
203
204 // Check for overlapping partitions....
205 problems += FindOverlaps();
206
207 // Check for mismatched MBR and GPT partitions...
208 problems += FindHybridMismatches();
209
210 // Verify that partitions don't run into GPT data areas....
211 problems += CheckGPTSize();
212
srs56941d1448a2009-12-31 21:20:19 -0500213 // Check that partitions are aligned on proper boundaries (for WD Advanced
214 // Format and similar disks)....
215 for (i = 0; i < mainHeader.numParts; i++) {
216 if ((partitions[i].GetFirstLBA() % sectorAlignment) != 0) {
217 printf("\nCaution: Partition %d doesn't begin on a %d-sector boundary. This may\n"
218 "result in degraded performance on some modern (2010 and later) hard disks.\n",
219 i + 1, sectorAlignment);
220 } // if
221 } // for
222
srs5694e4ac11e2009-08-31 10:13:04 -0400223 // Now compute available space, but only if no problems found, since
224 // problems could affect the results
225 if (problems == 0) {
226 totalFree = FindFreeBlocks(&numSegments, &largestSegment);
227 BytesToSI(totalFree * (uint64_t) blockSize, siTotal);
228 BytesToSI(largestSegment * (uint64_t) blockSize, siLargest);
229 printf("No problems found. %llu free sectors (%s) available in %u\n"
230 "segments, the largest of which is %llu sectors (%s) in size\n",
231 (unsigned long long) totalFree,
232 siTotal, numSegments, (unsigned long long) largestSegment,
233 siLargest);
234 } else {
235 printf("\nIdentified %d problems!\n", problems);
srs5694e7b4ff92009-08-18 13:16:10 -0400236 } // if/else
srs5694e4ac11e2009-08-31 10:13:04 -0400237
238 return (problems);
239} // GPTData::Verify()
srs5694e7b4ff92009-08-18 13:16:10 -0400240
241// Checks to see if the GPT tables overrun existing partitions; if they
srs5694221e0872009-08-29 15:00:31 -0400242// do, issues a warning but takes no action. Returns number of problems
243// detected (0 if OK, 1 to 2 if problems).
srs5694e7b4ff92009-08-18 13:16:10 -0400244int GPTData::CheckGPTSize(void) {
245 uint64_t overlap, firstUsedBlock, lastUsedBlock;
246 uint32_t i;
srs5694221e0872009-08-29 15:00:31 -0400247 int numProbs = 0;
srs5694e7b4ff92009-08-18 13:16:10 -0400248
249 // first, locate the first & last used blocks
250 firstUsedBlock = UINT64_MAX;
251 lastUsedBlock = 0;
252 for (i = 0; i < mainHeader.numParts; i++) {
srs5694221e0872009-08-29 15:00:31 -0400253 if ((partitions[i].GetFirstLBA() < firstUsedBlock) &&
srs5694e4ac11e2009-08-31 10:13:04 -0400254 (partitions[i].GetFirstLBA() != 0))
srs5694221e0872009-08-29 15:00:31 -0400255 firstUsedBlock = partitions[i].GetFirstLBA();
256 if (partitions[i].GetLastLBA() > lastUsedBlock)
257 lastUsedBlock = partitions[i].GetLastLBA();
srs5694e7b4ff92009-08-18 13:16:10 -0400258 } // for
259
260 // If the disk size is 0 (the default), then it means that various
261 // variables aren't yet set, so the below tests will be useless;
262 // therefore we should skip everything
263 if (diskSize != 0) {
264 if (mainHeader.firstUsableLBA > firstUsedBlock) {
265 overlap = mainHeader.firstUsableLBA - firstUsedBlock;
srs5694221e0872009-08-29 15:00:31 -0400266 printf("Warning! Main partition table overlaps the first partition by %lu blocks!\n",
267 (unsigned long) overlap);
268 if (firstUsedBlock > 2) {
269 printf("Try reducing the partition table size by %lu entries.\n",
270 (unsigned long) (overlap * 4));
271 printf("(Use the 's' item on the experts' menu.)\n");
272 } else {
273 printf("You will need to delete this partition or resize it in another utility.\n");
274 } // if/else
275 numProbs++;
srs5694e7b4ff92009-08-18 13:16:10 -0400276 } // Problem at start of disk
277 if (mainHeader.lastUsableLBA < lastUsedBlock) {
278 overlap = lastUsedBlock - mainHeader.lastUsableLBA;
srs5694221e0872009-08-29 15:00:31 -0400279 printf("Warning! Secondary partition table overlaps the last partition by %lu blocks\n",
280 (unsigned long) overlap);
281 if (lastUsedBlock > (diskSize - 2)) {
282 printf("You will need to delete this partition or resize it in another utility.\n");
283 } else {
284 printf("Try reducing the partition table size by %lu entries.\n",
285 (unsigned long) (overlap * 4));
286 printf("(Use the 's' item on the experts' menu.)\n");
287 } // if/else
288 numProbs++;
srs5694e7b4ff92009-08-18 13:16:10 -0400289 } // Problem at end of disk
290 } // if (diskSize != 0)
srs5694221e0872009-08-29 15:00:31 -0400291 return numProbs;
srs5694e7b4ff92009-08-18 13:16:10 -0400292} // GPTData::CheckGPTSize()
293
srs5694e7b4ff92009-08-18 13:16:10 -0400294// Check the validity of the GPT header. Returns 1 if the main header
295// is valid, 2 if the backup header is valid, 3 if both are valid, and
296// 0 if neither is valid. Note that this function just checks the GPT
297// signature and revision numbers, not CRCs or other data.
298int GPTData::CheckHeaderValidity(void) {
299 int valid = 3;
300
301 if (mainHeader.signature != GPT_SIGNATURE) {
302 valid -= 1;
srs5694221e0872009-08-29 15:00:31 -0400303// printf("Main GPT signature invalid; read 0x%016llX, should be\n0x%016llX\n",
304// (unsigned long long) mainHeader.signature, (unsigned long long) GPT_SIGNATURE);
srs5694e7b4ff92009-08-18 13:16:10 -0400305 } else if ((mainHeader.revision != 0x00010000) && valid) {
306 valid -= 1;
307 printf("Unsupported GPT version in main header; read 0x%08lX, should be\n0x%08lX\n",
srs56945d58fe02010-01-03 20:57:08 -0500308 (unsigned long) mainHeader.revision, (unsigned long) UINT32_C(0x00010000));
srs5694e7b4ff92009-08-18 13:16:10 -0400309 } // if/else/if
310
311 if (secondHeader.signature != GPT_SIGNATURE) {
312 valid -= 2;
srs5694221e0872009-08-29 15:00:31 -0400313// printf("Secondary GPT signature invalid; read 0x%016llX, should be\n0x%016llX\n",
314// (unsigned long long) secondHeader.signature, (unsigned long long) GPT_SIGNATURE);
srs5694e7b4ff92009-08-18 13:16:10 -0400315 } else if ((secondHeader.revision != 0x00010000) && valid) {
316 valid -= 2;
317 printf("Unsupported GPT version in backup header; read 0x%08lX, should be\n0x%08lX\n",
srs56945d58fe02010-01-03 20:57:08 -0500318 (unsigned long) mainHeader.revision, (unsigned long) UINT32_C(0x00010000));
srs5694e7b4ff92009-08-18 13:16:10 -0400319 } // if/else/if
320
srs56942a9f5da2009-08-26 00:48:01 -0400321 // If MBR bad, check for an Apple disk signature
srs5694e35eb1b2009-09-14 00:29:34 -0400322 if ((protectiveMBR.GetValidity() == invalid) &&
srs5694e4ac11e2009-08-31 10:13:04 -0400323 (((mainHeader.signature << 32) == APM_SIGNATURE1) ||
srs56942a9f5da2009-08-26 00:48:01 -0400324 (mainHeader.signature << 32) == APM_SIGNATURE2)) {
srs5694221e0872009-08-29 15:00:31 -0400325 apmFound = 1; // Will display warning message later
srs56943f2fe992009-11-24 18:28:18 -0500326 } // if
srs56942a9f5da2009-08-26 00:48:01 -0400327
srs5694e4ac11e2009-08-31 10:13:04 -0400328 return valid;
srs5694e7b4ff92009-08-18 13:16:10 -0400329} // GPTData::CheckHeaderValidity()
330
331// Check the header CRC to see if it's OK...
srs56942a9f5da2009-08-26 00:48:01 -0400332// Note: Must be called BEFORE byte-order reversal on big-endian
333// systems!
srs5694e7b4ff92009-08-18 13:16:10 -0400334int GPTData::CheckHeaderCRC(struct GPTHeader* header) {
srs5694978041c2009-09-21 20:51:47 -0400335 uint32_t oldCRC, newCRC, hSize;
srs5694e7b4ff92009-08-18 13:16:10 -0400336
srs56942a9f5da2009-08-26 00:48:01 -0400337 // Back up old header CRC and then blank it, since it must be 0 for
srs5694e7b4ff92009-08-18 13:16:10 -0400338 // computation to be valid
339 oldCRC = header->headerCRC;
340 header->headerCRC = UINT32_C(0);
srs5694978041c2009-09-21 20:51:47 -0400341 hSize = header->headerSize;
342
343 // If big-endian system, reverse byte order
344 if (IsLittleEndian() == 0) {
345 ReverseBytes(&oldCRC, 4);
346 } // if
srs5694e7b4ff92009-08-18 13:16:10 -0400347
348 // Initialize CRC functions...
349 chksum_crc32gentab();
350
351 // Compute CRC, restore original, and return result of comparison
352 newCRC = chksum_crc32((unsigned char*) header, HEADER_SIZE);
srs5694978041c2009-09-21 20:51:47 -0400353 header->headerCRC = oldCRC;
srs5694e7b4ff92009-08-18 13:16:10 -0400354 return (oldCRC == newCRC);
355} // GPTData::CheckHeaderCRC()
356
srs56942a9f5da2009-08-26 00:48:01 -0400357// Recompute all the CRCs. Must be called before saving (but after reversing
358// byte order on big-endian systems) if any changes have been made.
srs5694e7b4ff92009-08-18 13:16:10 -0400359void GPTData::RecomputeCRCs(void) {
srs5694978041c2009-09-21 20:51:47 -0400360 uint32_t crc, hSize, trueNumParts;
srs56942a9f5da2009-08-26 00:48:01 -0400361 int littleEndian = 1;
srs5694e7b4ff92009-08-18 13:16:10 -0400362
363 // Initialize CRC functions...
364 chksum_crc32gentab();
365
srs5694978041c2009-09-21 20:51:47 -0400366 hSize = mainHeader.headerSize;
srs56942a9f5da2009-08-26 00:48:01 -0400367 littleEndian = IsLittleEndian();
368
srs5694e7b4ff92009-08-18 13:16:10 -0400369 // Compute CRC of partition tables & store in main and secondary headers
srs56942a9f5da2009-08-26 00:48:01 -0400370 trueNumParts = mainHeader.numParts;
371 if (littleEndian == 0)
srs5694221e0872009-08-29 15:00:31 -0400372 ReverseBytes(&trueNumParts, 4); // unreverse this key piece of data....
srs56942a9f5da2009-08-26 00:48:01 -0400373 crc = chksum_crc32((unsigned char*) partitions, trueNumParts * GPT_SIZE);
srs5694e7b4ff92009-08-18 13:16:10 -0400374 mainHeader.partitionEntriesCRC = crc;
375 secondHeader.partitionEntriesCRC = crc;
srs56942a9f5da2009-08-26 00:48:01 -0400376 if (littleEndian == 0) {
srs5694221e0872009-08-29 15:00:31 -0400377 ReverseBytes(&mainHeader.partitionEntriesCRC, 4);
378 ReverseBytes(&secondHeader.partitionEntriesCRC, 4);
srs56942a9f5da2009-08-26 00:48:01 -0400379 } // if
srs5694e7b4ff92009-08-18 13:16:10 -0400380
381 // Zero out GPT tables' own CRCs (required for correct computation)
382 mainHeader.headerCRC = 0;
383 secondHeader.headerCRC = 0;
384
385 // Compute & store CRCs of main & secondary headers...
srs5694978041c2009-09-21 20:51:47 -0400386 crc = chksum_crc32((unsigned char*) &mainHeader, hSize);
srs56942a9f5da2009-08-26 00:48:01 -0400387 if (littleEndian == 0)
srs5694221e0872009-08-29 15:00:31 -0400388 ReverseBytes(&crc, 4);
srs5694e7b4ff92009-08-18 13:16:10 -0400389 mainHeader.headerCRC = crc;
srs5694978041c2009-09-21 20:51:47 -0400390 crc = chksum_crc32((unsigned char*) &secondHeader, hSize);
srs56942a9f5da2009-08-26 00:48:01 -0400391 if (littleEndian == 0)
srs5694221e0872009-08-29 15:00:31 -0400392 ReverseBytes(&crc, 4);
srs5694e7b4ff92009-08-18 13:16:10 -0400393 secondHeader.headerCRC = crc;
394} // GPTData::RecomputeCRCs()
395
srs5694e7b4ff92009-08-18 13:16:10 -0400396// Rebuild the main GPT header, using the secondary header as a model.
397// Typically called when the main header has been found to be corrupt.
398void GPTData::RebuildMainHeader(void) {
399 int i;
400
401 mainHeader.signature = GPT_SIGNATURE;
402 mainHeader.revision = secondHeader.revision;
srs5694978041c2009-09-21 20:51:47 -0400403 mainHeader.headerSize = secondHeader.headerSize;
srs5694e7b4ff92009-08-18 13:16:10 -0400404 mainHeader.headerCRC = UINT32_C(0);
405 mainHeader.reserved = secondHeader.reserved;
406 mainHeader.currentLBA = secondHeader.backupLBA;
407 mainHeader.backupLBA = secondHeader.currentLBA;
408 mainHeader.firstUsableLBA = secondHeader.firstUsableLBA;
409 mainHeader.lastUsableLBA = secondHeader.lastUsableLBA;
410 mainHeader.diskGUID.data1 = secondHeader.diskGUID.data1;
411 mainHeader.diskGUID.data2 = secondHeader.diskGUID.data2;
412 mainHeader.partitionEntriesLBA = UINT64_C(2);
413 mainHeader.numParts = secondHeader.numParts;
414 mainHeader.sizeOfPartitionEntries = secondHeader.sizeOfPartitionEntries;
415 mainHeader.partitionEntriesCRC = secondHeader.partitionEntriesCRC;
416 for (i = 0 ; i < GPT_RESERVED; i++)
417 mainHeader.reserved2[i] = secondHeader.reserved2[i];
418} // GPTData::RebuildMainHeader()
419
420// Rebuild the secondary GPT header, using the main header as a model.
421void GPTData::RebuildSecondHeader(void) {
422 int i;
423
424 secondHeader.signature = GPT_SIGNATURE;
425 secondHeader.revision = mainHeader.revision;
srs5694978041c2009-09-21 20:51:47 -0400426 secondHeader.headerSize = mainHeader.headerSize;
srs5694e7b4ff92009-08-18 13:16:10 -0400427 secondHeader.headerCRC = UINT32_C(0);
428 secondHeader.reserved = mainHeader.reserved;
429 secondHeader.currentLBA = mainHeader.backupLBA;
430 secondHeader.backupLBA = mainHeader.currentLBA;
431 secondHeader.firstUsableLBA = mainHeader.firstUsableLBA;
432 secondHeader.lastUsableLBA = mainHeader.lastUsableLBA;
433 secondHeader.diskGUID.data1 = mainHeader.diskGUID.data1;
434 secondHeader.diskGUID.data2 = mainHeader.diskGUID.data2;
435 secondHeader.partitionEntriesLBA = secondHeader.lastUsableLBA + UINT64_C(1);
436 secondHeader.numParts = mainHeader.numParts;
437 secondHeader.sizeOfPartitionEntries = mainHeader.sizeOfPartitionEntries;
438 secondHeader.partitionEntriesCRC = mainHeader.partitionEntriesCRC;
439 for (i = 0 ; i < GPT_RESERVED; i++)
440 secondHeader.reserved2[i] = mainHeader.reserved2[i];
srs5694e4ac11e2009-08-31 10:13:04 -0400441} // GPTData::RebuildSecondHeader()
442
443// Search for hybrid MBR entries that have no corresponding GPT partition.
444// Returns number of such mismatches found
445int GPTData::FindHybridMismatches(void) {
446 int i, j, found, numFound = 0;
447 uint64_t mbrFirst, mbrLast;
448
449 for (i = 0; i < 4; i++) {
450 if ((protectiveMBR.GetType(i) != 0xEE) && (protectiveMBR.GetType(i) != 0x00)) {
451 j = 0;
452 found = 0;
453 do {
454 mbrFirst = (uint64_t) protectiveMBR.GetFirstSector(i);
455 mbrLast = mbrFirst + (uint64_t) protectiveMBR.GetLength(i) - UINT64_C(1);
456 if ((partitions[j].GetFirstLBA() == mbrFirst) &&
457 (partitions[j].GetLastLBA() == mbrLast))
458 found = 1;
459 j++;
460 } while ((!found) && (j < mainHeader.numParts));
461 if (!found) {
462 numFound++;
463 printf("\nWarning! Mismatched GPT and MBR partitions! MBR partition "
464 "%d, of type 0x%02X,\nhas no corresponding GPT partition! "
465 "You may continue, but this condition\nmight cause data loss"
466 " in the future!\a\n", i + 1, protectiveMBR.GetType(i));
467 } // if
468 } // if
469 } // for
470 return numFound;
471} // GPTData::FindHybridMismatches
472
473// Find overlapping partitions and warn user about them. Returns number of
474// overlapping partitions.
475int GPTData::FindOverlaps(void) {
476 int i, j, problems = 0;
477
478 for (i = 1; i < mainHeader.numParts; i++) {
479 for (j = 0; j < i; j++) {
480 if (partitions[i].DoTheyOverlap(&partitions[j])) {
481 problems++;
482 printf("\nProblem: partitions %d and %d overlap:\n", i + 1, j + 1);
483 printf(" Partition %d: %llu to %llu\n", i,
484 (unsigned long long) partitions[i].GetFirstLBA(),
485 (unsigned long long) partitions[i].GetLastLBA());
486 printf(" Partition %d: %llu to %llu\n", j,
487 (unsigned long long) partitions[j].GetFirstLBA(),
488 (unsigned long long) partitions[j].GetLastLBA());
489 } // if
490 } // for j...
491 } // for i...
492 return problems;
493} // GPTData::FindOverlaps()
494
495/******************************************************************
496 * *
497 * Begin functions that load data from disk or save data to disk. *
498 * *
499 ******************************************************************/
500
501// Scan for partition data. This function loads the MBR data (regular MBR or
502// protective MBR) and loads BSD disklabel data (which is probably invalid).
503// It also looks for APM data, forces a load of GPT data, and summarizes
504// the results.
505void GPTData::PartitionScan(int fd) {
506 BSDData bsdDisklabel;
srs5694e4ac11e2009-08-31 10:13:04 -0400507
508 // Read the MBR & check for BSD disklabel
509 protectiveMBR.ReadMBRData(fd);
srs5694e4ac11e2009-08-31 10:13:04 -0400510 bsdDisklabel.ReadBSDData(fd, 0, diskSize - 1);
srs5694e4ac11e2009-08-31 10:13:04 -0400511
512 // Load the GPT data, whether or not it's valid
513 ForceLoadGPTData(fd);
srs5694ba00fed2010-01-12 18:18:36 -0500514
515 if (!beQuiet) {
516 printf("Partition table scan:\n");
517 protectiveMBR.ShowState();
518 bsdDisklabel.ShowState();
519 ShowAPMState(); // Show whether there's an Apple Partition Map present
520 ShowGPTState(); // Show GPT status
521 printf("\n");
522 } // if
srs5694e4ac11e2009-08-31 10:13:04 -0400523
524 if (apmFound) {
525 printf("\n*******************************************************************\n");
526 printf("This disk appears to contain an Apple-format (APM) partition table!\n");
srs56945d58fe02010-01-03 20:57:08 -0500527 if (!justLooking) {
528 printf("It will be destroyed if you continue!\n");
529 } // if
srs5694e4ac11e2009-08-31 10:13:04 -0400530 printf("*******************************************************************\n\n\a");
531 } // if
srs5694e4ac11e2009-08-31 10:13:04 -0400532} // GPTData::PartitionScan()
533
534// Read GPT data from a disk.
535int GPTData::LoadPartitions(char* deviceFilename) {
536 int fd, err;
537 int allOK = 1, i;
538 uint64_t firstBlock, lastBlock;
539 BSDData bsdDisklabel;
540
541 // First, do a test to see if writing will be possible later....
542 fd = OpenForWrite(deviceFilename);
srs56945d58fe02010-01-03 20:57:08 -0500543 if ((fd == -1) && (!justLooking)) {
srs5694e4ac11e2009-08-31 10:13:04 -0400544 printf("\aNOTE: Write test failed with error number %d. It will be "
545 "impossible to save\nchanges to this disk's partition table!\n\n",
546 errno);
srs56945d58fe02010-01-03 20:57:08 -0500547 justLooking = 1;
548 } // if
srs5694e4ac11e2009-08-31 10:13:04 -0400549 close(fd);
550
551 if ((fd = open(deviceFilename, O_RDONLY)) != -1) {
552 // store disk information....
553 diskSize = disksize(fd, &err);
554 blockSize = (uint32_t) GetBlockSize(fd);
srs56945d58fe02010-01-03 20:57:08 -0500555 sectorAlignment = FindAlignment(fd);
srs5694e4ac11e2009-08-31 10:13:04 -0400556 strcpy(device, deviceFilename);
srs5694ba00fed2010-01-12 18:18:36 -0500557 PartitionScan(fd); // Check for partition types, load GPT, & print summary
srs5694e4ac11e2009-08-31 10:13:04 -0400558
srs5694ba00fed2010-01-12 18:18:36 -0500559 whichWasUsed = UseWhichPartitions();
560 switch (whichWasUsed) {
srs5694e4ac11e2009-08-31 10:13:04 -0400561 case use_mbr:
562 XFormPartitions();
563 break;
564 case use_bsd:
565 bsdDisklabel.ReadBSDData(fd, 0, diskSize - 1);
566// bsdDisklabel.DisplayBSDData();
567 ClearGPTData();
568 protectiveMBR.MakeProtectiveMBR(1); // clear boot area (option 1)
569 XFormDisklabel(&bsdDisklabel, 0);
570 break;
571 case use_gpt:
572 break;
573 case use_new:
574 ClearGPTData();
575 protectiveMBR.MakeProtectiveMBR();
576 break;
srs56943c0af382010-01-15 19:19:18 -0500577 case use_abort:
578 allOK = 0;
579 printf("Aborting because of invalid partition data!\n");
580 break;
srs5694e4ac11e2009-08-31 10:13:04 -0400581 } // switch
582
583 // Now find the first and last sectors used by partitions...
584 if (allOK) {
585 firstBlock = mainHeader.backupLBA; // start high
586 lastBlock = 0; // start low
587 for (i = 0; i < mainHeader.numParts; i++) {
588 if ((partitions[i].GetFirstLBA() < firstBlock) &&
589 (partitions[i].GetFirstLBA() > 0))
590 firstBlock = partitions[i].GetFirstLBA();
591 if (partitions[i].GetLastLBA() > lastBlock)
592 lastBlock = partitions[i].GetLastLBA();
593 } // for
srs56943c0af382010-01-15 19:19:18 -0500594 CheckGPTSize();
srs5694e4ac11e2009-08-31 10:13:04 -0400595 } // if
srs5694e4ac11e2009-08-31 10:13:04 -0400596 } else {
597 allOK = 0;
598 fprintf(stderr, "Problem opening %s for reading! Error is %d\n",
599 deviceFilename, errno);
600 if (errno == EACCES) { // User is probably not running as root
601 fprintf(stderr, "You must run this program as root or use sudo!\n");
602 } // if
603 } // if/else
604 return (allOK);
605} // GPTData::LoadPartitions()
606
607// Loads the GPT, as much as possible. Returns 1 if this seems to have
608// succeeded, 0 if there are obvious problems....
609int GPTData::ForceLoadGPTData(int fd) {
610 int allOK = 1, validHeaders;
611 off_t seekTo;
612 char* storage;
613 uint32_t newCRC, sizeOfParts;
614
615 // Seek to and read the main GPT header
srs56941e093722010-01-05 00:14:19 -0500616 lseek64(fd, blockSize, SEEK_SET);
617 if (myRead(fd, (char*) &mainHeader, 512) != 512) { // read main GPT header
618 fprintf(stderr, "Warning! Error %d reading main GPT header!\n", errno);
srs56945d58fe02010-01-03 20:57:08 -0500619 } // if
srs5694e4ac11e2009-08-31 10:13:04 -0400620 mainCrcOk = CheckHeaderCRC(&mainHeader);
621 if (IsLittleEndian() == 0) // big-endian system; adjust header byte order....
622 ReverseHeaderBytes(&mainHeader);
623
srs56943f2fe992009-11-24 18:28:18 -0500624 // Load backup header, check its CRC, and store the results of the
625 // check for future reference. Load backup header using pointer in main
626 // header if possible; but if main header has a CRC error, or if it
627 // points to beyond the end of the disk, load the last sector of the
628 // disk instead.
629 if (mainCrcOk) {
630 if (mainHeader.backupLBA < diskSize) {
631 seekTo = mainHeader.backupLBA * blockSize;
632 } else {
srs56941e093722010-01-05 00:14:19 -0500633 seekTo = (diskSize * blockSize) - blockSize;
srs56943f2fe992009-11-24 18:28:18 -0500634 printf("Warning! Disk size is smaller than the main header indicates! Loading\n"
635 "secondary header from the last sector of the disk! You should use 'v' to\n"
636 "verify disk integrity, and perhaps options on the experts' menu to repair\n"
637 "the disk.\n");
638 } // else
639 } else {
srs56941e093722010-01-05 00:14:19 -0500640 seekTo = (diskSize * blockSize) - blockSize;
srs56943f2fe992009-11-24 18:28:18 -0500641 } // if/else (mainCrcOk)
642
srs5694e4ac11e2009-08-31 10:13:04 -0400643 if (lseek64(fd, seekTo, SEEK_SET) != (off_t) -1) {
srs56941e093722010-01-05 00:14:19 -0500644 if (myRead(fd, (char*) &secondHeader, 512) != 512) { // read secondary GPT header
645 fprintf(stderr, "Warning! Error %d reading secondary GPT header!\n", errno);
srs56945d58fe02010-01-03 20:57:08 -0500646 } // if
srs5694e4ac11e2009-08-31 10:13:04 -0400647 secondCrcOk = CheckHeaderCRC(&secondHeader);
648 if (IsLittleEndian() == 0) // big-endian system; adjust header byte order....
649 ReverseHeaderBytes(&secondHeader);
650 } else {
651 allOK = 0;
652 state = gpt_invalid;
srs56943f2fe992009-11-24 18:28:18 -0500653 fprintf(stderr, "Unable to seek to secondary GPT header at sector %llu!\n",
srs56945d58fe02010-01-03 20:57:08 -0500654 (unsigned long long) (diskSize - (UINT64_C(1))));
srs5694e4ac11e2009-08-31 10:13:04 -0400655 } // if/else lseek
656
657 // Return valid headers code: 0 = both headers bad; 1 = main header
658 // good, backup bad; 2 = backup header good, main header bad;
659 // 3 = both headers good. Note these codes refer to valid GPT
660 // signatures and version numbers; more subtle problems will elude
661 // this check!
662 validHeaders = CheckHeaderValidity();
663
664 // Read partitions (from primary array)
665 if (validHeaders > 0) { // if at least one header is OK....
666 // GPT appears to be valid....
667 state = gpt_valid;
668
669 // We're calling the GPT valid, but there's a possibility that one
670 // of the two headers is corrupt. If so, use the one that seems to
671 // be in better shape to regenerate the bad one
672 if (validHeaders == 2) { // valid backup header, invalid main header
673 printf("Caution: invalid main GPT header, but valid backup; regenerating main header\n"
674 "from backup!\n");
675 RebuildMainHeader();
676 mainCrcOk = secondCrcOk; // Since copied, use CRC validity of backup
677 } else if (validHeaders == 1) { // valid main header, invalid backup
678 printf("Caution: invalid backup GPT header, but valid main header; regenerating\n"
679 "backup header from main header.\n");
680 RebuildSecondHeader();
681 secondCrcOk = mainCrcOk; // Since regenerated, use CRC validity of main
682 } // if/else/if
683
684 // Load the main partition table, including storing results of its
685 // CRC check
686 if (LoadMainTable() == 0)
687 allOK = 0;
688
689 // Load backup partition table into temporary storage to check
690 // its CRC and store the results, then discard this temporary
691 // storage, since we don't use it in any but recovery operations
692 seekTo = secondHeader.partitionEntriesLBA * (off_t) blockSize;
693 if ((lseek64(fd, seekTo, SEEK_SET) != (off_t) -1) && (secondCrcOk)) {
694 sizeOfParts = secondHeader.numParts * secondHeader.sizeOfPartitionEntries;
695 storage = (char*) malloc(sizeOfParts);
srs56941e093722010-01-05 00:14:19 -0500696 if (myRead(fd, storage, sizeOfParts) != sizeOfParts) {
srs56945d58fe02010-01-03 20:57:08 -0500697 fprintf(stderr, "Warning! Error %d reading backup partition table!\n", errno);
698 } // if
srs5694e4ac11e2009-08-31 10:13:04 -0400699 newCRC = chksum_crc32((unsigned char*) storage, sizeOfParts);
700 free(storage);
701 secondPartsCrcOk = (newCRC == secondHeader.partitionEntriesCRC);
702 } // if
703
704 // Check for valid CRCs and warn if there are problems
705 if ((mainCrcOk == 0) || (secondCrcOk == 0) || (mainPartsCrcOk == 0) ||
706 (secondPartsCrcOk == 0)) {
707 printf("Warning! One or more CRCs don't match. You should repair the disk!\n");
708 state = gpt_corrupt;
srs5694ba00fed2010-01-12 18:18:36 -0500709 } // if
srs5694e4ac11e2009-08-31 10:13:04 -0400710 } else {
711 state = gpt_invalid;
712 } // if/else
713 return allOK;
714} // GPTData::ForceLoadGPTData()
715
srs5694247657a2009-11-26 18:36:12 -0500716// Loads the partition table pointed to by the main GPT header. The
srs5694e4ac11e2009-08-31 10:13:04 -0400717// main GPT header in memory MUST be valid for this call to do anything
718// sensible!
719int GPTData::LoadMainTable(void) {
720 int fd, retval = 0;
721 uint32_t newCRC, sizeOfParts;
722
723 if ((fd = open(device, O_RDONLY)) != -1) {
724 // Set internal data structures for number of partitions on the disk
725 SetGPTSize(mainHeader.numParts);
726
727 // Load main partition table, and record whether its CRC
728 // matches the stored value
729 lseek64(fd, mainHeader.partitionEntriesLBA * blockSize, SEEK_SET);
730 sizeOfParts = mainHeader.numParts * mainHeader.sizeOfPartitionEntries;
srs56941e093722010-01-05 00:14:19 -0500731 if (myRead(fd, (char*) partitions, sizeOfParts) != sizeOfParts) {
srs56945d58fe02010-01-03 20:57:08 -0500732 fprintf(stderr, "Warning! Error %d when loading the main partition table!\n", errno);
733 } // if
srs5694e4ac11e2009-08-31 10:13:04 -0400734 newCRC = chksum_crc32((unsigned char*) partitions, sizeOfParts);
735 mainPartsCrcOk = (newCRC == mainHeader.partitionEntriesCRC);
736 if (IsLittleEndian() == 0)
737 ReversePartitionBytes();
738 retval = 1;
739 } // if
740 return retval;
741} // GPTData::LoadMainTable()
srs5694e7b4ff92009-08-18 13:16:10 -0400742
743// Load the second (backup) partition table as the primary partition
744// table. Used in repair functions
745void GPTData::LoadSecondTableAsMain(void) {
746 int fd;
747 off_t seekTo;
748 uint32_t sizeOfParts, newCRC;
749
750 if ((fd = open(device, O_RDONLY)) != -1) {
751 seekTo = secondHeader.partitionEntriesLBA * (off_t) blockSize;
752 if (lseek64(fd, seekTo, SEEK_SET) != (off_t) -1) {
753 SetGPTSize(secondHeader.numParts);
754 sizeOfParts = secondHeader.numParts * secondHeader.sizeOfPartitionEntries;
srs56941e093722010-01-05 00:14:19 -0500755 if (myRead(fd, (char*) partitions, sizeOfParts) != sizeOfParts) {
srs56945d58fe02010-01-03 20:57:08 -0500756 fprintf(stderr, "Warning! Read error %d! Misbehavior now likely!\n", errno);
757 } // if
srs5694e7b4ff92009-08-18 13:16:10 -0400758 newCRC = chksum_crc32((unsigned char*) partitions, sizeOfParts);
759 secondPartsCrcOk = (newCRC == secondHeader.partitionEntriesCRC);
srs5694e4ac11e2009-08-31 10:13:04 -0400760 mainPartsCrcOk = secondPartsCrcOk;
srs56942a9f5da2009-08-26 00:48:01 -0400761 if (IsLittleEndian() == 0)
762 ReversePartitionBytes();
srs5694e7b4ff92009-08-18 13:16:10 -0400763 if (!secondPartsCrcOk) {
764 printf("Error! After loading backup partitions, the CRC still doesn't check out!\n");
765 } // if
766 } else {
767 printf("Error! Couldn't seek to backup partition table!\n");
768 } // if/else
769 } else {
srs56941d1448a2009-12-31 21:20:19 -0500770 printf("Error! Couldn't open device %s when recovering backup partition table!\n", device);
srs5694e7b4ff92009-08-18 13:16:10 -0400771 } // if/else
772} // GPTData::LoadSecondTableAsMain()
773
srs5694e7b4ff92009-08-18 13:16:10 -0400774// Writes GPT (and protective MBR) to disk. Returns 1 on successful
775// write, 0 if there was a problem.
srs5694ba00fed2010-01-12 18:18:36 -0500776int GPTData::SaveGPTData(int quiet) {
srs5694978041c2009-09-21 20:51:47 -0400777 int allOK = 1;
srs5694e7b4ff92009-08-18 13:16:10 -0400778 char answer, line[256];
779 int fd;
780 uint64_t secondTable;
srs56942a9f5da2009-08-26 00:48:01 -0400781 uint32_t numParts;
srs5694e7b4ff92009-08-18 13:16:10 -0400782 off_t offset;
783
784 if (strlen(device) == 0) {
785 printf("Device not defined.\n");
786 } // if
787
788 // First do some final sanity checks....
srs56945d58fe02010-01-03 20:57:08 -0500789
790 // This test should only fail on read-only disks....
791 if (justLooking) {
792 printf("The justLooking flag is set. This probably means you can't write to the disk.\n");
793 allOK = 0;
794 } // if
795
srs5694e7b4ff92009-08-18 13:16:10 -0400796 // Is there enough space to hold the GPT headers and partition tables,
797 // given the partition sizes?
srs5694221e0872009-08-29 15:00:31 -0400798 if (CheckGPTSize() > 0) {
srs5694e7b4ff92009-08-18 13:16:10 -0400799 allOK = 0;
800 } // if
801
802 // Check that disk is really big enough to handle this...
803 if (mainHeader.backupLBA > diskSize) {
srs5694247657a2009-11-26 18:36:12 -0500804 fprintf(stderr, "Error! Disk is too small! The 'e' option on the experts' menu might fix the\n"
805 "problem (or it might not). Aborting!\n");
srs56945d58fe02010-01-03 20:57:08 -0500806 printf("(Disk size is %llu sectors, needs to be %llu sectors.)\n",
807 (unsigned long long) diskSize, (unsigned long long) mainHeader.backupLBA);
srs5694e7b4ff92009-08-18 13:16:10 -0400808 allOK = 0;
809 } // if
srs5694247657a2009-11-26 18:36:12 -0500810 // Check that second header is properly placed. Warn and ask if this should
811 // be corrected if the test fails....
srs5694ba00fed2010-01-12 18:18:36 -0500812 if ((mainHeader.backupLBA < (diskSize - UINT64_C(1))) && (quiet == 0)) {
srs5694247657a2009-11-26 18:36:12 -0500813 printf("Warning! Secondary header is placed too early on the disk! Do you want to\n"
814 "correct this problem? ");
815 if (GetYN() == 'Y') {
816 MoveSecondHeaderToEnd();
817 printf("Have moved second header and partition table to correct location.\n");
818 } else {
819 printf("Have not corrected the problem. Strange problems may occur in the future!\n");
820 } // if correction requested
821 } // if
srs5694e7b4ff92009-08-18 13:16:10 -0400822
823 // Check for overlapping partitions....
srs5694e4ac11e2009-08-31 10:13:04 -0400824 if (FindOverlaps() > 0) {
825 allOK = 0;
826 fprintf(stderr, "Aborting write operation!\n");
827 } // if
828
829 // Check for mismatched MBR and GPT data, but let it pass if found
830 // (function displays warning message)
831 FindHybridMismatches();
srs5694e7b4ff92009-08-18 13:16:10 -0400832
srs56942a9f5da2009-08-26 00:48:01 -0400833 // Pull out some data that's needed before doing byte-order reversal on
834 // big-endian systems....
835 numParts = mainHeader.numParts;
836 secondTable = secondHeader.partitionEntriesLBA;
837 if (IsLittleEndian() == 0) {
838 // Reverse partition bytes first, since that function requires non-reversed
839 // data from the main header....
840 ReversePartitionBytes();
841 ReverseHeaderBytes(&mainHeader);
842 ReverseHeaderBytes(&secondHeader);
843 } // if
srs5694e7b4ff92009-08-18 13:16:10 -0400844 RecomputeCRCs();
845
srs5694ba00fed2010-01-12 18:18:36 -0500846 if ((allOK) && (!quiet)) {
srs5694e7b4ff92009-08-18 13:16:10 -0400847 printf("\nFinal checks complete. About to write GPT data. THIS WILL OVERWRITE EXISTING\n");
848 printf("MBR PARTITIONS!! THIS PROGRAM IS BETA QUALITY AT BEST. IF YOU LOSE ALL YOUR\n");
849 printf("DATA, YOU HAVE ONLY YOURSELF TO BLAME IF YOU ANSWER 'Y' BELOW!\n\n");
srs56945d58fe02010-01-03 20:57:08 -0500850 printf("Do you want to proceed, possibly destroying your data? ");
851 answer = GetYN();
852 if (answer == 'Y') {
853 printf("OK; writing new GUID partition table (GPT).\n");
srs5694e7b4ff92009-08-18 13:16:10 -0400854 } else {
855 allOK = 0;
856 } // if/else
857 } // if
858
859 // Do it!
860 if (allOK) {
srs5694e4ac11e2009-08-31 10:13:04 -0400861 fd = OpenForWrite(device);
srs5694e7b4ff92009-08-18 13:16:10 -0400862 if (fd != -1) {
863 // First, write the protective MBR...
srs5694e4ac11e2009-08-31 10:13:04 -0400864 protectiveMBR.WriteMBRData(fd);
srs5694e7b4ff92009-08-18 13:16:10 -0400865
866 // Now write the main GPT header...
srs56941e093722010-01-05 00:14:19 -0500867 if (allOK) {
868 if (lseek64(fd, blockSize, SEEK_SET) != (off_t) -1) {
869 if (myWrite(fd, (char*) &mainHeader, 512) == -1)
870 allOK = 0;
871 } else allOK = 0; // if (lseek64()...)
872 } // if (allOK)
srs5694e7b4ff92009-08-18 13:16:10 -0400873
874 // Now write the main partition tables...
srs5694e4ac11e2009-08-31 10:13:04 -0400875 if (allOK) {
srs56941e093722010-01-05 00:14:19 -0500876 offset = mainHeader.partitionEntriesLBA * blockSize;
877 if (lseek64(fd, offset, SEEK_SET) != (off_t) - 1) {
878 if (myWrite(fd, (char*) partitions, GPT_SIZE * numParts) == -1)
srs5694e7b4ff92009-08-18 13:16:10 -0400879 allOK = 0;
srs56941e093722010-01-05 00:14:19 -0500880 } else allOK = 0; // if (lseek64()...)
881 } // if (allOK)
srs5694e7b4ff92009-08-18 13:16:10 -0400882
883 // Now seek to near the end to write the secondary GPT....
srs5694e4ac11e2009-08-31 10:13:04 -0400884 if (allOK) {
srs5694e7b4ff92009-08-18 13:16:10 -0400885 offset = (off_t) secondTable * (off_t) (blockSize);
886 if (lseek64(fd, offset, SEEK_SET) == (off_t) - 1) {
887 allOK = 0;
888 printf("Unable to seek to end of disk!\n");
889 } // if
890 } // if
891
892 // Now write the secondary partition tables....
srs56941e093722010-01-05 00:14:19 -0500893 if (allOK) {
894 if (myWrite(fd, (char*) partitions, GPT_SIZE * numParts) == -1)
srs5694e7b4ff92009-08-18 13:16:10 -0400895 allOK = 0;
srs56941e093722010-01-05 00:14:19 -0500896 } // if (allOK)
srs5694e7b4ff92009-08-18 13:16:10 -0400897
898 // Now write the secondary GPT header...
srs56941e093722010-01-05 00:14:19 -0500899 if (allOK) {
900 offset = mainHeader.backupLBA * blockSize;
901 if (lseek64(fd, offset, SEEK_SET) != (off_t) - 1) {
902 if (myWrite(fd, (char*) &secondHeader, 512) == -1)
903 allOK = 0;
904 } else allOK = 0; // if (lseek64()...)
905 } // if (allOK)
srs5694e7b4ff92009-08-18 13:16:10 -0400906
907 // re-read the partition table
908 if (allOK) {
srs5694e35eb1b2009-09-14 00:29:34 -0400909 DiskSync(fd);
srs5694e7b4ff92009-08-18 13:16:10 -0400910 } // if
911
912 if (allOK) { // writes completed OK
srs5694e4ac11e2009-08-31 10:13:04 -0400913 printf("The operation has completed successfully.\n");
srs5694e7b4ff92009-08-18 13:16:10 -0400914 } else {
srs5694e4ac11e2009-08-31 10:13:04 -0400915 printf("Warning! An error was reported when writing the partition table! This error\n");
916 printf("MIGHT be harmless, but you may have trashed the disk! Use parted and, if\n");
917 printf("necessary, restore your original partition table.\n");
srs5694e7b4ff92009-08-18 13:16:10 -0400918 } // if/else
srs5694e4ac11e2009-08-31 10:13:04 -0400919 close(fd);
srs5694e7b4ff92009-08-18 13:16:10 -0400920 } else {
srs5694e4ac11e2009-08-31 10:13:04 -0400921 fprintf(stderr, "Unable to open device %s for writing! Errno is %d! Aborting write!\n",
922 device, errno);
923 allOK = 0;
srs5694e7b4ff92009-08-18 13:16:10 -0400924 } // if/else
925 } else {
926 printf("Aborting write of new partition table.\n");
927 } // if
928
srs56942a9f5da2009-08-26 00:48:01 -0400929 if (IsLittleEndian() == 0) {
930 // Reverse (normalize) header bytes first, since ReversePartitionBytes()
931 // requires non-reversed data in mainHeader...
932 ReverseHeaderBytes(&mainHeader);
933 ReverseHeaderBytes(&secondHeader);
934 ReversePartitionBytes();
935 } // if
936
srs5694e7b4ff92009-08-18 13:16:10 -0400937 return (allOK);
938} // GPTData::SaveGPTData()
939
940// Save GPT data to a backup file. This function does much less error
941// checking than SaveGPTData(). It can therefore preserve many types of
942// corruption for later analysis; however, it preserves only the MBR,
943// the main GPT header, the backup GPT header, and the main partition
944// table; it discards the backup partition table, since it should be
945// identical to the main partition table on healthy disks.
946int GPTData::SaveGPTBackup(char* filename) {
srs56942a9f5da2009-08-26 00:48:01 -0400947 int fd, allOK = 1;
948 uint32_t numParts;
srs5694e7b4ff92009-08-18 13:16:10 -0400949
950 if ((fd = open(filename, O_WRONLY | O_CREAT, S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH)) != -1) {
srs56942a9f5da2009-08-26 00:48:01 -0400951 // Reverse the byte order, if necessary....
952 numParts = mainHeader.numParts;
953 if (IsLittleEndian() == 0) {
954 ReversePartitionBytes();
955 ReverseHeaderBytes(&mainHeader);
956 ReverseHeaderBytes(&secondHeader);
957 } // if
958
srs5694978041c2009-09-21 20:51:47 -0400959 // Recomputing the CRCs is likely to alter them, which could be bad
960 // if the intent is to save a potentially bad GPT for later analysis;
961 // but if we don't do this, we get bogus errors when we load the
962 // backup. I'm favoring misses over false alarms....
963 RecomputeCRCs();
964
srs56942a9f5da2009-08-26 00:48:01 -0400965 // Now write the protective MBR...
srs5694e7b4ff92009-08-18 13:16:10 -0400966 protectiveMBR.WriteMBRData(fd);
967
968 // Now write the main GPT header...
969 if (allOK)
srs56941e093722010-01-05 00:14:19 -0500970 if (myWrite(fd, (char*) &mainHeader, 512) == -1)
srs5694e7b4ff92009-08-18 13:16:10 -0400971 allOK = 0;
972
973 // Now write the secondary GPT header...
974 if (allOK)
srs56941e093722010-01-05 00:14:19 -0500975 if (myWrite(fd, (char*) &secondHeader, 512) == -1)
srs5694e4ac11e2009-08-31 10:13:04 -0400976 allOK = 0;
srs5694e7b4ff92009-08-18 13:16:10 -0400977
978 // Now write the main partition tables...
979 if (allOK) {
srs56941e093722010-01-05 00:14:19 -0500980 if (myWrite(fd, (char*) partitions, GPT_SIZE * numParts) == -1)
srs5694e7b4ff92009-08-18 13:16:10 -0400981 allOK = 0;
982 } // if
983
984 if (allOK) { // writes completed OK
985 printf("The operation has completed successfully.\n");
986 } else {
987 printf("Warning! An error was reported when writing the backup file.\n");
srs56948bb78762009-11-24 15:43:49 -0500988 printf("It may not be usable!\n");
srs5694e7b4ff92009-08-18 13:16:10 -0400989 } // if/else
990 close(fd);
srs56942a9f5da2009-08-26 00:48:01 -0400991
992 // Now reverse the byte-order reversal, if necessary....
993 if (IsLittleEndian() == 0) {
994 ReverseHeaderBytes(&mainHeader);
995 ReverseHeaderBytes(&secondHeader);
996 ReversePartitionBytes();
997 } // if
srs5694e7b4ff92009-08-18 13:16:10 -0400998 } else {
999 fprintf(stderr, "Unable to open file %s for writing! Aborting!\n", filename);
1000 allOK = 0;
1001 } // if/else
1002 return allOK;
1003} // GPTData::SaveGPTBackup()
1004
1005// Load GPT data from a backup file created by SaveGPTBackup(). This function
1006// does minimal error checking. It returns 1 if it completed successfully,
1007// 0 if there was a problem. In the latter case, it creates a new empty
1008// set of partitions.
1009int GPTData::LoadGPTBackup(char* filename) {
1010 int fd, allOK = 1, val;
1011 uint32_t numParts, sizeOfEntries, sizeOfParts, newCRC;
srs56942a9f5da2009-08-26 00:48:01 -04001012 int littleEndian = 1;
srs5694e7b4ff92009-08-18 13:16:10 -04001013
1014 if ((fd = open(filename, O_RDONLY)) != -1) {
srs56942a9f5da2009-08-26 00:48:01 -04001015 if (IsLittleEndian() == 0)
1016 littleEndian = 0;
1017
srs5694e7b4ff92009-08-18 13:16:10 -04001018 // Let the MBRData class load the saved MBR...
srs5694221e0872009-08-29 15:00:31 -04001019 protectiveMBR.ReadMBRData(fd, 0); // 0 = don't check block size
srs5694e7b4ff92009-08-18 13:16:10 -04001020
1021 // Load the main GPT header, check its vaility, and set the GPT
1022 // size based on the data
srs56941e093722010-01-05 00:14:19 -05001023 if (myRead(fd, (char*) &mainHeader, 512)) {
srs56945d58fe02010-01-03 20:57:08 -05001024 fprintf(stderr, "Warning! Read error %d; strange behavior now likely!\n", errno);
1025 } // if
srs5694e7b4ff92009-08-18 13:16:10 -04001026 mainCrcOk = CheckHeaderCRC(&mainHeader);
1027
srs56942a9f5da2009-08-26 00:48:01 -04001028 // Reverse byte order, if necessary
1029 if (littleEndian == 0) {
1030 ReverseHeaderBytes(&mainHeader);
1031 } // if
1032
srs5694e7b4ff92009-08-18 13:16:10 -04001033 // Load the backup GPT header in much the same way as the main
1034 // GPT header....
srs56941e093722010-01-05 00:14:19 -05001035 if (myRead(fd, (char*) &secondHeader, 512) != 512) {
srs56945d58fe02010-01-03 20:57:08 -05001036 fprintf(stderr, "Warning! Read error %d; strange behavior now likely!\n", errno);
1037 } // if
srs5694e7b4ff92009-08-18 13:16:10 -04001038 secondCrcOk = CheckHeaderCRC(&secondHeader);
1039
srs56942a9f5da2009-08-26 00:48:01 -04001040 // Reverse byte order, if necessary
1041 if (littleEndian == 0) {
1042 ReverseHeaderBytes(&secondHeader);
1043 } // if
1044
srs5694e7b4ff92009-08-18 13:16:10 -04001045 // Return valid headers code: 0 = both headers bad; 1 = main header
1046 // good, backup bad; 2 = backup header good, main header bad;
1047 // 3 = both headers good. Note these codes refer to valid GPT
1048 // signatures and version numbers; more subtle problems will elude
1049 // this check!
1050 if ((val = CheckHeaderValidity()) > 0) {
1051 if (val == 2) { // only backup header seems to be good
1052 numParts = secondHeader.numParts;
srs5694e4ac11e2009-08-31 10:13:04 -04001053 sizeOfEntries = secondHeader.sizeOfPartitionEntries;
srs5694e7b4ff92009-08-18 13:16:10 -04001054 } else { // main header is OK
1055 numParts = mainHeader.numParts;
1056 sizeOfEntries = mainHeader.sizeOfPartitionEntries;
1057 } // if/else
1058
1059 SetGPTSize(numParts);
1060
1061 // If current disk size doesn't match that of backup....
1062 if (secondHeader.currentLBA != diskSize - UINT64_C(1)) {
1063 printf("Warning! Current disk size doesn't match that of the backup!\n"
srs5694e4ac11e2009-08-31 10:13:04 -04001064 "Adjusting sizes to match, but subsequent problems are possible!\n");
srs5694247657a2009-11-26 18:36:12 -05001065 MoveSecondHeaderToEnd();
srs5694e7b4ff92009-08-18 13:16:10 -04001066 } // if
1067
1068 // Load main partition table, and record whether its CRC
1069 // matches the stored value
1070 sizeOfParts = numParts * sizeOfEntries;
srs56941e093722010-01-05 00:14:19 -05001071 if (myRead(fd, (char*) partitions, sizeOfParts) != sizeOfParts) {
srs56945d58fe02010-01-03 20:57:08 -05001072 fprintf(stderr, "Warning! Read error %d; strange behavior now likely!\n", errno);
1073 } // if
srs5694e7b4ff92009-08-18 13:16:10 -04001074
1075 newCRC = chksum_crc32((unsigned char*) partitions, sizeOfParts);
1076 mainPartsCrcOk = (newCRC == mainHeader.partitionEntriesCRC);
1077 secondPartsCrcOk = (newCRC == secondHeader.partitionEntriesCRC);
srs56942a9f5da2009-08-26 00:48:01 -04001078 // Reverse byte order, if necessary
srs5694e4ac11e2009-08-31 10:13:04 -04001079 if (littleEndian == 0) {
1080 ReversePartitionBytes();
1081 } // if
srs56942a9f5da2009-08-26 00:48:01 -04001082
srs5694e7b4ff92009-08-18 13:16:10 -04001083 } else {
1084 allOK = 0;
1085 } // if/else
1086 } else {
1087 allOK = 0;
1088 fprintf(stderr, "Unable to open file %s for reading! Aborting!\n", filename);
1089 } // if/else
1090
1091 // Something went badly wrong, so blank out partitions
1092 if (allOK == 0) {
1093 ClearGPTData();
1094 protectiveMBR.MakeProtectiveMBR();
1095 } // if
1096 return allOK;
1097} // GPTData::LoadGPTBackup()
1098
srs5694e4ac11e2009-08-31 10:13:04 -04001099// Tell user whether Apple Partition Map (APM) was discovered....
1100void GPTData::ShowAPMState(void) {
1101 if (apmFound)
1102 printf(" APM: present\n");
1103 else
1104 printf(" APM: not present\n");
1105} // GPTData::ShowAPMState()
1106
1107// Tell user about the state of the GPT data....
1108void GPTData::ShowGPTState(void) {
1109 switch (state) {
1110 case gpt_invalid:
1111 printf(" GPT: not present\n");
1112 break;
1113 case gpt_valid:
1114 printf(" GPT: present\n");
1115 break;
1116 case gpt_corrupt:
1117 printf(" GPT: damaged\n");
1118 break;
1119 default:
1120 printf("\a GPT: unknown -- bug!\n");
1121 break;
1122 } // switch
1123} // GPTData::ShowGPTState()
1124
1125// Display the basic GPT data
1126void GPTData::DisplayGPTData(void) {
srs5694e35eb1b2009-09-14 00:29:34 -04001127 int i;
srs5694e4ac11e2009-08-31 10:13:04 -04001128 char sizeInSI[255]; // String to hold size of disk in SI units
1129 char tempStr[255];
1130 uint64_t temp, totalFree;
1131
1132 BytesToSI(diskSize * blockSize, sizeInSI);
srs5694978041c2009-09-21 20:51:47 -04001133 printf("Disk %s: %llu sectors, %s\n", device,
1134 (unsigned long long) diskSize, sizeInSI);
srs56941e093722010-01-05 00:14:19 -05001135 printf("Logical sector size: %d bytes\n", blockSize);
srs5694e4ac11e2009-08-31 10:13:04 -04001136 printf("Disk identifier (GUID): %s\n", GUIDToStr(mainHeader.diskGUID, tempStr));
1137 printf("Partition table holds up to %lu entries\n", (unsigned long) mainHeader.numParts);
srs56945d58fe02010-01-03 20:57:08 -05001138 printf("First usable sector is %llu, last usable sector is %llu\n",
1139 (unsigned long long) mainHeader.firstUsableLBA,
1140 (unsigned long long) mainHeader.lastUsableLBA);
srs5694e4ac11e2009-08-31 10:13:04 -04001141 totalFree = FindFreeBlocks(&i, &temp);
srs56945d58fe02010-01-03 20:57:08 -05001142 printf("Total free space is %llu sectors (%s)\n", (unsigned long long) totalFree,
srs5694e4ac11e2009-08-31 10:13:04 -04001143 BytesToSI(totalFree * (uint64_t) blockSize, sizeInSI));
1144 printf("\nNumber Start (sector) End (sector) Size Code Name\n");
1145 for (i = 0; i < mainHeader.numParts; i++) {
srs5694978041c2009-09-21 20:51:47 -04001146 partitions[i].ShowSummary(i, blockSize);
srs5694e4ac11e2009-08-31 10:13:04 -04001147 } // for
1148} // GPTData::DisplayGPTData()
1149
1150// Get partition number from user and then call ShowPartDetails(partNum)
1151// to show its detailed information
1152void GPTData::ShowDetails(void) {
1153 int partNum;
1154 uint32_t low, high;
1155
1156 if (GetPartRange(&low, &high) > 0) {
1157 partNum = GetPartNum();
1158 ShowPartDetails(partNum);
1159 } else {
1160 printf("No partitions\n");
1161 } // if/else
1162} // GPTData::ShowDetails()
1163
1164// Show detailed information on the specified partition
1165void GPTData::ShowPartDetails(uint32_t partNum) {
1166 if (partitions[partNum].GetFirstLBA() != 0) {
1167 partitions[partNum].ShowDetails(blockSize);
1168 } else {
1169 printf("Partition #%d does not exist.", (int) (partNum + 1));
1170 } // if
1171} // GPTData::ShowPartDetails()
1172
1173/*********************************************************************
1174 * *
1175 * Begin functions that obtain information from the users, and often *
1176 * do something with that information (call other functions) *
1177 * *
1178 *********************************************************************/
1179
1180// Prompts user for partition number and returns the result.
1181uint32_t GPTData::GetPartNum(void) {
1182 uint32_t partNum;
1183 uint32_t low, high;
1184 char prompt[255];
1185
1186 if (GetPartRange(&low, &high) > 0) {
1187 sprintf(prompt, "Partition number (%d-%d): ", low + 1, high + 1);
1188 partNum = GetNumber(low + 1, high + 1, low, prompt);
1189 } else partNum = 1;
1190 return (partNum - 1);
1191} // GPTData::GetPartNum()
1192
1193// What it says: Resize the partition table. (Default is 128 entries.)
1194void GPTData::ResizePartitionTable(void) {
1195 int newSize;
1196 char prompt[255];
1197 uint32_t curLow, curHigh;
1198
1199 printf("Current partition table size is %lu.\n",
1200 (unsigned long) mainHeader.numParts);
1201 GetPartRange(&curLow, &curHigh);
1202 curHigh++; // since GetPartRange() returns numbers starting from 0...
1203 // There's no point in having fewer than four partitions....
1204 if (curHigh < 4)
1205 curHigh = 4;
1206 sprintf(prompt, "Enter new size (%d up, default %d): ", (int) curHigh,
1207 (int) NUM_GPT_ENTRIES);
1208 newSize = GetNumber(4, 65535, 128, prompt);
1209 if (newSize < 128) {
1210 printf("Caution: The partition table size should officially be 16KB or larger,\n"
1211 "which works out to 128 entries. In practice, smaller tables seem to\n"
1212 "work with most OSes, but this practice is risky. I'm proceeding with\n"
1213 "the resize, but you may want to reconsider this action and undo it.\n\n");
1214 } // if
1215 SetGPTSize(newSize);
1216} // GPTData::ResizePartitionTable()
1217
1218// Interactively create a partition
1219void GPTData::CreatePartition(void) {
1220 uint64_t firstBlock, firstInLargest, lastBlock, sector;
1221 char prompt[255];
1222 int partNum, firstFreePart = 0;
1223
1224 // Find first free partition...
1225 while (partitions[firstFreePart].GetFirstLBA() != 0) {
1226 firstFreePart++;
1227 } // while
1228
1229 if (((firstBlock = FindFirstAvailable()) != 0) &&
1230 (firstFreePart < mainHeader.numParts)) {
1231 lastBlock = FindLastAvailable(firstBlock);
1232 firstInLargest = FindFirstInLargest();
1233
1234 // Get partition number....
1235 do {
1236 sprintf(prompt, "Partition number (%d-%d, default %d): ", firstFreePart + 1,
1237 mainHeader.numParts, firstFreePart + 1);
1238 partNum = GetNumber(firstFreePart + 1, mainHeader.numParts,
1239 firstFreePart + 1, prompt) - 1;
1240 if (partitions[partNum].GetFirstLBA() != 0)
1241 printf("partition %d is in use.\n", partNum + 1);
1242 } while (partitions[partNum].GetFirstLBA() != 0);
1243
1244 // Get first block for new partition...
srs56945d58fe02010-01-03 20:57:08 -05001245 sprintf(prompt, "First sector (%llu-%llu, default = %llu) or {+-}size{KMGT}: ",
1246 (unsigned long long) firstBlock, (unsigned long long) lastBlock,
1247 (unsigned long long) firstInLargest);
srs5694e4ac11e2009-08-31 10:13:04 -04001248 do {
1249 sector = GetSectorNum(firstBlock, lastBlock, firstInLargest, prompt);
1250 } while (IsFree(sector) == 0);
srs56941d1448a2009-12-31 21:20:19 -05001251 Align(&sector); // Align sector to correct multiple
srs5694e4ac11e2009-08-31 10:13:04 -04001252 firstBlock = sector;
1253
1254 // Get last block for new partitions...
1255 lastBlock = FindLastInFree(firstBlock);
srs56945d58fe02010-01-03 20:57:08 -05001256 sprintf(prompt, "Last sector (%llu-%llu, default = %llu) or {+-}size{KMGT}: ",
1257 (unsigned long long) firstBlock, (unsigned long long) lastBlock,
1258 (unsigned long long) lastBlock);
srs5694e4ac11e2009-08-31 10:13:04 -04001259 do {
1260 sector = GetSectorNum(firstBlock, lastBlock, lastBlock, prompt);
1261 } while (IsFree(sector) == 0);
1262 lastBlock = sector;
1263
srs5694ba00fed2010-01-12 18:18:36 -05001264 firstFreePart = CreatePartition(partNum, firstBlock, lastBlock);
srs5694e4ac11e2009-08-31 10:13:04 -04001265 partitions[partNum].ChangeType();
1266 partitions[partNum].SetName((unsigned char*) partitions[partNum].GetNameType(prompt));
srs5694ba00fed2010-01-12 18:18:36 -05001267 } else {
1268 printf("No free sectors available\n");
1269 } // if/else
srs5694e4ac11e2009-08-31 10:13:04 -04001270} // GPTData::CreatePartition()
1271
1272// Interactively delete a partition (duh!)
1273void GPTData::DeletePartition(void) {
1274 int partNum;
1275 uint32_t low, high;
srs5694e4ac11e2009-08-31 10:13:04 -04001276 char prompt[255];
1277
1278 if (GetPartRange(&low, &high) > 0) {
1279 sprintf(prompt, "Partition number (%d-%d): ", low + 1, high + 1);
1280 partNum = GetNumber(low + 1, high + 1, low, prompt);
srs5694ba00fed2010-01-12 18:18:36 -05001281 DeletePartition(partNum - 1);
srs5694e4ac11e2009-08-31 10:13:04 -04001282 } else {
1283 printf("No partitions\n");
1284 } // if/else
1285} // GPTData::DeletePartition()
1286
1287// Prompt user for a partition number, then change its type code
1288// using ChangeGPTType(struct GPTPartition*) function.
1289void GPTData::ChangePartType(void) {
1290 int partNum;
1291 uint32_t low, high;
1292
1293 if (GetPartRange(&low, &high) > 0) {
1294 partNum = GetPartNum();
1295 partitions[partNum].ChangeType();
1296 } else {
1297 printf("No partitions\n");
1298 } // if/else
1299} // GPTData::ChangePartType()
1300
1301// Partition attributes seem to be rarely used, but I want a way to
1302// adjust them for completeness....
1303void GPTData::SetAttributes(uint32_t partNum) {
1304 Attributes theAttr;
1305
1306 theAttr.SetAttributes(partitions[partNum].GetAttributes());
1307 theAttr.DisplayAttributes();
1308 theAttr.ChangeAttributes();
1309 partitions[partNum].SetAttributes(theAttr.GetAttributes());
1310} // GPTData::SetAttributes()
1311
srs5694c0ca8f82009-08-20 21:35:25 -04001312// This function destroys the on-disk GPT structures. Returns 1 if the
1313// user confirms destruction, 0 if the user aborts.
srs5694978041c2009-09-21 20:51:47 -04001314// If prompt == 0, don't ask user about proceeding and do NOT wipe out
1315// MBR. (Set prompt == 0 when doing a GPT-to-MBR conversion.)
srs5694ba00fed2010-01-12 18:18:36 -05001316// If prompt == -1, don't ask user about proceeding and DO wipe out
1317// MBR.
srs5694978041c2009-09-21 20:51:47 -04001318int GPTData::DestroyGPT(int prompt) {
srs56941e093722010-01-05 00:14:19 -05001319 int fd, i, sum, tableSize;
srs5694978041c2009-09-21 20:51:47 -04001320 char blankSector[512], goOn = 'Y', blank = 'N';
srs56941e093722010-01-05 00:14:19 -05001321 char* emptyTable;
srs5694c0ca8f82009-08-20 21:35:25 -04001322
1323 for (i = 0; i < 512; i++) {
1324 blankSector[i] = '\0';
1325 } // for
1326
srs5694ba00fed2010-01-12 18:18:36 -05001327 if (((apmFound) || (bsdFound)) && (prompt > 0)) {
srs5694e35eb1b2009-09-14 00:29:34 -04001328 printf("WARNING: APM or BSD disklabel structures detected! This operation could\n"
1329 "damage any APM or BSD partitions on this disk!\n");
1330 } // if APM or BSD
srs5694ba00fed2010-01-12 18:18:36 -05001331 if (prompt > 0) {
srs5694978041c2009-09-21 20:51:47 -04001332 printf("\a\aAbout to wipe out GPT on %s. Proceed? ", device);
1333 goOn = GetYN();
1334 } // if
srs5694c0ca8f82009-08-20 21:35:25 -04001335 if (goOn == 'Y') {
1336 fd = open(device, O_WRONLY);
1337#ifdef __APPLE__
1338 // MacOS X requires a shared lock under some circumstances....
1339 if (fd < 0) {
1340 fd = open(device, O_WRONLY|O_SHLOCK);
1341 } // if
1342#endif
1343 if (fd != -1) {
srs56941e093722010-01-05 00:14:19 -05001344 lseek64(fd, mainHeader.currentLBA * blockSize, SEEK_SET); // seek to GPT header
1345 if (myWrite(fd, blankSector, 512) != 512) { // blank it out
srs56945d58fe02010-01-03 20:57:08 -05001346 fprintf(stderr, "Warning! GPT main header not overwritten! Error is %d\n", errno);
1347 } // if
srs56941e093722010-01-05 00:14:19 -05001348 lseek64(fd, mainHeader.partitionEntriesLBA * blockSize, SEEK_SET); // seek to partition table
1349 tableSize = mainHeader.numParts * mainHeader.sizeOfPartitionEntries;
1350 emptyTable = (char*) malloc(tableSize);
1351 for (i = 0; i < tableSize; i++)
1352 emptyTable[i] = (char) 0;
1353 sum = myWrite(fd, emptyTable, tableSize);
1354 if (sum != tableSize)
srs56945d58fe02010-01-03 20:57:08 -05001355 fprintf(stderr, "Warning! GPT main partition table not overwritten! Error is %d\n", errno);
srs56941e093722010-01-05 00:14:19 -05001356 lseek64(fd, secondHeader.partitionEntriesLBA * blockSize, SEEK_SET); // seek to partition table
1357 sum = myWrite(fd, emptyTable, tableSize);
1358 if (sum != tableSize)
srs56945d58fe02010-01-03 20:57:08 -05001359 fprintf(stderr, "Warning! GPT backup partition table not overwritten! Error is %d\n", errno);
srs56941e093722010-01-05 00:14:19 -05001360 lseek64(fd, secondHeader.currentLBA * blockSize, SEEK_SET); // seek to GPT header
1361 if (myWrite(fd, blankSector, 512) != 512) { // blank it out
srs56945d58fe02010-01-03 20:57:08 -05001362 fprintf(stderr, "Warning! GPT backup header not overwritten! Error is %d\n", errno);
1363 } // if
srs5694ba00fed2010-01-12 18:18:36 -05001364 if (prompt > 0) {
srs5694978041c2009-09-21 20:51:47 -04001365 printf("Blank out MBR? ");
1366 blank = GetYN();
srs5694ba00fed2010-01-12 18:18:36 -05001367 } // if
srs5694978041c2009-09-21 20:51:47 -04001368 // Note on below: Touch the MBR only if the user wants it completely
1369 // blanked out. Version 0.4.2 deleted the 0xEE partition and re-wrote
1370 // the MBR, but this could wipe out a valid MBR that the program
1371 // had subsequently discarded (say, if it conflicted with older GPT
1372 // structures).
srs5694ba00fed2010-01-12 18:18:36 -05001373 if ((blank == 'Y') || (prompt < 0)) {
srs5694c0ca8f82009-08-20 21:35:25 -04001374 lseek64(fd, 0, SEEK_SET);
srs56941e093722010-01-05 00:14:19 -05001375 if (myWrite(fd, blankSector, 512) != 512) { // blank it out
srs56945d58fe02010-01-03 20:57:08 -05001376 fprintf(stderr, "Warning! MBR not overwritten! Error is %d!\n", errno);
1377 } // if
srs5694978041c2009-09-21 20:51:47 -04001378 } else {
1379 printf("MBR is unchanged. You may need to delete an EFI GPT (0xEE) partition\n"
1380 "with fdisk or another tool.\n");
srs5694e35eb1b2009-09-14 00:29:34 -04001381 } // if/else
1382 DiskSync(fd);
srs5694c0ca8f82009-08-20 21:35:25 -04001383 close(fd);
srs5694e4ac11e2009-08-31 10:13:04 -04001384 printf("GPT data structures destroyed! You may now partition the disk using fdisk or\n"
1385 "other utilities. Program will now terminate.\n");
srs5694c0ca8f82009-08-20 21:35:25 -04001386 } else {
srs56941d1448a2009-12-31 21:20:19 -05001387 printf("Problem opening %s for writing! Program will now terminate.\n", device);
srs5694c0ca8f82009-08-20 21:35:25 -04001388 } // if/else (fd != -1)
1389 } // if (goOn == 'Y')
1390 return (goOn == 'Y');
1391} // GPTData::DestroyGPT()
1392
srs5694e4ac11e2009-08-31 10:13:04 -04001393/**************************************************************************
1394 * *
1395 * Partition table transformation functions (MBR or BSD disklabel to GPT) *
1396 * (some of these functions may require user interaction) *
1397 * *
1398 **************************************************************************/
1399
1400// Examines the MBR & GPT data, and perhaps asks the user questions, to
1401// determine which set of data to use: the MBR (use_mbr), the GPT (use_gpt),
1402// or create a new set of partitions (use_new)
1403WhichToUse GPTData::UseWhichPartitions(void) {
1404 WhichToUse which = use_new;
1405 MBRValidity mbrState;
1406 int answer;
1407
1408 mbrState = protectiveMBR.GetValidity();
1409
1410 if ((state == gpt_invalid) && ((mbrState == mbr) || (mbrState == hybrid))) {
srs56945d58fe02010-01-03 20:57:08 -05001411 printf("\n***************************************************************\n"
1412 "Found invalid GPT and valid MBR; converting MBR to GPT format.\n");
1413 if (!justLooking) {
1414 printf("\aTHIS OPERATON IS POTENTIALLY DESTRUCTIVE! Exit by typing 'q' if\n"
1415 "you don't want to convert your MBR partitions to GPT format!\n");
1416 } // if
1417 printf("***************************************************************\n\n");
srs5694e4ac11e2009-08-31 10:13:04 -04001418 which = use_mbr;
1419 } // if
1420
1421 if ((state == gpt_invalid) && bsdFound) {
srs56945d58fe02010-01-03 20:57:08 -05001422 printf("\n**********************************************************************\n"
srs5694e4ac11e2009-08-31 10:13:04 -04001423 "Found invalid GPT and valid BSD disklabel; converting BSD disklabel\n"
srs56945d58fe02010-01-03 20:57:08 -05001424 "to GPT format.");
1425 if (!justLooking) {
1426 printf("\a THIS OPERATON IS POTENTIALLY DESTRUCTIVE! Your first\n"
srs5694e4ac11e2009-08-31 10:13:04 -04001427 "BSD partition will likely be unusable. Exit by typing 'q' if you don't\n"
srs56945d58fe02010-01-03 20:57:08 -05001428 "want to convert your BSD partitions to GPT format!");
1429 } // if
1430 printf("\n**********************************************************************\n\n");
srs5694e4ac11e2009-08-31 10:13:04 -04001431 which = use_bsd;
1432 } // if
1433
1434 if ((state == gpt_valid) && (mbrState == gpt)) {
srs5694e4ac11e2009-08-31 10:13:04 -04001435 which = use_gpt;
srs56943c0af382010-01-15 19:19:18 -05001436 if (!beQuiet)
1437 printf("Found valid GPT with protective MBR; using GPT.\n");
srs5694e4ac11e2009-08-31 10:13:04 -04001438 } // if
1439 if ((state == gpt_valid) && (mbrState == hybrid)) {
srs5694e4ac11e2009-08-31 10:13:04 -04001440 which = use_gpt;
srs56943c0af382010-01-15 19:19:18 -05001441 if (!beQuiet)
1442 printf("Found valid GPT with hybrid MBR; using GPT.\n");
srs5694e4ac11e2009-08-31 10:13:04 -04001443 } // if
1444 if ((state == gpt_valid) && (mbrState == invalid)) {
1445 printf("\aFound valid GPT with corrupt MBR; using GPT and will create new\nprotective MBR on save.\n");
1446 which = use_gpt;
1447 protectiveMBR.MakeProtectiveMBR();
1448 } // if
1449 if ((state == gpt_valid) && (mbrState == mbr)) {
srs56943c0af382010-01-15 19:19:18 -05001450 if (!beQuiet) {
1451 printf("Found valid MBR and GPT. Which do you want to use?\n");
1452 answer = GetNumber(1, 3, 2, (char*) " 1 - MBR\n 2 - GPT\n 3 - Create blank GPT\n\nYour answer: ");
1453 if (answer == 1) {
1454 which = use_mbr;
1455 } else if (answer == 2) {
1456 which = use_gpt;
1457 protectiveMBR.MakeProtectiveMBR();
1458 printf("Using GPT and creating fresh protective MBR.\n");
1459 } else which = use_new;
1460 } else which = use_abort;
srs5694e4ac11e2009-08-31 10:13:04 -04001461 } // if
1462
1463 // Nasty decisions here -- GPT is present, but corrupt (bad CRCs or other
1464 // problems)
1465 if (state == gpt_corrupt) {
srs56943c0af382010-01-15 19:19:18 -05001466 if (beQuiet) {
1467 which = use_abort;
1468 } else {
1469 if ((mbrState == mbr) || (mbrState == hybrid)) {
1470 printf("Found valid MBR and corrupt GPT. Which do you want to use? (Using the\n"
1471 "GPT MAY permit recovery of GPT data.)\n");
1472 answer = GetNumber(1, 3, 2, (char*) " 1 - MBR\n 2 - GPT\n 3 - Create blank GPT\n\nYour answer: ");
1473 if (answer == 1) {
1474 which = use_mbr;
1475 // protectiveMBR.MakeProtectiveMBR();
1476 } else if (answer == 2) {
1477 which = use_gpt;
1478 } else which = use_new;
1479 } else if (mbrState == invalid) {
1480 printf("Found invalid MBR and corrupt GPT. What do you want to do? (Using the\n"
1481 "GPT MAY permit recovery of GPT data.)\n");
1482 answer = GetNumber(1, 2, 1, (char*) " 1 - GPT\n 2 - Create blank GPT\n\nYour answer: ");
1483 if (answer == 1) {
1484 which = use_gpt;
1485 } else which = use_new;
1486 } else { // corrupt GPT, MBR indicates it's a GPT disk....
1487 printf("\a\a****************************************************************************\n"
1488 "Caution: Found protective or hybrid MBR and corrupt GPT. Using GPT, but disk\n"
1489 "verification and recovery are STRONGLY recommended.\n"
1490 "****************************************************************************\n");
srs5694e4ac11e2009-08-31 10:13:04 -04001491 which = use_gpt;
srs56943c0af382010-01-15 19:19:18 -05001492 } // if/else/else
1493 } // else (beQuiet)
srs5694e4ac11e2009-08-31 10:13:04 -04001494 } // if (corrupt GPT)
1495
1496 if (which == use_new)
1497 printf("Creating new GPT entries.\n");
1498
1499 return which;
1500} // UseWhichPartitions()
1501
1502// Convert MBR partition table into GPT form
1503int GPTData::XFormPartitions(void) {
1504 int i, numToConvert;
1505 uint8_t origType;
1506 struct newGUID;
srs5694e4ac11e2009-08-31 10:13:04 -04001507
1508 // Clear out old data & prepare basics....
1509 ClearGPTData();
1510
1511 // Convert the smaller of the # of GPT or MBR partitions
srs5694978041c2009-09-21 20:51:47 -04001512 if (mainHeader.numParts > (MAX_MBR_PARTS))
1513 numToConvert = MAX_MBR_PARTS;
srs5694e4ac11e2009-08-31 10:13:04 -04001514 else
1515 numToConvert = mainHeader.numParts;
1516
1517 for (i = 0; i < numToConvert; i++) {
1518 origType = protectiveMBR.GetType(i);
1519 // don't waste CPU time trying to convert extended, hybrid protective, or
1520 // null (non-existent) partitions
srs5694e35eb1b2009-09-14 00:29:34 -04001521 if ((origType != 0x05) && (origType != 0x0f) && (origType != 0x85) &&
srs5694e4ac11e2009-08-31 10:13:04 -04001522 (origType != 0x00) && (origType != 0xEE))
1523 partitions[i] = protectiveMBR.AsGPT(i);
1524 } // for
1525
1526 // Convert MBR into protective MBR
1527 protectiveMBR.MakeProtectiveMBR();
1528
1529 // Record that all original CRCs were OK so as not to raise flags
1530 // when doing a disk verification
1531 mainCrcOk = secondCrcOk = mainPartsCrcOk = secondPartsCrcOk = 1;
1532
1533 return (1);
1534} // GPTData::XFormPartitions()
1535
1536// Transforms BSD disklabel on the specified partition (numbered from 0).
1537// If an invalid partition number is given, the program prompts for one.
1538// Returns the number of new partitions created.
1539int GPTData::XFormDisklabel(int i) {
1540 uint32_t low, high, partNum, startPart;
1541 uint16_t hexCode;
1542 int goOn = 1, numDone = 0;
1543 BSDData disklabel;
1544
1545 if (GetPartRange(&low, &high) != 0) {
1546 if ((i < low) || (i > high))
1547 partNum = GetPartNum();
1548 else
1549 partNum = (uint32_t) i;
1550
1551 // Find the partition after the last used one
1552 startPart = high + 1;
1553
1554 // Now see if the specified partition has a BSD type code....
1555 hexCode = partitions[partNum].GetHexType();
1556 if ((hexCode != 0xa500) && (hexCode != 0xa900)) {
1557 printf("Specified partition doesn't have a disklabel partition type "
1558 "code.\nContinue anyway?");
1559 goOn = (GetYN() == 'Y');
1560 } // if
1561
1562 // If all is OK, read the disklabel and convert it.
1563 if (goOn) {
1564 goOn = disklabel.ReadBSDData(device, partitions[partNum].GetFirstLBA(),
1565 partitions[partNum].GetLastLBA());
1566 if ((goOn) && (disklabel.IsDisklabel())) {
1567 numDone = XFormDisklabel(&disklabel, startPart);
1568 if (numDone == 1)
1569 printf("Converted %d BSD partition.\n", numDone);
1570 else
1571 printf("Converted %d BSD partitions.\n", numDone);
1572 } else {
1573 printf("Unable to convert partitions! Unrecognized BSD disklabel.\n");
1574 } // if/else
1575 } // if
1576 if (numDone > 0) { // converted partitions; delete carrier
1577 partitions[partNum].BlankPartition();
1578 } // if
1579 } else {
1580 printf("No partitions\n");
1581 } // if/else
1582 return numDone;
1583} // GPTData::XFormDisklable(int i)
1584
1585// Transform the partitions on an already-loaded BSD disklabel...
1586int GPTData::XFormDisklabel(BSDData* disklabel, int startPart) {
1587 int i, numDone = 0;
1588
1589 if ((disklabel->IsDisklabel()) && (startPart >= 0) &&
1590 (startPart < mainHeader.numParts)) {
1591 for (i = 0; i < disklabel->GetNumParts(); i++) {
1592 partitions[i + startPart] = disklabel->AsGPT(i);
1593 if (partitions[i + startPart].GetFirstLBA() != UINT64_C(0))
1594 numDone++;
1595 } // for
1596 } // if
1597
1598 // Record that all original CRCs were OK so as not to raise flags
1599 // when doing a disk verification
1600 mainCrcOk = secondCrcOk = mainPartsCrcOk = secondPartsCrcOk = 1;
1601
1602 return numDone;
1603} // GPTData::XFormDisklabel(BSDData* disklabel)
1604
srs5694978041c2009-09-21 20:51:47 -04001605// Add one GPT partition to MBR. Used by XFormToMBR() and MakeHybrid()
1606// functions. Returns 1 if operation was successful.
1607int GPTData::OnePartToMBR(uint32_t gptPart, int mbrPart) {
1608 int allOK = 1, typeCode, bootable;
1609 uint64_t length;
1610 char line[255];
srs56945d58fe02010-01-03 20:57:08 -05001611 char* junk;
srs5694978041c2009-09-21 20:51:47 -04001612
1613 if ((mbrPart < 0) || (mbrPart > 3)) {
1614 printf("MBR partition %d is out of range; omitting it.\n", mbrPart + 1);
1615 allOK = 0;
1616 } // if
1617 if (gptPart >= mainHeader.numParts) {
1618 printf("GPT partition %d is out of range; omitting it.\n", gptPart + 1);
1619 allOK = 0;
1620 } // if
1621 if (allOK && (partitions[gptPart].GetLastLBA() == UINT64_C(0))) {
1622 printf("GPT partition %d is undefined; omitting it.\n", gptPart + 1);
1623 allOK = 0;
1624 } // if
1625 if (allOK && (partitions[gptPart].GetFirstLBA() <= UINT32_MAX) &&
1626 (partitions[gptPart].GetLengthLBA() <= UINT32_MAX)) {
1627 if (partitions[gptPart].GetLastLBA() > UINT32_MAX) {
1628 printf("Caution: Partition end point past 32-bit pointer boundary;"
1629 " some OSes may\nreact strangely.\n");
1630 } // if partition ends past 32-bit (usually 2TiB) boundary
1631 do {
1632 printf("Enter an MBR hex code (default %02X): ",
1633 typeHelper.GUIDToID(partitions[gptPart].GetType()) / 256);
srs56945d58fe02010-01-03 20:57:08 -05001634 junk = fgets(line, 255, stdin);
srs5694978041c2009-09-21 20:51:47 -04001635 sscanf(line, "%x", &typeCode);
1636 if (line[0] == '\n')
1637 typeCode = partitions[gptPart].GetHexType() / 256;
1638 } while ((typeCode <= 0) || (typeCode > 255));
1639 printf("Set the bootable flag? ");
1640 bootable = (GetYN() == 'Y');
1641 length = partitions[gptPart].GetLengthLBA();
1642 protectiveMBR.MakePart(mbrPart, (uint32_t) partitions[gptPart].GetFirstLBA(),
1643 (uint32_t) length, typeCode, bootable);
1644 } else { // partition out of range
1645 printf("Partition %d begins beyond the 32-bit pointer limit of MBR "
1646 "partitions, or is\n too big; omitting it.\n", gptPart + 1);
1647 allOK = 0;
1648 } // if/else
1649 return allOK;
1650} // GPTData::OnePartToMBR()
1651
1652// Convert the GPT to MBR form. This function is necessarily limited; it
1653// handles at most four partitions and creates layouts that ignore CHS
1654// geometries. Returns the number of converted partitions; if this value
1655// is over 0, the calling function should call DestroyGPT() to destroy
1656// the GPT data, and then exit.
1657int GPTData::XFormToMBR(void) {
1658 char line[255];
srs56945d58fe02010-01-03 20:57:08 -05001659 char* junk;
srs5694978041c2009-09-21 20:51:47 -04001660 int i, j, numParts, numConverted = 0;
1661 uint32_t partNums[4];
1662
1663 // Get the numbers of up to four partitions to add to the
1664 // hybrid MBR....
1665 numParts = CountParts();
1666 printf("Counted %d partitions.\n", numParts);
1667
1668 // Prepare the MBR for conversion (empty it of existing partitions).
1669 protectiveMBR.EmptyMBR(0);
1670 protectiveMBR.SetDiskSize(diskSize);
1671
1672 if (numParts > 4) { // Over four partitions; engage in triage
1673 printf("Type from one to four GPT partition numbers, separated by spaces, to be\n"
1674 "used in the MBR, in sequence: ");
srs56945d58fe02010-01-03 20:57:08 -05001675 junk = fgets(line, 255, stdin);
srs5694978041c2009-09-21 20:51:47 -04001676 numParts = sscanf(line, "%d %d %d %d", &partNums[0], &partNums[1],
1677 &partNums[2], &partNums[3]);
1678 } else { // Four or fewer partitions; convert them all
1679 i = j = 0;
1680 while ((j < numParts) && (i < mainHeader.numParts)) {
1681 if (partitions[i].GetFirstLBA() > 0) { // if GPT part. is defined
1682 partNums[j++] = ++i; // flag it for conversion
1683 } else i++;
1684 } // while
1685 } // if/else
1686
1687 for (i = 0; i < numParts; i++) {
1688 j = partNums[i] - 1;
1689 printf("\nCreating entry for partition #%d\n", j + 1);
1690 numConverted += OnePartToMBR(j, i);
1691 } // for
srs5694ba00fed2010-01-12 18:18:36 -05001692 printf("MBR writing returned %d\n", protectiveMBR.WriteMBRData(device));
srs5694978041c2009-09-21 20:51:47 -04001693 return numConverted;
1694} // GPTData::XFormToMBR()
1695
srs5694e4ac11e2009-08-31 10:13:04 -04001696// Create a hybrid MBR -- an ugly, funky thing that helps GPT work with
1697// OSes that don't understand GPT.
1698void GPTData::MakeHybrid(void) {
1699 uint32_t partNums[3];
1700 char line[255];
srs56945d58fe02010-01-03 20:57:08 -05001701 char* junk;
srs5694978041c2009-09-21 20:51:47 -04001702 int numParts, numConverted = 0, i, j, typeCode, mbrNum;
srs5694e4ac11e2009-08-31 10:13:04 -04001703 char fillItUp = 'M'; // fill extra partition entries? (Yes/No/Maybe)
srs5694978041c2009-09-21 20:51:47 -04001704 char eeFirst = 'Y'; // Whether EFI GPT (0xEE) partition comes first in table
srs5694e4ac11e2009-08-31 10:13:04 -04001705
1706 printf("\nWARNING! Hybrid MBRs are flaky and potentially dangerous! If you decide not\n"
1707 "to use one, just hit the Enter key at the below prompt and your MBR\n"
1708 "partition table will be untouched.\n\n\a");
1709
1710 // Now get the numbers of up to three partitions to add to the
1711 // hybrid MBR....
1712 printf("Type from one to three GPT partition numbers, separated by spaces, to be\n"
1713 "added to the hybrid MBR, in sequence: ");
srs56945d58fe02010-01-03 20:57:08 -05001714 junk = fgets(line, 255, stdin);
srs5694e4ac11e2009-08-31 10:13:04 -04001715 numParts = sscanf(line, "%d %d %d", &partNums[0], &partNums[1], &partNums[2]);
1716
1717 if (numParts > 0) {
1718 // Blank out the protective MBR, but leave the boot loader code
1719 // alone....
1720 protectiveMBR.EmptyMBR(0);
1721 protectiveMBR.SetDiskSize(diskSize);
1722 printf("Place EFI GPT (0xEE) partition first in MBR (good for GRUB)? ");
1723 eeFirst = GetYN();
1724 } // if
1725
1726 for (i = 0; i < numParts; i++) {
1727 j = partNums[i] - 1;
1728 printf("\nCreating entry for partition #%d\n", j + 1);
srs5694978041c2009-09-21 20:51:47 -04001729 if (eeFirst == 'Y')
1730 mbrNum = i + 1;
1731 else
1732 mbrNum = i;
1733 numConverted += OnePartToMBR(j, mbrNum);
srs5694e4ac11e2009-08-31 10:13:04 -04001734 } // for
1735
srs5694978041c2009-09-21 20:51:47 -04001736 if ((numParts > 0) && (numConverted > 0)) { // User opted to create a hybrid MBR....
srs5694e4ac11e2009-08-31 10:13:04 -04001737 // Create EFI protective partition that covers the start of the disk.
1738 // If this location (covering the main GPT data structures) is omitted,
1739 // Linux won't find any partitions on the disk. Note that this is
1740 // NUMBERED AFTER the hybrid partitions, contrary to what the
1741 // gptsync utility does. This is because Windows seems to choke on
1742 // disks with a 0xEE partition in the first slot and subsequent
1743 // additional partitions, unless it boots from the disk.
1744 if (eeFirst == 'Y')
1745 mbrNum = 0;
1746 else
1747 mbrNum = numParts;
1748 protectiveMBR.MakePart(mbrNum, 1, protectiveMBR.FindLastInFree(1), 0xEE);
srs5694978041c2009-09-21 20:51:47 -04001749 protectiveMBR.SetHybrid();
srs5694e4ac11e2009-08-31 10:13:04 -04001750
1751 // ... and for good measure, if there are any partition spaces left,
1752 // optionally create another protective EFI partition to cover as much
1753 // space as possible....
1754 for (i = 0; i < 4; i++) {
1755 if (protectiveMBR.GetType(i) == 0x00) { // unused entry....
1756 if (fillItUp == 'M') {
1757 printf("\nUnused partition space(s) found. Use one to protect more partitions? ");
1758 fillItUp = GetYN();
1759 typeCode = 0x00; // use this to flag a need to get type code
1760 } // if
1761 if (fillItUp == 'Y') {
1762 while ((typeCode <= 0) || (typeCode > 255)) {
1763 printf("Enter an MBR hex code (EE is EFI GPT, but may confuse MacOS): ");
1764 // Comment on above: Mac OS treats disks with more than one
1765 // 0xEE MBR partition as MBR disks, not as GPT disks.
srs56945d58fe02010-01-03 20:57:08 -05001766 junk = fgets(line, 255, stdin);
srs5694e4ac11e2009-08-31 10:13:04 -04001767 sscanf(line, "%x", &typeCode);
1768 if (line[0] == '\n')
1769 typeCode = 0;
1770 } // while
1771 protectiveMBR.MakeBiggestPart(i, typeCode); // make a partition
1772 } // if (fillItUp == 'Y')
1773 } // if unused entry
1774 } // for (i = 0; i < 4; i++)
1775 } // if (numParts > 0)
1776} // GPTData::MakeHybrid()
1777
1778/**********************************************************************
1779 * *
1780 * Functions that adjust GPT data structures WITHOUT user interaction *
1781 * (they may display information for the user's benefit, though) *
1782 * *
1783 **********************************************************************/
1784
1785// Resizes GPT to specified number of entries. Creates a new table if
srs5694ba00fed2010-01-12 18:18:36 -05001786// necessary, copies data if it already exists. Returns 1 if all goes
1787// well, 0 if an error is encountered.
srs5694e4ac11e2009-08-31 10:13:04 -04001788int GPTData::SetGPTSize(uint32_t numEntries) {
1789 struct GPTPart* newParts;
1790 struct GPTPart* trash;
1791 uint32_t i, high, copyNum;
1792 int allOK = 1;
1793
1794 // First, adjust numEntries upward, if necessary, to get a number
1795 // that fills the allocated sectors
1796 i = blockSize / GPT_SIZE;
1797 if ((numEntries % i) != 0) {
1798 printf("Adjusting GPT size from %lu ", (unsigned long) numEntries);
1799 numEntries = ((numEntries / i) + 1) * i;
1800 printf("to %lu to fill the sector\n", (unsigned long) numEntries);
1801 } // if
1802
srs5694247657a2009-11-26 18:36:12 -05001803 // Do the work only if the # of partitions is changing. Along with being
1804 // efficient, this prevents mucking the with location of the secondary
1805 // partition table, which causes problems when loading data from a RAID
1806 // array that's been expanded because this function is called when loading
1807 // data.
1808 if ((numEntries != mainHeader.numParts) || (partitions == NULL)) {
1809 newParts = (GPTPart*) calloc(numEntries, sizeof (GPTPart));
1810 if (newParts != NULL) {
1811 if (partitions != NULL) { // existing partitions; copy them over
1812 GetPartRange(&i, &high);
1813 if (numEntries < (high + 1)) { // Highest entry too high for new #
1814 printf("The highest-numbered partition is %lu, which is greater than the requested\n"
1815 "partition table size of %d; cannot resize. Perhaps sorting will help.\n",
1816 (unsigned long) (high + 1), numEntries);
1817 allOK = 0;
1818 } else { // go ahead with copy
1819 if (numEntries < mainHeader.numParts)
1820 copyNum = numEntries;
1821 else
1822 copyNum = mainHeader.numParts;
1823 for (i = 0; i < copyNum; i++) {
1824 newParts[i] = partitions[i];
1825 } // for
1826 trash = partitions;
1827 partitions = newParts;
1828 free(trash);
1829 } // if
1830 } else { // No existing partition table; just create it
srs5694e4ac11e2009-08-31 10:13:04 -04001831 partitions = newParts;
srs5694247657a2009-11-26 18:36:12 -05001832 } // if/else existing partitions
1833 mainHeader.numParts = numEntries;
1834 secondHeader.numParts = numEntries;
1835 mainHeader.firstUsableLBA = ((numEntries * GPT_SIZE) / blockSize) + 2 ;
1836 secondHeader.firstUsableLBA = mainHeader.firstUsableLBA;
1837 MoveSecondHeaderToEnd();
1838 if (diskSize > 0)
1839 CheckGPTSize();
1840 } else { // Bad memory allocation
1841 fprintf(stderr, "Error allocating memory for partition table!\n");
1842 allOK = 0;
1843 } // if/else
srs5694e4ac11e2009-08-31 10:13:04 -04001844 } // if/else
1845 return (allOK);
1846} // GPTData::SetGPTSize()
1847
1848// Blank the partition array
1849void GPTData::BlankPartitions(void) {
1850 uint32_t i;
1851
1852 for (i = 0; i < mainHeader.numParts; i++) {
1853 partitions[i].BlankPartition();
1854 } // for
1855} // GPTData::BlankPartitions()
1856
srs5694ba00fed2010-01-12 18:18:36 -05001857// Delete a partition by number. Returns 1 if successful,
1858// 0 if there was a problem. Returns 1 if partition was in
1859// range, 0 if it was out of range.
1860int GPTData::DeletePartition(uint32_t partNum) {
1861 uint64_t startSector, length;
1862 uint32_t low, high, numParts, retval = 1;;
1863
1864 numParts = GetPartRange(&low, &high);
1865 if ((numParts > 0) && (partNum >= low) && (partNum <= high)) {
1866 // In case there's a protective MBR, look for & delete matching
1867 // MBR partition....
1868 startSector = partitions[partNum].GetFirstLBA();
1869 length = partitions[partNum].GetLengthLBA();
1870 protectiveMBR.DeleteByLocation(startSector, length);
1871
1872 // Now delete the GPT partition
1873 partitions[partNum].BlankPartition();
1874 } else {
1875 fprintf(stderr, "Partition number %d out of range!\n", partNum + 1);
1876 retval = 0;
1877 } // if/else
1878 return retval;
1879} // GPTData::DeletePartition(uint32_t partNum)
1880
1881// Non-interactively create a partition. Note that this function is overloaded
1882// with another of the same name but different parameters; that one prompts
1883// the user for data. This one returns 1 if the operation was successful, 0
1884// if a problem was discovered.
1885int GPTData::CreatePartition(uint32_t partNum, uint64_t startSector, uint64_t endSector) {
1886 int retval = 1; // assume there'll be no problems
1887
1888 if (IsFreePartNum(partNum)) {
1889 Align(&startSector); // Align sector to correct multiple
1890 if (IsFree(startSector) && (startSector <= endSector)) {
1891 if (FindLastInFree(startSector) >= endSector) {
1892 partitions[partNum].SetFirstLBA(startSector);
1893 partitions[partNum].SetLastLBA(endSector);
1894 partitions[partNum].SetType(0x0700);
1895 partitions[partNum].SetUniqueGUID(1);
1896 } else retval = 0; // if free space until endSector
1897 } else retval = 0; // if startSector is free
1898 } else retval = 0; // if legal partition number
1899 return retval;
1900} // GPTData::CreatePartition(partNum, startSector, endSector)
1901
srs5694e4ac11e2009-08-31 10:13:04 -04001902// Sort the GPT entries, eliminating gaps and making for a logical
1903// ordering. Relies on QuickSortGPT() for the bulk of the work
1904void GPTData::SortGPT(void) {
1905 int i, lastPart = 0;
1906 GPTPart temp;
1907
1908 // First, find the last partition with data, so as not to
1909 // spend needless time sorting empty entries....
1910 for (i = 0; i < mainHeader.numParts; i++) {
1911 if (partitions[i].GetFirstLBA() > 0)
1912 lastPart = i;
1913 } // for
1914
1915 // Now swap empties with the last partitions, to simplify the logic
1916 // in the Quicksort function....
1917 i = 0;
1918 while (i < lastPart) {
1919 if (partitions[i].GetFirstLBA() == 0) {
1920 temp = partitions[i];
1921 partitions[i] = partitions[lastPart];
1922 partitions[lastPart] = temp;
1923 lastPart--;
1924 } // if
1925 i++;
1926 } // while
1927
1928 // Now call the recursive quick sort routine to do the real work....
1929 QuickSortGPT(partitions, 0, lastPart);
1930} // GPTData::SortGPT()
1931
1932// Set up data structures for entirely new set of partitions on the
1933// specified device. Returns 1 if OK, 0 if there were problems.
srs5694e35eb1b2009-09-14 00:29:34 -04001934// Note that this function does NOT clear the protectiveMBR data
1935// structure, since it may hold the original MBR partitions if the
1936// program was launched on an MBR disk, and those may need to be
1937// converted to GPT format.
srs5694e4ac11e2009-08-31 10:13:04 -04001938int GPTData::ClearGPTData(void) {
srs5694e35eb1b2009-09-14 00:29:34 -04001939 int goOn = 1, i;
srs5694e4ac11e2009-08-31 10:13:04 -04001940
1941 // Set up the partition table....
1942 free(partitions);
1943 partitions = NULL;
1944 SetGPTSize(NUM_GPT_ENTRIES);
1945
1946 // Now initialize a bunch of stuff that's static....
1947 mainHeader.signature = GPT_SIGNATURE;
1948 mainHeader.revision = 0x00010000;
srs5694978041c2009-09-21 20:51:47 -04001949 mainHeader.headerSize = HEADER_SIZE;
srs5694e4ac11e2009-08-31 10:13:04 -04001950 mainHeader.reserved = 0;
1951 mainHeader.currentLBA = UINT64_C(1);
1952 mainHeader.partitionEntriesLBA = (uint64_t) 2;
1953 mainHeader.sizeOfPartitionEntries = GPT_SIZE;
1954 for (i = 0; i < GPT_RESERVED; i++) {
1955 mainHeader.reserved2[i] = '\0';
1956 } // for
1957
1958 // Now some semi-static items (computed based on end of disk)
1959 mainHeader.backupLBA = diskSize - UINT64_C(1);
1960 mainHeader.lastUsableLBA = diskSize - mainHeader.firstUsableLBA;
1961
1962 // Set a unique GUID for the disk, based on random numbers
1963 // rand() is only 32 bits, so multiply together to fill a 64-bit value
1964 mainHeader.diskGUID.data1 = (uint64_t) rand() * (uint64_t) rand();
1965 mainHeader.diskGUID.data2 = (uint64_t) rand() * (uint64_t) rand();
1966
1967 // Copy main header to backup header
1968 RebuildSecondHeader();
1969
1970 // Blank out the partitions array....
1971 BlankPartitions();
1972
1973 // Flag all CRCs as being OK....
1974 mainCrcOk = 1;
1975 secondCrcOk = 1;
1976 mainPartsCrcOk = 1;
1977 secondPartsCrcOk = 1;
1978
1979 return (goOn);
1980} // GPTData::ClearGPTData()
1981
srs5694247657a2009-11-26 18:36:12 -05001982// Set the location of the second GPT header data to the end of the disk.
1983// Used internally and called by the 'e' option on the recovery &
1984// transformation menu, to help users of RAID arrays who add disk space
1985// to their arrays.
1986void GPTData::MoveSecondHeaderToEnd() {
srs56948bb78762009-11-24 15:43:49 -05001987 mainHeader.backupLBA = secondHeader.currentLBA = diskSize - UINT64_C(1);
1988 mainHeader.lastUsableLBA = secondHeader.lastUsableLBA = diskSize - mainHeader.firstUsableLBA;
1989 secondHeader.partitionEntriesLBA = secondHeader.lastUsableLBA + UINT64_C(1);
1990} // GPTData::FixSecondHeaderLocation()
1991
srs5694ba00fed2010-01-12 18:18:36 -05001992int GPTData::SetName(uint32_t partNum, char* theName) {
1993 int retval = 1;
1994 if (!IsFreePartNum(partNum))
1995 partitions[partNum].SetName((unsigned char*) theName);
1996 else retval = 0;
1997
1998 return retval;
srs5694e4ac11e2009-08-31 10:13:04 -04001999} // GPTData::SetName
2000
2001// Set the disk GUID to the specified value. Note that the header CRCs must
2002// be recomputed after calling this function.
2003void GPTData::SetDiskGUID(GUIDData newGUID) {
2004 mainHeader.diskGUID = newGUID;
2005 secondHeader.diskGUID = newGUID;
2006} // SetDiskGUID()
2007
2008// Set the unique GUID of the specified partition. Returns 1 on
2009// successful completion, 0 if there were problems (invalid
2010// partition number).
2011int GPTData::SetPartitionGUID(uint32_t pn, GUIDData theGUID) {
2012 int retval = 0;
2013
2014 if (pn < mainHeader.numParts) {
2015 if (partitions[pn].GetFirstLBA() != UINT64_C(0)) {
2016 partitions[pn].SetUniqueGUID(theGUID);
2017 retval = 1;
2018 } // if
2019 } // if
2020 return retval;
2021} // GPTData::SetPartitionGUID()
2022
srs5694ba00fed2010-01-12 18:18:36 -05002023// Change partition type code non-interactively. Returns 1 if
2024// successful, 0 if not....
2025int GPTData::ChangePartType(uint32_t partNum, uint16_t hexCode) {
2026 int retval = 1;
2027
2028 if (!IsFreePartNum(partNum)) {
2029 partitions[partNum].SetType(hexCode);
2030 } else retval = 0;
2031 return retval;
2032} // GPTData::ChangePartType()
2033
srs56941d1448a2009-12-31 21:20:19 -05002034// Adjust sector number so that it falls on a sector boundary that's a
2035// multiple of sectorAlignment. This is done to improve the performance
2036// of Western Digital Advanced Format disks and disks with similar
2037// technology from other companies, which use 4096-byte sectors
2038// internally although they translate to 512-byte sectors for the
2039// benefit of the OS. If partitions aren't properly aligned on these
2040// disks, some filesystem data structures can span multiple physical
2041// sectors, degrading performance. This function should be called
2042// only on the FIRST sector of the partition, not the last!
2043// This function returns 1 if the alignment was altered, 0 if it
2044// was unchanged.
2045int GPTData::Align(uint64_t* sector) {
2046 int retval = 0, sectorOK = 0;
2047 uint64_t earlier, later, testSector, original;
2048
2049 if ((*sector % sectorAlignment) != 0) {
2050 original = *sector;
2051 retval = 1;
2052 earlier = (*sector / sectorAlignment) * sectorAlignment;
2053 later = earlier + (uint64_t) sectorAlignment;
2054
2055 // Check to see that every sector between the earlier one and the
2056 // requested one is clear, and that it's not too early....
2057 if (earlier >= mainHeader.firstUsableLBA) {
2058// printf("earlier is %llu, first usable is %llu\n", earlier, mainHeader.firstUsableLBA);
2059 sectorOK = 1;
2060 testSector = earlier;
2061 do {
2062 sectorOK = IsFree(testSector++);
2063 } while ((sectorOK == 1) && (testSector < *sector));
2064 if (sectorOK == 1) {
2065 *sector = earlier;
2066// printf("Moved sector earlier.\n");
2067 } // if
2068 } // if firstUsableLBA check
2069
2070 // If couldn't move the sector earlier, try to move it later instead....
2071 if ((sectorOK != 1) && (later <= mainHeader.lastUsableLBA)) {
2072 sectorOK = 1;
2073 testSector = later;
2074 do {
2075 sectorOK = IsFree(testSector--);
2076 } while ((sectorOK == 1) && (testSector > *sector));
2077 if (sectorOK == 1) {
2078 *sector = later;
2079// printf("Moved sector later\n");
2080 } // if
2081 } // if
2082
2083 // If sector was changed successfully, inform the user of this fact.
2084 // Otherwise, notify the user that it couldn't be done....
2085 if (sectorOK == 1) {
2086 printf("Information: Moved requested sector from %llu to %llu for\n"
srs5694ba00fed2010-01-12 18:18:36 -05002087 "alignment purposes.\n",
srs56945d58fe02010-01-03 20:57:08 -05002088 (unsigned long long) original, (unsigned long long) *sector);
srs5694ba00fed2010-01-12 18:18:36 -05002089 if (!beQuiet)
2090 printf("Use 'l' on the experts' menu to adjust alignment\n");
srs56941d1448a2009-12-31 21:20:19 -05002091 } else {
2092 printf("Information: Sector not aligned on %d-sector boundary and could not be moved.\n"
2093 "If you're using a Western Digital Advanced Format or similar disk with\n"
2094 "underlying 4096-byte sectors, performance may suffer.\n", sectorAlignment);
2095 retval = 0;
2096 } // if/else
2097 } // if
2098 return retval;
2099} // GPTData::Align()
2100
srs5694e4ac11e2009-08-31 10:13:04 -04002101/********************************************************
2102 * *
2103 * Functions that return data about GPT data structures *
2104 * (most of these are inline in gpt.h) *
2105 * *
2106 ********************************************************/
2107
2108// Find the low and high used partition numbers (numbered from 0).
2109// Return value is the number of partitions found. Note that the
2110// *low and *high values are both set to 0 when no partitions
2111// are found, as well as when a single partition in the first
2112// position exists. Thus, the return value is the only way to
2113// tell when no partitions exist.
2114int GPTData::GetPartRange(uint32_t *low, uint32_t *high) {
2115 uint32_t i;
2116 int numFound = 0;
2117
2118 *low = mainHeader.numParts + 1; // code for "not found"
2119 *high = 0;
2120 if (mainHeader.numParts > 0) { // only try if partition table exists...
2121 for (i = 0; i < mainHeader.numParts; i++) {
2122 if (partitions[i].GetFirstLBA() != UINT64_C(0)) { // it exists
2123 *high = i; // since we're counting up, set the high value
2124 // Set the low value only if it's not yet found...
2125 if (*low == (mainHeader.numParts + 1)) *low = i;
2126 numFound++;
2127 } // if
2128 } // for
2129 } // if
2130
2131 // Above will leave *low pointing to its "not found" value if no partitions
2132 // are defined, so reset to 0 if this is the case....
2133 if (*low == (mainHeader.numParts + 1))
2134 *low = 0;
2135 return numFound;
2136} // GPTData::GetPartRange()
2137
srs5694978041c2009-09-21 20:51:47 -04002138// Returns the number of defined partitions.
2139uint32_t GPTData::CountParts(void) {
2140 int i, counted = 0;
2141
2142 for (i = 0; i < mainHeader.numParts; i++) {
2143 if (partitions[i].GetFirstLBA() > 0)
2144 counted++;
2145 } // for
2146 return counted;
2147} // GPTData::CountParts()
2148
srs5694e4ac11e2009-08-31 10:13:04 -04002149/****************************************************
2150 * *
2151 * Functions that return data about disk free space *
2152 * *
2153 ****************************************************/
2154
2155// Find the first available block after the starting point; returns 0 if
2156// there are no available blocks left
2157uint64_t GPTData::FindFirstAvailable(uint64_t start) {
2158 uint64_t first;
2159 uint32_t i;
2160 int firstMoved = 0;
2161
2162 // Begin from the specified starting point or from the first usable
2163 // LBA, whichever is greater...
2164 if (start < mainHeader.firstUsableLBA)
2165 first = mainHeader.firstUsableLBA;
2166 else
2167 first = start;
2168
2169 // ...now search through all partitions; if first is within an
2170 // existing partition, move it to the next sector after that
2171 // partition and repeat. If first was moved, set firstMoved
2172 // flag; repeat until firstMoved is not set, so as to catch
2173 // cases where partitions are out of sequential order....
2174 do {
2175 firstMoved = 0;
2176 for (i = 0; i < mainHeader.numParts; i++) {
2177 if ((first >= partitions[i].GetFirstLBA()) &&
2178 (first <= partitions[i].GetLastLBA())) { // in existing part.
2179 first = partitions[i].GetLastLBA() + 1;
2180 firstMoved = 1;
2181 } // if
2182 } // for
2183 } while (firstMoved == 1);
2184 if (first > mainHeader.lastUsableLBA)
2185 first = 0;
2186 return (first);
2187} // GPTData::FindFirstAvailable()
2188
2189// Finds the first available sector in the largest block of unallocated
2190// space on the disk. Returns 0 if there are no available blocks left
2191uint64_t GPTData::FindFirstInLargest(void) {
srs5694e35eb1b2009-09-14 00:29:34 -04002192 uint64_t start, firstBlock, lastBlock, segmentSize, selectedSize = 0, selectedSegment = 0;
srs5694e4ac11e2009-08-31 10:13:04 -04002193
2194 start = 0;
2195 do {
2196 firstBlock = FindFirstAvailable(start);
2197 if (firstBlock != UINT32_C(0)) { // something's free...
2198 lastBlock = FindLastInFree(firstBlock);
2199 segmentSize = lastBlock - firstBlock + UINT32_C(1);
2200 if (segmentSize > selectedSize) {
2201 selectedSize = segmentSize;
2202 selectedSegment = firstBlock;
2203 } // if
2204 start = lastBlock + 1;
2205 } // if
2206 } while (firstBlock != 0);
2207 return selectedSegment;
2208} // GPTData::FindFirstInLargest()
2209
2210// Find the last available block on the disk at or after the start
2211// block. Returns 0 if there are no available partitions after
2212// (or including) start.
2213uint64_t GPTData::FindLastAvailable(uint64_t start) {
2214 uint64_t last;
2215 uint32_t i;
2216 int lastMoved = 0;
2217
2218 // Start by assuming the last usable LBA is available....
2219 last = mainHeader.lastUsableLBA;
2220
2221 // ...now, similar to algorithm in FindFirstAvailable(), search
2222 // through all partitions, moving last when it's in an existing
2223 // partition. Set the lastMoved flag so we repeat to catch cases
2224 // where partitions are out of logical order.
2225 do {
2226 lastMoved = 0;
2227 for (i = 0; i < mainHeader.numParts; i++) {
2228 if ((last >= partitions[i].GetFirstLBA()) &&
2229 (last <= partitions[i].GetLastLBA())) { // in existing part.
2230 last = partitions[i].GetFirstLBA() - 1;
2231 lastMoved = 1;
2232 } // if
2233 } // for
2234 } while (lastMoved == 1);
2235 if (last < mainHeader.firstUsableLBA)
2236 last = 0;
2237 return (last);
2238} // GPTData::FindLastAvailable()
2239
2240// Find the last available block in the free space pointed to by start.
2241uint64_t GPTData::FindLastInFree(uint64_t start) {
2242 uint64_t nearestStart;
2243 uint32_t i;
2244
2245 nearestStart = mainHeader.lastUsableLBA;
2246 for (i = 0; i < mainHeader.numParts; i++) {
2247 if ((nearestStart > partitions[i].GetFirstLBA()) &&
2248 (partitions[i].GetFirstLBA() > start)) {
2249 nearestStart = partitions[i].GetFirstLBA() - 1;
2250 } // if
2251 } // for
2252 return (nearestStart);
2253} // GPTData::FindLastInFree()
2254
2255// Finds the total number of free blocks, the number of segments in which
2256// they reside, and the size of the largest of those segments
2257uint64_t GPTData::FindFreeBlocks(int *numSegments, uint64_t *largestSegment) {
2258 uint64_t start = UINT64_C(0); // starting point for each search
2259 uint64_t totalFound = UINT64_C(0); // running total
2260 uint64_t firstBlock; // first block in a segment
2261 uint64_t lastBlock; // last block in a segment
2262 uint64_t segmentSize; // size of segment in blocks
2263 int num = 0;
2264
2265 *largestSegment = UINT64_C(0);
2266 do {
2267 firstBlock = FindFirstAvailable(start);
2268 if (firstBlock != UINT64_C(0)) { // something's free...
2269 lastBlock = FindLastInFree(firstBlock);
2270 segmentSize = lastBlock - firstBlock + UINT64_C(1);
2271 if (segmentSize > *largestSegment) {
2272 *largestSegment = segmentSize;
2273 } // if
2274 totalFound += segmentSize;
2275 num++;
2276 start = lastBlock + 1;
2277 } // if
2278 } while (firstBlock != 0);
2279 *numSegments = num;
2280 return totalFound;
2281} // GPTData::FindFreeBlocks()
2282
2283// Returns 1 if sector is unallocated, 0 if it's allocated to a partition
2284int GPTData::IsFree(uint64_t sector) {
2285 int isFree = 1;
2286 uint32_t i;
2287
2288 for (i = 0; i < mainHeader.numParts; i++) {
2289 if ((sector >= partitions[i].GetFirstLBA()) &&
2290 (sector <= partitions[i].GetLastLBA())) {
2291 isFree = 0;
2292 } // if
2293 } // for
srs5694e35eb1b2009-09-14 00:29:34 -04002294 if ((sector < mainHeader.firstUsableLBA) ||
srs5694e4ac11e2009-08-31 10:13:04 -04002295 (sector > mainHeader.lastUsableLBA)) {
2296 isFree = 0;
2297 } // if
2298 return (isFree);
2299} // GPTData::IsFree()
2300
srs5694ba00fed2010-01-12 18:18:36 -05002301// Returns 1 if partNum is unused.
2302int GPTData::IsFreePartNum(uint32_t partNum) {
2303 int retval = 1;
2304
2305 if ((partNum >= 0) && (partNum < mainHeader.numParts)) {
2306 if ((partitions[partNum].GetFirstLBA() != UINT64_C(0)) ||
2307 (partitions[partNum].GetLastLBA() != UINT64_C(0))) {
2308 retval = 0;
2309 } // if partition is in use
2310 } else retval = 0;
2311
2312 return retval;
2313} // GPTData::IsFreePartNum()
2314
srs5694e4ac11e2009-08-31 10:13:04 -04002315/********************************
2316 * *
2317 * Endianness support functions *
2318 * *
2319 ********************************/
2320
srs56942a9f5da2009-08-26 00:48:01 -04002321void GPTData::ReverseHeaderBytes(struct GPTHeader* header) {
srs5694221e0872009-08-29 15:00:31 -04002322 ReverseBytes(&header->signature, 8);
2323 ReverseBytes(&header->revision, 4);
2324 ReverseBytes(&header->headerSize, 4);
2325 ReverseBytes(&header->headerCRC, 4);
2326 ReverseBytes(&header->reserved, 4);
2327 ReverseBytes(&header->currentLBA, 8);
2328 ReverseBytes(&header->backupLBA, 8);
2329 ReverseBytes(&header->firstUsableLBA, 8);
2330 ReverseBytes(&header->lastUsableLBA, 8);
2331 ReverseBytes(&header->partitionEntriesLBA, 8);
2332 ReverseBytes(&header->numParts, 4);
2333 ReverseBytes(&header->sizeOfPartitionEntries, 4);
2334 ReverseBytes(&header->partitionEntriesCRC, 4);
2335 ReverseBytes(&header->reserved2, GPT_RESERVED);
2336 ReverseBytes(&header->diskGUID.data1, 8);
2337 ReverseBytes(&header->diskGUID.data2, 8);
srs56942a9f5da2009-08-26 00:48:01 -04002338} // GPTData::ReverseHeaderBytes()
2339
2340// IMPORTANT NOTE: This function requires non-reversed mainHeader
2341// structure!
2342void GPTData::ReversePartitionBytes() {
2343 uint32_t i;
2344
2345 // Check GPT signature on big-endian systems; this will mismatch
2346 // if the function is called out of order. Unfortunately, it'll also
2347 // mismatch if there's data corruption.
2348 if ((mainHeader.signature != GPT_SIGNATURE) && (IsLittleEndian() == 0)) {
2349 printf("GPT signature mismatch in GPTData::ReversePartitionBytes(). This indicates\n"
srs5694e4ac11e2009-08-31 10:13:04 -04002350 "data corruption or a misplaced call to this function.\n");
srs56942a9f5da2009-08-26 00:48:01 -04002351 } // if signature mismatch....
2352 for (i = 0; i < mainHeader.numParts; i++) {
srs5694221e0872009-08-29 15:00:31 -04002353 partitions[i].ReversePartBytes();
srs56942a9f5da2009-08-26 00:48:01 -04002354 } // for
2355} // GPTData::ReversePartitionBytes()
2356
2357/******************************************
2358 * *
2359 * Additional non-class support functions *
2360 * *
2361 ******************************************/
2362
srs5694e7b4ff92009-08-18 13:16:10 -04002363// Check to be sure that data type sizes are correct. The basic types (uint*_t) should
2364// never fail these tests, but the struct types may fail depending on compile options.
2365// Specifically, the -fpack-struct option to gcc may be required to ensure proper structure
2366// sizes.
2367int SizesOK(void) {
2368 int allOK = 1;
srs5694e7b4ff92009-08-18 13:16:10 -04002369
2370 if (sizeof(uint8_t) != 1) {
srs56941d1448a2009-12-31 21:20:19 -05002371 fprintf(stderr, "uint8_t is %lu bytes, should be 1 byte; aborting!\n", sizeof(uint8_t));
srs5694e7b4ff92009-08-18 13:16:10 -04002372 allOK = 0;
2373 } // if
2374 if (sizeof(uint16_t) != 2) {
srs56941d1448a2009-12-31 21:20:19 -05002375 fprintf(stderr, "uint16_t is %lu bytes, should be 2 bytes; aborting!\n", sizeof(uint16_t));
srs5694e7b4ff92009-08-18 13:16:10 -04002376 allOK = 0;
2377 } // if
2378 if (sizeof(uint32_t) != 4) {
srs56941d1448a2009-12-31 21:20:19 -05002379 fprintf(stderr, "uint32_t is %lu bytes, should be 4 bytes; aborting!\n", sizeof(uint32_t));
srs5694e7b4ff92009-08-18 13:16:10 -04002380 allOK = 0;
2381 } // if
2382 if (sizeof(uint64_t) != 8) {
srs56941d1448a2009-12-31 21:20:19 -05002383 fprintf(stderr, "uint64_t is %lu bytes, should be 8 bytes; aborting!\n", sizeof(uint64_t));
srs5694e7b4ff92009-08-18 13:16:10 -04002384 allOK = 0;
2385 } // if
2386 if (sizeof(struct MBRRecord) != 16) {
srs56941d1448a2009-12-31 21:20:19 -05002387 fprintf(stderr, "MBRRecord is %lu bytes, should be 16 bytes; aborting!\n", sizeof(MBRRecord));
srs5694e7b4ff92009-08-18 13:16:10 -04002388 allOK = 0;
2389 } // if
srs5694978041c2009-09-21 20:51:47 -04002390 if (sizeof(struct TempMBR) != 512) {
srs56941d1448a2009-12-31 21:20:19 -05002391 fprintf(stderr, "TempMBR is %lu bytes, should be 512 bytes; aborting!\n", sizeof(TempMBR));
srs5694e7b4ff92009-08-18 13:16:10 -04002392 allOK = 0;
2393 } // if
2394 if (sizeof(struct GPTHeader) != 512) {
srs56941d1448a2009-12-31 21:20:19 -05002395 fprintf(stderr, "GPTHeader is %lu bytes, should be 512 bytes; aborting!\n", sizeof(GPTHeader));
srs5694e7b4ff92009-08-18 13:16:10 -04002396 allOK = 0;
2397 } // if
srs5694221e0872009-08-29 15:00:31 -04002398 if (sizeof(GPTPart) != 128) {
srs56941d1448a2009-12-31 21:20:19 -05002399 fprintf(stderr, "GPTPart is %lu bytes, should be 128 bytes; aborting!\n", sizeof(GPTPart));
srs5694221e0872009-08-29 15:00:31 -04002400 allOK = 0;
2401 } // if
2402// Determine endianness; set allOK = 0 if running on big-endian hardware
srs56942a9f5da2009-08-26 00:48:01 -04002403 if (IsLittleEndian() == 0) {
2404 fprintf(stderr, "\aRunning on big-endian hardware. Big-endian support is new and poorly"
srs5694e4ac11e2009-08-31 10:13:04 -04002405 " tested!\nBeware!\n");
srs56942a9f5da2009-08-26 00:48:01 -04002406 // allOK = 0;
srs5694e7b4ff92009-08-18 13:16:10 -04002407 } // if
2408 return (allOK);
2409} // SizesOK()
srs5694e4ac11e2009-08-31 10:13:04 -04002410