blob: 39c570eb2de81d0bb47a264639be7da7b0d2b3e2 [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 "
srs56947dbb9322010-01-20 16:56:30 -0500545 "impossible to save\nchanges to this disk's partition table!\n",
srs5694e4ac11e2009-08-31 10:13:04 -0400546 errno);
srs56947dbb9322010-01-20 16:56:30 -0500547#ifdef __FreeBSD__
548 printf("You may be able to enable writes by exiting this program, typing\n"
549 "'sysctl kern.geom.debugflags=16' at a shell prompt, and re-running this\n"
550 "program.\n");
551#endif
552 printf("\n");
srs56945d58fe02010-01-03 20:57:08 -0500553 justLooking = 1;
554 } // if
srs5694e4ac11e2009-08-31 10:13:04 -0400555 close(fd);
556
557 if ((fd = open(deviceFilename, O_RDONLY)) != -1) {
558 // store disk information....
559 diskSize = disksize(fd, &err);
560 blockSize = (uint32_t) GetBlockSize(fd);
srs56945d58fe02010-01-03 20:57:08 -0500561 sectorAlignment = FindAlignment(fd);
srs5694e4ac11e2009-08-31 10:13:04 -0400562 strcpy(device, deviceFilename);
srs5694ba00fed2010-01-12 18:18:36 -0500563 PartitionScan(fd); // Check for partition types, load GPT, & print summary
srs5694e4ac11e2009-08-31 10:13:04 -0400564
srs5694ba00fed2010-01-12 18:18:36 -0500565 whichWasUsed = UseWhichPartitions();
566 switch (whichWasUsed) {
srs5694e4ac11e2009-08-31 10:13:04 -0400567 case use_mbr:
568 XFormPartitions();
569 break;
570 case use_bsd:
571 bsdDisklabel.ReadBSDData(fd, 0, diskSize - 1);
572// bsdDisklabel.DisplayBSDData();
573 ClearGPTData();
574 protectiveMBR.MakeProtectiveMBR(1); // clear boot area (option 1)
575 XFormDisklabel(&bsdDisklabel, 0);
576 break;
577 case use_gpt:
578 break;
579 case use_new:
580 ClearGPTData();
581 protectiveMBR.MakeProtectiveMBR();
582 break;
srs56943c0af382010-01-15 19:19:18 -0500583 case use_abort:
584 allOK = 0;
585 printf("Aborting because of invalid partition data!\n");
586 break;
srs5694e4ac11e2009-08-31 10:13:04 -0400587 } // switch
588
589 // Now find the first and last sectors used by partitions...
590 if (allOK) {
591 firstBlock = mainHeader.backupLBA; // start high
592 lastBlock = 0; // start low
593 for (i = 0; i < mainHeader.numParts; i++) {
594 if ((partitions[i].GetFirstLBA() < firstBlock) &&
595 (partitions[i].GetFirstLBA() > 0))
596 firstBlock = partitions[i].GetFirstLBA();
597 if (partitions[i].GetLastLBA() > lastBlock)
598 lastBlock = partitions[i].GetLastLBA();
599 } // for
srs56943c0af382010-01-15 19:19:18 -0500600 CheckGPTSize();
srs5694e4ac11e2009-08-31 10:13:04 -0400601 } // if
srs5694e4ac11e2009-08-31 10:13:04 -0400602 } else {
603 allOK = 0;
604 fprintf(stderr, "Problem opening %s for reading! Error is %d\n",
605 deviceFilename, errno);
606 if (errno == EACCES) { // User is probably not running as root
607 fprintf(stderr, "You must run this program as root or use sudo!\n");
608 } // if
609 } // if/else
610 return (allOK);
611} // GPTData::LoadPartitions()
612
613// Loads the GPT, as much as possible. Returns 1 if this seems to have
614// succeeded, 0 if there are obvious problems....
615int GPTData::ForceLoadGPTData(int fd) {
616 int allOK = 1, validHeaders;
617 off_t seekTo;
618 char* storage;
619 uint32_t newCRC, sizeOfParts;
620
621 // Seek to and read the main GPT header
srs56941e093722010-01-05 00:14:19 -0500622 lseek64(fd, blockSize, SEEK_SET);
623 if (myRead(fd, (char*) &mainHeader, 512) != 512) { // read main GPT header
624 fprintf(stderr, "Warning! Error %d reading main GPT header!\n", errno);
srs56945d58fe02010-01-03 20:57:08 -0500625 } // if
srs5694e4ac11e2009-08-31 10:13:04 -0400626 mainCrcOk = CheckHeaderCRC(&mainHeader);
627 if (IsLittleEndian() == 0) // big-endian system; adjust header byte order....
628 ReverseHeaderBytes(&mainHeader);
629
srs56943f2fe992009-11-24 18:28:18 -0500630 // Load backup header, check its CRC, and store the results of the
631 // check for future reference. Load backup header using pointer in main
632 // header if possible; but if main header has a CRC error, or if it
633 // points to beyond the end of the disk, load the last sector of the
634 // disk instead.
635 if (mainCrcOk) {
636 if (mainHeader.backupLBA < diskSize) {
637 seekTo = mainHeader.backupLBA * blockSize;
638 } else {
srs56941e093722010-01-05 00:14:19 -0500639 seekTo = (diskSize * blockSize) - blockSize;
srs56943f2fe992009-11-24 18:28:18 -0500640 printf("Warning! Disk size is smaller than the main header indicates! Loading\n"
641 "secondary header from the last sector of the disk! You should use 'v' to\n"
642 "verify disk integrity, and perhaps options on the experts' menu to repair\n"
643 "the disk.\n");
644 } // else
645 } else {
srs56941e093722010-01-05 00:14:19 -0500646 seekTo = (diskSize * blockSize) - blockSize;
srs56943f2fe992009-11-24 18:28:18 -0500647 } // if/else (mainCrcOk)
648
srs5694e4ac11e2009-08-31 10:13:04 -0400649 if (lseek64(fd, seekTo, SEEK_SET) != (off_t) -1) {
srs56941e093722010-01-05 00:14:19 -0500650 if (myRead(fd, (char*) &secondHeader, 512) != 512) { // read secondary GPT header
651 fprintf(stderr, "Warning! Error %d reading secondary GPT header!\n", errno);
srs56945d58fe02010-01-03 20:57:08 -0500652 } // if
srs5694e4ac11e2009-08-31 10:13:04 -0400653 secondCrcOk = CheckHeaderCRC(&secondHeader);
654 if (IsLittleEndian() == 0) // big-endian system; adjust header byte order....
655 ReverseHeaderBytes(&secondHeader);
656 } else {
657 allOK = 0;
658 state = gpt_invalid;
srs56943f2fe992009-11-24 18:28:18 -0500659 fprintf(stderr, "Unable to seek to secondary GPT header at sector %llu!\n",
srs56945d58fe02010-01-03 20:57:08 -0500660 (unsigned long long) (diskSize - (UINT64_C(1))));
srs5694e4ac11e2009-08-31 10:13:04 -0400661 } // if/else lseek
662
663 // Return valid headers code: 0 = both headers bad; 1 = main header
664 // good, backup bad; 2 = backup header good, main header bad;
665 // 3 = both headers good. Note these codes refer to valid GPT
666 // signatures and version numbers; more subtle problems will elude
667 // this check!
668 validHeaders = CheckHeaderValidity();
669
670 // Read partitions (from primary array)
671 if (validHeaders > 0) { // if at least one header is OK....
672 // GPT appears to be valid....
673 state = gpt_valid;
674
675 // We're calling the GPT valid, but there's a possibility that one
676 // of the two headers is corrupt. If so, use the one that seems to
677 // be in better shape to regenerate the bad one
678 if (validHeaders == 2) { // valid backup header, invalid main header
679 printf("Caution: invalid main GPT header, but valid backup; regenerating main header\n"
680 "from backup!\n");
681 RebuildMainHeader();
682 mainCrcOk = secondCrcOk; // Since copied, use CRC validity of backup
683 } else if (validHeaders == 1) { // valid main header, invalid backup
684 printf("Caution: invalid backup GPT header, but valid main header; regenerating\n"
685 "backup header from main header.\n");
686 RebuildSecondHeader();
687 secondCrcOk = mainCrcOk; // Since regenerated, use CRC validity of main
688 } // if/else/if
689
690 // Load the main partition table, including storing results of its
691 // CRC check
692 if (LoadMainTable() == 0)
693 allOK = 0;
694
695 // Load backup partition table into temporary storage to check
696 // its CRC and store the results, then discard this temporary
697 // storage, since we don't use it in any but recovery operations
698 seekTo = secondHeader.partitionEntriesLBA * (off_t) blockSize;
699 if ((lseek64(fd, seekTo, SEEK_SET) != (off_t) -1) && (secondCrcOk)) {
700 sizeOfParts = secondHeader.numParts * secondHeader.sizeOfPartitionEntries;
701 storage = (char*) malloc(sizeOfParts);
srs56941e093722010-01-05 00:14:19 -0500702 if (myRead(fd, storage, sizeOfParts) != sizeOfParts) {
srs56945d58fe02010-01-03 20:57:08 -0500703 fprintf(stderr, "Warning! Error %d reading backup partition table!\n", errno);
704 } // if
srs5694e4ac11e2009-08-31 10:13:04 -0400705 newCRC = chksum_crc32((unsigned char*) storage, sizeOfParts);
706 free(storage);
707 secondPartsCrcOk = (newCRC == secondHeader.partitionEntriesCRC);
708 } // if
709
710 // Check for valid CRCs and warn if there are problems
711 if ((mainCrcOk == 0) || (secondCrcOk == 0) || (mainPartsCrcOk == 0) ||
712 (secondPartsCrcOk == 0)) {
713 printf("Warning! One or more CRCs don't match. You should repair the disk!\n");
714 state = gpt_corrupt;
srs5694ba00fed2010-01-12 18:18:36 -0500715 } // if
srs5694e4ac11e2009-08-31 10:13:04 -0400716 } else {
717 state = gpt_invalid;
718 } // if/else
719 return allOK;
720} // GPTData::ForceLoadGPTData()
721
srs5694247657a2009-11-26 18:36:12 -0500722// Loads the partition table pointed to by the main GPT header. The
srs5694e4ac11e2009-08-31 10:13:04 -0400723// main GPT header in memory MUST be valid for this call to do anything
724// sensible!
725int GPTData::LoadMainTable(void) {
726 int fd, retval = 0;
727 uint32_t newCRC, sizeOfParts;
728
729 if ((fd = open(device, O_RDONLY)) != -1) {
730 // Set internal data structures for number of partitions on the disk
731 SetGPTSize(mainHeader.numParts);
732
733 // Load main partition table, and record whether its CRC
734 // matches the stored value
735 lseek64(fd, mainHeader.partitionEntriesLBA * blockSize, SEEK_SET);
736 sizeOfParts = mainHeader.numParts * mainHeader.sizeOfPartitionEntries;
srs56941e093722010-01-05 00:14:19 -0500737 if (myRead(fd, (char*) partitions, sizeOfParts) != sizeOfParts) {
srs56945d58fe02010-01-03 20:57:08 -0500738 fprintf(stderr, "Warning! Error %d when loading the main partition table!\n", errno);
739 } // if
srs5694e4ac11e2009-08-31 10:13:04 -0400740 newCRC = chksum_crc32((unsigned char*) partitions, sizeOfParts);
741 mainPartsCrcOk = (newCRC == mainHeader.partitionEntriesCRC);
742 if (IsLittleEndian() == 0)
743 ReversePartitionBytes();
744 retval = 1;
745 } // if
746 return retval;
747} // GPTData::LoadMainTable()
srs5694e7b4ff92009-08-18 13:16:10 -0400748
749// Load the second (backup) partition table as the primary partition
750// table. Used in repair functions
751void GPTData::LoadSecondTableAsMain(void) {
752 int fd;
753 off_t seekTo;
754 uint32_t sizeOfParts, newCRC;
755
756 if ((fd = open(device, O_RDONLY)) != -1) {
757 seekTo = secondHeader.partitionEntriesLBA * (off_t) blockSize;
758 if (lseek64(fd, seekTo, SEEK_SET) != (off_t) -1) {
759 SetGPTSize(secondHeader.numParts);
760 sizeOfParts = secondHeader.numParts * secondHeader.sizeOfPartitionEntries;
srs56941e093722010-01-05 00:14:19 -0500761 if (myRead(fd, (char*) partitions, sizeOfParts) != sizeOfParts) {
srs56945d58fe02010-01-03 20:57:08 -0500762 fprintf(stderr, "Warning! Read error %d! Misbehavior now likely!\n", errno);
763 } // if
srs5694e7b4ff92009-08-18 13:16:10 -0400764 newCRC = chksum_crc32((unsigned char*) partitions, sizeOfParts);
765 secondPartsCrcOk = (newCRC == secondHeader.partitionEntriesCRC);
srs5694e4ac11e2009-08-31 10:13:04 -0400766 mainPartsCrcOk = secondPartsCrcOk;
srs56942a9f5da2009-08-26 00:48:01 -0400767 if (IsLittleEndian() == 0)
768 ReversePartitionBytes();
srs5694e7b4ff92009-08-18 13:16:10 -0400769 if (!secondPartsCrcOk) {
770 printf("Error! After loading backup partitions, the CRC still doesn't check out!\n");
771 } // if
772 } else {
773 printf("Error! Couldn't seek to backup partition table!\n");
774 } // if/else
775 } else {
srs56941d1448a2009-12-31 21:20:19 -0500776 printf("Error! Couldn't open device %s when recovering backup partition table!\n", device);
srs5694e7b4ff92009-08-18 13:16:10 -0400777 } // if/else
778} // GPTData::LoadSecondTableAsMain()
779
srs5694e7b4ff92009-08-18 13:16:10 -0400780// Writes GPT (and protective MBR) to disk. Returns 1 on successful
781// write, 0 if there was a problem.
srs5694ba00fed2010-01-12 18:18:36 -0500782int GPTData::SaveGPTData(int quiet) {
srs5694978041c2009-09-21 20:51:47 -0400783 int allOK = 1;
srs5694e7b4ff92009-08-18 13:16:10 -0400784 char answer, line[256];
785 int fd;
786 uint64_t secondTable;
srs56942a9f5da2009-08-26 00:48:01 -0400787 uint32_t numParts;
srs5694e7b4ff92009-08-18 13:16:10 -0400788 off_t offset;
789
790 if (strlen(device) == 0) {
791 printf("Device not defined.\n");
792 } // if
793
794 // First do some final sanity checks....
srs56945d58fe02010-01-03 20:57:08 -0500795
796 // This test should only fail on read-only disks....
797 if (justLooking) {
798 printf("The justLooking flag is set. This probably means you can't write to the disk.\n");
799 allOK = 0;
800 } // if
801
srs5694e7b4ff92009-08-18 13:16:10 -0400802 // Is there enough space to hold the GPT headers and partition tables,
803 // given the partition sizes?
srs5694221e0872009-08-29 15:00:31 -0400804 if (CheckGPTSize() > 0) {
srs5694e7b4ff92009-08-18 13:16:10 -0400805 allOK = 0;
806 } // if
807
808 // Check that disk is really big enough to handle this...
809 if (mainHeader.backupLBA > diskSize) {
srs5694247657a2009-11-26 18:36:12 -0500810 fprintf(stderr, "Error! Disk is too small! The 'e' option on the experts' menu might fix the\n"
811 "problem (or it might not). Aborting!\n");
srs56945d58fe02010-01-03 20:57:08 -0500812 printf("(Disk size is %llu sectors, needs to be %llu sectors.)\n",
813 (unsigned long long) diskSize, (unsigned long long) mainHeader.backupLBA);
srs5694e7b4ff92009-08-18 13:16:10 -0400814 allOK = 0;
815 } // if
srs5694247657a2009-11-26 18:36:12 -0500816 // Check that second header is properly placed. Warn and ask if this should
817 // be corrected if the test fails....
srs5694ba00fed2010-01-12 18:18:36 -0500818 if ((mainHeader.backupLBA < (diskSize - UINT64_C(1))) && (quiet == 0)) {
srs5694247657a2009-11-26 18:36:12 -0500819 printf("Warning! Secondary header is placed too early on the disk! Do you want to\n"
820 "correct this problem? ");
821 if (GetYN() == 'Y') {
822 MoveSecondHeaderToEnd();
823 printf("Have moved second header and partition table to correct location.\n");
824 } else {
825 printf("Have not corrected the problem. Strange problems may occur in the future!\n");
826 } // if correction requested
827 } // if
srs5694e7b4ff92009-08-18 13:16:10 -0400828
829 // Check for overlapping partitions....
srs5694e4ac11e2009-08-31 10:13:04 -0400830 if (FindOverlaps() > 0) {
831 allOK = 0;
832 fprintf(stderr, "Aborting write operation!\n");
833 } // if
834
835 // Check for mismatched MBR and GPT data, but let it pass if found
836 // (function displays warning message)
837 FindHybridMismatches();
srs5694e7b4ff92009-08-18 13:16:10 -0400838
srs56942a9f5da2009-08-26 00:48:01 -0400839 // Pull out some data that's needed before doing byte-order reversal on
840 // big-endian systems....
841 numParts = mainHeader.numParts;
842 secondTable = secondHeader.partitionEntriesLBA;
843 if (IsLittleEndian() == 0) {
844 // Reverse partition bytes first, since that function requires non-reversed
845 // data from the main header....
846 ReversePartitionBytes();
847 ReverseHeaderBytes(&mainHeader);
848 ReverseHeaderBytes(&secondHeader);
849 } // if
srs5694e7b4ff92009-08-18 13:16:10 -0400850 RecomputeCRCs();
851
srs5694ba00fed2010-01-12 18:18:36 -0500852 if ((allOK) && (!quiet)) {
srs5694e7b4ff92009-08-18 13:16:10 -0400853 printf("\nFinal checks complete. About to write GPT data. THIS WILL OVERWRITE EXISTING\n");
854 printf("MBR PARTITIONS!! THIS PROGRAM IS BETA QUALITY AT BEST. IF YOU LOSE ALL YOUR\n");
855 printf("DATA, YOU HAVE ONLY YOURSELF TO BLAME IF YOU ANSWER 'Y' BELOW!\n\n");
srs56945d58fe02010-01-03 20:57:08 -0500856 printf("Do you want to proceed, possibly destroying your data? ");
857 answer = GetYN();
858 if (answer == 'Y') {
859 printf("OK; writing new GUID partition table (GPT).\n");
srs5694e7b4ff92009-08-18 13:16:10 -0400860 } else {
861 allOK = 0;
862 } // if/else
863 } // if
864
865 // Do it!
866 if (allOK) {
srs5694e4ac11e2009-08-31 10:13:04 -0400867 fd = OpenForWrite(device);
srs5694e7b4ff92009-08-18 13:16:10 -0400868 if (fd != -1) {
869 // First, write the protective MBR...
srs5694e4ac11e2009-08-31 10:13:04 -0400870 protectiveMBR.WriteMBRData(fd);
srs5694e7b4ff92009-08-18 13:16:10 -0400871
872 // Now write the main GPT header...
srs56941e093722010-01-05 00:14:19 -0500873 if (allOK) {
874 if (lseek64(fd, blockSize, SEEK_SET) != (off_t) -1) {
875 if (myWrite(fd, (char*) &mainHeader, 512) == -1)
876 allOK = 0;
877 } else allOK = 0; // if (lseek64()...)
878 } // if (allOK)
srs5694e7b4ff92009-08-18 13:16:10 -0400879
880 // Now write the main partition tables...
srs5694e4ac11e2009-08-31 10:13:04 -0400881 if (allOK) {
srs56941e093722010-01-05 00:14:19 -0500882 offset = mainHeader.partitionEntriesLBA * blockSize;
883 if (lseek64(fd, offset, SEEK_SET) != (off_t) - 1) {
884 if (myWrite(fd, (char*) partitions, GPT_SIZE * numParts) == -1)
srs5694e7b4ff92009-08-18 13:16:10 -0400885 allOK = 0;
srs56941e093722010-01-05 00:14:19 -0500886 } else allOK = 0; // if (lseek64()...)
887 } // if (allOK)
srs5694e7b4ff92009-08-18 13:16:10 -0400888
889 // Now seek to near the end to write the secondary GPT....
srs5694e4ac11e2009-08-31 10:13:04 -0400890 if (allOK) {
srs5694e7b4ff92009-08-18 13:16:10 -0400891 offset = (off_t) secondTable * (off_t) (blockSize);
892 if (lseek64(fd, offset, SEEK_SET) == (off_t) - 1) {
893 allOK = 0;
894 printf("Unable to seek to end of disk!\n");
895 } // if
896 } // if
897
898 // Now write the secondary partition tables....
srs56941e093722010-01-05 00:14:19 -0500899 if (allOK) {
900 if (myWrite(fd, (char*) partitions, GPT_SIZE * numParts) == -1)
srs5694e7b4ff92009-08-18 13:16:10 -0400901 allOK = 0;
srs56941e093722010-01-05 00:14:19 -0500902 } // if (allOK)
srs5694e7b4ff92009-08-18 13:16:10 -0400903
904 // Now write the secondary GPT header...
srs56941e093722010-01-05 00:14:19 -0500905 if (allOK) {
906 offset = mainHeader.backupLBA * blockSize;
907 if (lseek64(fd, offset, SEEK_SET) != (off_t) - 1) {
908 if (myWrite(fd, (char*) &secondHeader, 512) == -1)
909 allOK = 0;
910 } else allOK = 0; // if (lseek64()...)
911 } // if (allOK)
srs5694e7b4ff92009-08-18 13:16:10 -0400912
913 // re-read the partition table
914 if (allOK) {
srs5694e35eb1b2009-09-14 00:29:34 -0400915 DiskSync(fd);
srs5694e7b4ff92009-08-18 13:16:10 -0400916 } // if
917
918 if (allOK) { // writes completed OK
srs5694e4ac11e2009-08-31 10:13:04 -0400919 printf("The operation has completed successfully.\n");
srs5694e7b4ff92009-08-18 13:16:10 -0400920 } else {
srs5694e4ac11e2009-08-31 10:13:04 -0400921 printf("Warning! An error was reported when writing the partition table! This error\n");
922 printf("MIGHT be harmless, but you may have trashed the disk! Use parted and, if\n");
923 printf("necessary, restore your original partition table.\n");
srs5694e7b4ff92009-08-18 13:16:10 -0400924 } // if/else
srs5694e4ac11e2009-08-31 10:13:04 -0400925 close(fd);
srs5694e7b4ff92009-08-18 13:16:10 -0400926 } else {
srs5694e4ac11e2009-08-31 10:13:04 -0400927 fprintf(stderr, "Unable to open device %s for writing! Errno is %d! Aborting write!\n",
928 device, errno);
929 allOK = 0;
srs5694e7b4ff92009-08-18 13:16:10 -0400930 } // if/else
931 } else {
932 printf("Aborting write of new partition table.\n");
933 } // if
934
srs56942a9f5da2009-08-26 00:48:01 -0400935 if (IsLittleEndian() == 0) {
936 // Reverse (normalize) header bytes first, since ReversePartitionBytes()
937 // requires non-reversed data in mainHeader...
938 ReverseHeaderBytes(&mainHeader);
939 ReverseHeaderBytes(&secondHeader);
940 ReversePartitionBytes();
941 } // if
942
srs5694e7b4ff92009-08-18 13:16:10 -0400943 return (allOK);
944} // GPTData::SaveGPTData()
945
946// Save GPT data to a backup file. This function does much less error
947// checking than SaveGPTData(). It can therefore preserve many types of
948// corruption for later analysis; however, it preserves only the MBR,
949// the main GPT header, the backup GPT header, and the main partition
950// table; it discards the backup partition table, since it should be
951// identical to the main partition table on healthy disks.
952int GPTData::SaveGPTBackup(char* filename) {
srs56942a9f5da2009-08-26 00:48:01 -0400953 int fd, allOK = 1;
954 uint32_t numParts;
srs5694e7b4ff92009-08-18 13:16:10 -0400955
956 if ((fd = open(filename, O_WRONLY | O_CREAT, S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH)) != -1) {
srs56942a9f5da2009-08-26 00:48:01 -0400957 // Reverse the byte order, if necessary....
958 numParts = mainHeader.numParts;
959 if (IsLittleEndian() == 0) {
960 ReversePartitionBytes();
961 ReverseHeaderBytes(&mainHeader);
962 ReverseHeaderBytes(&secondHeader);
963 } // if
964
srs5694978041c2009-09-21 20:51:47 -0400965 // Recomputing the CRCs is likely to alter them, which could be bad
966 // if the intent is to save a potentially bad GPT for later analysis;
967 // but if we don't do this, we get bogus errors when we load the
968 // backup. I'm favoring misses over false alarms....
969 RecomputeCRCs();
970
srs56942a9f5da2009-08-26 00:48:01 -0400971 // Now write the protective MBR...
srs5694e7b4ff92009-08-18 13:16:10 -0400972 protectiveMBR.WriteMBRData(fd);
973
974 // Now write the main GPT header...
975 if (allOK)
srs56941e093722010-01-05 00:14:19 -0500976 if (myWrite(fd, (char*) &mainHeader, 512) == -1)
srs5694e7b4ff92009-08-18 13:16:10 -0400977 allOK = 0;
978
979 // Now write the secondary GPT header...
980 if (allOK)
srs56941e093722010-01-05 00:14:19 -0500981 if (myWrite(fd, (char*) &secondHeader, 512) == -1)
srs5694e4ac11e2009-08-31 10:13:04 -0400982 allOK = 0;
srs5694e7b4ff92009-08-18 13:16:10 -0400983
984 // Now write the main partition tables...
985 if (allOK) {
srs56941e093722010-01-05 00:14:19 -0500986 if (myWrite(fd, (char*) partitions, GPT_SIZE * numParts) == -1)
srs5694e7b4ff92009-08-18 13:16:10 -0400987 allOK = 0;
988 } // if
989
990 if (allOK) { // writes completed OK
991 printf("The operation has completed successfully.\n");
992 } else {
993 printf("Warning! An error was reported when writing the backup file.\n");
srs56948bb78762009-11-24 15:43:49 -0500994 printf("It may not be usable!\n");
srs5694e7b4ff92009-08-18 13:16:10 -0400995 } // if/else
996 close(fd);
srs56942a9f5da2009-08-26 00:48:01 -0400997
998 // Now reverse the byte-order reversal, if necessary....
999 if (IsLittleEndian() == 0) {
1000 ReverseHeaderBytes(&mainHeader);
1001 ReverseHeaderBytes(&secondHeader);
1002 ReversePartitionBytes();
1003 } // if
srs5694e7b4ff92009-08-18 13:16:10 -04001004 } else {
1005 fprintf(stderr, "Unable to open file %s for writing! Aborting!\n", filename);
1006 allOK = 0;
1007 } // if/else
1008 return allOK;
1009} // GPTData::SaveGPTBackup()
1010
1011// Load GPT data from a backup file created by SaveGPTBackup(). This function
1012// does minimal error checking. It returns 1 if it completed successfully,
1013// 0 if there was a problem. In the latter case, it creates a new empty
1014// set of partitions.
1015int GPTData::LoadGPTBackup(char* filename) {
1016 int fd, allOK = 1, val;
1017 uint32_t numParts, sizeOfEntries, sizeOfParts, newCRC;
srs56942a9f5da2009-08-26 00:48:01 -04001018 int littleEndian = 1;
srs5694e7b4ff92009-08-18 13:16:10 -04001019
1020 if ((fd = open(filename, O_RDONLY)) != -1) {
srs56942a9f5da2009-08-26 00:48:01 -04001021 if (IsLittleEndian() == 0)
1022 littleEndian = 0;
1023
srs5694e7b4ff92009-08-18 13:16:10 -04001024 // Let the MBRData class load the saved MBR...
srs5694221e0872009-08-29 15:00:31 -04001025 protectiveMBR.ReadMBRData(fd, 0); // 0 = don't check block size
srs5694e7b4ff92009-08-18 13:16:10 -04001026
1027 // Load the main GPT header, check its vaility, and set the GPT
1028 // size based on the data
srs56941e093722010-01-05 00:14:19 -05001029 if (myRead(fd, (char*) &mainHeader, 512)) {
srs56945d58fe02010-01-03 20:57:08 -05001030 fprintf(stderr, "Warning! Read error %d; strange behavior now likely!\n", errno);
1031 } // if
srs5694e7b4ff92009-08-18 13:16:10 -04001032 mainCrcOk = CheckHeaderCRC(&mainHeader);
1033
srs56942a9f5da2009-08-26 00:48:01 -04001034 // Reverse byte order, if necessary
1035 if (littleEndian == 0) {
1036 ReverseHeaderBytes(&mainHeader);
1037 } // if
1038
srs5694e7b4ff92009-08-18 13:16:10 -04001039 // Load the backup GPT header in much the same way as the main
1040 // GPT header....
srs56941e093722010-01-05 00:14:19 -05001041 if (myRead(fd, (char*) &secondHeader, 512) != 512) {
srs56945d58fe02010-01-03 20:57:08 -05001042 fprintf(stderr, "Warning! Read error %d; strange behavior now likely!\n", errno);
1043 } // if
srs5694e7b4ff92009-08-18 13:16:10 -04001044 secondCrcOk = CheckHeaderCRC(&secondHeader);
1045
srs56942a9f5da2009-08-26 00:48:01 -04001046 // Reverse byte order, if necessary
1047 if (littleEndian == 0) {
1048 ReverseHeaderBytes(&secondHeader);
1049 } // if
1050
srs5694e7b4ff92009-08-18 13:16:10 -04001051 // Return valid headers code: 0 = both headers bad; 1 = main header
1052 // good, backup bad; 2 = backup header good, main header bad;
1053 // 3 = both headers good. Note these codes refer to valid GPT
1054 // signatures and version numbers; more subtle problems will elude
1055 // this check!
1056 if ((val = CheckHeaderValidity()) > 0) {
1057 if (val == 2) { // only backup header seems to be good
1058 numParts = secondHeader.numParts;
srs5694e4ac11e2009-08-31 10:13:04 -04001059 sizeOfEntries = secondHeader.sizeOfPartitionEntries;
srs5694e7b4ff92009-08-18 13:16:10 -04001060 } else { // main header is OK
1061 numParts = mainHeader.numParts;
1062 sizeOfEntries = mainHeader.sizeOfPartitionEntries;
1063 } // if/else
1064
1065 SetGPTSize(numParts);
1066
1067 // If current disk size doesn't match that of backup....
1068 if (secondHeader.currentLBA != diskSize - UINT64_C(1)) {
1069 printf("Warning! Current disk size doesn't match that of the backup!\n"
srs5694e4ac11e2009-08-31 10:13:04 -04001070 "Adjusting sizes to match, but subsequent problems are possible!\n");
srs5694247657a2009-11-26 18:36:12 -05001071 MoveSecondHeaderToEnd();
srs5694e7b4ff92009-08-18 13:16:10 -04001072 } // if
1073
1074 // Load main partition table, and record whether its CRC
1075 // matches the stored value
1076 sizeOfParts = numParts * sizeOfEntries;
srs56941e093722010-01-05 00:14:19 -05001077 if (myRead(fd, (char*) partitions, sizeOfParts) != sizeOfParts) {
srs56945d58fe02010-01-03 20:57:08 -05001078 fprintf(stderr, "Warning! Read error %d; strange behavior now likely!\n", errno);
1079 } // if
srs5694e7b4ff92009-08-18 13:16:10 -04001080
1081 newCRC = chksum_crc32((unsigned char*) partitions, sizeOfParts);
1082 mainPartsCrcOk = (newCRC == mainHeader.partitionEntriesCRC);
1083 secondPartsCrcOk = (newCRC == secondHeader.partitionEntriesCRC);
srs56942a9f5da2009-08-26 00:48:01 -04001084 // Reverse byte order, if necessary
srs5694e4ac11e2009-08-31 10:13:04 -04001085 if (littleEndian == 0) {
1086 ReversePartitionBytes();
1087 } // if
srs56942a9f5da2009-08-26 00:48:01 -04001088
srs5694e7b4ff92009-08-18 13:16:10 -04001089 } else {
1090 allOK = 0;
1091 } // if/else
1092 } else {
1093 allOK = 0;
1094 fprintf(stderr, "Unable to open file %s for reading! Aborting!\n", filename);
1095 } // if/else
1096
1097 // Something went badly wrong, so blank out partitions
1098 if (allOK == 0) {
1099 ClearGPTData();
1100 protectiveMBR.MakeProtectiveMBR();
1101 } // if
1102 return allOK;
1103} // GPTData::LoadGPTBackup()
1104
srs5694e4ac11e2009-08-31 10:13:04 -04001105// Tell user whether Apple Partition Map (APM) was discovered....
1106void GPTData::ShowAPMState(void) {
1107 if (apmFound)
1108 printf(" APM: present\n");
1109 else
1110 printf(" APM: not present\n");
1111} // GPTData::ShowAPMState()
1112
1113// Tell user about the state of the GPT data....
1114void GPTData::ShowGPTState(void) {
1115 switch (state) {
1116 case gpt_invalid:
1117 printf(" GPT: not present\n");
1118 break;
1119 case gpt_valid:
1120 printf(" GPT: present\n");
1121 break;
1122 case gpt_corrupt:
1123 printf(" GPT: damaged\n");
1124 break;
1125 default:
1126 printf("\a GPT: unknown -- bug!\n");
1127 break;
1128 } // switch
1129} // GPTData::ShowGPTState()
1130
1131// Display the basic GPT data
1132void GPTData::DisplayGPTData(void) {
srs5694e35eb1b2009-09-14 00:29:34 -04001133 int i;
srs5694e4ac11e2009-08-31 10:13:04 -04001134 char sizeInSI[255]; // String to hold size of disk in SI units
1135 char tempStr[255];
1136 uint64_t temp, totalFree;
1137
1138 BytesToSI(diskSize * blockSize, sizeInSI);
srs5694978041c2009-09-21 20:51:47 -04001139 printf("Disk %s: %llu sectors, %s\n", device,
1140 (unsigned long long) diskSize, sizeInSI);
srs56941e093722010-01-05 00:14:19 -05001141 printf("Logical sector size: %d bytes\n", blockSize);
srs5694e4ac11e2009-08-31 10:13:04 -04001142 printf("Disk identifier (GUID): %s\n", GUIDToStr(mainHeader.diskGUID, tempStr));
1143 printf("Partition table holds up to %lu entries\n", (unsigned long) mainHeader.numParts);
srs56945d58fe02010-01-03 20:57:08 -05001144 printf("First usable sector is %llu, last usable sector is %llu\n",
1145 (unsigned long long) mainHeader.firstUsableLBA,
1146 (unsigned long long) mainHeader.lastUsableLBA);
srs5694e4ac11e2009-08-31 10:13:04 -04001147 totalFree = FindFreeBlocks(&i, &temp);
srs56945d58fe02010-01-03 20:57:08 -05001148 printf("Total free space is %llu sectors (%s)\n", (unsigned long long) totalFree,
srs5694e4ac11e2009-08-31 10:13:04 -04001149 BytesToSI(totalFree * (uint64_t) blockSize, sizeInSI));
1150 printf("\nNumber Start (sector) End (sector) Size Code Name\n");
1151 for (i = 0; i < mainHeader.numParts; i++) {
srs5694978041c2009-09-21 20:51:47 -04001152 partitions[i].ShowSummary(i, blockSize);
srs5694e4ac11e2009-08-31 10:13:04 -04001153 } // for
1154} // GPTData::DisplayGPTData()
1155
1156// Get partition number from user and then call ShowPartDetails(partNum)
1157// to show its detailed information
1158void GPTData::ShowDetails(void) {
1159 int partNum;
1160 uint32_t low, high;
1161
1162 if (GetPartRange(&low, &high) > 0) {
1163 partNum = GetPartNum();
1164 ShowPartDetails(partNum);
1165 } else {
1166 printf("No partitions\n");
1167 } // if/else
1168} // GPTData::ShowDetails()
1169
1170// Show detailed information on the specified partition
1171void GPTData::ShowPartDetails(uint32_t partNum) {
1172 if (partitions[partNum].GetFirstLBA() != 0) {
1173 partitions[partNum].ShowDetails(blockSize);
1174 } else {
1175 printf("Partition #%d does not exist.", (int) (partNum + 1));
1176 } // if
1177} // GPTData::ShowPartDetails()
1178
1179/*********************************************************************
1180 * *
1181 * Begin functions that obtain information from the users, and often *
1182 * do something with that information (call other functions) *
1183 * *
1184 *********************************************************************/
1185
1186// Prompts user for partition number and returns the result.
1187uint32_t GPTData::GetPartNum(void) {
1188 uint32_t partNum;
1189 uint32_t low, high;
1190 char prompt[255];
1191
1192 if (GetPartRange(&low, &high) > 0) {
1193 sprintf(prompt, "Partition number (%d-%d): ", low + 1, high + 1);
1194 partNum = GetNumber(low + 1, high + 1, low, prompt);
1195 } else partNum = 1;
1196 return (partNum - 1);
1197} // GPTData::GetPartNum()
1198
1199// What it says: Resize the partition table. (Default is 128 entries.)
1200void GPTData::ResizePartitionTable(void) {
1201 int newSize;
1202 char prompt[255];
1203 uint32_t curLow, curHigh;
1204
1205 printf("Current partition table size is %lu.\n",
1206 (unsigned long) mainHeader.numParts);
1207 GetPartRange(&curLow, &curHigh);
1208 curHigh++; // since GetPartRange() returns numbers starting from 0...
1209 // There's no point in having fewer than four partitions....
1210 if (curHigh < 4)
1211 curHigh = 4;
1212 sprintf(prompt, "Enter new size (%d up, default %d): ", (int) curHigh,
1213 (int) NUM_GPT_ENTRIES);
1214 newSize = GetNumber(4, 65535, 128, prompt);
1215 if (newSize < 128) {
1216 printf("Caution: The partition table size should officially be 16KB or larger,\n"
1217 "which works out to 128 entries. In practice, smaller tables seem to\n"
1218 "work with most OSes, but this practice is risky. I'm proceeding with\n"
1219 "the resize, but you may want to reconsider this action and undo it.\n\n");
1220 } // if
1221 SetGPTSize(newSize);
1222} // GPTData::ResizePartitionTable()
1223
1224// Interactively create a partition
1225void GPTData::CreatePartition(void) {
1226 uint64_t firstBlock, firstInLargest, lastBlock, sector;
1227 char prompt[255];
1228 int partNum, firstFreePart = 0;
1229
1230 // Find first free partition...
1231 while (partitions[firstFreePart].GetFirstLBA() != 0) {
1232 firstFreePart++;
1233 } // while
1234
1235 if (((firstBlock = FindFirstAvailable()) != 0) &&
1236 (firstFreePart < mainHeader.numParts)) {
1237 lastBlock = FindLastAvailable(firstBlock);
1238 firstInLargest = FindFirstInLargest();
1239
1240 // Get partition number....
1241 do {
1242 sprintf(prompt, "Partition number (%d-%d, default %d): ", firstFreePart + 1,
1243 mainHeader.numParts, firstFreePart + 1);
1244 partNum = GetNumber(firstFreePart + 1, mainHeader.numParts,
1245 firstFreePart + 1, prompt) - 1;
1246 if (partitions[partNum].GetFirstLBA() != 0)
1247 printf("partition %d is in use.\n", partNum + 1);
1248 } while (partitions[partNum].GetFirstLBA() != 0);
1249
1250 // Get first block for new partition...
srs56945d58fe02010-01-03 20:57:08 -05001251 sprintf(prompt, "First sector (%llu-%llu, default = %llu) or {+-}size{KMGT}: ",
1252 (unsigned long long) firstBlock, (unsigned long long) lastBlock,
1253 (unsigned long long) firstInLargest);
srs5694e4ac11e2009-08-31 10:13:04 -04001254 do {
1255 sector = GetSectorNum(firstBlock, lastBlock, firstInLargest, prompt);
1256 } while (IsFree(sector) == 0);
srs56941d1448a2009-12-31 21:20:19 -05001257 Align(&sector); // Align sector to correct multiple
srs5694e4ac11e2009-08-31 10:13:04 -04001258 firstBlock = sector;
1259
1260 // Get last block for new partitions...
1261 lastBlock = FindLastInFree(firstBlock);
srs56945d58fe02010-01-03 20:57:08 -05001262 sprintf(prompt, "Last sector (%llu-%llu, default = %llu) or {+-}size{KMGT}: ",
1263 (unsigned long long) firstBlock, (unsigned long long) lastBlock,
1264 (unsigned long long) lastBlock);
srs5694e4ac11e2009-08-31 10:13:04 -04001265 do {
1266 sector = GetSectorNum(firstBlock, lastBlock, lastBlock, prompt);
1267 } while (IsFree(sector) == 0);
1268 lastBlock = sector;
1269
srs5694ba00fed2010-01-12 18:18:36 -05001270 firstFreePart = CreatePartition(partNum, firstBlock, lastBlock);
srs5694e4ac11e2009-08-31 10:13:04 -04001271 partitions[partNum].ChangeType();
1272 partitions[partNum].SetName((unsigned char*) partitions[partNum].GetNameType(prompt));
srs5694ba00fed2010-01-12 18:18:36 -05001273 } else {
1274 printf("No free sectors available\n");
1275 } // if/else
srs5694e4ac11e2009-08-31 10:13:04 -04001276} // GPTData::CreatePartition()
1277
1278// Interactively delete a partition (duh!)
1279void GPTData::DeletePartition(void) {
1280 int partNum;
1281 uint32_t low, high;
srs5694e4ac11e2009-08-31 10:13:04 -04001282 char prompt[255];
1283
1284 if (GetPartRange(&low, &high) > 0) {
1285 sprintf(prompt, "Partition number (%d-%d): ", low + 1, high + 1);
1286 partNum = GetNumber(low + 1, high + 1, low, prompt);
srs5694ba00fed2010-01-12 18:18:36 -05001287 DeletePartition(partNum - 1);
srs5694e4ac11e2009-08-31 10:13:04 -04001288 } else {
1289 printf("No partitions\n");
1290 } // if/else
1291} // GPTData::DeletePartition()
1292
1293// Prompt user for a partition number, then change its type code
1294// using ChangeGPTType(struct GPTPartition*) function.
1295void GPTData::ChangePartType(void) {
1296 int partNum;
1297 uint32_t low, high;
1298
1299 if (GetPartRange(&low, &high) > 0) {
1300 partNum = GetPartNum();
1301 partitions[partNum].ChangeType();
1302 } else {
1303 printf("No partitions\n");
1304 } // if/else
1305} // GPTData::ChangePartType()
1306
1307// Partition attributes seem to be rarely used, but I want a way to
1308// adjust them for completeness....
1309void GPTData::SetAttributes(uint32_t partNum) {
1310 Attributes theAttr;
1311
1312 theAttr.SetAttributes(partitions[partNum].GetAttributes());
1313 theAttr.DisplayAttributes();
1314 theAttr.ChangeAttributes();
1315 partitions[partNum].SetAttributes(theAttr.GetAttributes());
1316} // GPTData::SetAttributes()
1317
srs5694c0ca8f82009-08-20 21:35:25 -04001318// This function destroys the on-disk GPT structures. Returns 1 if the
1319// user confirms destruction, 0 if the user aborts.
srs5694978041c2009-09-21 20:51:47 -04001320// If prompt == 0, don't ask user about proceeding and do NOT wipe out
1321// MBR. (Set prompt == 0 when doing a GPT-to-MBR conversion.)
srs5694ba00fed2010-01-12 18:18:36 -05001322// If prompt == -1, don't ask user about proceeding and DO wipe out
1323// MBR.
srs5694978041c2009-09-21 20:51:47 -04001324int GPTData::DestroyGPT(int prompt) {
srs56941e093722010-01-05 00:14:19 -05001325 int fd, i, sum, tableSize;
srs5694978041c2009-09-21 20:51:47 -04001326 char blankSector[512], goOn = 'Y', blank = 'N';
srs56941e093722010-01-05 00:14:19 -05001327 char* emptyTable;
srs5694c0ca8f82009-08-20 21:35:25 -04001328
1329 for (i = 0; i < 512; i++) {
1330 blankSector[i] = '\0';
1331 } // for
1332
srs5694ba00fed2010-01-12 18:18:36 -05001333 if (((apmFound) || (bsdFound)) && (prompt > 0)) {
srs5694e35eb1b2009-09-14 00:29:34 -04001334 printf("WARNING: APM or BSD disklabel structures detected! This operation could\n"
1335 "damage any APM or BSD partitions on this disk!\n");
1336 } // if APM or BSD
srs5694ba00fed2010-01-12 18:18:36 -05001337 if (prompt > 0) {
srs5694978041c2009-09-21 20:51:47 -04001338 printf("\a\aAbout to wipe out GPT on %s. Proceed? ", device);
1339 goOn = GetYN();
1340 } // if
srs5694c0ca8f82009-08-20 21:35:25 -04001341 if (goOn == 'Y') {
1342 fd = open(device, O_WRONLY);
1343#ifdef __APPLE__
1344 // MacOS X requires a shared lock under some circumstances....
1345 if (fd < 0) {
1346 fd = open(device, O_WRONLY|O_SHLOCK);
1347 } // if
1348#endif
1349 if (fd != -1) {
srs56941e093722010-01-05 00:14:19 -05001350 lseek64(fd, mainHeader.currentLBA * blockSize, SEEK_SET); // seek to GPT header
1351 if (myWrite(fd, blankSector, 512) != 512) { // blank it out
srs56945d58fe02010-01-03 20:57:08 -05001352 fprintf(stderr, "Warning! GPT main header not overwritten! Error is %d\n", errno);
1353 } // if
srs56941e093722010-01-05 00:14:19 -05001354 lseek64(fd, mainHeader.partitionEntriesLBA * blockSize, SEEK_SET); // seek to partition table
1355 tableSize = mainHeader.numParts * mainHeader.sizeOfPartitionEntries;
1356 emptyTable = (char*) malloc(tableSize);
1357 for (i = 0; i < tableSize; i++)
1358 emptyTable[i] = (char) 0;
1359 sum = myWrite(fd, emptyTable, tableSize);
1360 if (sum != tableSize)
srs56945d58fe02010-01-03 20:57:08 -05001361 fprintf(stderr, "Warning! GPT main partition table not overwritten! Error is %d\n", errno);
srs56941e093722010-01-05 00:14:19 -05001362 lseek64(fd, secondHeader.partitionEntriesLBA * blockSize, SEEK_SET); // seek to partition table
1363 sum = myWrite(fd, emptyTable, tableSize);
1364 if (sum != tableSize)
srs56945d58fe02010-01-03 20:57:08 -05001365 fprintf(stderr, "Warning! GPT backup partition table not overwritten! Error is %d\n", errno);
srs56941e093722010-01-05 00:14:19 -05001366 lseek64(fd, secondHeader.currentLBA * blockSize, SEEK_SET); // seek to GPT header
1367 if (myWrite(fd, blankSector, 512) != 512) { // blank it out
srs56945d58fe02010-01-03 20:57:08 -05001368 fprintf(stderr, "Warning! GPT backup header not overwritten! Error is %d\n", errno);
1369 } // if
srs5694ba00fed2010-01-12 18:18:36 -05001370 if (prompt > 0) {
srs5694978041c2009-09-21 20:51:47 -04001371 printf("Blank out MBR? ");
1372 blank = GetYN();
srs5694ba00fed2010-01-12 18:18:36 -05001373 } // if
srs5694978041c2009-09-21 20:51:47 -04001374 // Note on below: Touch the MBR only if the user wants it completely
1375 // blanked out. Version 0.4.2 deleted the 0xEE partition and re-wrote
1376 // the MBR, but this could wipe out a valid MBR that the program
1377 // had subsequently discarded (say, if it conflicted with older GPT
1378 // structures).
srs5694ba00fed2010-01-12 18:18:36 -05001379 if ((blank == 'Y') || (prompt < 0)) {
srs5694c0ca8f82009-08-20 21:35:25 -04001380 lseek64(fd, 0, SEEK_SET);
srs56941e093722010-01-05 00:14:19 -05001381 if (myWrite(fd, blankSector, 512) != 512) { // blank it out
srs56945d58fe02010-01-03 20:57:08 -05001382 fprintf(stderr, "Warning! MBR not overwritten! Error is %d!\n", errno);
1383 } // if
srs5694978041c2009-09-21 20:51:47 -04001384 } else {
1385 printf("MBR is unchanged. You may need to delete an EFI GPT (0xEE) partition\n"
1386 "with fdisk or another tool.\n");
srs5694e35eb1b2009-09-14 00:29:34 -04001387 } // if/else
1388 DiskSync(fd);
srs5694c0ca8f82009-08-20 21:35:25 -04001389 close(fd);
srs5694e4ac11e2009-08-31 10:13:04 -04001390 printf("GPT data structures destroyed! You may now partition the disk using fdisk or\n"
1391 "other utilities. Program will now terminate.\n");
srs5694c0ca8f82009-08-20 21:35:25 -04001392 } else {
srs56941d1448a2009-12-31 21:20:19 -05001393 printf("Problem opening %s for writing! Program will now terminate.\n", device);
srs5694c0ca8f82009-08-20 21:35:25 -04001394 } // if/else (fd != -1)
1395 } // if (goOn == 'Y')
1396 return (goOn == 'Y');
1397} // GPTData::DestroyGPT()
1398
srs5694e4ac11e2009-08-31 10:13:04 -04001399/**************************************************************************
1400 * *
1401 * Partition table transformation functions (MBR or BSD disklabel to GPT) *
1402 * (some of these functions may require user interaction) *
1403 * *
1404 **************************************************************************/
1405
1406// Examines the MBR & GPT data, and perhaps asks the user questions, to
1407// determine which set of data to use: the MBR (use_mbr), the GPT (use_gpt),
1408// or create a new set of partitions (use_new)
1409WhichToUse GPTData::UseWhichPartitions(void) {
1410 WhichToUse which = use_new;
1411 MBRValidity mbrState;
1412 int answer;
1413
1414 mbrState = protectiveMBR.GetValidity();
1415
1416 if ((state == gpt_invalid) && ((mbrState == mbr) || (mbrState == hybrid))) {
srs56945d58fe02010-01-03 20:57:08 -05001417 printf("\n***************************************************************\n"
1418 "Found invalid GPT and valid MBR; converting MBR to GPT format.\n");
1419 if (!justLooking) {
1420 printf("\aTHIS OPERATON IS POTENTIALLY DESTRUCTIVE! Exit by typing 'q' if\n"
1421 "you don't want to convert your MBR partitions to GPT format!\n");
1422 } // if
1423 printf("***************************************************************\n\n");
srs5694e4ac11e2009-08-31 10:13:04 -04001424 which = use_mbr;
1425 } // if
1426
1427 if ((state == gpt_invalid) && bsdFound) {
srs56945d58fe02010-01-03 20:57:08 -05001428 printf("\n**********************************************************************\n"
srs5694e4ac11e2009-08-31 10:13:04 -04001429 "Found invalid GPT and valid BSD disklabel; converting BSD disklabel\n"
srs56945d58fe02010-01-03 20:57:08 -05001430 "to GPT format.");
1431 if (!justLooking) {
1432 printf("\a THIS OPERATON IS POTENTIALLY DESTRUCTIVE! Your first\n"
srs5694e4ac11e2009-08-31 10:13:04 -04001433 "BSD partition will likely be unusable. Exit by typing 'q' if you don't\n"
srs56945d58fe02010-01-03 20:57:08 -05001434 "want to convert your BSD partitions to GPT format!");
1435 } // if
1436 printf("\n**********************************************************************\n\n");
srs5694e4ac11e2009-08-31 10:13:04 -04001437 which = use_bsd;
1438 } // if
1439
1440 if ((state == gpt_valid) && (mbrState == gpt)) {
srs5694e4ac11e2009-08-31 10:13:04 -04001441 which = use_gpt;
srs56943c0af382010-01-15 19:19:18 -05001442 if (!beQuiet)
1443 printf("Found valid GPT with protective MBR; using GPT.\n");
srs5694e4ac11e2009-08-31 10:13:04 -04001444 } // if
1445 if ((state == gpt_valid) && (mbrState == hybrid)) {
srs5694e4ac11e2009-08-31 10:13:04 -04001446 which = use_gpt;
srs56943c0af382010-01-15 19:19:18 -05001447 if (!beQuiet)
1448 printf("Found valid GPT with hybrid MBR; using GPT.\n");
srs5694e4ac11e2009-08-31 10:13:04 -04001449 } // if
1450 if ((state == gpt_valid) && (mbrState == invalid)) {
1451 printf("\aFound valid GPT with corrupt MBR; using GPT and will create new\nprotective MBR on save.\n");
1452 which = use_gpt;
1453 protectiveMBR.MakeProtectiveMBR();
1454 } // if
1455 if ((state == gpt_valid) && (mbrState == mbr)) {
srs56943c0af382010-01-15 19:19:18 -05001456 if (!beQuiet) {
1457 printf("Found valid MBR and GPT. Which do you want to use?\n");
1458 answer = GetNumber(1, 3, 2, (char*) " 1 - MBR\n 2 - GPT\n 3 - Create blank GPT\n\nYour answer: ");
1459 if (answer == 1) {
1460 which = use_mbr;
1461 } else if (answer == 2) {
1462 which = use_gpt;
1463 protectiveMBR.MakeProtectiveMBR();
1464 printf("Using GPT and creating fresh protective MBR.\n");
1465 } else which = use_new;
1466 } else which = use_abort;
srs5694e4ac11e2009-08-31 10:13:04 -04001467 } // if
1468
1469 // Nasty decisions here -- GPT is present, but corrupt (bad CRCs or other
1470 // problems)
1471 if (state == gpt_corrupt) {
srs56943c0af382010-01-15 19:19:18 -05001472 if (beQuiet) {
1473 which = use_abort;
1474 } else {
1475 if ((mbrState == mbr) || (mbrState == hybrid)) {
1476 printf("Found valid MBR and corrupt GPT. Which do you want to use? (Using the\n"
1477 "GPT MAY permit recovery of GPT data.)\n");
1478 answer = GetNumber(1, 3, 2, (char*) " 1 - MBR\n 2 - GPT\n 3 - Create blank GPT\n\nYour answer: ");
1479 if (answer == 1) {
1480 which = use_mbr;
1481 // protectiveMBR.MakeProtectiveMBR();
1482 } else if (answer == 2) {
1483 which = use_gpt;
1484 } else which = use_new;
1485 } else if (mbrState == invalid) {
1486 printf("Found invalid MBR and corrupt GPT. What do you want to do? (Using the\n"
1487 "GPT MAY permit recovery of GPT data.)\n");
1488 answer = GetNumber(1, 2, 1, (char*) " 1 - GPT\n 2 - Create blank GPT\n\nYour answer: ");
1489 if (answer == 1) {
1490 which = use_gpt;
1491 } else which = use_new;
1492 } else { // corrupt GPT, MBR indicates it's a GPT disk....
1493 printf("\a\a****************************************************************************\n"
1494 "Caution: Found protective or hybrid MBR and corrupt GPT. Using GPT, but disk\n"
1495 "verification and recovery are STRONGLY recommended.\n"
1496 "****************************************************************************\n");
srs5694e4ac11e2009-08-31 10:13:04 -04001497 which = use_gpt;
srs56943c0af382010-01-15 19:19:18 -05001498 } // if/else/else
1499 } // else (beQuiet)
srs5694e4ac11e2009-08-31 10:13:04 -04001500 } // if (corrupt GPT)
1501
1502 if (which == use_new)
1503 printf("Creating new GPT entries.\n");
1504
1505 return which;
1506} // UseWhichPartitions()
1507
1508// Convert MBR partition table into GPT form
1509int GPTData::XFormPartitions(void) {
1510 int i, numToConvert;
1511 uint8_t origType;
1512 struct newGUID;
srs5694e4ac11e2009-08-31 10:13:04 -04001513
1514 // Clear out old data & prepare basics....
1515 ClearGPTData();
1516
1517 // Convert the smaller of the # of GPT or MBR partitions
srs5694978041c2009-09-21 20:51:47 -04001518 if (mainHeader.numParts > (MAX_MBR_PARTS))
1519 numToConvert = MAX_MBR_PARTS;
srs5694e4ac11e2009-08-31 10:13:04 -04001520 else
1521 numToConvert = mainHeader.numParts;
1522
1523 for (i = 0; i < numToConvert; i++) {
1524 origType = protectiveMBR.GetType(i);
1525 // don't waste CPU time trying to convert extended, hybrid protective, or
1526 // null (non-existent) partitions
srs5694e35eb1b2009-09-14 00:29:34 -04001527 if ((origType != 0x05) && (origType != 0x0f) && (origType != 0x85) &&
srs5694e4ac11e2009-08-31 10:13:04 -04001528 (origType != 0x00) && (origType != 0xEE))
1529 partitions[i] = protectiveMBR.AsGPT(i);
1530 } // for
1531
1532 // Convert MBR into protective MBR
1533 protectiveMBR.MakeProtectiveMBR();
1534
1535 // Record that all original CRCs were OK so as not to raise flags
1536 // when doing a disk verification
1537 mainCrcOk = secondCrcOk = mainPartsCrcOk = secondPartsCrcOk = 1;
1538
1539 return (1);
1540} // GPTData::XFormPartitions()
1541
1542// Transforms BSD disklabel on the specified partition (numbered from 0).
1543// If an invalid partition number is given, the program prompts for one.
1544// Returns the number of new partitions created.
1545int GPTData::XFormDisklabel(int i) {
1546 uint32_t low, high, partNum, startPart;
1547 uint16_t hexCode;
1548 int goOn = 1, numDone = 0;
1549 BSDData disklabel;
1550
1551 if (GetPartRange(&low, &high) != 0) {
1552 if ((i < low) || (i > high))
1553 partNum = GetPartNum();
1554 else
1555 partNum = (uint32_t) i;
1556
1557 // Find the partition after the last used one
1558 startPart = high + 1;
1559
1560 // Now see if the specified partition has a BSD type code....
1561 hexCode = partitions[partNum].GetHexType();
1562 if ((hexCode != 0xa500) && (hexCode != 0xa900)) {
1563 printf("Specified partition doesn't have a disklabel partition type "
1564 "code.\nContinue anyway?");
1565 goOn = (GetYN() == 'Y');
1566 } // if
1567
1568 // If all is OK, read the disklabel and convert it.
1569 if (goOn) {
1570 goOn = disklabel.ReadBSDData(device, partitions[partNum].GetFirstLBA(),
1571 partitions[partNum].GetLastLBA());
1572 if ((goOn) && (disklabel.IsDisklabel())) {
1573 numDone = XFormDisklabel(&disklabel, startPart);
1574 if (numDone == 1)
1575 printf("Converted %d BSD partition.\n", numDone);
1576 else
1577 printf("Converted %d BSD partitions.\n", numDone);
1578 } else {
1579 printf("Unable to convert partitions! Unrecognized BSD disklabel.\n");
1580 } // if/else
1581 } // if
1582 if (numDone > 0) { // converted partitions; delete carrier
1583 partitions[partNum].BlankPartition();
1584 } // if
1585 } else {
1586 printf("No partitions\n");
1587 } // if/else
1588 return numDone;
1589} // GPTData::XFormDisklable(int i)
1590
1591// Transform the partitions on an already-loaded BSD disklabel...
1592int GPTData::XFormDisklabel(BSDData* disklabel, int startPart) {
1593 int i, numDone = 0;
1594
1595 if ((disklabel->IsDisklabel()) && (startPart >= 0) &&
1596 (startPart < mainHeader.numParts)) {
1597 for (i = 0; i < disklabel->GetNumParts(); i++) {
1598 partitions[i + startPart] = disklabel->AsGPT(i);
1599 if (partitions[i + startPart].GetFirstLBA() != UINT64_C(0))
1600 numDone++;
1601 } // for
1602 } // if
1603
1604 // Record that all original CRCs were OK so as not to raise flags
1605 // when doing a disk verification
1606 mainCrcOk = secondCrcOk = mainPartsCrcOk = secondPartsCrcOk = 1;
1607
1608 return numDone;
1609} // GPTData::XFormDisklabel(BSDData* disklabel)
1610
srs5694978041c2009-09-21 20:51:47 -04001611// Add one GPT partition to MBR. Used by XFormToMBR() and MakeHybrid()
1612// functions. Returns 1 if operation was successful.
1613int GPTData::OnePartToMBR(uint32_t gptPart, int mbrPart) {
1614 int allOK = 1, typeCode, bootable;
1615 uint64_t length;
1616 char line[255];
srs56945d58fe02010-01-03 20:57:08 -05001617 char* junk;
srs5694978041c2009-09-21 20:51:47 -04001618
1619 if ((mbrPart < 0) || (mbrPart > 3)) {
1620 printf("MBR partition %d is out of range; omitting it.\n", mbrPart + 1);
1621 allOK = 0;
1622 } // if
1623 if (gptPart >= mainHeader.numParts) {
1624 printf("GPT partition %d is out of range; omitting it.\n", gptPart + 1);
1625 allOK = 0;
1626 } // if
1627 if (allOK && (partitions[gptPart].GetLastLBA() == UINT64_C(0))) {
1628 printf("GPT partition %d is undefined; omitting it.\n", gptPart + 1);
1629 allOK = 0;
1630 } // if
1631 if (allOK && (partitions[gptPart].GetFirstLBA() <= UINT32_MAX) &&
1632 (partitions[gptPart].GetLengthLBA() <= UINT32_MAX)) {
1633 if (partitions[gptPart].GetLastLBA() > UINT32_MAX) {
1634 printf("Caution: Partition end point past 32-bit pointer boundary;"
1635 " some OSes may\nreact strangely.\n");
1636 } // if partition ends past 32-bit (usually 2TiB) boundary
1637 do {
1638 printf("Enter an MBR hex code (default %02X): ",
1639 typeHelper.GUIDToID(partitions[gptPart].GetType()) / 256);
srs56945d58fe02010-01-03 20:57:08 -05001640 junk = fgets(line, 255, stdin);
srs5694978041c2009-09-21 20:51:47 -04001641 sscanf(line, "%x", &typeCode);
1642 if (line[0] == '\n')
1643 typeCode = partitions[gptPart].GetHexType() / 256;
1644 } while ((typeCode <= 0) || (typeCode > 255));
1645 printf("Set the bootable flag? ");
1646 bootable = (GetYN() == 'Y');
1647 length = partitions[gptPart].GetLengthLBA();
1648 protectiveMBR.MakePart(mbrPart, (uint32_t) partitions[gptPart].GetFirstLBA(),
1649 (uint32_t) length, typeCode, bootable);
1650 } else { // partition out of range
1651 printf("Partition %d begins beyond the 32-bit pointer limit of MBR "
1652 "partitions, or is\n too big; omitting it.\n", gptPart + 1);
1653 allOK = 0;
1654 } // if/else
1655 return allOK;
1656} // GPTData::OnePartToMBR()
1657
1658// Convert the GPT to MBR form. This function is necessarily limited; it
1659// handles at most four partitions and creates layouts that ignore CHS
1660// geometries. Returns the number of converted partitions; if this value
1661// is over 0, the calling function should call DestroyGPT() to destroy
1662// the GPT data, and then exit.
1663int GPTData::XFormToMBR(void) {
1664 char line[255];
srs56945d58fe02010-01-03 20:57:08 -05001665 char* junk;
srs5694978041c2009-09-21 20:51:47 -04001666 int i, j, numParts, numConverted = 0;
1667 uint32_t partNums[4];
1668
1669 // Get the numbers of up to four partitions to add to the
1670 // hybrid MBR....
1671 numParts = CountParts();
1672 printf("Counted %d partitions.\n", numParts);
1673
1674 // Prepare the MBR for conversion (empty it of existing partitions).
1675 protectiveMBR.EmptyMBR(0);
1676 protectiveMBR.SetDiskSize(diskSize);
1677
1678 if (numParts > 4) { // Over four partitions; engage in triage
1679 printf("Type from one to four GPT partition numbers, separated by spaces, to be\n"
1680 "used in the MBR, in sequence: ");
srs56945d58fe02010-01-03 20:57:08 -05001681 junk = fgets(line, 255, stdin);
srs5694978041c2009-09-21 20:51:47 -04001682 numParts = sscanf(line, "%d %d %d %d", &partNums[0], &partNums[1],
1683 &partNums[2], &partNums[3]);
1684 } else { // Four or fewer partitions; convert them all
1685 i = j = 0;
1686 while ((j < numParts) && (i < mainHeader.numParts)) {
1687 if (partitions[i].GetFirstLBA() > 0) { // if GPT part. is defined
1688 partNums[j++] = ++i; // flag it for conversion
1689 } else i++;
1690 } // while
1691 } // if/else
1692
1693 for (i = 0; i < numParts; i++) {
1694 j = partNums[i] - 1;
1695 printf("\nCreating entry for partition #%d\n", j + 1);
1696 numConverted += OnePartToMBR(j, i);
1697 } // for
srs5694ba00fed2010-01-12 18:18:36 -05001698 printf("MBR writing returned %d\n", protectiveMBR.WriteMBRData(device));
srs5694978041c2009-09-21 20:51:47 -04001699 return numConverted;
1700} // GPTData::XFormToMBR()
1701
srs5694e4ac11e2009-08-31 10:13:04 -04001702// Create a hybrid MBR -- an ugly, funky thing that helps GPT work with
1703// OSes that don't understand GPT.
1704void GPTData::MakeHybrid(void) {
1705 uint32_t partNums[3];
1706 char line[255];
srs56945d58fe02010-01-03 20:57:08 -05001707 char* junk;
srs5694978041c2009-09-21 20:51:47 -04001708 int numParts, numConverted = 0, i, j, typeCode, mbrNum;
srs5694e4ac11e2009-08-31 10:13:04 -04001709 char fillItUp = 'M'; // fill extra partition entries? (Yes/No/Maybe)
srs5694978041c2009-09-21 20:51:47 -04001710 char eeFirst = 'Y'; // Whether EFI GPT (0xEE) partition comes first in table
srs5694e4ac11e2009-08-31 10:13:04 -04001711
1712 printf("\nWARNING! Hybrid MBRs are flaky and potentially dangerous! If you decide not\n"
1713 "to use one, just hit the Enter key at the below prompt and your MBR\n"
1714 "partition table will be untouched.\n\n\a");
1715
1716 // Now get the numbers of up to three partitions to add to the
1717 // hybrid MBR....
1718 printf("Type from one to three GPT partition numbers, separated by spaces, to be\n"
1719 "added to the hybrid MBR, in sequence: ");
srs56945d58fe02010-01-03 20:57:08 -05001720 junk = fgets(line, 255, stdin);
srs5694e4ac11e2009-08-31 10:13:04 -04001721 numParts = sscanf(line, "%d %d %d", &partNums[0], &partNums[1], &partNums[2]);
1722
1723 if (numParts > 0) {
1724 // Blank out the protective MBR, but leave the boot loader code
1725 // alone....
1726 protectiveMBR.EmptyMBR(0);
1727 protectiveMBR.SetDiskSize(diskSize);
1728 printf("Place EFI GPT (0xEE) partition first in MBR (good for GRUB)? ");
1729 eeFirst = GetYN();
1730 } // if
1731
1732 for (i = 0; i < numParts; i++) {
1733 j = partNums[i] - 1;
1734 printf("\nCreating entry for partition #%d\n", j + 1);
srs5694978041c2009-09-21 20:51:47 -04001735 if (eeFirst == 'Y')
1736 mbrNum = i + 1;
1737 else
1738 mbrNum = i;
1739 numConverted += OnePartToMBR(j, mbrNum);
srs5694e4ac11e2009-08-31 10:13:04 -04001740 } // for
1741
srs5694978041c2009-09-21 20:51:47 -04001742 if ((numParts > 0) && (numConverted > 0)) { // User opted to create a hybrid MBR....
srs5694e4ac11e2009-08-31 10:13:04 -04001743 // Create EFI protective partition that covers the start of the disk.
1744 // If this location (covering the main GPT data structures) is omitted,
1745 // Linux won't find any partitions on the disk. Note that this is
1746 // NUMBERED AFTER the hybrid partitions, contrary to what the
1747 // gptsync utility does. This is because Windows seems to choke on
1748 // disks with a 0xEE partition in the first slot and subsequent
1749 // additional partitions, unless it boots from the disk.
1750 if (eeFirst == 'Y')
1751 mbrNum = 0;
1752 else
1753 mbrNum = numParts;
1754 protectiveMBR.MakePart(mbrNum, 1, protectiveMBR.FindLastInFree(1), 0xEE);
srs5694978041c2009-09-21 20:51:47 -04001755 protectiveMBR.SetHybrid();
srs5694e4ac11e2009-08-31 10:13:04 -04001756
1757 // ... and for good measure, if there are any partition spaces left,
1758 // optionally create another protective EFI partition to cover as much
1759 // space as possible....
1760 for (i = 0; i < 4; i++) {
1761 if (protectiveMBR.GetType(i) == 0x00) { // unused entry....
1762 if (fillItUp == 'M') {
1763 printf("\nUnused partition space(s) found. Use one to protect more partitions? ");
1764 fillItUp = GetYN();
1765 typeCode = 0x00; // use this to flag a need to get type code
1766 } // if
1767 if (fillItUp == 'Y') {
1768 while ((typeCode <= 0) || (typeCode > 255)) {
1769 printf("Enter an MBR hex code (EE is EFI GPT, but may confuse MacOS): ");
1770 // Comment on above: Mac OS treats disks with more than one
1771 // 0xEE MBR partition as MBR disks, not as GPT disks.
srs56945d58fe02010-01-03 20:57:08 -05001772 junk = fgets(line, 255, stdin);
srs5694e4ac11e2009-08-31 10:13:04 -04001773 sscanf(line, "%x", &typeCode);
1774 if (line[0] == '\n')
1775 typeCode = 0;
1776 } // while
1777 protectiveMBR.MakeBiggestPart(i, typeCode); // make a partition
1778 } // if (fillItUp == 'Y')
1779 } // if unused entry
1780 } // for (i = 0; i < 4; i++)
1781 } // if (numParts > 0)
1782} // GPTData::MakeHybrid()
1783
1784/**********************************************************************
1785 * *
1786 * Functions that adjust GPT data structures WITHOUT user interaction *
1787 * (they may display information for the user's benefit, though) *
1788 * *
1789 **********************************************************************/
1790
1791// Resizes GPT to specified number of entries. Creates a new table if
srs5694ba00fed2010-01-12 18:18:36 -05001792// necessary, copies data if it already exists. Returns 1 if all goes
1793// well, 0 if an error is encountered.
srs5694e4ac11e2009-08-31 10:13:04 -04001794int GPTData::SetGPTSize(uint32_t numEntries) {
1795 struct GPTPart* newParts;
1796 struct GPTPart* trash;
1797 uint32_t i, high, copyNum;
1798 int allOK = 1;
1799
1800 // First, adjust numEntries upward, if necessary, to get a number
1801 // that fills the allocated sectors
1802 i = blockSize / GPT_SIZE;
1803 if ((numEntries % i) != 0) {
1804 printf("Adjusting GPT size from %lu ", (unsigned long) numEntries);
1805 numEntries = ((numEntries / i) + 1) * i;
1806 printf("to %lu to fill the sector\n", (unsigned long) numEntries);
1807 } // if
1808
srs5694247657a2009-11-26 18:36:12 -05001809 // Do the work only if the # of partitions is changing. Along with being
1810 // efficient, this prevents mucking the with location of the secondary
1811 // partition table, which causes problems when loading data from a RAID
1812 // array that's been expanded because this function is called when loading
1813 // data.
1814 if ((numEntries != mainHeader.numParts) || (partitions == NULL)) {
1815 newParts = (GPTPart*) calloc(numEntries, sizeof (GPTPart));
1816 if (newParts != NULL) {
1817 if (partitions != NULL) { // existing partitions; copy them over
1818 GetPartRange(&i, &high);
1819 if (numEntries < (high + 1)) { // Highest entry too high for new #
1820 printf("The highest-numbered partition is %lu, which is greater than the requested\n"
1821 "partition table size of %d; cannot resize. Perhaps sorting will help.\n",
1822 (unsigned long) (high + 1), numEntries);
1823 allOK = 0;
1824 } else { // go ahead with copy
1825 if (numEntries < mainHeader.numParts)
1826 copyNum = numEntries;
1827 else
1828 copyNum = mainHeader.numParts;
1829 for (i = 0; i < copyNum; i++) {
1830 newParts[i] = partitions[i];
1831 } // for
1832 trash = partitions;
1833 partitions = newParts;
1834 free(trash);
1835 } // if
1836 } else { // No existing partition table; just create it
srs5694e4ac11e2009-08-31 10:13:04 -04001837 partitions = newParts;
srs5694247657a2009-11-26 18:36:12 -05001838 } // if/else existing partitions
1839 mainHeader.numParts = numEntries;
1840 secondHeader.numParts = numEntries;
1841 mainHeader.firstUsableLBA = ((numEntries * GPT_SIZE) / blockSize) + 2 ;
1842 secondHeader.firstUsableLBA = mainHeader.firstUsableLBA;
1843 MoveSecondHeaderToEnd();
1844 if (diskSize > 0)
1845 CheckGPTSize();
1846 } else { // Bad memory allocation
1847 fprintf(stderr, "Error allocating memory for partition table!\n");
1848 allOK = 0;
1849 } // if/else
srs5694e4ac11e2009-08-31 10:13:04 -04001850 } // if/else
1851 return (allOK);
1852} // GPTData::SetGPTSize()
1853
1854// Blank the partition array
1855void GPTData::BlankPartitions(void) {
1856 uint32_t i;
1857
1858 for (i = 0; i < mainHeader.numParts; i++) {
1859 partitions[i].BlankPartition();
1860 } // for
1861} // GPTData::BlankPartitions()
1862
srs5694ba00fed2010-01-12 18:18:36 -05001863// Delete a partition by number. Returns 1 if successful,
1864// 0 if there was a problem. Returns 1 if partition was in
1865// range, 0 if it was out of range.
1866int GPTData::DeletePartition(uint32_t partNum) {
1867 uint64_t startSector, length;
1868 uint32_t low, high, numParts, retval = 1;;
1869
1870 numParts = GetPartRange(&low, &high);
1871 if ((numParts > 0) && (partNum >= low) && (partNum <= high)) {
1872 // In case there's a protective MBR, look for & delete matching
1873 // MBR partition....
1874 startSector = partitions[partNum].GetFirstLBA();
1875 length = partitions[partNum].GetLengthLBA();
1876 protectiveMBR.DeleteByLocation(startSector, length);
1877
1878 // Now delete the GPT partition
1879 partitions[partNum].BlankPartition();
1880 } else {
1881 fprintf(stderr, "Partition number %d out of range!\n", partNum + 1);
1882 retval = 0;
1883 } // if/else
1884 return retval;
1885} // GPTData::DeletePartition(uint32_t partNum)
1886
1887// Non-interactively create a partition. Note that this function is overloaded
1888// with another of the same name but different parameters; that one prompts
1889// the user for data. This one returns 1 if the operation was successful, 0
1890// if a problem was discovered.
1891int GPTData::CreatePartition(uint32_t partNum, uint64_t startSector, uint64_t endSector) {
1892 int retval = 1; // assume there'll be no problems
1893
1894 if (IsFreePartNum(partNum)) {
1895 Align(&startSector); // Align sector to correct multiple
1896 if (IsFree(startSector) && (startSector <= endSector)) {
1897 if (FindLastInFree(startSector) >= endSector) {
1898 partitions[partNum].SetFirstLBA(startSector);
1899 partitions[partNum].SetLastLBA(endSector);
1900 partitions[partNum].SetType(0x0700);
1901 partitions[partNum].SetUniqueGUID(1);
1902 } else retval = 0; // if free space until endSector
1903 } else retval = 0; // if startSector is free
1904 } else retval = 0; // if legal partition number
1905 return retval;
1906} // GPTData::CreatePartition(partNum, startSector, endSector)
1907
srs5694e4ac11e2009-08-31 10:13:04 -04001908// Sort the GPT entries, eliminating gaps and making for a logical
1909// ordering. Relies on QuickSortGPT() for the bulk of the work
1910void GPTData::SortGPT(void) {
1911 int i, lastPart = 0;
1912 GPTPart temp;
1913
1914 // First, find the last partition with data, so as not to
1915 // spend needless time sorting empty entries....
1916 for (i = 0; i < mainHeader.numParts; i++) {
1917 if (partitions[i].GetFirstLBA() > 0)
1918 lastPart = i;
1919 } // for
1920
1921 // Now swap empties with the last partitions, to simplify the logic
1922 // in the Quicksort function....
1923 i = 0;
1924 while (i < lastPart) {
1925 if (partitions[i].GetFirstLBA() == 0) {
1926 temp = partitions[i];
1927 partitions[i] = partitions[lastPart];
1928 partitions[lastPart] = temp;
1929 lastPart--;
1930 } // if
1931 i++;
1932 } // while
1933
1934 // Now call the recursive quick sort routine to do the real work....
1935 QuickSortGPT(partitions, 0, lastPart);
1936} // GPTData::SortGPT()
1937
1938// Set up data structures for entirely new set of partitions on the
1939// specified device. Returns 1 if OK, 0 if there were problems.
srs5694e35eb1b2009-09-14 00:29:34 -04001940// Note that this function does NOT clear the protectiveMBR data
1941// structure, since it may hold the original MBR partitions if the
1942// program was launched on an MBR disk, and those may need to be
1943// converted to GPT format.
srs5694e4ac11e2009-08-31 10:13:04 -04001944int GPTData::ClearGPTData(void) {
srs5694e35eb1b2009-09-14 00:29:34 -04001945 int goOn = 1, i;
srs5694e4ac11e2009-08-31 10:13:04 -04001946
1947 // Set up the partition table....
1948 free(partitions);
1949 partitions = NULL;
1950 SetGPTSize(NUM_GPT_ENTRIES);
1951
1952 // Now initialize a bunch of stuff that's static....
1953 mainHeader.signature = GPT_SIGNATURE;
1954 mainHeader.revision = 0x00010000;
srs5694978041c2009-09-21 20:51:47 -04001955 mainHeader.headerSize = HEADER_SIZE;
srs5694e4ac11e2009-08-31 10:13:04 -04001956 mainHeader.reserved = 0;
1957 mainHeader.currentLBA = UINT64_C(1);
1958 mainHeader.partitionEntriesLBA = (uint64_t) 2;
1959 mainHeader.sizeOfPartitionEntries = GPT_SIZE;
1960 for (i = 0; i < GPT_RESERVED; i++) {
1961 mainHeader.reserved2[i] = '\0';
1962 } // for
1963
1964 // Now some semi-static items (computed based on end of disk)
1965 mainHeader.backupLBA = diskSize - UINT64_C(1);
1966 mainHeader.lastUsableLBA = diskSize - mainHeader.firstUsableLBA;
1967
1968 // Set a unique GUID for the disk, based on random numbers
1969 // rand() is only 32 bits, so multiply together to fill a 64-bit value
1970 mainHeader.diskGUID.data1 = (uint64_t) rand() * (uint64_t) rand();
1971 mainHeader.diskGUID.data2 = (uint64_t) rand() * (uint64_t) rand();
1972
1973 // Copy main header to backup header
1974 RebuildSecondHeader();
1975
1976 // Blank out the partitions array....
1977 BlankPartitions();
1978
1979 // Flag all CRCs as being OK....
1980 mainCrcOk = 1;
1981 secondCrcOk = 1;
1982 mainPartsCrcOk = 1;
1983 secondPartsCrcOk = 1;
1984
1985 return (goOn);
1986} // GPTData::ClearGPTData()
1987
srs5694247657a2009-11-26 18:36:12 -05001988// Set the location of the second GPT header data to the end of the disk.
1989// Used internally and called by the 'e' option on the recovery &
1990// transformation menu, to help users of RAID arrays who add disk space
1991// to their arrays.
1992void GPTData::MoveSecondHeaderToEnd() {
srs56948bb78762009-11-24 15:43:49 -05001993 mainHeader.backupLBA = secondHeader.currentLBA = diskSize - UINT64_C(1);
1994 mainHeader.lastUsableLBA = secondHeader.lastUsableLBA = diskSize - mainHeader.firstUsableLBA;
1995 secondHeader.partitionEntriesLBA = secondHeader.lastUsableLBA + UINT64_C(1);
1996} // GPTData::FixSecondHeaderLocation()
1997
srs5694ba00fed2010-01-12 18:18:36 -05001998int GPTData::SetName(uint32_t partNum, char* theName) {
1999 int retval = 1;
2000 if (!IsFreePartNum(partNum))
2001 partitions[partNum].SetName((unsigned char*) theName);
2002 else retval = 0;
2003
2004 return retval;
srs5694e4ac11e2009-08-31 10:13:04 -04002005} // GPTData::SetName
2006
2007// Set the disk GUID to the specified value. Note that the header CRCs must
2008// be recomputed after calling this function.
2009void GPTData::SetDiskGUID(GUIDData newGUID) {
2010 mainHeader.diskGUID = newGUID;
2011 secondHeader.diskGUID = newGUID;
2012} // SetDiskGUID()
2013
2014// Set the unique GUID of the specified partition. Returns 1 on
2015// successful completion, 0 if there were problems (invalid
2016// partition number).
2017int GPTData::SetPartitionGUID(uint32_t pn, GUIDData theGUID) {
2018 int retval = 0;
2019
2020 if (pn < mainHeader.numParts) {
2021 if (partitions[pn].GetFirstLBA() != UINT64_C(0)) {
2022 partitions[pn].SetUniqueGUID(theGUID);
2023 retval = 1;
2024 } // if
2025 } // if
2026 return retval;
2027} // GPTData::SetPartitionGUID()
2028
srs5694ba00fed2010-01-12 18:18:36 -05002029// Change partition type code non-interactively. Returns 1 if
2030// successful, 0 if not....
2031int GPTData::ChangePartType(uint32_t partNum, uint16_t hexCode) {
2032 int retval = 1;
2033
2034 if (!IsFreePartNum(partNum)) {
2035 partitions[partNum].SetType(hexCode);
2036 } else retval = 0;
2037 return retval;
2038} // GPTData::ChangePartType()
2039
srs56941d1448a2009-12-31 21:20:19 -05002040// Adjust sector number so that it falls on a sector boundary that's a
2041// multiple of sectorAlignment. This is done to improve the performance
2042// of Western Digital Advanced Format disks and disks with similar
2043// technology from other companies, which use 4096-byte sectors
2044// internally although they translate to 512-byte sectors for the
2045// benefit of the OS. If partitions aren't properly aligned on these
2046// disks, some filesystem data structures can span multiple physical
2047// sectors, degrading performance. This function should be called
2048// only on the FIRST sector of the partition, not the last!
2049// This function returns 1 if the alignment was altered, 0 if it
2050// was unchanged.
2051int GPTData::Align(uint64_t* sector) {
2052 int retval = 0, sectorOK = 0;
2053 uint64_t earlier, later, testSector, original;
2054
2055 if ((*sector % sectorAlignment) != 0) {
2056 original = *sector;
2057 retval = 1;
2058 earlier = (*sector / sectorAlignment) * sectorAlignment;
2059 later = earlier + (uint64_t) sectorAlignment;
2060
2061 // Check to see that every sector between the earlier one and the
2062 // requested one is clear, and that it's not too early....
2063 if (earlier >= mainHeader.firstUsableLBA) {
2064// printf("earlier is %llu, first usable is %llu\n", earlier, mainHeader.firstUsableLBA);
2065 sectorOK = 1;
2066 testSector = earlier;
2067 do {
2068 sectorOK = IsFree(testSector++);
2069 } while ((sectorOK == 1) && (testSector < *sector));
2070 if (sectorOK == 1) {
2071 *sector = earlier;
2072// printf("Moved sector earlier.\n");
2073 } // if
2074 } // if firstUsableLBA check
2075
2076 // If couldn't move the sector earlier, try to move it later instead....
2077 if ((sectorOK != 1) && (later <= mainHeader.lastUsableLBA)) {
2078 sectorOK = 1;
2079 testSector = later;
2080 do {
2081 sectorOK = IsFree(testSector--);
2082 } while ((sectorOK == 1) && (testSector > *sector));
2083 if (sectorOK == 1) {
2084 *sector = later;
2085// printf("Moved sector later\n");
2086 } // if
2087 } // if
2088
2089 // If sector was changed successfully, inform the user of this fact.
2090 // Otherwise, notify the user that it couldn't be done....
2091 if (sectorOK == 1) {
2092 printf("Information: Moved requested sector from %llu to %llu for\n"
srs5694ba00fed2010-01-12 18:18:36 -05002093 "alignment purposes.\n",
srs56945d58fe02010-01-03 20:57:08 -05002094 (unsigned long long) original, (unsigned long long) *sector);
srs5694ba00fed2010-01-12 18:18:36 -05002095 if (!beQuiet)
2096 printf("Use 'l' on the experts' menu to adjust alignment\n");
srs56941d1448a2009-12-31 21:20:19 -05002097 } else {
2098 printf("Information: Sector not aligned on %d-sector boundary and could not be moved.\n"
2099 "If you're using a Western Digital Advanced Format or similar disk with\n"
2100 "underlying 4096-byte sectors, performance may suffer.\n", sectorAlignment);
2101 retval = 0;
2102 } // if/else
2103 } // if
2104 return retval;
2105} // GPTData::Align()
2106
srs5694e4ac11e2009-08-31 10:13:04 -04002107/********************************************************
2108 * *
2109 * Functions that return data about GPT data structures *
2110 * (most of these are inline in gpt.h) *
2111 * *
2112 ********************************************************/
2113
2114// Find the low and high used partition numbers (numbered from 0).
2115// Return value is the number of partitions found. Note that the
2116// *low and *high values are both set to 0 when no partitions
2117// are found, as well as when a single partition in the first
2118// position exists. Thus, the return value is the only way to
2119// tell when no partitions exist.
2120int GPTData::GetPartRange(uint32_t *low, uint32_t *high) {
2121 uint32_t i;
2122 int numFound = 0;
2123
2124 *low = mainHeader.numParts + 1; // code for "not found"
2125 *high = 0;
2126 if (mainHeader.numParts > 0) { // only try if partition table exists...
2127 for (i = 0; i < mainHeader.numParts; i++) {
2128 if (partitions[i].GetFirstLBA() != UINT64_C(0)) { // it exists
2129 *high = i; // since we're counting up, set the high value
2130 // Set the low value only if it's not yet found...
2131 if (*low == (mainHeader.numParts + 1)) *low = i;
2132 numFound++;
2133 } // if
2134 } // for
2135 } // if
2136
2137 // Above will leave *low pointing to its "not found" value if no partitions
2138 // are defined, so reset to 0 if this is the case....
2139 if (*low == (mainHeader.numParts + 1))
2140 *low = 0;
2141 return numFound;
2142} // GPTData::GetPartRange()
2143
srs5694978041c2009-09-21 20:51:47 -04002144// Returns the number of defined partitions.
2145uint32_t GPTData::CountParts(void) {
2146 int i, counted = 0;
2147
2148 for (i = 0; i < mainHeader.numParts; i++) {
2149 if (partitions[i].GetFirstLBA() > 0)
2150 counted++;
2151 } // for
2152 return counted;
2153} // GPTData::CountParts()
2154
srs5694e4ac11e2009-08-31 10:13:04 -04002155/****************************************************
2156 * *
2157 * Functions that return data about disk free space *
2158 * *
2159 ****************************************************/
2160
2161// Find the first available block after the starting point; returns 0 if
2162// there are no available blocks left
2163uint64_t GPTData::FindFirstAvailable(uint64_t start) {
2164 uint64_t first;
2165 uint32_t i;
2166 int firstMoved = 0;
2167
2168 // Begin from the specified starting point or from the first usable
2169 // LBA, whichever is greater...
2170 if (start < mainHeader.firstUsableLBA)
2171 first = mainHeader.firstUsableLBA;
2172 else
2173 first = start;
2174
2175 // ...now search through all partitions; if first is within an
2176 // existing partition, move it to the next sector after that
2177 // partition and repeat. If first was moved, set firstMoved
2178 // flag; repeat until firstMoved is not set, so as to catch
2179 // cases where partitions are out of sequential order....
2180 do {
2181 firstMoved = 0;
2182 for (i = 0; i < mainHeader.numParts; i++) {
2183 if ((first >= partitions[i].GetFirstLBA()) &&
2184 (first <= partitions[i].GetLastLBA())) { // in existing part.
2185 first = partitions[i].GetLastLBA() + 1;
2186 firstMoved = 1;
2187 } // if
2188 } // for
2189 } while (firstMoved == 1);
2190 if (first > mainHeader.lastUsableLBA)
2191 first = 0;
2192 return (first);
2193} // GPTData::FindFirstAvailable()
2194
2195// Finds the first available sector in the largest block of unallocated
2196// space on the disk. Returns 0 if there are no available blocks left
2197uint64_t GPTData::FindFirstInLargest(void) {
srs5694e35eb1b2009-09-14 00:29:34 -04002198 uint64_t start, firstBlock, lastBlock, segmentSize, selectedSize = 0, selectedSegment = 0;
srs5694e4ac11e2009-08-31 10:13:04 -04002199
2200 start = 0;
2201 do {
2202 firstBlock = FindFirstAvailable(start);
2203 if (firstBlock != UINT32_C(0)) { // something's free...
2204 lastBlock = FindLastInFree(firstBlock);
2205 segmentSize = lastBlock - firstBlock + UINT32_C(1);
2206 if (segmentSize > selectedSize) {
2207 selectedSize = segmentSize;
2208 selectedSegment = firstBlock;
2209 } // if
2210 start = lastBlock + 1;
2211 } // if
2212 } while (firstBlock != 0);
2213 return selectedSegment;
2214} // GPTData::FindFirstInLargest()
2215
2216// Find the last available block on the disk at or after the start
2217// block. Returns 0 if there are no available partitions after
2218// (or including) start.
2219uint64_t GPTData::FindLastAvailable(uint64_t start) {
2220 uint64_t last;
2221 uint32_t i;
2222 int lastMoved = 0;
2223
2224 // Start by assuming the last usable LBA is available....
2225 last = mainHeader.lastUsableLBA;
2226
2227 // ...now, similar to algorithm in FindFirstAvailable(), search
2228 // through all partitions, moving last when it's in an existing
2229 // partition. Set the lastMoved flag so we repeat to catch cases
2230 // where partitions are out of logical order.
2231 do {
2232 lastMoved = 0;
2233 for (i = 0; i < mainHeader.numParts; i++) {
2234 if ((last >= partitions[i].GetFirstLBA()) &&
2235 (last <= partitions[i].GetLastLBA())) { // in existing part.
2236 last = partitions[i].GetFirstLBA() - 1;
2237 lastMoved = 1;
2238 } // if
2239 } // for
2240 } while (lastMoved == 1);
2241 if (last < mainHeader.firstUsableLBA)
2242 last = 0;
2243 return (last);
2244} // GPTData::FindLastAvailable()
2245
2246// Find the last available block in the free space pointed to by start.
2247uint64_t GPTData::FindLastInFree(uint64_t start) {
2248 uint64_t nearestStart;
2249 uint32_t i;
2250
2251 nearestStart = mainHeader.lastUsableLBA;
2252 for (i = 0; i < mainHeader.numParts; i++) {
2253 if ((nearestStart > partitions[i].GetFirstLBA()) &&
2254 (partitions[i].GetFirstLBA() > start)) {
2255 nearestStart = partitions[i].GetFirstLBA() - 1;
2256 } // if
2257 } // for
2258 return (nearestStart);
2259} // GPTData::FindLastInFree()
2260
2261// Finds the total number of free blocks, the number of segments in which
2262// they reside, and the size of the largest of those segments
2263uint64_t GPTData::FindFreeBlocks(int *numSegments, uint64_t *largestSegment) {
2264 uint64_t start = UINT64_C(0); // starting point for each search
2265 uint64_t totalFound = UINT64_C(0); // running total
2266 uint64_t firstBlock; // first block in a segment
2267 uint64_t lastBlock; // last block in a segment
2268 uint64_t segmentSize; // size of segment in blocks
2269 int num = 0;
2270
2271 *largestSegment = UINT64_C(0);
2272 do {
2273 firstBlock = FindFirstAvailable(start);
2274 if (firstBlock != UINT64_C(0)) { // something's free...
2275 lastBlock = FindLastInFree(firstBlock);
2276 segmentSize = lastBlock - firstBlock + UINT64_C(1);
2277 if (segmentSize > *largestSegment) {
2278 *largestSegment = segmentSize;
2279 } // if
2280 totalFound += segmentSize;
2281 num++;
2282 start = lastBlock + 1;
2283 } // if
2284 } while (firstBlock != 0);
2285 *numSegments = num;
2286 return totalFound;
2287} // GPTData::FindFreeBlocks()
2288
2289// Returns 1 if sector is unallocated, 0 if it's allocated to a partition
2290int GPTData::IsFree(uint64_t sector) {
2291 int isFree = 1;
2292 uint32_t i;
2293
2294 for (i = 0; i < mainHeader.numParts; i++) {
2295 if ((sector >= partitions[i].GetFirstLBA()) &&
2296 (sector <= partitions[i].GetLastLBA())) {
2297 isFree = 0;
2298 } // if
2299 } // for
srs5694e35eb1b2009-09-14 00:29:34 -04002300 if ((sector < mainHeader.firstUsableLBA) ||
srs5694e4ac11e2009-08-31 10:13:04 -04002301 (sector > mainHeader.lastUsableLBA)) {
2302 isFree = 0;
2303 } // if
2304 return (isFree);
2305} // GPTData::IsFree()
2306
srs5694ba00fed2010-01-12 18:18:36 -05002307// Returns 1 if partNum is unused.
2308int GPTData::IsFreePartNum(uint32_t partNum) {
2309 int retval = 1;
2310
2311 if ((partNum >= 0) && (partNum < mainHeader.numParts)) {
2312 if ((partitions[partNum].GetFirstLBA() != UINT64_C(0)) ||
2313 (partitions[partNum].GetLastLBA() != UINT64_C(0))) {
2314 retval = 0;
2315 } // if partition is in use
2316 } else retval = 0;
2317
2318 return retval;
2319} // GPTData::IsFreePartNum()
2320
srs5694e4ac11e2009-08-31 10:13:04 -04002321/********************************
2322 * *
2323 * Endianness support functions *
2324 * *
2325 ********************************/
2326
srs56942a9f5da2009-08-26 00:48:01 -04002327void GPTData::ReverseHeaderBytes(struct GPTHeader* header) {
srs5694221e0872009-08-29 15:00:31 -04002328 ReverseBytes(&header->signature, 8);
2329 ReverseBytes(&header->revision, 4);
2330 ReverseBytes(&header->headerSize, 4);
2331 ReverseBytes(&header->headerCRC, 4);
2332 ReverseBytes(&header->reserved, 4);
2333 ReverseBytes(&header->currentLBA, 8);
2334 ReverseBytes(&header->backupLBA, 8);
2335 ReverseBytes(&header->firstUsableLBA, 8);
2336 ReverseBytes(&header->lastUsableLBA, 8);
2337 ReverseBytes(&header->partitionEntriesLBA, 8);
2338 ReverseBytes(&header->numParts, 4);
2339 ReverseBytes(&header->sizeOfPartitionEntries, 4);
2340 ReverseBytes(&header->partitionEntriesCRC, 4);
2341 ReverseBytes(&header->reserved2, GPT_RESERVED);
2342 ReverseBytes(&header->diskGUID.data1, 8);
2343 ReverseBytes(&header->diskGUID.data2, 8);
srs56942a9f5da2009-08-26 00:48:01 -04002344} // GPTData::ReverseHeaderBytes()
2345
2346// IMPORTANT NOTE: This function requires non-reversed mainHeader
2347// structure!
2348void GPTData::ReversePartitionBytes() {
2349 uint32_t i;
2350
2351 // Check GPT signature on big-endian systems; this will mismatch
2352 // if the function is called out of order. Unfortunately, it'll also
2353 // mismatch if there's data corruption.
2354 if ((mainHeader.signature != GPT_SIGNATURE) && (IsLittleEndian() == 0)) {
2355 printf("GPT signature mismatch in GPTData::ReversePartitionBytes(). This indicates\n"
srs5694e4ac11e2009-08-31 10:13:04 -04002356 "data corruption or a misplaced call to this function.\n");
srs56942a9f5da2009-08-26 00:48:01 -04002357 } // if signature mismatch....
2358 for (i = 0; i < mainHeader.numParts; i++) {
srs5694221e0872009-08-29 15:00:31 -04002359 partitions[i].ReversePartBytes();
srs56942a9f5da2009-08-26 00:48:01 -04002360 } // for
2361} // GPTData::ReversePartitionBytes()
2362
2363/******************************************
2364 * *
2365 * Additional non-class support functions *
2366 * *
2367 ******************************************/
2368
srs5694e7b4ff92009-08-18 13:16:10 -04002369// Check to be sure that data type sizes are correct. The basic types (uint*_t) should
2370// never fail these tests, but the struct types may fail depending on compile options.
2371// Specifically, the -fpack-struct option to gcc may be required to ensure proper structure
2372// sizes.
2373int SizesOK(void) {
2374 int allOK = 1;
srs5694e7b4ff92009-08-18 13:16:10 -04002375
2376 if (sizeof(uint8_t) != 1) {
srs56941d1448a2009-12-31 21:20:19 -05002377 fprintf(stderr, "uint8_t is %lu bytes, should be 1 byte; aborting!\n", sizeof(uint8_t));
srs5694e7b4ff92009-08-18 13:16:10 -04002378 allOK = 0;
2379 } // if
2380 if (sizeof(uint16_t) != 2) {
srs56941d1448a2009-12-31 21:20:19 -05002381 fprintf(stderr, "uint16_t is %lu bytes, should be 2 bytes; aborting!\n", sizeof(uint16_t));
srs5694e7b4ff92009-08-18 13:16:10 -04002382 allOK = 0;
2383 } // if
2384 if (sizeof(uint32_t) != 4) {
srs56941d1448a2009-12-31 21:20:19 -05002385 fprintf(stderr, "uint32_t is %lu bytes, should be 4 bytes; aborting!\n", sizeof(uint32_t));
srs5694e7b4ff92009-08-18 13:16:10 -04002386 allOK = 0;
2387 } // if
2388 if (sizeof(uint64_t) != 8) {
srs56941d1448a2009-12-31 21:20:19 -05002389 fprintf(stderr, "uint64_t is %lu bytes, should be 8 bytes; aborting!\n", sizeof(uint64_t));
srs5694e7b4ff92009-08-18 13:16:10 -04002390 allOK = 0;
2391 } // if
2392 if (sizeof(struct MBRRecord) != 16) {
srs56941d1448a2009-12-31 21:20:19 -05002393 fprintf(stderr, "MBRRecord is %lu bytes, should be 16 bytes; aborting!\n", sizeof(MBRRecord));
srs5694e7b4ff92009-08-18 13:16:10 -04002394 allOK = 0;
2395 } // if
srs5694978041c2009-09-21 20:51:47 -04002396 if (sizeof(struct TempMBR) != 512) {
srs56941d1448a2009-12-31 21:20:19 -05002397 fprintf(stderr, "TempMBR is %lu bytes, should be 512 bytes; aborting!\n", sizeof(TempMBR));
srs5694e7b4ff92009-08-18 13:16:10 -04002398 allOK = 0;
2399 } // if
2400 if (sizeof(struct GPTHeader) != 512) {
srs56941d1448a2009-12-31 21:20:19 -05002401 fprintf(stderr, "GPTHeader is %lu bytes, should be 512 bytes; aborting!\n", sizeof(GPTHeader));
srs5694e7b4ff92009-08-18 13:16:10 -04002402 allOK = 0;
2403 } // if
srs5694221e0872009-08-29 15:00:31 -04002404 if (sizeof(GPTPart) != 128) {
srs56941d1448a2009-12-31 21:20:19 -05002405 fprintf(stderr, "GPTPart is %lu bytes, should be 128 bytes; aborting!\n", sizeof(GPTPart));
srs5694221e0872009-08-29 15:00:31 -04002406 allOK = 0;
2407 } // if
2408// Determine endianness; set allOK = 0 if running on big-endian hardware
srs56942a9f5da2009-08-26 00:48:01 -04002409 if (IsLittleEndian() == 0) {
2410 fprintf(stderr, "\aRunning on big-endian hardware. Big-endian support is new and poorly"
srs5694e4ac11e2009-08-31 10:13:04 -04002411 " tested!\nBeware!\n");
srs56942a9f5da2009-08-26 00:48:01 -04002412 // allOK = 0;
srs5694e7b4ff92009-08-18 13:16:10 -04002413 } // if
2414 return (allOK);
2415} // SizesOK()
srs5694e4ac11e2009-08-31 10:13:04 -04002416