blob: 7788aa8ff3588d94bcea0c3cf19a34fae05c4411 [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;
srs5694e7b4ff92009-08-18 13:16:10 -040077 LoadPartitions(filename);
78} // GPTData(char* filename) constructor
79
srs5694e4ac11e2009-08-31 10:13:04 -040080// Destructor
srs5694e7b4ff92009-08-18 13:16:10 -040081GPTData::~GPTData(void) {
82 free(partitions);
83} // GPTData destructor
84
srs5694e4ac11e2009-08-31 10:13:04 -040085/*********************************************************************
86 * *
87 * Begin functions that verify data, or that adjust the verification *
88 * information (compute CRCs, rebuild headers) *
89 * *
90 *********************************************************************/
srs5694e7b4ff92009-08-18 13:16:10 -040091
srs5694e4ac11e2009-08-31 10:13:04 -040092// Perform detailed verification, reporting on any problems found, but
93// do *NOT* recover from these problems. Returns the total number of
94// problems identified.
95int GPTData::Verify(void) {
srs56941d1448a2009-12-31 21:20:19 -050096 int problems = 0, numSegments, i;
97 uint64_t totalFree, largestSegment, firstSector;
srs5694e4ac11e2009-08-31 10:13:04 -040098 char tempStr[255], siTotal[255], siLargest[255];
99
100 // First, check for CRC errors in the GPT data....
101 if (!mainCrcOk) {
102 problems++;
103 printf("\nProblem: The CRC for the main GPT header is invalid. The main GPT header may\n"
104 "be corrupt. Consider loading the backup GPT header to rebuild the main GPT\n"
105 "header\n");
106 } // if
107 if (!mainPartsCrcOk) {
108 problems++;
109 printf("\nProblem: The CRC for the main partition table is invalid. This table may be\n"
110 "corrupt. Consider loading the backup partition table.\n");
111 } // if
112 if (!secondCrcOk) {
113 problems++;
114 printf("\nProblem: The CRC for the backup GPT header is invalid. The backup GPT header\n"
115 "may be corrupt. Consider using the main GPT header to rebuild the backup GPT\n"
116 "header.\n");
117 } // if
118 if (!secondPartsCrcOk) {
119 problems++;
120 printf("\nCaution: The CRC for the backup partition table is invalid. This table may\n"
121 "be corrupt. This program will automatically create a new backup partition\n"
122 "table when you save your partitions.\n");
srs5694e7b4ff92009-08-18 13:16:10 -0400123 } // if
124
srs5694978041c2009-09-21 20:51:47 -0400125 // Now check that the main and backup headers both point to themselves....
126 if (mainHeader.currentLBA != 1) {
127 problems++;
128 printf("\nProblem: The main header's self-pointer doesn't point to itself. This problem\n"
129 "is being automatically corrected, but it may be a symptom of more serious\n"
130 "problems. Think carefully before saving changes with 'w' or using this disk.\n");
131 mainHeader.currentLBA = 1;
132 } // if
133 if (secondHeader.currentLBA != (diskSize - UINT64_C(1))) {
134 problems++;
srs56943f2fe992009-11-24 18:28:18 -0500135 printf("\nProblem: The secondary header's self-pointer indicates that it doesn't reside\n"
136 "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 -0500137 "option on the experts' menu to adjust the secondary header's and partition\n"
138 "table's locations.\n");
srs5694978041c2009-09-21 20:51:47 -0400139 } // if
140
141 // Now check that critical main and backup GPT entries match each other
srs5694e4ac11e2009-08-31 10:13:04 -0400142 if (mainHeader.currentLBA != secondHeader.backupLBA) {
143 problems++;
144 printf("\nProblem: main GPT header's current LBA pointer (%llu) doesn't\n"
145 "match the backup GPT header's LBA pointer(%llu)\n",
146 (unsigned long long) mainHeader.currentLBA,
147 (unsigned long long) secondHeader.backupLBA);
148 } // if
149 if (mainHeader.backupLBA != secondHeader.currentLBA) {
150 problems++;
151 printf("\nProblem: main GPT header's backup LBA pointer (%llu) doesn't\n"
152 "match the backup GPT header's current LBA pointer (%llu)\n",
153 (unsigned long long) mainHeader.backupLBA,
154 (unsigned long long) secondHeader.currentLBA);
155 } // if
156 if (mainHeader.firstUsableLBA != secondHeader.firstUsableLBA) {
157 problems++;
158 printf("\nProblem: main GPT header's first usable LBA pointer (%llu) doesn't\n"
159 "match the backup GPT header's first usable LBA pointer (%llu)\n",
160 (unsigned long long) mainHeader.firstUsableLBA,
161 (unsigned long long) secondHeader.firstUsableLBA);
162 } // if
163 if (mainHeader.lastUsableLBA != secondHeader.lastUsableLBA) {
164 problems++;
165 printf("\nProblem: main GPT header's last usable LBA pointer (%llu) doesn't\n"
166 "match the backup GPT header's last usable LBA pointer (%llu)\n",
167 (unsigned long long) mainHeader.lastUsableLBA,
168 (unsigned long long) secondHeader.lastUsableLBA);
169 } // if
170 if ((mainHeader.diskGUID.data1 != secondHeader.diskGUID.data1) ||
171 (mainHeader.diskGUID.data2 != secondHeader.diskGUID.data2)) {
172 problems++;
173 printf("\nProblem: main header's disk GUID (%s) doesn't\n",
174 GUIDToStr(mainHeader.diskGUID, tempStr));
175 printf("match the backup GPT header's disk GUID (%s)\n",
176 GUIDToStr(secondHeader.diskGUID, tempStr));
177 } // if
178 if (mainHeader.numParts != secondHeader.numParts) {
179 problems++;
180 printf("\nProblem: main GPT header's number of partitions (%lu) doesn't\n"
181 "match the backup GPT header's number of partitions (%lu)\n",
182 (unsigned long) mainHeader.numParts,
183 (unsigned long) secondHeader.numParts);
184 } // if
185 if (mainHeader.sizeOfPartitionEntries != secondHeader.sizeOfPartitionEntries) {
186 problems++;
187 printf("\nProblem: main GPT header's size of partition entries (%lu) doesn't\n"
188 "match the backup GPT header's size of partition entries (%lu)\n",
189 (unsigned long) mainHeader.sizeOfPartitionEntries,
190 (unsigned long) secondHeader.sizeOfPartitionEntries);
191 } // if
192
193 // Now check for a few other miscellaneous problems...
194 // Check that the disk size will hold the data...
195 if (mainHeader.backupLBA > diskSize) {
196 problems++;
197 printf("\nProblem: Disk is too small to hold all the data!\n");
198 printf("(Disk size is %llu sectors, needs to be %llu sectors.)\n",
199 (unsigned long long) diskSize,
200 (unsigned long long) mainHeader.backupLBA);
201 } // if
202
203 // Check for overlapping partitions....
204 problems += FindOverlaps();
205
206 // Check for mismatched MBR and GPT partitions...
207 problems += FindHybridMismatches();
208
209 // Verify that partitions don't run into GPT data areas....
210 problems += CheckGPTSize();
211
srs56941d1448a2009-12-31 21:20:19 -0500212 // Check that partitions are aligned on proper boundaries (for WD Advanced
213 // Format and similar disks)....
214 for (i = 0; i < mainHeader.numParts; i++) {
215 if ((partitions[i].GetFirstLBA() % sectorAlignment) != 0) {
216 printf("\nCaution: Partition %d doesn't begin on a %d-sector boundary. This may\n"
217 "result in degraded performance on some modern (2010 and later) hard disks.\n",
218 i + 1, sectorAlignment);
219 } // if
220 } // for
221
srs5694e4ac11e2009-08-31 10:13:04 -0400222 // Now compute available space, but only if no problems found, since
223 // problems could affect the results
224 if (problems == 0) {
225 totalFree = FindFreeBlocks(&numSegments, &largestSegment);
226 BytesToSI(totalFree * (uint64_t) blockSize, siTotal);
227 BytesToSI(largestSegment * (uint64_t) blockSize, siLargest);
228 printf("No problems found. %llu free sectors (%s) available in %u\n"
229 "segments, the largest of which is %llu sectors (%s) in size\n",
230 (unsigned long long) totalFree,
231 siTotal, numSegments, (unsigned long long) largestSegment,
232 siLargest);
233 } else {
234 printf("\nIdentified %d problems!\n", problems);
srs5694e7b4ff92009-08-18 13:16:10 -0400235 } // if/else
srs5694e4ac11e2009-08-31 10:13:04 -0400236
237 return (problems);
238} // GPTData::Verify()
srs5694e7b4ff92009-08-18 13:16:10 -0400239
240// Checks to see if the GPT tables overrun existing partitions; if they
srs5694221e0872009-08-29 15:00:31 -0400241// do, issues a warning but takes no action. Returns number of problems
242// detected (0 if OK, 1 to 2 if problems).
srs5694e7b4ff92009-08-18 13:16:10 -0400243int GPTData::CheckGPTSize(void) {
244 uint64_t overlap, firstUsedBlock, lastUsedBlock;
245 uint32_t i;
srs5694221e0872009-08-29 15:00:31 -0400246 int numProbs = 0;
srs5694e7b4ff92009-08-18 13:16:10 -0400247
248 // first, locate the first & last used blocks
249 firstUsedBlock = UINT64_MAX;
250 lastUsedBlock = 0;
251 for (i = 0; i < mainHeader.numParts; i++) {
srs5694221e0872009-08-29 15:00:31 -0400252 if ((partitions[i].GetFirstLBA() < firstUsedBlock) &&
srs5694e4ac11e2009-08-31 10:13:04 -0400253 (partitions[i].GetFirstLBA() != 0))
srs5694221e0872009-08-29 15:00:31 -0400254 firstUsedBlock = partitions[i].GetFirstLBA();
255 if (partitions[i].GetLastLBA() > lastUsedBlock)
256 lastUsedBlock = partitions[i].GetLastLBA();
srs5694e7b4ff92009-08-18 13:16:10 -0400257 } // for
258
259 // If the disk size is 0 (the default), then it means that various
260 // variables aren't yet set, so the below tests will be useless;
261 // therefore we should skip everything
262 if (diskSize != 0) {
263 if (mainHeader.firstUsableLBA > firstUsedBlock) {
264 overlap = mainHeader.firstUsableLBA - firstUsedBlock;
srs5694221e0872009-08-29 15:00:31 -0400265 printf("Warning! Main partition table overlaps the first partition by %lu blocks!\n",
266 (unsigned long) overlap);
267 if (firstUsedBlock > 2) {
268 printf("Try reducing the partition table size by %lu entries.\n",
269 (unsigned long) (overlap * 4));
270 printf("(Use the 's' item on the experts' menu.)\n");
271 } else {
272 printf("You will need to delete this partition or resize it in another utility.\n");
273 } // if/else
274 numProbs++;
srs5694e7b4ff92009-08-18 13:16:10 -0400275 } // Problem at start of disk
276 if (mainHeader.lastUsableLBA < lastUsedBlock) {
277 overlap = lastUsedBlock - mainHeader.lastUsableLBA;
srs5694221e0872009-08-29 15:00:31 -0400278 printf("Warning! Secondary partition table overlaps the last partition by %lu blocks\n",
279 (unsigned long) overlap);
280 if (lastUsedBlock > (diskSize - 2)) {
281 printf("You will need to delete this partition or resize it in another utility.\n");
282 } else {
283 printf("Try reducing the partition table size by %lu entries.\n",
284 (unsigned long) (overlap * 4));
285 printf("(Use the 's' item on the experts' menu.)\n");
286 } // if/else
287 numProbs++;
srs5694e7b4ff92009-08-18 13:16:10 -0400288 } // Problem at end of disk
289 } // if (diskSize != 0)
srs5694221e0872009-08-29 15:00:31 -0400290 return numProbs;
srs5694e7b4ff92009-08-18 13:16:10 -0400291} // GPTData::CheckGPTSize()
292
srs5694e7b4ff92009-08-18 13:16:10 -0400293// Check the validity of the GPT header. Returns 1 if the main header
294// is valid, 2 if the backup header is valid, 3 if both are valid, and
295// 0 if neither is valid. Note that this function just checks the GPT
296// signature and revision numbers, not CRCs or other data.
297int GPTData::CheckHeaderValidity(void) {
298 int valid = 3;
299
300 if (mainHeader.signature != GPT_SIGNATURE) {
301 valid -= 1;
srs5694221e0872009-08-29 15:00:31 -0400302// printf("Main GPT signature invalid; read 0x%016llX, should be\n0x%016llX\n",
303// (unsigned long long) mainHeader.signature, (unsigned long long) GPT_SIGNATURE);
srs5694e7b4ff92009-08-18 13:16:10 -0400304 } else if ((mainHeader.revision != 0x00010000) && valid) {
305 valid -= 1;
306 printf("Unsupported GPT version in main header; read 0x%08lX, should be\n0x%08lX\n",
srs56945d58fe02010-01-03 20:57:08 -0500307 (unsigned long) mainHeader.revision, (unsigned long) UINT32_C(0x00010000));
srs5694e7b4ff92009-08-18 13:16:10 -0400308 } // if/else/if
309
310 if (secondHeader.signature != GPT_SIGNATURE) {
311 valid -= 2;
srs5694221e0872009-08-29 15:00:31 -0400312// printf("Secondary GPT signature invalid; read 0x%016llX, should be\n0x%016llX\n",
313// (unsigned long long) secondHeader.signature, (unsigned long long) GPT_SIGNATURE);
srs5694e7b4ff92009-08-18 13:16:10 -0400314 } else if ((secondHeader.revision != 0x00010000) && valid) {
315 valid -= 2;
316 printf("Unsupported GPT version in backup header; read 0x%08lX, should be\n0x%08lX\n",
srs56945d58fe02010-01-03 20:57:08 -0500317 (unsigned long) mainHeader.revision, (unsigned long) UINT32_C(0x00010000));
srs5694e7b4ff92009-08-18 13:16:10 -0400318 } // if/else/if
319
srs56942a9f5da2009-08-26 00:48:01 -0400320 // If MBR bad, check for an Apple disk signature
srs5694e35eb1b2009-09-14 00:29:34 -0400321 if ((protectiveMBR.GetValidity() == invalid) &&
srs5694e4ac11e2009-08-31 10:13:04 -0400322 (((mainHeader.signature << 32) == APM_SIGNATURE1) ||
srs56942a9f5da2009-08-26 00:48:01 -0400323 (mainHeader.signature << 32) == APM_SIGNATURE2)) {
srs5694221e0872009-08-29 15:00:31 -0400324 apmFound = 1; // Will display warning message later
srs56943f2fe992009-11-24 18:28:18 -0500325 } // if
srs56942a9f5da2009-08-26 00:48:01 -0400326
srs5694e4ac11e2009-08-31 10:13:04 -0400327 return valid;
srs5694e7b4ff92009-08-18 13:16:10 -0400328} // GPTData::CheckHeaderValidity()
329
330// Check the header CRC to see if it's OK...
srs56942a9f5da2009-08-26 00:48:01 -0400331// Note: Must be called BEFORE byte-order reversal on big-endian
332// systems!
srs5694e7b4ff92009-08-18 13:16:10 -0400333int GPTData::CheckHeaderCRC(struct GPTHeader* header) {
srs5694978041c2009-09-21 20:51:47 -0400334 uint32_t oldCRC, newCRC, hSize;
srs5694e7b4ff92009-08-18 13:16:10 -0400335
srs56942a9f5da2009-08-26 00:48:01 -0400336 // Back up old header CRC and then blank it, since it must be 0 for
srs5694e7b4ff92009-08-18 13:16:10 -0400337 // computation to be valid
338 oldCRC = header->headerCRC;
339 header->headerCRC = UINT32_C(0);
srs5694978041c2009-09-21 20:51:47 -0400340 hSize = header->headerSize;
341
342 // If big-endian system, reverse byte order
343 if (IsLittleEndian() == 0) {
344 ReverseBytes(&oldCRC, 4);
345 } // if
srs5694e7b4ff92009-08-18 13:16:10 -0400346
347 // Initialize CRC functions...
348 chksum_crc32gentab();
349
350 // Compute CRC, restore original, and return result of comparison
351 newCRC = chksum_crc32((unsigned char*) header, HEADER_SIZE);
srs5694978041c2009-09-21 20:51:47 -0400352 header->headerCRC = oldCRC;
srs5694e7b4ff92009-08-18 13:16:10 -0400353 return (oldCRC == newCRC);
354} // GPTData::CheckHeaderCRC()
355
srs56942a9f5da2009-08-26 00:48:01 -0400356// Recompute all the CRCs. Must be called before saving (but after reversing
357// byte order on big-endian systems) if any changes have been made.
srs5694e7b4ff92009-08-18 13:16:10 -0400358void GPTData::RecomputeCRCs(void) {
srs5694978041c2009-09-21 20:51:47 -0400359 uint32_t crc, hSize, trueNumParts;
srs56942a9f5da2009-08-26 00:48:01 -0400360 int littleEndian = 1;
srs5694e7b4ff92009-08-18 13:16:10 -0400361
362 // Initialize CRC functions...
363 chksum_crc32gentab();
364
srs5694978041c2009-09-21 20:51:47 -0400365 hSize = mainHeader.headerSize;
srs56942a9f5da2009-08-26 00:48:01 -0400366 littleEndian = IsLittleEndian();
367
srs5694e7b4ff92009-08-18 13:16:10 -0400368 // Compute CRC of partition tables & store in main and secondary headers
srs56942a9f5da2009-08-26 00:48:01 -0400369 trueNumParts = mainHeader.numParts;
370 if (littleEndian == 0)
srs5694221e0872009-08-29 15:00:31 -0400371 ReverseBytes(&trueNumParts, 4); // unreverse this key piece of data....
srs56942a9f5da2009-08-26 00:48:01 -0400372 crc = chksum_crc32((unsigned char*) partitions, trueNumParts * GPT_SIZE);
srs5694e7b4ff92009-08-18 13:16:10 -0400373 mainHeader.partitionEntriesCRC = crc;
374 secondHeader.partitionEntriesCRC = crc;
srs56942a9f5da2009-08-26 00:48:01 -0400375 if (littleEndian == 0) {
srs5694221e0872009-08-29 15:00:31 -0400376 ReverseBytes(&mainHeader.partitionEntriesCRC, 4);
377 ReverseBytes(&secondHeader.partitionEntriesCRC, 4);
srs56942a9f5da2009-08-26 00:48:01 -0400378 } // if
srs5694e7b4ff92009-08-18 13:16:10 -0400379
380 // Zero out GPT tables' own CRCs (required for correct computation)
381 mainHeader.headerCRC = 0;
382 secondHeader.headerCRC = 0;
383
384 // Compute & store CRCs of main & secondary headers...
srs5694978041c2009-09-21 20:51:47 -0400385 crc = chksum_crc32((unsigned char*) &mainHeader, hSize);
srs56942a9f5da2009-08-26 00:48:01 -0400386 if (littleEndian == 0)
srs5694221e0872009-08-29 15:00:31 -0400387 ReverseBytes(&crc, 4);
srs5694e7b4ff92009-08-18 13:16:10 -0400388 mainHeader.headerCRC = crc;
srs5694978041c2009-09-21 20:51:47 -0400389 crc = chksum_crc32((unsigned char*) &secondHeader, hSize);
srs56942a9f5da2009-08-26 00:48:01 -0400390 if (littleEndian == 0)
srs5694221e0872009-08-29 15:00:31 -0400391 ReverseBytes(&crc, 4);
srs5694e7b4ff92009-08-18 13:16:10 -0400392 secondHeader.headerCRC = crc;
393} // GPTData::RecomputeCRCs()
394
srs5694e7b4ff92009-08-18 13:16:10 -0400395// Rebuild the main GPT header, using the secondary header as a model.
396// Typically called when the main header has been found to be corrupt.
397void GPTData::RebuildMainHeader(void) {
398 int i;
399
400 mainHeader.signature = GPT_SIGNATURE;
401 mainHeader.revision = secondHeader.revision;
srs5694978041c2009-09-21 20:51:47 -0400402 mainHeader.headerSize = secondHeader.headerSize;
srs5694e7b4ff92009-08-18 13:16:10 -0400403 mainHeader.headerCRC = UINT32_C(0);
404 mainHeader.reserved = secondHeader.reserved;
405 mainHeader.currentLBA = secondHeader.backupLBA;
406 mainHeader.backupLBA = secondHeader.currentLBA;
407 mainHeader.firstUsableLBA = secondHeader.firstUsableLBA;
408 mainHeader.lastUsableLBA = secondHeader.lastUsableLBA;
409 mainHeader.diskGUID.data1 = secondHeader.diskGUID.data1;
410 mainHeader.diskGUID.data2 = secondHeader.diskGUID.data2;
411 mainHeader.partitionEntriesLBA = UINT64_C(2);
412 mainHeader.numParts = secondHeader.numParts;
413 mainHeader.sizeOfPartitionEntries = secondHeader.sizeOfPartitionEntries;
414 mainHeader.partitionEntriesCRC = secondHeader.partitionEntriesCRC;
415 for (i = 0 ; i < GPT_RESERVED; i++)
416 mainHeader.reserved2[i] = secondHeader.reserved2[i];
417} // GPTData::RebuildMainHeader()
418
419// Rebuild the secondary GPT header, using the main header as a model.
420void GPTData::RebuildSecondHeader(void) {
421 int i;
422
423 secondHeader.signature = GPT_SIGNATURE;
424 secondHeader.revision = mainHeader.revision;
srs5694978041c2009-09-21 20:51:47 -0400425 secondHeader.headerSize = mainHeader.headerSize;
srs5694e7b4ff92009-08-18 13:16:10 -0400426 secondHeader.headerCRC = UINT32_C(0);
427 secondHeader.reserved = mainHeader.reserved;
428 secondHeader.currentLBA = mainHeader.backupLBA;
429 secondHeader.backupLBA = mainHeader.currentLBA;
430 secondHeader.firstUsableLBA = mainHeader.firstUsableLBA;
431 secondHeader.lastUsableLBA = mainHeader.lastUsableLBA;
432 secondHeader.diskGUID.data1 = mainHeader.diskGUID.data1;
433 secondHeader.diskGUID.data2 = mainHeader.diskGUID.data2;
434 secondHeader.partitionEntriesLBA = secondHeader.lastUsableLBA + UINT64_C(1);
435 secondHeader.numParts = mainHeader.numParts;
436 secondHeader.sizeOfPartitionEntries = mainHeader.sizeOfPartitionEntries;
437 secondHeader.partitionEntriesCRC = mainHeader.partitionEntriesCRC;
438 for (i = 0 ; i < GPT_RESERVED; i++)
439 secondHeader.reserved2[i] = mainHeader.reserved2[i];
srs5694e4ac11e2009-08-31 10:13:04 -0400440} // GPTData::RebuildSecondHeader()
441
442// Search for hybrid MBR entries that have no corresponding GPT partition.
443// Returns number of such mismatches found
444int GPTData::FindHybridMismatches(void) {
445 int i, j, found, numFound = 0;
446 uint64_t mbrFirst, mbrLast;
447
448 for (i = 0; i < 4; i++) {
449 if ((protectiveMBR.GetType(i) != 0xEE) && (protectiveMBR.GetType(i) != 0x00)) {
450 j = 0;
451 found = 0;
452 do {
453 mbrFirst = (uint64_t) protectiveMBR.GetFirstSector(i);
454 mbrLast = mbrFirst + (uint64_t) protectiveMBR.GetLength(i) - UINT64_C(1);
455 if ((partitions[j].GetFirstLBA() == mbrFirst) &&
456 (partitions[j].GetLastLBA() == mbrLast))
457 found = 1;
458 j++;
459 } while ((!found) && (j < mainHeader.numParts));
460 if (!found) {
461 numFound++;
462 printf("\nWarning! Mismatched GPT and MBR partitions! MBR partition "
463 "%d, of type 0x%02X,\nhas no corresponding GPT partition! "
464 "You may continue, but this condition\nmight cause data loss"
465 " in the future!\a\n", i + 1, protectiveMBR.GetType(i));
466 } // if
467 } // if
468 } // for
469 return numFound;
470} // GPTData::FindHybridMismatches
471
472// Find overlapping partitions and warn user about them. Returns number of
473// overlapping partitions.
474int GPTData::FindOverlaps(void) {
475 int i, j, problems = 0;
476
477 for (i = 1; i < mainHeader.numParts; i++) {
478 for (j = 0; j < i; j++) {
479 if (partitions[i].DoTheyOverlap(&partitions[j])) {
480 problems++;
481 printf("\nProblem: partitions %d and %d overlap:\n", i + 1, j + 1);
482 printf(" Partition %d: %llu to %llu\n", i,
483 (unsigned long long) partitions[i].GetFirstLBA(),
484 (unsigned long long) partitions[i].GetLastLBA());
485 printf(" Partition %d: %llu to %llu\n", j,
486 (unsigned long long) partitions[j].GetFirstLBA(),
487 (unsigned long long) partitions[j].GetLastLBA());
488 } // if
489 } // for j...
490 } // for i...
491 return problems;
492} // GPTData::FindOverlaps()
493
494/******************************************************************
495 * *
496 * Begin functions that load data from disk or save data to disk. *
497 * *
498 ******************************************************************/
499
500// Scan for partition data. This function loads the MBR data (regular MBR or
501// protective MBR) and loads BSD disklabel data (which is probably invalid).
502// It also looks for APM data, forces a load of GPT data, and summarizes
503// the results.
504void GPTData::PartitionScan(int fd) {
505 BSDData bsdDisklabel;
srs5694e4ac11e2009-08-31 10:13:04 -0400506
507 // Read the MBR & check for BSD disklabel
508 protectiveMBR.ReadMBRData(fd);
srs5694e4ac11e2009-08-31 10:13:04 -0400509 bsdDisklabel.ReadBSDData(fd, 0, diskSize - 1);
srs5694e4ac11e2009-08-31 10:13:04 -0400510
511 // Load the GPT data, whether or not it's valid
512 ForceLoadGPTData(fd);
srs5694ba00fed2010-01-12 18:18:36 -0500513
514 if (!beQuiet) {
515 printf("Partition table scan:\n");
516 protectiveMBR.ShowState();
517 bsdDisklabel.ShowState();
518 ShowAPMState(); // Show whether there's an Apple Partition Map present
519 ShowGPTState(); // Show GPT status
520 printf("\n");
521 } // if
srs5694e4ac11e2009-08-31 10:13:04 -0400522
523 if (apmFound) {
524 printf("\n*******************************************************************\n");
525 printf("This disk appears to contain an Apple-format (APM) partition table!\n");
srs56945d58fe02010-01-03 20:57:08 -0500526 if (!justLooking) {
527 printf("It will be destroyed if you continue!\n");
528 } // if
srs5694e4ac11e2009-08-31 10:13:04 -0400529 printf("*******************************************************************\n\n\a");
530 } // if
srs5694e4ac11e2009-08-31 10:13:04 -0400531} // GPTData::PartitionScan()
532
533// Read GPT data from a disk.
534int GPTData::LoadPartitions(char* deviceFilename) {
535 int fd, err;
536 int allOK = 1, i;
537 uint64_t firstBlock, lastBlock;
538 BSDData bsdDisklabel;
539
540 // First, do a test to see if writing will be possible later....
541 fd = OpenForWrite(deviceFilename);
srs56945d58fe02010-01-03 20:57:08 -0500542 if ((fd == -1) && (!justLooking)) {
srs5694e4ac11e2009-08-31 10:13:04 -0400543 printf("\aNOTE: Write test failed with error number %d. It will be "
544 "impossible to save\nchanges to this disk's partition table!\n\n",
545 errno);
srs56945d58fe02010-01-03 20:57:08 -0500546 justLooking = 1;
547 } // if
srs5694e4ac11e2009-08-31 10:13:04 -0400548 close(fd);
549
550 if ((fd = open(deviceFilename, O_RDONLY)) != -1) {
551 // store disk information....
552 diskSize = disksize(fd, &err);
553 blockSize = (uint32_t) GetBlockSize(fd);
srs56945d58fe02010-01-03 20:57:08 -0500554 sectorAlignment = FindAlignment(fd);
srs5694e4ac11e2009-08-31 10:13:04 -0400555 strcpy(device, deviceFilename);
srs5694ba00fed2010-01-12 18:18:36 -0500556 PartitionScan(fd); // Check for partition types, load GPT, & print summary
srs5694e4ac11e2009-08-31 10:13:04 -0400557
srs5694ba00fed2010-01-12 18:18:36 -0500558 whichWasUsed = UseWhichPartitions();
559 switch (whichWasUsed) {
srs5694e4ac11e2009-08-31 10:13:04 -0400560 case use_mbr:
561 XFormPartitions();
562 break;
563 case use_bsd:
564 bsdDisklabel.ReadBSDData(fd, 0, diskSize - 1);
565// bsdDisklabel.DisplayBSDData();
566 ClearGPTData();
567 protectiveMBR.MakeProtectiveMBR(1); // clear boot area (option 1)
568 XFormDisklabel(&bsdDisklabel, 0);
569 break;
570 case use_gpt:
571 break;
572 case use_new:
573 ClearGPTData();
574 protectiveMBR.MakeProtectiveMBR();
575 break;
576 } // switch
577
578 // Now find the first and last sectors used by partitions...
579 if (allOK) {
580 firstBlock = mainHeader.backupLBA; // start high
581 lastBlock = 0; // start low
582 for (i = 0; i < mainHeader.numParts; i++) {
583 if ((partitions[i].GetFirstLBA() < firstBlock) &&
584 (partitions[i].GetFirstLBA() > 0))
585 firstBlock = partitions[i].GetFirstLBA();
586 if (partitions[i].GetLastLBA() > lastBlock)
587 lastBlock = partitions[i].GetLastLBA();
588 } // for
589 } // if
590 CheckGPTSize();
591 } else {
592 allOK = 0;
593 fprintf(stderr, "Problem opening %s for reading! Error is %d\n",
594 deviceFilename, errno);
595 if (errno == EACCES) { // User is probably not running as root
596 fprintf(stderr, "You must run this program as root or use sudo!\n");
597 } // if
598 } // if/else
599 return (allOK);
600} // GPTData::LoadPartitions()
601
602// Loads the GPT, as much as possible. Returns 1 if this seems to have
603// succeeded, 0 if there are obvious problems....
604int GPTData::ForceLoadGPTData(int fd) {
605 int allOK = 1, validHeaders;
606 off_t seekTo;
607 char* storage;
608 uint32_t newCRC, sizeOfParts;
609
610 // Seek to and read the main GPT header
srs56941e093722010-01-05 00:14:19 -0500611 lseek64(fd, blockSize, SEEK_SET);
612 if (myRead(fd, (char*) &mainHeader, 512) != 512) { // read main GPT header
613 fprintf(stderr, "Warning! Error %d reading main GPT header!\n", errno);
srs56945d58fe02010-01-03 20:57:08 -0500614 } // if
srs5694e4ac11e2009-08-31 10:13:04 -0400615 mainCrcOk = CheckHeaderCRC(&mainHeader);
616 if (IsLittleEndian() == 0) // big-endian system; adjust header byte order....
617 ReverseHeaderBytes(&mainHeader);
618
srs56943f2fe992009-11-24 18:28:18 -0500619 // Load backup header, check its CRC, and store the results of the
620 // check for future reference. Load backup header using pointer in main
621 // header if possible; but if main header has a CRC error, or if it
622 // points to beyond the end of the disk, load the last sector of the
623 // disk instead.
624 if (mainCrcOk) {
625 if (mainHeader.backupLBA < diskSize) {
626 seekTo = mainHeader.backupLBA * blockSize;
627 } else {
srs56941e093722010-01-05 00:14:19 -0500628 seekTo = (diskSize * blockSize) - blockSize;
srs56943f2fe992009-11-24 18:28:18 -0500629 printf("Warning! Disk size is smaller than the main header indicates! Loading\n"
630 "secondary header from the last sector of the disk! You should use 'v' to\n"
631 "verify disk integrity, and perhaps options on the experts' menu to repair\n"
632 "the disk.\n");
633 } // else
634 } else {
srs56941e093722010-01-05 00:14:19 -0500635 seekTo = (diskSize * blockSize) - blockSize;
srs56943f2fe992009-11-24 18:28:18 -0500636 } // if/else (mainCrcOk)
637
srs5694e4ac11e2009-08-31 10:13:04 -0400638 if (lseek64(fd, seekTo, SEEK_SET) != (off_t) -1) {
srs56941e093722010-01-05 00:14:19 -0500639 if (myRead(fd, (char*) &secondHeader, 512) != 512) { // read secondary GPT header
640 fprintf(stderr, "Warning! Error %d reading secondary GPT header!\n", errno);
srs56945d58fe02010-01-03 20:57:08 -0500641 } // if
srs5694e4ac11e2009-08-31 10:13:04 -0400642 secondCrcOk = CheckHeaderCRC(&secondHeader);
643 if (IsLittleEndian() == 0) // big-endian system; adjust header byte order....
644 ReverseHeaderBytes(&secondHeader);
645 } else {
646 allOK = 0;
647 state = gpt_invalid;
srs56943f2fe992009-11-24 18:28:18 -0500648 fprintf(stderr, "Unable to seek to secondary GPT header at sector %llu!\n",
srs56945d58fe02010-01-03 20:57:08 -0500649 (unsigned long long) (diskSize - (UINT64_C(1))));
srs5694e4ac11e2009-08-31 10:13:04 -0400650 } // if/else lseek
651
652 // Return valid headers code: 0 = both headers bad; 1 = main header
653 // good, backup bad; 2 = backup header good, main header bad;
654 // 3 = both headers good. Note these codes refer to valid GPT
655 // signatures and version numbers; more subtle problems will elude
656 // this check!
657 validHeaders = CheckHeaderValidity();
658
659 // Read partitions (from primary array)
660 if (validHeaders > 0) { // if at least one header is OK....
661 // GPT appears to be valid....
662 state = gpt_valid;
663
664 // We're calling the GPT valid, but there's a possibility that one
665 // of the two headers is corrupt. If so, use the one that seems to
666 // be in better shape to regenerate the bad one
667 if (validHeaders == 2) { // valid backup header, invalid main header
668 printf("Caution: invalid main GPT header, but valid backup; regenerating main header\n"
669 "from backup!\n");
670 RebuildMainHeader();
671 mainCrcOk = secondCrcOk; // Since copied, use CRC validity of backup
672 } else if (validHeaders == 1) { // valid main header, invalid backup
673 printf("Caution: invalid backup GPT header, but valid main header; regenerating\n"
674 "backup header from main header.\n");
675 RebuildSecondHeader();
676 secondCrcOk = mainCrcOk; // Since regenerated, use CRC validity of main
677 } // if/else/if
678
679 // Load the main partition table, including storing results of its
680 // CRC check
681 if (LoadMainTable() == 0)
682 allOK = 0;
683
684 // Load backup partition table into temporary storage to check
685 // its CRC and store the results, then discard this temporary
686 // storage, since we don't use it in any but recovery operations
687 seekTo = secondHeader.partitionEntriesLBA * (off_t) blockSize;
688 if ((lseek64(fd, seekTo, SEEK_SET) != (off_t) -1) && (secondCrcOk)) {
689 sizeOfParts = secondHeader.numParts * secondHeader.sizeOfPartitionEntries;
690 storage = (char*) malloc(sizeOfParts);
srs56941e093722010-01-05 00:14:19 -0500691 if (myRead(fd, storage, sizeOfParts) != sizeOfParts) {
srs56945d58fe02010-01-03 20:57:08 -0500692 fprintf(stderr, "Warning! Error %d reading backup partition table!\n", errno);
693 } // if
srs5694e4ac11e2009-08-31 10:13:04 -0400694 newCRC = chksum_crc32((unsigned char*) storage, sizeOfParts);
695 free(storage);
696 secondPartsCrcOk = (newCRC == secondHeader.partitionEntriesCRC);
697 } // if
698
699 // Check for valid CRCs and warn if there are problems
700 if ((mainCrcOk == 0) || (secondCrcOk == 0) || (mainPartsCrcOk == 0) ||
701 (secondPartsCrcOk == 0)) {
702 printf("Warning! One or more CRCs don't match. You should repair the disk!\n");
703 state = gpt_corrupt;
srs5694ba00fed2010-01-12 18:18:36 -0500704 } // if
srs5694e4ac11e2009-08-31 10:13:04 -0400705 } else {
706 state = gpt_invalid;
707 } // if/else
708 return allOK;
709} // GPTData::ForceLoadGPTData()
710
srs5694247657a2009-11-26 18:36:12 -0500711// Loads the partition table pointed to by the main GPT header. The
srs5694e4ac11e2009-08-31 10:13:04 -0400712// main GPT header in memory MUST be valid for this call to do anything
713// sensible!
714int GPTData::LoadMainTable(void) {
715 int fd, retval = 0;
716 uint32_t newCRC, sizeOfParts;
717
718 if ((fd = open(device, O_RDONLY)) != -1) {
719 // Set internal data structures for number of partitions on the disk
720 SetGPTSize(mainHeader.numParts);
721
722 // Load main partition table, and record whether its CRC
723 // matches the stored value
724 lseek64(fd, mainHeader.partitionEntriesLBA * blockSize, SEEK_SET);
725 sizeOfParts = mainHeader.numParts * mainHeader.sizeOfPartitionEntries;
srs56941e093722010-01-05 00:14:19 -0500726 if (myRead(fd, (char*) partitions, sizeOfParts) != sizeOfParts) {
srs56945d58fe02010-01-03 20:57:08 -0500727 fprintf(stderr, "Warning! Error %d when loading the main partition table!\n", errno);
728 } // if
srs5694e4ac11e2009-08-31 10:13:04 -0400729 newCRC = chksum_crc32((unsigned char*) partitions, sizeOfParts);
730 mainPartsCrcOk = (newCRC == mainHeader.partitionEntriesCRC);
731 if (IsLittleEndian() == 0)
732 ReversePartitionBytes();
733 retval = 1;
734 } // if
735 return retval;
736} // GPTData::LoadMainTable()
srs5694e7b4ff92009-08-18 13:16:10 -0400737
738// Load the second (backup) partition table as the primary partition
739// table. Used in repair functions
740void GPTData::LoadSecondTableAsMain(void) {
741 int fd;
742 off_t seekTo;
743 uint32_t sizeOfParts, newCRC;
744
745 if ((fd = open(device, O_RDONLY)) != -1) {
746 seekTo = secondHeader.partitionEntriesLBA * (off_t) blockSize;
747 if (lseek64(fd, seekTo, SEEK_SET) != (off_t) -1) {
748 SetGPTSize(secondHeader.numParts);
749 sizeOfParts = secondHeader.numParts * secondHeader.sizeOfPartitionEntries;
srs56941e093722010-01-05 00:14:19 -0500750 if (myRead(fd, (char*) partitions, sizeOfParts) != sizeOfParts) {
srs56945d58fe02010-01-03 20:57:08 -0500751 fprintf(stderr, "Warning! Read error %d! Misbehavior now likely!\n", errno);
752 } // if
srs5694e7b4ff92009-08-18 13:16:10 -0400753 newCRC = chksum_crc32((unsigned char*) partitions, sizeOfParts);
754 secondPartsCrcOk = (newCRC == secondHeader.partitionEntriesCRC);
srs5694e4ac11e2009-08-31 10:13:04 -0400755 mainPartsCrcOk = secondPartsCrcOk;
srs56942a9f5da2009-08-26 00:48:01 -0400756 if (IsLittleEndian() == 0)
757 ReversePartitionBytes();
srs5694e7b4ff92009-08-18 13:16:10 -0400758 if (!secondPartsCrcOk) {
759 printf("Error! After loading backup partitions, the CRC still doesn't check out!\n");
760 } // if
761 } else {
762 printf("Error! Couldn't seek to backup partition table!\n");
763 } // if/else
764 } else {
srs56941d1448a2009-12-31 21:20:19 -0500765 printf("Error! Couldn't open device %s when recovering backup partition table!\n", device);
srs5694e7b4ff92009-08-18 13:16:10 -0400766 } // if/else
767} // GPTData::LoadSecondTableAsMain()
768
srs5694e7b4ff92009-08-18 13:16:10 -0400769// Writes GPT (and protective MBR) to disk. Returns 1 on successful
770// write, 0 if there was a problem.
srs5694ba00fed2010-01-12 18:18:36 -0500771int GPTData::SaveGPTData(int quiet) {
srs5694978041c2009-09-21 20:51:47 -0400772 int allOK = 1;
srs5694e7b4ff92009-08-18 13:16:10 -0400773 char answer, line[256];
774 int fd;
775 uint64_t secondTable;
srs56942a9f5da2009-08-26 00:48:01 -0400776 uint32_t numParts;
srs5694e7b4ff92009-08-18 13:16:10 -0400777 off_t offset;
778
779 if (strlen(device) == 0) {
780 printf("Device not defined.\n");
781 } // if
782
783 // First do some final sanity checks....
srs56945d58fe02010-01-03 20:57:08 -0500784
785 // This test should only fail on read-only disks....
786 if (justLooking) {
787 printf("The justLooking flag is set. This probably means you can't write to the disk.\n");
788 allOK = 0;
789 } // if
790
srs5694e7b4ff92009-08-18 13:16:10 -0400791 // Is there enough space to hold the GPT headers and partition tables,
792 // given the partition sizes?
srs5694221e0872009-08-29 15:00:31 -0400793 if (CheckGPTSize() > 0) {
srs5694e7b4ff92009-08-18 13:16:10 -0400794 allOK = 0;
795 } // if
796
797 // Check that disk is really big enough to handle this...
798 if (mainHeader.backupLBA > diskSize) {
srs5694247657a2009-11-26 18:36:12 -0500799 fprintf(stderr, "Error! Disk is too small! The 'e' option on the experts' menu might fix the\n"
800 "problem (or it might not). Aborting!\n");
srs56945d58fe02010-01-03 20:57:08 -0500801 printf("(Disk size is %llu sectors, needs to be %llu sectors.)\n",
802 (unsigned long long) diskSize, (unsigned long long) mainHeader.backupLBA);
srs5694e7b4ff92009-08-18 13:16:10 -0400803 allOK = 0;
804 } // if
srs5694247657a2009-11-26 18:36:12 -0500805 // Check that second header is properly placed. Warn and ask if this should
806 // be corrected if the test fails....
srs5694ba00fed2010-01-12 18:18:36 -0500807 if ((mainHeader.backupLBA < (diskSize - UINT64_C(1))) && (quiet == 0)) {
srs5694247657a2009-11-26 18:36:12 -0500808 printf("Warning! Secondary header is placed too early on the disk! Do you want to\n"
809 "correct this problem? ");
810 if (GetYN() == 'Y') {
811 MoveSecondHeaderToEnd();
812 printf("Have moved second header and partition table to correct location.\n");
813 } else {
814 printf("Have not corrected the problem. Strange problems may occur in the future!\n");
815 } // if correction requested
816 } // if
srs5694e7b4ff92009-08-18 13:16:10 -0400817
818 // Check for overlapping partitions....
srs5694e4ac11e2009-08-31 10:13:04 -0400819 if (FindOverlaps() > 0) {
820 allOK = 0;
821 fprintf(stderr, "Aborting write operation!\n");
822 } // if
823
824 // Check for mismatched MBR and GPT data, but let it pass if found
825 // (function displays warning message)
826 FindHybridMismatches();
srs5694e7b4ff92009-08-18 13:16:10 -0400827
srs56942a9f5da2009-08-26 00:48:01 -0400828 // Pull out some data that's needed before doing byte-order reversal on
829 // big-endian systems....
830 numParts = mainHeader.numParts;
831 secondTable = secondHeader.partitionEntriesLBA;
832 if (IsLittleEndian() == 0) {
833 // Reverse partition bytes first, since that function requires non-reversed
834 // data from the main header....
835 ReversePartitionBytes();
836 ReverseHeaderBytes(&mainHeader);
837 ReverseHeaderBytes(&secondHeader);
838 } // if
srs5694e7b4ff92009-08-18 13:16:10 -0400839 RecomputeCRCs();
840
srs5694ba00fed2010-01-12 18:18:36 -0500841 if ((allOK) && (!quiet)) {
srs5694e7b4ff92009-08-18 13:16:10 -0400842 printf("\nFinal checks complete. About to write GPT data. THIS WILL OVERWRITE EXISTING\n");
843 printf("MBR PARTITIONS!! THIS PROGRAM IS BETA QUALITY AT BEST. IF YOU LOSE ALL YOUR\n");
844 printf("DATA, YOU HAVE ONLY YOURSELF TO BLAME IF YOU ANSWER 'Y' BELOW!\n\n");
srs56945d58fe02010-01-03 20:57:08 -0500845 printf("Do you want to proceed, possibly destroying your data? ");
846 answer = GetYN();
847 if (answer == 'Y') {
848 printf("OK; writing new GUID partition table (GPT).\n");
srs5694e7b4ff92009-08-18 13:16:10 -0400849 } else {
850 allOK = 0;
851 } // if/else
852 } // if
853
854 // Do it!
855 if (allOK) {
srs5694e4ac11e2009-08-31 10:13:04 -0400856 fd = OpenForWrite(device);
srs5694e7b4ff92009-08-18 13:16:10 -0400857 if (fd != -1) {
858 // First, write the protective MBR...
srs5694e4ac11e2009-08-31 10:13:04 -0400859 protectiveMBR.WriteMBRData(fd);
srs5694e7b4ff92009-08-18 13:16:10 -0400860
861 // Now write the main GPT header...
srs56941e093722010-01-05 00:14:19 -0500862 if (allOK) {
863 if (lseek64(fd, blockSize, SEEK_SET) != (off_t) -1) {
864 if (myWrite(fd, (char*) &mainHeader, 512) == -1)
865 allOK = 0;
866 } else allOK = 0; // if (lseek64()...)
867 } // if (allOK)
srs5694e7b4ff92009-08-18 13:16:10 -0400868
869 // Now write the main partition tables...
srs5694e4ac11e2009-08-31 10:13:04 -0400870 if (allOK) {
srs56941e093722010-01-05 00:14:19 -0500871 offset = mainHeader.partitionEntriesLBA * blockSize;
872 if (lseek64(fd, offset, SEEK_SET) != (off_t) - 1) {
873 if (myWrite(fd, (char*) partitions, GPT_SIZE * numParts) == -1)
srs5694e7b4ff92009-08-18 13:16:10 -0400874 allOK = 0;
srs56941e093722010-01-05 00:14:19 -0500875 } else allOK = 0; // if (lseek64()...)
876 } // if (allOK)
srs5694e7b4ff92009-08-18 13:16:10 -0400877
878 // Now seek to near the end to write the secondary GPT....
srs5694e4ac11e2009-08-31 10:13:04 -0400879 if (allOK) {
srs5694e7b4ff92009-08-18 13:16:10 -0400880 offset = (off_t) secondTable * (off_t) (blockSize);
881 if (lseek64(fd, offset, SEEK_SET) == (off_t) - 1) {
882 allOK = 0;
883 printf("Unable to seek to end of disk!\n");
884 } // if
885 } // if
886
887 // Now write the secondary partition tables....
srs56941e093722010-01-05 00:14:19 -0500888 if (allOK) {
889 if (myWrite(fd, (char*) partitions, GPT_SIZE * numParts) == -1)
srs5694e7b4ff92009-08-18 13:16:10 -0400890 allOK = 0;
srs56941e093722010-01-05 00:14:19 -0500891 } // if (allOK)
srs5694e7b4ff92009-08-18 13:16:10 -0400892
893 // Now write the secondary GPT header...
srs56941e093722010-01-05 00:14:19 -0500894 if (allOK) {
895 offset = mainHeader.backupLBA * blockSize;
896 if (lseek64(fd, offset, SEEK_SET) != (off_t) - 1) {
897 if (myWrite(fd, (char*) &secondHeader, 512) == -1)
898 allOK = 0;
899 } else allOK = 0; // if (lseek64()...)
900 } // if (allOK)
srs5694e7b4ff92009-08-18 13:16:10 -0400901
902 // re-read the partition table
903 if (allOK) {
srs5694e35eb1b2009-09-14 00:29:34 -0400904 DiskSync(fd);
srs5694e7b4ff92009-08-18 13:16:10 -0400905 } // if
906
907 if (allOK) { // writes completed OK
srs5694e4ac11e2009-08-31 10:13:04 -0400908 printf("The operation has completed successfully.\n");
srs5694e7b4ff92009-08-18 13:16:10 -0400909 } else {
srs5694e4ac11e2009-08-31 10:13:04 -0400910 printf("Warning! An error was reported when writing the partition table! This error\n");
911 printf("MIGHT be harmless, but you may have trashed the disk! Use parted and, if\n");
912 printf("necessary, restore your original partition table.\n");
srs5694e7b4ff92009-08-18 13:16:10 -0400913 } // if/else
srs5694e4ac11e2009-08-31 10:13:04 -0400914 close(fd);
srs5694e7b4ff92009-08-18 13:16:10 -0400915 } else {
srs5694e4ac11e2009-08-31 10:13:04 -0400916 fprintf(stderr, "Unable to open device %s for writing! Errno is %d! Aborting write!\n",
917 device, errno);
918 allOK = 0;
srs5694e7b4ff92009-08-18 13:16:10 -0400919 } // if/else
920 } else {
921 printf("Aborting write of new partition table.\n");
922 } // if
923
srs56942a9f5da2009-08-26 00:48:01 -0400924 if (IsLittleEndian() == 0) {
925 // Reverse (normalize) header bytes first, since ReversePartitionBytes()
926 // requires non-reversed data in mainHeader...
927 ReverseHeaderBytes(&mainHeader);
928 ReverseHeaderBytes(&secondHeader);
929 ReversePartitionBytes();
930 } // if
931
srs5694e7b4ff92009-08-18 13:16:10 -0400932 return (allOK);
933} // GPTData::SaveGPTData()
934
935// Save GPT data to a backup file. This function does much less error
936// checking than SaveGPTData(). It can therefore preserve many types of
937// corruption for later analysis; however, it preserves only the MBR,
938// the main GPT header, the backup GPT header, and the main partition
939// table; it discards the backup partition table, since it should be
940// identical to the main partition table on healthy disks.
941int GPTData::SaveGPTBackup(char* filename) {
srs56942a9f5da2009-08-26 00:48:01 -0400942 int fd, allOK = 1;
943 uint32_t numParts;
srs5694e7b4ff92009-08-18 13:16:10 -0400944
945 if ((fd = open(filename, O_WRONLY | O_CREAT, S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH)) != -1) {
srs56942a9f5da2009-08-26 00:48:01 -0400946 // Reverse the byte order, if necessary....
947 numParts = mainHeader.numParts;
948 if (IsLittleEndian() == 0) {
949 ReversePartitionBytes();
950 ReverseHeaderBytes(&mainHeader);
951 ReverseHeaderBytes(&secondHeader);
952 } // if
953
srs5694978041c2009-09-21 20:51:47 -0400954 // Recomputing the CRCs is likely to alter them, which could be bad
955 // if the intent is to save a potentially bad GPT for later analysis;
956 // but if we don't do this, we get bogus errors when we load the
957 // backup. I'm favoring misses over false alarms....
958 RecomputeCRCs();
959
srs56942a9f5da2009-08-26 00:48:01 -0400960 // Now write the protective MBR...
srs5694e7b4ff92009-08-18 13:16:10 -0400961 protectiveMBR.WriteMBRData(fd);
962
963 // Now write the main GPT header...
964 if (allOK)
srs56941e093722010-01-05 00:14:19 -0500965 if (myWrite(fd, (char*) &mainHeader, 512) == -1)
srs5694e7b4ff92009-08-18 13:16:10 -0400966 allOK = 0;
967
968 // Now write the secondary GPT header...
969 if (allOK)
srs56941e093722010-01-05 00:14:19 -0500970 if (myWrite(fd, (char*) &secondHeader, 512) == -1)
srs5694e4ac11e2009-08-31 10:13:04 -0400971 allOK = 0;
srs5694e7b4ff92009-08-18 13:16:10 -0400972
973 // Now write the main partition tables...
974 if (allOK) {
srs56941e093722010-01-05 00:14:19 -0500975 if (myWrite(fd, (char*) partitions, GPT_SIZE * numParts) == -1)
srs5694e7b4ff92009-08-18 13:16:10 -0400976 allOK = 0;
977 } // if
978
979 if (allOK) { // writes completed OK
980 printf("The operation has completed successfully.\n");
981 } else {
982 printf("Warning! An error was reported when writing the backup file.\n");
srs56948bb78762009-11-24 15:43:49 -0500983 printf("It may not be usable!\n");
srs5694e7b4ff92009-08-18 13:16:10 -0400984 } // if/else
985 close(fd);
srs56942a9f5da2009-08-26 00:48:01 -0400986
987 // Now reverse the byte-order reversal, if necessary....
988 if (IsLittleEndian() == 0) {
989 ReverseHeaderBytes(&mainHeader);
990 ReverseHeaderBytes(&secondHeader);
991 ReversePartitionBytes();
992 } // if
srs5694e7b4ff92009-08-18 13:16:10 -0400993 } else {
994 fprintf(stderr, "Unable to open file %s for writing! Aborting!\n", filename);
995 allOK = 0;
996 } // if/else
997 return allOK;
998} // GPTData::SaveGPTBackup()
999
1000// Load GPT data from a backup file created by SaveGPTBackup(). This function
1001// does minimal error checking. It returns 1 if it completed successfully,
1002// 0 if there was a problem. In the latter case, it creates a new empty
1003// set of partitions.
1004int GPTData::LoadGPTBackup(char* filename) {
1005 int fd, allOK = 1, val;
1006 uint32_t numParts, sizeOfEntries, sizeOfParts, newCRC;
srs56942a9f5da2009-08-26 00:48:01 -04001007 int littleEndian = 1;
srs5694e7b4ff92009-08-18 13:16:10 -04001008
1009 if ((fd = open(filename, O_RDONLY)) != -1) {
srs56942a9f5da2009-08-26 00:48:01 -04001010 if (IsLittleEndian() == 0)
1011 littleEndian = 0;
1012
srs5694e7b4ff92009-08-18 13:16:10 -04001013 // Let the MBRData class load the saved MBR...
srs5694221e0872009-08-29 15:00:31 -04001014 protectiveMBR.ReadMBRData(fd, 0); // 0 = don't check block size
srs5694e7b4ff92009-08-18 13:16:10 -04001015
1016 // Load the main GPT header, check its vaility, and set the GPT
1017 // size based on the data
srs56941e093722010-01-05 00:14:19 -05001018 if (myRead(fd, (char*) &mainHeader, 512)) {
srs56945d58fe02010-01-03 20:57:08 -05001019 fprintf(stderr, "Warning! Read error %d; strange behavior now likely!\n", errno);
1020 } // if
srs5694e7b4ff92009-08-18 13:16:10 -04001021 mainCrcOk = CheckHeaderCRC(&mainHeader);
1022
srs56942a9f5da2009-08-26 00:48:01 -04001023 // Reverse byte order, if necessary
1024 if (littleEndian == 0) {
1025 ReverseHeaderBytes(&mainHeader);
1026 } // if
1027
srs5694e7b4ff92009-08-18 13:16:10 -04001028 // Load the backup GPT header in much the same way as the main
1029 // GPT header....
srs56941e093722010-01-05 00:14:19 -05001030 if (myRead(fd, (char*) &secondHeader, 512) != 512) {
srs56945d58fe02010-01-03 20:57:08 -05001031 fprintf(stderr, "Warning! Read error %d; strange behavior now likely!\n", errno);
1032 } // if
srs5694e7b4ff92009-08-18 13:16:10 -04001033 secondCrcOk = CheckHeaderCRC(&secondHeader);
1034
srs56942a9f5da2009-08-26 00:48:01 -04001035 // Reverse byte order, if necessary
1036 if (littleEndian == 0) {
1037 ReverseHeaderBytes(&secondHeader);
1038 } // if
1039
srs5694e7b4ff92009-08-18 13:16:10 -04001040 // Return valid headers code: 0 = both headers bad; 1 = main header
1041 // good, backup bad; 2 = backup header good, main header bad;
1042 // 3 = both headers good. Note these codes refer to valid GPT
1043 // signatures and version numbers; more subtle problems will elude
1044 // this check!
1045 if ((val = CheckHeaderValidity()) > 0) {
1046 if (val == 2) { // only backup header seems to be good
1047 numParts = secondHeader.numParts;
srs5694e4ac11e2009-08-31 10:13:04 -04001048 sizeOfEntries = secondHeader.sizeOfPartitionEntries;
srs5694e7b4ff92009-08-18 13:16:10 -04001049 } else { // main header is OK
1050 numParts = mainHeader.numParts;
1051 sizeOfEntries = mainHeader.sizeOfPartitionEntries;
1052 } // if/else
1053
1054 SetGPTSize(numParts);
1055
1056 // If current disk size doesn't match that of backup....
1057 if (secondHeader.currentLBA != diskSize - UINT64_C(1)) {
1058 printf("Warning! Current disk size doesn't match that of the backup!\n"
srs5694e4ac11e2009-08-31 10:13:04 -04001059 "Adjusting sizes to match, but subsequent problems are possible!\n");
srs5694247657a2009-11-26 18:36:12 -05001060 MoveSecondHeaderToEnd();
srs5694e7b4ff92009-08-18 13:16:10 -04001061 } // if
1062
1063 // Load main partition table, and record whether its CRC
1064 // matches the stored value
1065 sizeOfParts = numParts * sizeOfEntries;
srs56941e093722010-01-05 00:14:19 -05001066 if (myRead(fd, (char*) partitions, sizeOfParts) != sizeOfParts) {
srs56945d58fe02010-01-03 20:57:08 -05001067 fprintf(stderr, "Warning! Read error %d; strange behavior now likely!\n", errno);
1068 } // if
srs5694e7b4ff92009-08-18 13:16:10 -04001069
1070 newCRC = chksum_crc32((unsigned char*) partitions, sizeOfParts);
1071 mainPartsCrcOk = (newCRC == mainHeader.partitionEntriesCRC);
1072 secondPartsCrcOk = (newCRC == secondHeader.partitionEntriesCRC);
srs56942a9f5da2009-08-26 00:48:01 -04001073 // Reverse byte order, if necessary
srs5694e4ac11e2009-08-31 10:13:04 -04001074 if (littleEndian == 0) {
1075 ReversePartitionBytes();
1076 } // if
srs56942a9f5da2009-08-26 00:48:01 -04001077
srs5694e7b4ff92009-08-18 13:16:10 -04001078 } else {
1079 allOK = 0;
1080 } // if/else
1081 } else {
1082 allOK = 0;
1083 fprintf(stderr, "Unable to open file %s for reading! Aborting!\n", filename);
1084 } // if/else
1085
1086 // Something went badly wrong, so blank out partitions
1087 if (allOK == 0) {
1088 ClearGPTData();
1089 protectiveMBR.MakeProtectiveMBR();
1090 } // if
1091 return allOK;
1092} // GPTData::LoadGPTBackup()
1093
srs5694e4ac11e2009-08-31 10:13:04 -04001094// Tell user whether Apple Partition Map (APM) was discovered....
1095void GPTData::ShowAPMState(void) {
1096 if (apmFound)
1097 printf(" APM: present\n");
1098 else
1099 printf(" APM: not present\n");
1100} // GPTData::ShowAPMState()
1101
1102// Tell user about the state of the GPT data....
1103void GPTData::ShowGPTState(void) {
1104 switch (state) {
1105 case gpt_invalid:
1106 printf(" GPT: not present\n");
1107 break;
1108 case gpt_valid:
1109 printf(" GPT: present\n");
1110 break;
1111 case gpt_corrupt:
1112 printf(" GPT: damaged\n");
1113 break;
1114 default:
1115 printf("\a GPT: unknown -- bug!\n");
1116 break;
1117 } // switch
1118} // GPTData::ShowGPTState()
1119
1120// Display the basic GPT data
1121void GPTData::DisplayGPTData(void) {
srs5694e35eb1b2009-09-14 00:29:34 -04001122 int i;
srs5694e4ac11e2009-08-31 10:13:04 -04001123 char sizeInSI[255]; // String to hold size of disk in SI units
1124 char tempStr[255];
1125 uint64_t temp, totalFree;
1126
1127 BytesToSI(diskSize * blockSize, sizeInSI);
srs5694978041c2009-09-21 20:51:47 -04001128 printf("Disk %s: %llu sectors, %s\n", device,
1129 (unsigned long long) diskSize, sizeInSI);
srs56941e093722010-01-05 00:14:19 -05001130 printf("Logical sector size: %d bytes\n", blockSize);
srs5694e4ac11e2009-08-31 10:13:04 -04001131 printf("Disk identifier (GUID): %s\n", GUIDToStr(mainHeader.diskGUID, tempStr));
1132 printf("Partition table holds up to %lu entries\n", (unsigned long) mainHeader.numParts);
srs56945d58fe02010-01-03 20:57:08 -05001133 printf("First usable sector is %llu, last usable sector is %llu\n",
1134 (unsigned long long) mainHeader.firstUsableLBA,
1135 (unsigned long long) mainHeader.lastUsableLBA);
srs5694e4ac11e2009-08-31 10:13:04 -04001136 totalFree = FindFreeBlocks(&i, &temp);
srs56945d58fe02010-01-03 20:57:08 -05001137 printf("Total free space is %llu sectors (%s)\n", (unsigned long long) totalFree,
srs5694e4ac11e2009-08-31 10:13:04 -04001138 BytesToSI(totalFree * (uint64_t) blockSize, sizeInSI));
1139 printf("\nNumber Start (sector) End (sector) Size Code Name\n");
1140 for (i = 0; i < mainHeader.numParts; i++) {
srs5694978041c2009-09-21 20:51:47 -04001141 partitions[i].ShowSummary(i, blockSize);
srs5694e4ac11e2009-08-31 10:13:04 -04001142 } // for
1143} // GPTData::DisplayGPTData()
1144
1145// Get partition number from user and then call ShowPartDetails(partNum)
1146// to show its detailed information
1147void GPTData::ShowDetails(void) {
1148 int partNum;
1149 uint32_t low, high;
1150
1151 if (GetPartRange(&low, &high) > 0) {
1152 partNum = GetPartNum();
1153 ShowPartDetails(partNum);
1154 } else {
1155 printf("No partitions\n");
1156 } // if/else
1157} // GPTData::ShowDetails()
1158
1159// Show detailed information on the specified partition
1160void GPTData::ShowPartDetails(uint32_t partNum) {
1161 if (partitions[partNum].GetFirstLBA() != 0) {
1162 partitions[partNum].ShowDetails(blockSize);
1163 } else {
1164 printf("Partition #%d does not exist.", (int) (partNum + 1));
1165 } // if
1166} // GPTData::ShowPartDetails()
1167
1168/*********************************************************************
1169 * *
1170 * Begin functions that obtain information from the users, and often *
1171 * do something with that information (call other functions) *
1172 * *
1173 *********************************************************************/
1174
1175// Prompts user for partition number and returns the result.
1176uint32_t GPTData::GetPartNum(void) {
1177 uint32_t partNum;
1178 uint32_t low, high;
1179 char prompt[255];
1180
1181 if (GetPartRange(&low, &high) > 0) {
1182 sprintf(prompt, "Partition number (%d-%d): ", low + 1, high + 1);
1183 partNum = GetNumber(low + 1, high + 1, low, prompt);
1184 } else partNum = 1;
1185 return (partNum - 1);
1186} // GPTData::GetPartNum()
1187
1188// What it says: Resize the partition table. (Default is 128 entries.)
1189void GPTData::ResizePartitionTable(void) {
1190 int newSize;
1191 char prompt[255];
1192 uint32_t curLow, curHigh;
1193
1194 printf("Current partition table size is %lu.\n",
1195 (unsigned long) mainHeader.numParts);
1196 GetPartRange(&curLow, &curHigh);
1197 curHigh++; // since GetPartRange() returns numbers starting from 0...
1198 // There's no point in having fewer than four partitions....
1199 if (curHigh < 4)
1200 curHigh = 4;
1201 sprintf(prompt, "Enter new size (%d up, default %d): ", (int) curHigh,
1202 (int) NUM_GPT_ENTRIES);
1203 newSize = GetNumber(4, 65535, 128, prompt);
1204 if (newSize < 128) {
1205 printf("Caution: The partition table size should officially be 16KB or larger,\n"
1206 "which works out to 128 entries. In practice, smaller tables seem to\n"
1207 "work with most OSes, but this practice is risky. I'm proceeding with\n"
1208 "the resize, but you may want to reconsider this action and undo it.\n\n");
1209 } // if
1210 SetGPTSize(newSize);
1211} // GPTData::ResizePartitionTable()
1212
1213// Interactively create a partition
1214void GPTData::CreatePartition(void) {
1215 uint64_t firstBlock, firstInLargest, lastBlock, sector;
1216 char prompt[255];
1217 int partNum, firstFreePart = 0;
1218
1219 // Find first free partition...
1220 while (partitions[firstFreePart].GetFirstLBA() != 0) {
1221 firstFreePart++;
1222 } // while
1223
1224 if (((firstBlock = FindFirstAvailable()) != 0) &&
1225 (firstFreePart < mainHeader.numParts)) {
1226 lastBlock = FindLastAvailable(firstBlock);
1227 firstInLargest = FindFirstInLargest();
1228
1229 // Get partition number....
1230 do {
1231 sprintf(prompt, "Partition number (%d-%d, default %d): ", firstFreePart + 1,
1232 mainHeader.numParts, firstFreePart + 1);
1233 partNum = GetNumber(firstFreePart + 1, mainHeader.numParts,
1234 firstFreePart + 1, prompt) - 1;
1235 if (partitions[partNum].GetFirstLBA() != 0)
1236 printf("partition %d is in use.\n", partNum + 1);
1237 } while (partitions[partNum].GetFirstLBA() != 0);
1238
1239 // Get first block for new partition...
srs56945d58fe02010-01-03 20:57:08 -05001240 sprintf(prompt, "First sector (%llu-%llu, default = %llu) or {+-}size{KMGT}: ",
1241 (unsigned long long) firstBlock, (unsigned long long) lastBlock,
1242 (unsigned long long) firstInLargest);
srs5694e4ac11e2009-08-31 10:13:04 -04001243 do {
1244 sector = GetSectorNum(firstBlock, lastBlock, firstInLargest, prompt);
1245 } while (IsFree(sector) == 0);
srs56941d1448a2009-12-31 21:20:19 -05001246 Align(&sector); // Align sector to correct multiple
srs5694e4ac11e2009-08-31 10:13:04 -04001247 firstBlock = sector;
1248
1249 // Get last block for new partitions...
1250 lastBlock = FindLastInFree(firstBlock);
srs56945d58fe02010-01-03 20:57:08 -05001251 sprintf(prompt, "Last sector (%llu-%llu, default = %llu) or {+-}size{KMGT}: ",
1252 (unsigned long long) firstBlock, (unsigned long long) lastBlock,
1253 (unsigned long long) lastBlock);
srs5694e4ac11e2009-08-31 10:13:04 -04001254 do {
1255 sector = GetSectorNum(firstBlock, lastBlock, lastBlock, prompt);
1256 } while (IsFree(sector) == 0);
1257 lastBlock = sector;
1258
srs5694ba00fed2010-01-12 18:18:36 -05001259 firstFreePart = CreatePartition(partNum, firstBlock, lastBlock);
srs5694e4ac11e2009-08-31 10:13:04 -04001260 partitions[partNum].ChangeType();
1261 partitions[partNum].SetName((unsigned char*) partitions[partNum].GetNameType(prompt));
srs5694ba00fed2010-01-12 18:18:36 -05001262 } else {
1263 printf("No free sectors available\n");
1264 } // if/else
srs5694e4ac11e2009-08-31 10:13:04 -04001265} // GPTData::CreatePartition()
1266
1267// Interactively delete a partition (duh!)
1268void GPTData::DeletePartition(void) {
1269 int partNum;
1270 uint32_t low, high;
srs5694e4ac11e2009-08-31 10:13:04 -04001271 char prompt[255];
1272
1273 if (GetPartRange(&low, &high) > 0) {
1274 sprintf(prompt, "Partition number (%d-%d): ", low + 1, high + 1);
1275 partNum = GetNumber(low + 1, high + 1, low, prompt);
srs5694ba00fed2010-01-12 18:18:36 -05001276 DeletePartition(partNum - 1);
srs5694e4ac11e2009-08-31 10:13:04 -04001277 } else {
1278 printf("No partitions\n");
1279 } // if/else
1280} // GPTData::DeletePartition()
1281
1282// Prompt user for a partition number, then change its type code
1283// using ChangeGPTType(struct GPTPartition*) function.
1284void GPTData::ChangePartType(void) {
1285 int partNum;
1286 uint32_t low, high;
1287
1288 if (GetPartRange(&low, &high) > 0) {
1289 partNum = GetPartNum();
1290 partitions[partNum].ChangeType();
1291 } else {
1292 printf("No partitions\n");
1293 } // if/else
1294} // GPTData::ChangePartType()
1295
1296// Partition attributes seem to be rarely used, but I want a way to
1297// adjust them for completeness....
1298void GPTData::SetAttributes(uint32_t partNum) {
1299 Attributes theAttr;
1300
1301 theAttr.SetAttributes(partitions[partNum].GetAttributes());
1302 theAttr.DisplayAttributes();
1303 theAttr.ChangeAttributes();
1304 partitions[partNum].SetAttributes(theAttr.GetAttributes());
1305} // GPTData::SetAttributes()
1306
srs5694c0ca8f82009-08-20 21:35:25 -04001307// This function destroys the on-disk GPT structures. Returns 1 if the
1308// user confirms destruction, 0 if the user aborts.
srs5694978041c2009-09-21 20:51:47 -04001309// If prompt == 0, don't ask user about proceeding and do NOT wipe out
1310// MBR. (Set prompt == 0 when doing a GPT-to-MBR conversion.)
srs5694ba00fed2010-01-12 18:18:36 -05001311// If prompt == -1, don't ask user about proceeding and DO wipe out
1312// MBR.
srs5694978041c2009-09-21 20:51:47 -04001313int GPTData::DestroyGPT(int prompt) {
srs56941e093722010-01-05 00:14:19 -05001314 int fd, i, sum, tableSize;
srs5694978041c2009-09-21 20:51:47 -04001315 char blankSector[512], goOn = 'Y', blank = 'N';
srs56941e093722010-01-05 00:14:19 -05001316 char* emptyTable;
srs5694c0ca8f82009-08-20 21:35:25 -04001317
1318 for (i = 0; i < 512; i++) {
1319 blankSector[i] = '\0';
1320 } // for
1321
srs5694ba00fed2010-01-12 18:18:36 -05001322 if (((apmFound) || (bsdFound)) && (prompt > 0)) {
srs5694e35eb1b2009-09-14 00:29:34 -04001323 printf("WARNING: APM or BSD disklabel structures detected! This operation could\n"
1324 "damage any APM or BSD partitions on this disk!\n");
1325 } // if APM or BSD
srs5694ba00fed2010-01-12 18:18:36 -05001326 if (prompt > 0) {
srs5694978041c2009-09-21 20:51:47 -04001327 printf("\a\aAbout to wipe out GPT on %s. Proceed? ", device);
1328 goOn = GetYN();
1329 } // if
srs5694c0ca8f82009-08-20 21:35:25 -04001330 if (goOn == 'Y') {
1331 fd = open(device, O_WRONLY);
1332#ifdef __APPLE__
1333 // MacOS X requires a shared lock under some circumstances....
1334 if (fd < 0) {
1335 fd = open(device, O_WRONLY|O_SHLOCK);
1336 } // if
1337#endif
1338 if (fd != -1) {
srs56941e093722010-01-05 00:14:19 -05001339 lseek64(fd, mainHeader.currentLBA * blockSize, SEEK_SET); // seek to GPT header
1340 if (myWrite(fd, blankSector, 512) != 512) { // blank it out
srs56945d58fe02010-01-03 20:57:08 -05001341 fprintf(stderr, "Warning! GPT main header not overwritten! Error is %d\n", errno);
1342 } // if
srs56941e093722010-01-05 00:14:19 -05001343 lseek64(fd, mainHeader.partitionEntriesLBA * blockSize, SEEK_SET); // seek to partition table
1344 tableSize = mainHeader.numParts * mainHeader.sizeOfPartitionEntries;
1345 emptyTable = (char*) malloc(tableSize);
1346 for (i = 0; i < tableSize; i++)
1347 emptyTable[i] = (char) 0;
1348 sum = myWrite(fd, emptyTable, tableSize);
1349 if (sum != tableSize)
srs56945d58fe02010-01-03 20:57:08 -05001350 fprintf(stderr, "Warning! GPT main partition table not overwritten! Error is %d\n", errno);
srs56941e093722010-01-05 00:14:19 -05001351 lseek64(fd, secondHeader.partitionEntriesLBA * blockSize, SEEK_SET); // seek to partition table
1352 sum = myWrite(fd, emptyTable, tableSize);
1353 if (sum != tableSize)
srs56945d58fe02010-01-03 20:57:08 -05001354 fprintf(stderr, "Warning! GPT backup partition table not overwritten! Error is %d\n", errno);
srs56941e093722010-01-05 00:14:19 -05001355 lseek64(fd, secondHeader.currentLBA * blockSize, SEEK_SET); // seek to GPT header
1356 if (myWrite(fd, blankSector, 512) != 512) { // blank it out
srs56945d58fe02010-01-03 20:57:08 -05001357 fprintf(stderr, "Warning! GPT backup header not overwritten! Error is %d\n", errno);
1358 } // if
srs5694ba00fed2010-01-12 18:18:36 -05001359 if (prompt > 0) {
srs5694978041c2009-09-21 20:51:47 -04001360 printf("Blank out MBR? ");
1361 blank = GetYN();
srs5694ba00fed2010-01-12 18:18:36 -05001362 } // if
srs5694978041c2009-09-21 20:51:47 -04001363 // Note on below: Touch the MBR only if the user wants it completely
1364 // blanked out. Version 0.4.2 deleted the 0xEE partition and re-wrote
1365 // the MBR, but this could wipe out a valid MBR that the program
1366 // had subsequently discarded (say, if it conflicted with older GPT
1367 // structures).
srs5694ba00fed2010-01-12 18:18:36 -05001368 if ((blank == 'Y') || (prompt < 0)) {
srs5694c0ca8f82009-08-20 21:35:25 -04001369 lseek64(fd, 0, SEEK_SET);
srs56941e093722010-01-05 00:14:19 -05001370 if (myWrite(fd, blankSector, 512) != 512) { // blank it out
srs56945d58fe02010-01-03 20:57:08 -05001371 fprintf(stderr, "Warning! MBR not overwritten! Error is %d!\n", errno);
1372 } // if
srs5694978041c2009-09-21 20:51:47 -04001373 } else {
1374 printf("MBR is unchanged. You may need to delete an EFI GPT (0xEE) partition\n"
1375 "with fdisk or another tool.\n");
srs5694e35eb1b2009-09-14 00:29:34 -04001376 } // if/else
1377 DiskSync(fd);
srs5694c0ca8f82009-08-20 21:35:25 -04001378 close(fd);
srs5694e4ac11e2009-08-31 10:13:04 -04001379 printf("GPT data structures destroyed! You may now partition the disk using fdisk or\n"
1380 "other utilities. Program will now terminate.\n");
srs5694c0ca8f82009-08-20 21:35:25 -04001381 } else {
srs56941d1448a2009-12-31 21:20:19 -05001382 printf("Problem opening %s for writing! Program will now terminate.\n", device);
srs5694c0ca8f82009-08-20 21:35:25 -04001383 } // if/else (fd != -1)
1384 } // if (goOn == 'Y')
1385 return (goOn == 'Y');
1386} // GPTData::DestroyGPT()
1387
srs5694e4ac11e2009-08-31 10:13:04 -04001388/**************************************************************************
1389 * *
1390 * Partition table transformation functions (MBR or BSD disklabel to GPT) *
1391 * (some of these functions may require user interaction) *
1392 * *
1393 **************************************************************************/
1394
1395// Examines the MBR & GPT data, and perhaps asks the user questions, to
1396// determine which set of data to use: the MBR (use_mbr), the GPT (use_gpt),
1397// or create a new set of partitions (use_new)
1398WhichToUse GPTData::UseWhichPartitions(void) {
1399 WhichToUse which = use_new;
1400 MBRValidity mbrState;
1401 int answer;
1402
1403 mbrState = protectiveMBR.GetValidity();
1404
1405 if ((state == gpt_invalid) && ((mbrState == mbr) || (mbrState == hybrid))) {
srs56945d58fe02010-01-03 20:57:08 -05001406 printf("\n***************************************************************\n"
1407 "Found invalid GPT and valid MBR; converting MBR to GPT format.\n");
1408 if (!justLooking) {
1409 printf("\aTHIS OPERATON IS POTENTIALLY DESTRUCTIVE! Exit by typing 'q' if\n"
1410 "you don't want to convert your MBR partitions to GPT format!\n");
1411 } // if
1412 printf("***************************************************************\n\n");
srs5694e4ac11e2009-08-31 10:13:04 -04001413 which = use_mbr;
1414 } // if
1415
1416 if ((state == gpt_invalid) && bsdFound) {
srs56945d58fe02010-01-03 20:57:08 -05001417 printf("\n**********************************************************************\n"
srs5694e4ac11e2009-08-31 10:13:04 -04001418 "Found invalid GPT and valid BSD disklabel; converting BSD disklabel\n"
srs56945d58fe02010-01-03 20:57:08 -05001419 "to GPT format.");
1420 if (!justLooking) {
1421 printf("\a THIS OPERATON IS POTENTIALLY DESTRUCTIVE! Your first\n"
srs5694e4ac11e2009-08-31 10:13:04 -04001422 "BSD partition will likely be unusable. Exit by typing 'q' if you don't\n"
srs56945d58fe02010-01-03 20:57:08 -05001423 "want to convert your BSD partitions to GPT format!");
1424 } // if
1425 printf("\n**********************************************************************\n\n");
srs5694e4ac11e2009-08-31 10:13:04 -04001426 which = use_bsd;
1427 } // if
1428
1429 if ((state == gpt_valid) && (mbrState == gpt)) {
1430 printf("Found valid GPT with protective MBR; using GPT.\n");
1431 which = use_gpt;
1432 } // if
1433 if ((state == gpt_valid) && (mbrState == hybrid)) {
1434 printf("Found valid GPT with hybrid MBR; using GPT.\n");
1435 which = use_gpt;
1436 } // if
1437 if ((state == gpt_valid) && (mbrState == invalid)) {
1438 printf("\aFound valid GPT with corrupt MBR; using GPT and will create new\nprotective MBR on save.\n");
1439 which = use_gpt;
1440 protectiveMBR.MakeProtectiveMBR();
1441 } // if
1442 if ((state == gpt_valid) && (mbrState == mbr)) {
1443 printf("Found valid MBR and GPT. Which do you want to use?\n");
1444 answer = GetNumber(1, 3, 2, (char*) " 1 - MBR\n 2 - GPT\n 3 - Create blank GPT\n\nYour answer: ");
1445 if (answer == 1) {
1446 which = use_mbr;
1447 } else if (answer == 2) {
1448 which = use_gpt;
1449 protectiveMBR.MakeProtectiveMBR();
1450 printf("Using GPT and creating fresh protective MBR.\n");
1451 } else which = use_new;
1452 } // if
1453
1454 // Nasty decisions here -- GPT is present, but corrupt (bad CRCs or other
1455 // problems)
1456 if (state == gpt_corrupt) {
1457 if ((mbrState == mbr) || (mbrState == hybrid)) {
1458 printf("Found valid MBR and corrupt GPT. Which do you want to use? (Using the\n"
1459 "GPT MAY permit recovery of GPT data.)\n");
1460 answer = GetNumber(1, 3, 2, (char*) " 1 - MBR\n 2 - GPT\n 3 - Create blank GPT\n\nYour answer: ");
1461 if (answer == 1) {
1462 which = use_mbr;
1463// protectiveMBR.MakeProtectiveMBR();
1464 } else if (answer == 2) {
1465 which = use_gpt;
1466 } else which = use_new;
1467 } else if (mbrState == invalid) {
1468 printf("Found invalid MBR and corrupt GPT. What do you want to do? (Using the\n"
1469 "GPT MAY permit recovery of GPT data.)\n");
1470 answer = GetNumber(1, 2, 1, (char*) " 1 - GPT\n 2 - Create blank GPT\n\nYour answer: ");
1471 if (answer == 1) {
1472 which = use_gpt;
1473 } else which = use_new;
1474 } else { // corrupt GPT, MBR indicates it's a GPT disk....
1475 printf("\a\a****************************************************************************\n"
1476 "Caution: Found protective or hybrid MBR and corrupt GPT. Using GPT, but disk\n"
1477 "verification and recovery are STRONGLY recommended.\n"
1478 "****************************************************************************\n");
srs5694247657a2009-11-26 18:36:12 -05001479 which = use_gpt;
srs5694e4ac11e2009-08-31 10:13:04 -04001480 } // if/else/else
1481 } // if (corrupt GPT)
1482
1483 if (which == use_new)
1484 printf("Creating new GPT entries.\n");
1485
1486 return which;
1487} // UseWhichPartitions()
1488
1489// Convert MBR partition table into GPT form
1490int GPTData::XFormPartitions(void) {
1491 int i, numToConvert;
1492 uint8_t origType;
1493 struct newGUID;
srs5694e4ac11e2009-08-31 10:13:04 -04001494
1495 // Clear out old data & prepare basics....
1496 ClearGPTData();
1497
1498 // Convert the smaller of the # of GPT or MBR partitions
srs5694978041c2009-09-21 20:51:47 -04001499 if (mainHeader.numParts > (MAX_MBR_PARTS))
1500 numToConvert = MAX_MBR_PARTS;
srs5694e4ac11e2009-08-31 10:13:04 -04001501 else
1502 numToConvert = mainHeader.numParts;
1503
1504 for (i = 0; i < numToConvert; i++) {
1505 origType = protectiveMBR.GetType(i);
1506 // don't waste CPU time trying to convert extended, hybrid protective, or
1507 // null (non-existent) partitions
srs5694e35eb1b2009-09-14 00:29:34 -04001508 if ((origType != 0x05) && (origType != 0x0f) && (origType != 0x85) &&
srs5694e4ac11e2009-08-31 10:13:04 -04001509 (origType != 0x00) && (origType != 0xEE))
1510 partitions[i] = protectiveMBR.AsGPT(i);
1511 } // for
1512
1513 // Convert MBR into protective MBR
1514 protectiveMBR.MakeProtectiveMBR();
1515
1516 // Record that all original CRCs were OK so as not to raise flags
1517 // when doing a disk verification
1518 mainCrcOk = secondCrcOk = mainPartsCrcOk = secondPartsCrcOk = 1;
1519
1520 return (1);
1521} // GPTData::XFormPartitions()
1522
1523// Transforms BSD disklabel on the specified partition (numbered from 0).
1524// If an invalid partition number is given, the program prompts for one.
1525// Returns the number of new partitions created.
1526int GPTData::XFormDisklabel(int i) {
1527 uint32_t low, high, partNum, startPart;
1528 uint16_t hexCode;
1529 int goOn = 1, numDone = 0;
1530 BSDData disklabel;
1531
1532 if (GetPartRange(&low, &high) != 0) {
1533 if ((i < low) || (i > high))
1534 partNum = GetPartNum();
1535 else
1536 partNum = (uint32_t) i;
1537
1538 // Find the partition after the last used one
1539 startPart = high + 1;
1540
1541 // Now see if the specified partition has a BSD type code....
1542 hexCode = partitions[partNum].GetHexType();
1543 if ((hexCode != 0xa500) && (hexCode != 0xa900)) {
1544 printf("Specified partition doesn't have a disklabel partition type "
1545 "code.\nContinue anyway?");
1546 goOn = (GetYN() == 'Y');
1547 } // if
1548
1549 // If all is OK, read the disklabel and convert it.
1550 if (goOn) {
1551 goOn = disklabel.ReadBSDData(device, partitions[partNum].GetFirstLBA(),
1552 partitions[partNum].GetLastLBA());
1553 if ((goOn) && (disklabel.IsDisklabel())) {
1554 numDone = XFormDisklabel(&disklabel, startPart);
1555 if (numDone == 1)
1556 printf("Converted %d BSD partition.\n", numDone);
1557 else
1558 printf("Converted %d BSD partitions.\n", numDone);
1559 } else {
1560 printf("Unable to convert partitions! Unrecognized BSD disklabel.\n");
1561 } // if/else
1562 } // if
1563 if (numDone > 0) { // converted partitions; delete carrier
1564 partitions[partNum].BlankPartition();
1565 } // if
1566 } else {
1567 printf("No partitions\n");
1568 } // if/else
1569 return numDone;
1570} // GPTData::XFormDisklable(int i)
1571
1572// Transform the partitions on an already-loaded BSD disklabel...
1573int GPTData::XFormDisklabel(BSDData* disklabel, int startPart) {
1574 int i, numDone = 0;
1575
1576 if ((disklabel->IsDisklabel()) && (startPart >= 0) &&
1577 (startPart < mainHeader.numParts)) {
1578 for (i = 0; i < disklabel->GetNumParts(); i++) {
1579 partitions[i + startPart] = disklabel->AsGPT(i);
1580 if (partitions[i + startPart].GetFirstLBA() != UINT64_C(0))
1581 numDone++;
1582 } // for
1583 } // if
1584
1585 // Record that all original CRCs were OK so as not to raise flags
1586 // when doing a disk verification
1587 mainCrcOk = secondCrcOk = mainPartsCrcOk = secondPartsCrcOk = 1;
1588
1589 return numDone;
1590} // GPTData::XFormDisklabel(BSDData* disklabel)
1591
srs5694978041c2009-09-21 20:51:47 -04001592// Add one GPT partition to MBR. Used by XFormToMBR() and MakeHybrid()
1593// functions. Returns 1 if operation was successful.
1594int GPTData::OnePartToMBR(uint32_t gptPart, int mbrPart) {
1595 int allOK = 1, typeCode, bootable;
1596 uint64_t length;
1597 char line[255];
srs56945d58fe02010-01-03 20:57:08 -05001598 char* junk;
srs5694978041c2009-09-21 20:51:47 -04001599
1600 if ((mbrPart < 0) || (mbrPart > 3)) {
1601 printf("MBR partition %d is out of range; omitting it.\n", mbrPart + 1);
1602 allOK = 0;
1603 } // if
1604 if (gptPart >= mainHeader.numParts) {
1605 printf("GPT partition %d is out of range; omitting it.\n", gptPart + 1);
1606 allOK = 0;
1607 } // if
1608 if (allOK && (partitions[gptPart].GetLastLBA() == UINT64_C(0))) {
1609 printf("GPT partition %d is undefined; omitting it.\n", gptPart + 1);
1610 allOK = 0;
1611 } // if
1612 if (allOK && (partitions[gptPart].GetFirstLBA() <= UINT32_MAX) &&
1613 (partitions[gptPart].GetLengthLBA() <= UINT32_MAX)) {
1614 if (partitions[gptPart].GetLastLBA() > UINT32_MAX) {
1615 printf("Caution: Partition end point past 32-bit pointer boundary;"
1616 " some OSes may\nreact strangely.\n");
1617 } // if partition ends past 32-bit (usually 2TiB) boundary
1618 do {
1619 printf("Enter an MBR hex code (default %02X): ",
1620 typeHelper.GUIDToID(partitions[gptPart].GetType()) / 256);
srs56945d58fe02010-01-03 20:57:08 -05001621 junk = fgets(line, 255, stdin);
srs5694978041c2009-09-21 20:51:47 -04001622 sscanf(line, "%x", &typeCode);
1623 if (line[0] == '\n')
1624 typeCode = partitions[gptPart].GetHexType() / 256;
1625 } while ((typeCode <= 0) || (typeCode > 255));
1626 printf("Set the bootable flag? ");
1627 bootable = (GetYN() == 'Y');
1628 length = partitions[gptPart].GetLengthLBA();
1629 protectiveMBR.MakePart(mbrPart, (uint32_t) partitions[gptPart].GetFirstLBA(),
1630 (uint32_t) length, typeCode, bootable);
1631 } else { // partition out of range
1632 printf("Partition %d begins beyond the 32-bit pointer limit of MBR "
1633 "partitions, or is\n too big; omitting it.\n", gptPart + 1);
1634 allOK = 0;
1635 } // if/else
1636 return allOK;
1637} // GPTData::OnePartToMBR()
1638
1639// Convert the GPT to MBR form. This function is necessarily limited; it
1640// handles at most four partitions and creates layouts that ignore CHS
1641// geometries. Returns the number of converted partitions; if this value
1642// is over 0, the calling function should call DestroyGPT() to destroy
1643// the GPT data, and then exit.
1644int GPTData::XFormToMBR(void) {
1645 char line[255];
srs56945d58fe02010-01-03 20:57:08 -05001646 char* junk;
srs5694978041c2009-09-21 20:51:47 -04001647 int i, j, numParts, numConverted = 0;
1648 uint32_t partNums[4];
1649
1650 // Get the numbers of up to four partitions to add to the
1651 // hybrid MBR....
1652 numParts = CountParts();
1653 printf("Counted %d partitions.\n", numParts);
1654
1655 // Prepare the MBR for conversion (empty it of existing partitions).
1656 protectiveMBR.EmptyMBR(0);
1657 protectiveMBR.SetDiskSize(diskSize);
1658
1659 if (numParts > 4) { // Over four partitions; engage in triage
1660 printf("Type from one to four GPT partition numbers, separated by spaces, to be\n"
1661 "used in the MBR, in sequence: ");
srs56945d58fe02010-01-03 20:57:08 -05001662 junk = fgets(line, 255, stdin);
srs5694978041c2009-09-21 20:51:47 -04001663 numParts = sscanf(line, "%d %d %d %d", &partNums[0], &partNums[1],
1664 &partNums[2], &partNums[3]);
1665 } else { // Four or fewer partitions; convert them all
1666 i = j = 0;
1667 while ((j < numParts) && (i < mainHeader.numParts)) {
1668 if (partitions[i].GetFirstLBA() > 0) { // if GPT part. is defined
1669 partNums[j++] = ++i; // flag it for conversion
1670 } else i++;
1671 } // while
1672 } // if/else
1673
1674 for (i = 0; i < numParts; i++) {
1675 j = partNums[i] - 1;
1676 printf("\nCreating entry for partition #%d\n", j + 1);
1677 numConverted += OnePartToMBR(j, i);
1678 } // for
srs5694ba00fed2010-01-12 18:18:36 -05001679 printf("MBR writing returned %d\n", protectiveMBR.WriteMBRData(device));
srs5694978041c2009-09-21 20:51:47 -04001680 return numConverted;
1681} // GPTData::XFormToMBR()
1682
srs5694e4ac11e2009-08-31 10:13:04 -04001683// Create a hybrid MBR -- an ugly, funky thing that helps GPT work with
1684// OSes that don't understand GPT.
1685void GPTData::MakeHybrid(void) {
1686 uint32_t partNums[3];
1687 char line[255];
srs56945d58fe02010-01-03 20:57:08 -05001688 char* junk;
srs5694978041c2009-09-21 20:51:47 -04001689 int numParts, numConverted = 0, i, j, typeCode, mbrNum;
srs5694e4ac11e2009-08-31 10:13:04 -04001690 char fillItUp = 'M'; // fill extra partition entries? (Yes/No/Maybe)
srs5694978041c2009-09-21 20:51:47 -04001691 char eeFirst = 'Y'; // Whether EFI GPT (0xEE) partition comes first in table
srs5694e4ac11e2009-08-31 10:13:04 -04001692
1693 printf("\nWARNING! Hybrid MBRs are flaky and potentially dangerous! If you decide not\n"
1694 "to use one, just hit the Enter key at the below prompt and your MBR\n"
1695 "partition table will be untouched.\n\n\a");
1696
1697 // Now get the numbers of up to three partitions to add to the
1698 // hybrid MBR....
1699 printf("Type from one to three GPT partition numbers, separated by spaces, to be\n"
1700 "added to the hybrid MBR, in sequence: ");
srs56945d58fe02010-01-03 20:57:08 -05001701 junk = fgets(line, 255, stdin);
srs5694e4ac11e2009-08-31 10:13:04 -04001702 numParts = sscanf(line, "%d %d %d", &partNums[0], &partNums[1], &partNums[2]);
1703
1704 if (numParts > 0) {
1705 // Blank out the protective MBR, but leave the boot loader code
1706 // alone....
1707 protectiveMBR.EmptyMBR(0);
1708 protectiveMBR.SetDiskSize(diskSize);
1709 printf("Place EFI GPT (0xEE) partition first in MBR (good for GRUB)? ");
1710 eeFirst = GetYN();
1711 } // if
1712
1713 for (i = 0; i < numParts; i++) {
1714 j = partNums[i] - 1;
1715 printf("\nCreating entry for partition #%d\n", j + 1);
srs5694978041c2009-09-21 20:51:47 -04001716 if (eeFirst == 'Y')
1717 mbrNum = i + 1;
1718 else
1719 mbrNum = i;
1720 numConverted += OnePartToMBR(j, mbrNum);
srs5694e4ac11e2009-08-31 10:13:04 -04001721 } // for
1722
srs5694978041c2009-09-21 20:51:47 -04001723 if ((numParts > 0) && (numConverted > 0)) { // User opted to create a hybrid MBR....
srs5694e4ac11e2009-08-31 10:13:04 -04001724 // Create EFI protective partition that covers the start of the disk.
1725 // If this location (covering the main GPT data structures) is omitted,
1726 // Linux won't find any partitions on the disk. Note that this is
1727 // NUMBERED AFTER the hybrid partitions, contrary to what the
1728 // gptsync utility does. This is because Windows seems to choke on
1729 // disks with a 0xEE partition in the first slot and subsequent
1730 // additional partitions, unless it boots from the disk.
1731 if (eeFirst == 'Y')
1732 mbrNum = 0;
1733 else
1734 mbrNum = numParts;
1735 protectiveMBR.MakePart(mbrNum, 1, protectiveMBR.FindLastInFree(1), 0xEE);
srs5694978041c2009-09-21 20:51:47 -04001736 protectiveMBR.SetHybrid();
srs5694e4ac11e2009-08-31 10:13:04 -04001737
1738 // ... and for good measure, if there are any partition spaces left,
1739 // optionally create another protective EFI partition to cover as much
1740 // space as possible....
1741 for (i = 0; i < 4; i++) {
1742 if (protectiveMBR.GetType(i) == 0x00) { // unused entry....
1743 if (fillItUp == 'M') {
1744 printf("\nUnused partition space(s) found. Use one to protect more partitions? ");
1745 fillItUp = GetYN();
1746 typeCode = 0x00; // use this to flag a need to get type code
1747 } // if
1748 if (fillItUp == 'Y') {
1749 while ((typeCode <= 0) || (typeCode > 255)) {
1750 printf("Enter an MBR hex code (EE is EFI GPT, but may confuse MacOS): ");
1751 // Comment on above: Mac OS treats disks with more than one
1752 // 0xEE MBR partition as MBR disks, not as GPT disks.
srs56945d58fe02010-01-03 20:57:08 -05001753 junk = fgets(line, 255, stdin);
srs5694e4ac11e2009-08-31 10:13:04 -04001754 sscanf(line, "%x", &typeCode);
1755 if (line[0] == '\n')
1756 typeCode = 0;
1757 } // while
1758 protectiveMBR.MakeBiggestPart(i, typeCode); // make a partition
1759 } // if (fillItUp == 'Y')
1760 } // if unused entry
1761 } // for (i = 0; i < 4; i++)
1762 } // if (numParts > 0)
1763} // GPTData::MakeHybrid()
1764
1765/**********************************************************************
1766 * *
1767 * Functions that adjust GPT data structures WITHOUT user interaction *
1768 * (they may display information for the user's benefit, though) *
1769 * *
1770 **********************************************************************/
1771
1772// Resizes GPT to specified number of entries. Creates a new table if
srs5694ba00fed2010-01-12 18:18:36 -05001773// necessary, copies data if it already exists. Returns 1 if all goes
1774// well, 0 if an error is encountered.
srs5694e4ac11e2009-08-31 10:13:04 -04001775int GPTData::SetGPTSize(uint32_t numEntries) {
1776 struct GPTPart* newParts;
1777 struct GPTPart* trash;
1778 uint32_t i, high, copyNum;
1779 int allOK = 1;
1780
1781 // First, adjust numEntries upward, if necessary, to get a number
1782 // that fills the allocated sectors
1783 i = blockSize / GPT_SIZE;
1784 if ((numEntries % i) != 0) {
1785 printf("Adjusting GPT size from %lu ", (unsigned long) numEntries);
1786 numEntries = ((numEntries / i) + 1) * i;
1787 printf("to %lu to fill the sector\n", (unsigned long) numEntries);
1788 } // if
1789
srs5694247657a2009-11-26 18:36:12 -05001790 // Do the work only if the # of partitions is changing. Along with being
1791 // efficient, this prevents mucking the with location of the secondary
1792 // partition table, which causes problems when loading data from a RAID
1793 // array that's been expanded because this function is called when loading
1794 // data.
1795 if ((numEntries != mainHeader.numParts) || (partitions == NULL)) {
1796 newParts = (GPTPart*) calloc(numEntries, sizeof (GPTPart));
1797 if (newParts != NULL) {
1798 if (partitions != NULL) { // existing partitions; copy them over
1799 GetPartRange(&i, &high);
1800 if (numEntries < (high + 1)) { // Highest entry too high for new #
1801 printf("The highest-numbered partition is %lu, which is greater than the requested\n"
1802 "partition table size of %d; cannot resize. Perhaps sorting will help.\n",
1803 (unsigned long) (high + 1), numEntries);
1804 allOK = 0;
1805 } else { // go ahead with copy
1806 if (numEntries < mainHeader.numParts)
1807 copyNum = numEntries;
1808 else
1809 copyNum = mainHeader.numParts;
1810 for (i = 0; i < copyNum; i++) {
1811 newParts[i] = partitions[i];
1812 } // for
1813 trash = partitions;
1814 partitions = newParts;
1815 free(trash);
1816 } // if
1817 } else { // No existing partition table; just create it
srs5694e4ac11e2009-08-31 10:13:04 -04001818 partitions = newParts;
srs5694247657a2009-11-26 18:36:12 -05001819 } // if/else existing partitions
1820 mainHeader.numParts = numEntries;
1821 secondHeader.numParts = numEntries;
1822 mainHeader.firstUsableLBA = ((numEntries * GPT_SIZE) / blockSize) + 2 ;
1823 secondHeader.firstUsableLBA = mainHeader.firstUsableLBA;
1824 MoveSecondHeaderToEnd();
1825 if (diskSize > 0)
1826 CheckGPTSize();
1827 } else { // Bad memory allocation
1828 fprintf(stderr, "Error allocating memory for partition table!\n");
1829 allOK = 0;
1830 } // if/else
srs5694e4ac11e2009-08-31 10:13:04 -04001831 } // if/else
1832 return (allOK);
1833} // GPTData::SetGPTSize()
1834
1835// Blank the partition array
1836void GPTData::BlankPartitions(void) {
1837 uint32_t i;
1838
1839 for (i = 0; i < mainHeader.numParts; i++) {
1840 partitions[i].BlankPartition();
1841 } // for
1842} // GPTData::BlankPartitions()
1843
srs5694ba00fed2010-01-12 18:18:36 -05001844// Delete a partition by number. Returns 1 if successful,
1845// 0 if there was a problem. Returns 1 if partition was in
1846// range, 0 if it was out of range.
1847int GPTData::DeletePartition(uint32_t partNum) {
1848 uint64_t startSector, length;
1849 uint32_t low, high, numParts, retval = 1;;
1850
1851 numParts = GetPartRange(&low, &high);
1852 if ((numParts > 0) && (partNum >= low) && (partNum <= high)) {
1853 // In case there's a protective MBR, look for & delete matching
1854 // MBR partition....
1855 startSector = partitions[partNum].GetFirstLBA();
1856 length = partitions[partNum].GetLengthLBA();
1857 protectiveMBR.DeleteByLocation(startSector, length);
1858
1859 // Now delete the GPT partition
1860 partitions[partNum].BlankPartition();
1861 } else {
1862 fprintf(stderr, "Partition number %d out of range!\n", partNum + 1);
1863 retval = 0;
1864 } // if/else
1865 return retval;
1866} // GPTData::DeletePartition(uint32_t partNum)
1867
1868// Non-interactively create a partition. Note that this function is overloaded
1869// with another of the same name but different parameters; that one prompts
1870// the user for data. This one returns 1 if the operation was successful, 0
1871// if a problem was discovered.
1872int GPTData::CreatePartition(uint32_t partNum, uint64_t startSector, uint64_t endSector) {
1873 int retval = 1; // assume there'll be no problems
1874
1875 if (IsFreePartNum(partNum)) {
1876 Align(&startSector); // Align sector to correct multiple
1877 if (IsFree(startSector) && (startSector <= endSector)) {
1878 if (FindLastInFree(startSector) >= endSector) {
1879 partitions[partNum].SetFirstLBA(startSector);
1880 partitions[partNum].SetLastLBA(endSector);
1881 partitions[partNum].SetType(0x0700);
1882 partitions[partNum].SetUniqueGUID(1);
1883 } else retval = 0; // if free space until endSector
1884 } else retval = 0; // if startSector is free
1885 } else retval = 0; // if legal partition number
1886 return retval;
1887} // GPTData::CreatePartition(partNum, startSector, endSector)
1888
srs5694e4ac11e2009-08-31 10:13:04 -04001889// Sort the GPT entries, eliminating gaps and making for a logical
1890// ordering. Relies on QuickSortGPT() for the bulk of the work
1891void GPTData::SortGPT(void) {
1892 int i, lastPart = 0;
1893 GPTPart temp;
1894
1895 // First, find the last partition with data, so as not to
1896 // spend needless time sorting empty entries....
1897 for (i = 0; i < mainHeader.numParts; i++) {
1898 if (partitions[i].GetFirstLBA() > 0)
1899 lastPart = i;
1900 } // for
1901
1902 // Now swap empties with the last partitions, to simplify the logic
1903 // in the Quicksort function....
1904 i = 0;
1905 while (i < lastPart) {
1906 if (partitions[i].GetFirstLBA() == 0) {
1907 temp = partitions[i];
1908 partitions[i] = partitions[lastPart];
1909 partitions[lastPart] = temp;
1910 lastPart--;
1911 } // if
1912 i++;
1913 } // while
1914
1915 // Now call the recursive quick sort routine to do the real work....
1916 QuickSortGPT(partitions, 0, lastPart);
1917} // GPTData::SortGPT()
1918
1919// Set up data structures for entirely new set of partitions on the
1920// specified device. Returns 1 if OK, 0 if there were problems.
srs5694e35eb1b2009-09-14 00:29:34 -04001921// Note that this function does NOT clear the protectiveMBR data
1922// structure, since it may hold the original MBR partitions if the
1923// program was launched on an MBR disk, and those may need to be
1924// converted to GPT format.
srs5694e4ac11e2009-08-31 10:13:04 -04001925int GPTData::ClearGPTData(void) {
srs5694e35eb1b2009-09-14 00:29:34 -04001926 int goOn = 1, i;
srs5694e4ac11e2009-08-31 10:13:04 -04001927
1928 // Set up the partition table....
1929 free(partitions);
1930 partitions = NULL;
1931 SetGPTSize(NUM_GPT_ENTRIES);
1932
1933 // Now initialize a bunch of stuff that's static....
1934 mainHeader.signature = GPT_SIGNATURE;
1935 mainHeader.revision = 0x00010000;
srs5694978041c2009-09-21 20:51:47 -04001936 mainHeader.headerSize = HEADER_SIZE;
srs5694e4ac11e2009-08-31 10:13:04 -04001937 mainHeader.reserved = 0;
1938 mainHeader.currentLBA = UINT64_C(1);
1939 mainHeader.partitionEntriesLBA = (uint64_t) 2;
1940 mainHeader.sizeOfPartitionEntries = GPT_SIZE;
1941 for (i = 0; i < GPT_RESERVED; i++) {
1942 mainHeader.reserved2[i] = '\0';
1943 } // for
1944
1945 // Now some semi-static items (computed based on end of disk)
1946 mainHeader.backupLBA = diskSize - UINT64_C(1);
1947 mainHeader.lastUsableLBA = diskSize - mainHeader.firstUsableLBA;
1948
1949 // Set a unique GUID for the disk, based on random numbers
1950 // rand() is only 32 bits, so multiply together to fill a 64-bit value
1951 mainHeader.diskGUID.data1 = (uint64_t) rand() * (uint64_t) rand();
1952 mainHeader.diskGUID.data2 = (uint64_t) rand() * (uint64_t) rand();
1953
1954 // Copy main header to backup header
1955 RebuildSecondHeader();
1956
1957 // Blank out the partitions array....
1958 BlankPartitions();
1959
1960 // Flag all CRCs as being OK....
1961 mainCrcOk = 1;
1962 secondCrcOk = 1;
1963 mainPartsCrcOk = 1;
1964 secondPartsCrcOk = 1;
1965
1966 return (goOn);
1967} // GPTData::ClearGPTData()
1968
srs5694247657a2009-11-26 18:36:12 -05001969// Set the location of the second GPT header data to the end of the disk.
1970// Used internally and called by the 'e' option on the recovery &
1971// transformation menu, to help users of RAID arrays who add disk space
1972// to their arrays.
1973void GPTData::MoveSecondHeaderToEnd() {
srs56948bb78762009-11-24 15:43:49 -05001974 mainHeader.backupLBA = secondHeader.currentLBA = diskSize - UINT64_C(1);
1975 mainHeader.lastUsableLBA = secondHeader.lastUsableLBA = diskSize - mainHeader.firstUsableLBA;
1976 secondHeader.partitionEntriesLBA = secondHeader.lastUsableLBA + UINT64_C(1);
1977} // GPTData::FixSecondHeaderLocation()
1978
srs5694ba00fed2010-01-12 18:18:36 -05001979int GPTData::SetName(uint32_t partNum, char* theName) {
1980 int retval = 1;
1981 if (!IsFreePartNum(partNum))
1982 partitions[partNum].SetName((unsigned char*) theName);
1983 else retval = 0;
1984
1985 return retval;
srs5694e4ac11e2009-08-31 10:13:04 -04001986} // GPTData::SetName
1987
1988// Set the disk GUID to the specified value. Note that the header CRCs must
1989// be recomputed after calling this function.
1990void GPTData::SetDiskGUID(GUIDData newGUID) {
1991 mainHeader.diskGUID = newGUID;
1992 secondHeader.diskGUID = newGUID;
1993} // SetDiskGUID()
1994
1995// Set the unique GUID of the specified partition. Returns 1 on
1996// successful completion, 0 if there were problems (invalid
1997// partition number).
1998int GPTData::SetPartitionGUID(uint32_t pn, GUIDData theGUID) {
1999 int retval = 0;
2000
2001 if (pn < mainHeader.numParts) {
2002 if (partitions[pn].GetFirstLBA() != UINT64_C(0)) {
2003 partitions[pn].SetUniqueGUID(theGUID);
2004 retval = 1;
2005 } // if
2006 } // if
2007 return retval;
2008} // GPTData::SetPartitionGUID()
2009
srs5694ba00fed2010-01-12 18:18:36 -05002010// Change partition type code non-interactively. Returns 1 if
2011// successful, 0 if not....
2012int GPTData::ChangePartType(uint32_t partNum, uint16_t hexCode) {
2013 int retval = 1;
2014
2015 if (!IsFreePartNum(partNum)) {
2016 partitions[partNum].SetType(hexCode);
2017 } else retval = 0;
2018 return retval;
2019} // GPTData::ChangePartType()
2020
srs56941d1448a2009-12-31 21:20:19 -05002021// Adjust sector number so that it falls on a sector boundary that's a
2022// multiple of sectorAlignment. This is done to improve the performance
2023// of Western Digital Advanced Format disks and disks with similar
2024// technology from other companies, which use 4096-byte sectors
2025// internally although they translate to 512-byte sectors for the
2026// benefit of the OS. If partitions aren't properly aligned on these
2027// disks, some filesystem data structures can span multiple physical
2028// sectors, degrading performance. This function should be called
2029// only on the FIRST sector of the partition, not the last!
2030// This function returns 1 if the alignment was altered, 0 if it
2031// was unchanged.
2032int GPTData::Align(uint64_t* sector) {
2033 int retval = 0, sectorOK = 0;
2034 uint64_t earlier, later, testSector, original;
2035
2036 if ((*sector % sectorAlignment) != 0) {
2037 original = *sector;
2038 retval = 1;
2039 earlier = (*sector / sectorAlignment) * sectorAlignment;
2040 later = earlier + (uint64_t) sectorAlignment;
2041
2042 // Check to see that every sector between the earlier one and the
2043 // requested one is clear, and that it's not too early....
2044 if (earlier >= mainHeader.firstUsableLBA) {
2045// printf("earlier is %llu, first usable is %llu\n", earlier, mainHeader.firstUsableLBA);
2046 sectorOK = 1;
2047 testSector = earlier;
2048 do {
2049 sectorOK = IsFree(testSector++);
2050 } while ((sectorOK == 1) && (testSector < *sector));
2051 if (sectorOK == 1) {
2052 *sector = earlier;
2053// printf("Moved sector earlier.\n");
2054 } // if
2055 } // if firstUsableLBA check
2056
2057 // If couldn't move the sector earlier, try to move it later instead....
2058 if ((sectorOK != 1) && (later <= mainHeader.lastUsableLBA)) {
2059 sectorOK = 1;
2060 testSector = later;
2061 do {
2062 sectorOK = IsFree(testSector--);
2063 } while ((sectorOK == 1) && (testSector > *sector));
2064 if (sectorOK == 1) {
2065 *sector = later;
2066// printf("Moved sector later\n");
2067 } // if
2068 } // if
2069
2070 // If sector was changed successfully, inform the user of this fact.
2071 // Otherwise, notify the user that it couldn't be done....
2072 if (sectorOK == 1) {
2073 printf("Information: Moved requested sector from %llu to %llu for\n"
srs5694ba00fed2010-01-12 18:18:36 -05002074 "alignment purposes.\n",
srs56945d58fe02010-01-03 20:57:08 -05002075 (unsigned long long) original, (unsigned long long) *sector);
srs5694ba00fed2010-01-12 18:18:36 -05002076 if (!beQuiet)
2077 printf("Use 'l' on the experts' menu to adjust alignment\n");
srs56941d1448a2009-12-31 21:20:19 -05002078 } else {
2079 printf("Information: Sector not aligned on %d-sector boundary and could not be moved.\n"
2080 "If you're using a Western Digital Advanced Format or similar disk with\n"
2081 "underlying 4096-byte sectors, performance may suffer.\n", sectorAlignment);
2082 retval = 0;
2083 } // if/else
2084 } // if
2085 return retval;
2086} // GPTData::Align()
2087
srs5694e4ac11e2009-08-31 10:13:04 -04002088/********************************************************
2089 * *
2090 * Functions that return data about GPT data structures *
2091 * (most of these are inline in gpt.h) *
2092 * *
2093 ********************************************************/
2094
2095// Find the low and high used partition numbers (numbered from 0).
2096// Return value is the number of partitions found. Note that the
2097// *low and *high values are both set to 0 when no partitions
2098// are found, as well as when a single partition in the first
2099// position exists. Thus, the return value is the only way to
2100// tell when no partitions exist.
2101int GPTData::GetPartRange(uint32_t *low, uint32_t *high) {
2102 uint32_t i;
2103 int numFound = 0;
2104
2105 *low = mainHeader.numParts + 1; // code for "not found"
2106 *high = 0;
2107 if (mainHeader.numParts > 0) { // only try if partition table exists...
2108 for (i = 0; i < mainHeader.numParts; i++) {
2109 if (partitions[i].GetFirstLBA() != UINT64_C(0)) { // it exists
2110 *high = i; // since we're counting up, set the high value
2111 // Set the low value only if it's not yet found...
2112 if (*low == (mainHeader.numParts + 1)) *low = i;
2113 numFound++;
2114 } // if
2115 } // for
2116 } // if
2117
2118 // Above will leave *low pointing to its "not found" value if no partitions
2119 // are defined, so reset to 0 if this is the case....
2120 if (*low == (mainHeader.numParts + 1))
2121 *low = 0;
2122 return numFound;
2123} // GPTData::GetPartRange()
2124
srs5694978041c2009-09-21 20:51:47 -04002125// Returns the number of defined partitions.
2126uint32_t GPTData::CountParts(void) {
2127 int i, counted = 0;
2128
2129 for (i = 0; i < mainHeader.numParts; i++) {
2130 if (partitions[i].GetFirstLBA() > 0)
2131 counted++;
2132 } // for
2133 return counted;
2134} // GPTData::CountParts()
2135
srs5694e4ac11e2009-08-31 10:13:04 -04002136/****************************************************
2137 * *
2138 * Functions that return data about disk free space *
2139 * *
2140 ****************************************************/
2141
2142// Find the first available block after the starting point; returns 0 if
2143// there are no available blocks left
2144uint64_t GPTData::FindFirstAvailable(uint64_t start) {
2145 uint64_t first;
2146 uint32_t i;
2147 int firstMoved = 0;
2148
2149 // Begin from the specified starting point or from the first usable
2150 // LBA, whichever is greater...
2151 if (start < mainHeader.firstUsableLBA)
2152 first = mainHeader.firstUsableLBA;
2153 else
2154 first = start;
2155
2156 // ...now search through all partitions; if first is within an
2157 // existing partition, move it to the next sector after that
2158 // partition and repeat. If first was moved, set firstMoved
2159 // flag; repeat until firstMoved is not set, so as to catch
2160 // cases where partitions are out of sequential order....
2161 do {
2162 firstMoved = 0;
2163 for (i = 0; i < mainHeader.numParts; i++) {
2164 if ((first >= partitions[i].GetFirstLBA()) &&
2165 (first <= partitions[i].GetLastLBA())) { // in existing part.
2166 first = partitions[i].GetLastLBA() + 1;
2167 firstMoved = 1;
2168 } // if
2169 } // for
2170 } while (firstMoved == 1);
2171 if (first > mainHeader.lastUsableLBA)
2172 first = 0;
2173 return (first);
2174} // GPTData::FindFirstAvailable()
2175
2176// Finds the first available sector in the largest block of unallocated
2177// space on the disk. Returns 0 if there are no available blocks left
2178uint64_t GPTData::FindFirstInLargest(void) {
srs5694e35eb1b2009-09-14 00:29:34 -04002179 uint64_t start, firstBlock, lastBlock, segmentSize, selectedSize = 0, selectedSegment = 0;
srs5694e4ac11e2009-08-31 10:13:04 -04002180
2181 start = 0;
2182 do {
2183 firstBlock = FindFirstAvailable(start);
2184 if (firstBlock != UINT32_C(0)) { // something's free...
2185 lastBlock = FindLastInFree(firstBlock);
2186 segmentSize = lastBlock - firstBlock + UINT32_C(1);
2187 if (segmentSize > selectedSize) {
2188 selectedSize = segmentSize;
2189 selectedSegment = firstBlock;
2190 } // if
2191 start = lastBlock + 1;
2192 } // if
2193 } while (firstBlock != 0);
2194 return selectedSegment;
2195} // GPTData::FindFirstInLargest()
2196
2197// Find the last available block on the disk at or after the start
2198// block. Returns 0 if there are no available partitions after
2199// (or including) start.
2200uint64_t GPTData::FindLastAvailable(uint64_t start) {
2201 uint64_t last;
2202 uint32_t i;
2203 int lastMoved = 0;
2204
2205 // Start by assuming the last usable LBA is available....
2206 last = mainHeader.lastUsableLBA;
2207
2208 // ...now, similar to algorithm in FindFirstAvailable(), search
2209 // through all partitions, moving last when it's in an existing
2210 // partition. Set the lastMoved flag so we repeat to catch cases
2211 // where partitions are out of logical order.
2212 do {
2213 lastMoved = 0;
2214 for (i = 0; i < mainHeader.numParts; i++) {
2215 if ((last >= partitions[i].GetFirstLBA()) &&
2216 (last <= partitions[i].GetLastLBA())) { // in existing part.
2217 last = partitions[i].GetFirstLBA() - 1;
2218 lastMoved = 1;
2219 } // if
2220 } // for
2221 } while (lastMoved == 1);
2222 if (last < mainHeader.firstUsableLBA)
2223 last = 0;
2224 return (last);
2225} // GPTData::FindLastAvailable()
2226
2227// Find the last available block in the free space pointed to by start.
2228uint64_t GPTData::FindLastInFree(uint64_t start) {
2229 uint64_t nearestStart;
2230 uint32_t i;
2231
2232 nearestStart = mainHeader.lastUsableLBA;
2233 for (i = 0; i < mainHeader.numParts; i++) {
2234 if ((nearestStart > partitions[i].GetFirstLBA()) &&
2235 (partitions[i].GetFirstLBA() > start)) {
2236 nearestStart = partitions[i].GetFirstLBA() - 1;
2237 } // if
2238 } // for
2239 return (nearestStart);
2240} // GPTData::FindLastInFree()
2241
2242// Finds the total number of free blocks, the number of segments in which
2243// they reside, and the size of the largest of those segments
2244uint64_t GPTData::FindFreeBlocks(int *numSegments, uint64_t *largestSegment) {
2245 uint64_t start = UINT64_C(0); // starting point for each search
2246 uint64_t totalFound = UINT64_C(0); // running total
2247 uint64_t firstBlock; // first block in a segment
2248 uint64_t lastBlock; // last block in a segment
2249 uint64_t segmentSize; // size of segment in blocks
2250 int num = 0;
2251
2252 *largestSegment = UINT64_C(0);
2253 do {
2254 firstBlock = FindFirstAvailable(start);
2255 if (firstBlock != UINT64_C(0)) { // something's free...
2256 lastBlock = FindLastInFree(firstBlock);
2257 segmentSize = lastBlock - firstBlock + UINT64_C(1);
2258 if (segmentSize > *largestSegment) {
2259 *largestSegment = segmentSize;
2260 } // if
2261 totalFound += segmentSize;
2262 num++;
2263 start = lastBlock + 1;
2264 } // if
2265 } while (firstBlock != 0);
2266 *numSegments = num;
2267 return totalFound;
2268} // GPTData::FindFreeBlocks()
2269
2270// Returns 1 if sector is unallocated, 0 if it's allocated to a partition
2271int GPTData::IsFree(uint64_t sector) {
2272 int isFree = 1;
2273 uint32_t i;
2274
2275 for (i = 0; i < mainHeader.numParts; i++) {
2276 if ((sector >= partitions[i].GetFirstLBA()) &&
2277 (sector <= partitions[i].GetLastLBA())) {
2278 isFree = 0;
2279 } // if
2280 } // for
srs5694e35eb1b2009-09-14 00:29:34 -04002281 if ((sector < mainHeader.firstUsableLBA) ||
srs5694e4ac11e2009-08-31 10:13:04 -04002282 (sector > mainHeader.lastUsableLBA)) {
2283 isFree = 0;
2284 } // if
2285 return (isFree);
2286} // GPTData::IsFree()
2287
srs5694ba00fed2010-01-12 18:18:36 -05002288// Returns 1 if partNum is unused.
2289int GPTData::IsFreePartNum(uint32_t partNum) {
2290 int retval = 1;
2291
2292 if ((partNum >= 0) && (partNum < mainHeader.numParts)) {
2293 if ((partitions[partNum].GetFirstLBA() != UINT64_C(0)) ||
2294 (partitions[partNum].GetLastLBA() != UINT64_C(0))) {
2295 retval = 0;
2296 } // if partition is in use
2297 } else retval = 0;
2298
2299 return retval;
2300} // GPTData::IsFreePartNum()
2301
srs5694e4ac11e2009-08-31 10:13:04 -04002302/********************************
2303 * *
2304 * Endianness support functions *
2305 * *
2306 ********************************/
2307
srs56942a9f5da2009-08-26 00:48:01 -04002308void GPTData::ReverseHeaderBytes(struct GPTHeader* header) {
srs5694221e0872009-08-29 15:00:31 -04002309 ReverseBytes(&header->signature, 8);
2310 ReverseBytes(&header->revision, 4);
2311 ReverseBytes(&header->headerSize, 4);
2312 ReverseBytes(&header->headerCRC, 4);
2313 ReverseBytes(&header->reserved, 4);
2314 ReverseBytes(&header->currentLBA, 8);
2315 ReverseBytes(&header->backupLBA, 8);
2316 ReverseBytes(&header->firstUsableLBA, 8);
2317 ReverseBytes(&header->lastUsableLBA, 8);
2318 ReverseBytes(&header->partitionEntriesLBA, 8);
2319 ReverseBytes(&header->numParts, 4);
2320 ReverseBytes(&header->sizeOfPartitionEntries, 4);
2321 ReverseBytes(&header->partitionEntriesCRC, 4);
2322 ReverseBytes(&header->reserved2, GPT_RESERVED);
2323 ReverseBytes(&header->diskGUID.data1, 8);
2324 ReverseBytes(&header->diskGUID.data2, 8);
srs56942a9f5da2009-08-26 00:48:01 -04002325} // GPTData::ReverseHeaderBytes()
2326
2327// IMPORTANT NOTE: This function requires non-reversed mainHeader
2328// structure!
2329void GPTData::ReversePartitionBytes() {
2330 uint32_t i;
2331
2332 // Check GPT signature on big-endian systems; this will mismatch
2333 // if the function is called out of order. Unfortunately, it'll also
2334 // mismatch if there's data corruption.
2335 if ((mainHeader.signature != GPT_SIGNATURE) && (IsLittleEndian() == 0)) {
2336 printf("GPT signature mismatch in GPTData::ReversePartitionBytes(). This indicates\n"
srs5694e4ac11e2009-08-31 10:13:04 -04002337 "data corruption or a misplaced call to this function.\n");
srs56942a9f5da2009-08-26 00:48:01 -04002338 } // if signature mismatch....
2339 for (i = 0; i < mainHeader.numParts; i++) {
srs5694221e0872009-08-29 15:00:31 -04002340 partitions[i].ReversePartBytes();
srs56942a9f5da2009-08-26 00:48:01 -04002341 } // for
2342} // GPTData::ReversePartitionBytes()
2343
2344/******************************************
2345 * *
2346 * Additional non-class support functions *
2347 * *
2348 ******************************************/
2349
srs5694e7b4ff92009-08-18 13:16:10 -04002350// Check to be sure that data type sizes are correct. The basic types (uint*_t) should
2351// never fail these tests, but the struct types may fail depending on compile options.
2352// Specifically, the -fpack-struct option to gcc may be required to ensure proper structure
2353// sizes.
2354int SizesOK(void) {
2355 int allOK = 1;
srs5694e7b4ff92009-08-18 13:16:10 -04002356
2357 if (sizeof(uint8_t) != 1) {
srs56941d1448a2009-12-31 21:20:19 -05002358 fprintf(stderr, "uint8_t is %lu bytes, should be 1 byte; aborting!\n", sizeof(uint8_t));
srs5694e7b4ff92009-08-18 13:16:10 -04002359 allOK = 0;
2360 } // if
2361 if (sizeof(uint16_t) != 2) {
srs56941d1448a2009-12-31 21:20:19 -05002362 fprintf(stderr, "uint16_t is %lu bytes, should be 2 bytes; aborting!\n", sizeof(uint16_t));
srs5694e7b4ff92009-08-18 13:16:10 -04002363 allOK = 0;
2364 } // if
2365 if (sizeof(uint32_t) != 4) {
srs56941d1448a2009-12-31 21:20:19 -05002366 fprintf(stderr, "uint32_t is %lu bytes, should be 4 bytes; aborting!\n", sizeof(uint32_t));
srs5694e7b4ff92009-08-18 13:16:10 -04002367 allOK = 0;
2368 } // if
2369 if (sizeof(uint64_t) != 8) {
srs56941d1448a2009-12-31 21:20:19 -05002370 fprintf(stderr, "uint64_t is %lu bytes, should be 8 bytes; aborting!\n", sizeof(uint64_t));
srs5694e7b4ff92009-08-18 13:16:10 -04002371 allOK = 0;
2372 } // if
2373 if (sizeof(struct MBRRecord) != 16) {
srs56941d1448a2009-12-31 21:20:19 -05002374 fprintf(stderr, "MBRRecord is %lu bytes, should be 16 bytes; aborting!\n", sizeof(MBRRecord));
srs5694e7b4ff92009-08-18 13:16:10 -04002375 allOK = 0;
2376 } // if
srs5694978041c2009-09-21 20:51:47 -04002377 if (sizeof(struct TempMBR) != 512) {
srs56941d1448a2009-12-31 21:20:19 -05002378 fprintf(stderr, "TempMBR is %lu bytes, should be 512 bytes; aborting!\n", sizeof(TempMBR));
srs5694e7b4ff92009-08-18 13:16:10 -04002379 allOK = 0;
2380 } // if
2381 if (sizeof(struct GPTHeader) != 512) {
srs56941d1448a2009-12-31 21:20:19 -05002382 fprintf(stderr, "GPTHeader is %lu bytes, should be 512 bytes; aborting!\n", sizeof(GPTHeader));
srs5694e7b4ff92009-08-18 13:16:10 -04002383 allOK = 0;
2384 } // if
srs5694221e0872009-08-29 15:00:31 -04002385 if (sizeof(GPTPart) != 128) {
srs56941d1448a2009-12-31 21:20:19 -05002386 fprintf(stderr, "GPTPart is %lu bytes, should be 128 bytes; aborting!\n", sizeof(GPTPart));
srs5694221e0872009-08-29 15:00:31 -04002387 allOK = 0;
2388 } // if
2389// Determine endianness; set allOK = 0 if running on big-endian hardware
srs56942a9f5da2009-08-26 00:48:01 -04002390 if (IsLittleEndian() == 0) {
2391 fprintf(stderr, "\aRunning on big-endian hardware. Big-endian support is new and poorly"
srs5694e4ac11e2009-08-31 10:13:04 -04002392 " tested!\nBeware!\n");
srs56942a9f5da2009-08-26 00:48:01 -04002393 // allOK = 0;
srs5694e7b4ff92009-08-18 13:16:10 -04002394 } // if
2395 return (allOK);
2396} // SizesOK()
srs5694e4ac11e2009-08-31 10:13:04 -04002397