blob: 7352edc55a1234f6c50f870a5d85627591b2ba37 [file] [log] [blame]
srs5694e7b4ff92009-08-18 13:16:10 -04001/* gpt.cc -- Functions for loading, saving, and manipulating legacy MBR and GPT partition
2 data. */
3
srs5694e4ac11e2009-08-31 10:13:04 -04004/* By Rod Smith, initial coding January to February, 2009 */
srs5694e7b4ff92009-08-18 13:16:10 -04005
srs5694221e0872009-08-29 15:00:31 -04006/* This program is copyright (c) 2009 by Roderick W. Smith. It is distributed
7 under the terms of the GNU GPL version 2, as detailed in the COPYING file. */
8
srs5694e7b4ff92009-08-18 13:16:10 -04009#define __STDC_LIMIT_MACROS
10#define __STDC_CONSTANT_MACROS
11
12#include <stdio.h>
13#include <unistd.h>
14#include <stdlib.h>
15#include <stdint.h>
16#include <fcntl.h>
17#include <string.h>
18#include <time.h>
19#include <sys/stat.h>
20#include <errno.h>
21#include "crc32.h"
22#include "gpt.h"
srs5694221e0872009-08-29 15:00:31 -040023#include "bsd.h"
srs5694e7b4ff92009-08-18 13:16:10 -040024#include "support.h"
25#include "parttypes.h"
26#include "attributes.h"
27
28using namespace std;
29
30/****************************************
31 * *
32 * GPTData class and related structures *
33 * *
34 ****************************************/
35
srs5694e4ac11e2009-08-31 10:13:04 -040036// Default constructor
srs5694e7b4ff92009-08-18 13:16:10 -040037GPTData::GPTData(void) {
38 blockSize = SECTOR_SIZE; // set a default
39 diskSize = 0;
40 partitions = NULL;
41 state = gpt_valid;
42 strcpy(device, "");
srs56945d58fe02010-01-03 20:57:08 -050043 justLooking = 0;
srs5694e7b4ff92009-08-18 13:16:10 -040044 mainCrcOk = 0;
45 secondCrcOk = 0;
46 mainPartsCrcOk = 0;
47 secondPartsCrcOk = 0;
srs5694221e0872009-08-29 15:00:31 -040048 apmFound = 0;
49 bsdFound = 0;
srs56941d1448a2009-12-31 21:20:19 -050050 sectorAlignment = 8; // Align partitions on 4096-byte boundaries by default
srs5694e7b4ff92009-08-18 13:16:10 -040051 srand((unsigned int) time(NULL));
srs56941e093722010-01-05 00:14:19 -050052 mainHeader.numParts = 0;
srs5694e7b4ff92009-08-18 13:16:10 -040053 SetGPTSize(NUM_GPT_ENTRIES);
54} // GPTData default constructor
55
56// The following constructor loads GPT data from a device file
57GPTData::GPTData(char* filename) {
58 blockSize = SECTOR_SIZE; // set a default
59 diskSize = 0;
60 partitions = NULL;
61 state = gpt_invalid;
62 strcpy(device, "");
srs56945d58fe02010-01-03 20:57:08 -050063 justLooking = 0;
srs5694e7b4ff92009-08-18 13:16:10 -040064 mainCrcOk = 0;
65 secondCrcOk = 0;
66 mainPartsCrcOk = 0;
67 secondPartsCrcOk = 0;
srs5694221e0872009-08-29 15:00:31 -040068 apmFound = 0;
69 bsdFound = 0;
srs56941d1448a2009-12-31 21:20:19 -050070 sectorAlignment = 8; // Align partitions on 4096-byte boundaries by default
srs5694e7b4ff92009-08-18 13:16:10 -040071 srand((unsigned int) time(NULL));
srs56941e093722010-01-05 00:14:19 -050072 mainHeader.numParts = 0;
srs5694e7b4ff92009-08-18 13:16:10 -040073 LoadPartitions(filename);
74} // GPTData(char* filename) constructor
75
srs5694e4ac11e2009-08-31 10:13:04 -040076// Destructor
srs5694e7b4ff92009-08-18 13:16:10 -040077GPTData::~GPTData(void) {
78 free(partitions);
79} // GPTData destructor
80
srs5694e4ac11e2009-08-31 10:13:04 -040081/*********************************************************************
82 * *
83 * Begin functions that verify data, or that adjust the verification *
84 * information (compute CRCs, rebuild headers) *
85 * *
86 *********************************************************************/
srs5694e7b4ff92009-08-18 13:16:10 -040087
srs5694e4ac11e2009-08-31 10:13:04 -040088// Perform detailed verification, reporting on any problems found, but
89// do *NOT* recover from these problems. Returns the total number of
90// problems identified.
91int GPTData::Verify(void) {
srs56941d1448a2009-12-31 21:20:19 -050092 int problems = 0, numSegments, i;
93 uint64_t totalFree, largestSegment, firstSector;
srs5694e4ac11e2009-08-31 10:13:04 -040094 char tempStr[255], siTotal[255], siLargest[255];
95
96 // First, check for CRC errors in the GPT data....
97 if (!mainCrcOk) {
98 problems++;
99 printf("\nProblem: The CRC for the main GPT header is invalid. The main GPT header may\n"
100 "be corrupt. Consider loading the backup GPT header to rebuild the main GPT\n"
101 "header\n");
102 } // if
103 if (!mainPartsCrcOk) {
104 problems++;
105 printf("\nProblem: The CRC for the main partition table is invalid. This table may be\n"
106 "corrupt. Consider loading the backup partition table.\n");
107 } // if
108 if (!secondCrcOk) {
109 problems++;
110 printf("\nProblem: The CRC for the backup GPT header is invalid. The backup GPT header\n"
111 "may be corrupt. Consider using the main GPT header to rebuild the backup GPT\n"
112 "header.\n");
113 } // if
114 if (!secondPartsCrcOk) {
115 problems++;
116 printf("\nCaution: The CRC for the backup partition table is invalid. This table may\n"
117 "be corrupt. This program will automatically create a new backup partition\n"
118 "table when you save your partitions.\n");
srs5694e7b4ff92009-08-18 13:16:10 -0400119 } // if
120
srs5694978041c2009-09-21 20:51:47 -0400121 // Now check that the main and backup headers both point to themselves....
122 if (mainHeader.currentLBA != 1) {
123 problems++;
124 printf("\nProblem: The main header's self-pointer doesn't point to itself. This problem\n"
125 "is being automatically corrected, but it may be a symptom of more serious\n"
126 "problems. Think carefully before saving changes with 'w' or using this disk.\n");
127 mainHeader.currentLBA = 1;
128 } // if
129 if (secondHeader.currentLBA != (diskSize - UINT64_C(1))) {
130 problems++;
srs56943f2fe992009-11-24 18:28:18 -0500131 printf("\nProblem: The secondary header's self-pointer indicates that it doesn't reside\n"
132 "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 -0500133 "option on the experts' menu to adjust the secondary header's and partition\n"
134 "table's locations.\n");
srs5694978041c2009-09-21 20:51:47 -0400135 } // if
136
137 // Now check that critical main and backup GPT entries match each other
srs5694e4ac11e2009-08-31 10:13:04 -0400138 if (mainHeader.currentLBA != secondHeader.backupLBA) {
139 problems++;
140 printf("\nProblem: main GPT header's current LBA pointer (%llu) doesn't\n"
141 "match the backup GPT header's LBA pointer(%llu)\n",
142 (unsigned long long) mainHeader.currentLBA,
143 (unsigned long long) secondHeader.backupLBA);
144 } // if
145 if (mainHeader.backupLBA != secondHeader.currentLBA) {
146 problems++;
147 printf("\nProblem: main GPT header's backup LBA pointer (%llu) doesn't\n"
148 "match the backup GPT header's current LBA pointer (%llu)\n",
149 (unsigned long long) mainHeader.backupLBA,
150 (unsigned long long) secondHeader.currentLBA);
151 } // if
152 if (mainHeader.firstUsableLBA != secondHeader.firstUsableLBA) {
153 problems++;
154 printf("\nProblem: main GPT header's first usable LBA pointer (%llu) doesn't\n"
155 "match the backup GPT header's first usable LBA pointer (%llu)\n",
156 (unsigned long long) mainHeader.firstUsableLBA,
157 (unsigned long long) secondHeader.firstUsableLBA);
158 } // if
159 if (mainHeader.lastUsableLBA != secondHeader.lastUsableLBA) {
160 problems++;
161 printf("\nProblem: main GPT header's last usable LBA pointer (%llu) doesn't\n"
162 "match the backup GPT header's last usable LBA pointer (%llu)\n",
163 (unsigned long long) mainHeader.lastUsableLBA,
164 (unsigned long long) secondHeader.lastUsableLBA);
165 } // if
166 if ((mainHeader.diskGUID.data1 != secondHeader.diskGUID.data1) ||
167 (mainHeader.diskGUID.data2 != secondHeader.diskGUID.data2)) {
168 problems++;
169 printf("\nProblem: main header's disk GUID (%s) doesn't\n",
170 GUIDToStr(mainHeader.diskGUID, tempStr));
171 printf("match the backup GPT header's disk GUID (%s)\n",
172 GUIDToStr(secondHeader.diskGUID, tempStr));
173 } // if
174 if (mainHeader.numParts != secondHeader.numParts) {
175 problems++;
176 printf("\nProblem: main GPT header's number of partitions (%lu) doesn't\n"
177 "match the backup GPT header's number of partitions (%lu)\n",
178 (unsigned long) mainHeader.numParts,
179 (unsigned long) secondHeader.numParts);
180 } // if
181 if (mainHeader.sizeOfPartitionEntries != secondHeader.sizeOfPartitionEntries) {
182 problems++;
183 printf("\nProblem: main GPT header's size of partition entries (%lu) doesn't\n"
184 "match the backup GPT header's size of partition entries (%lu)\n",
185 (unsigned long) mainHeader.sizeOfPartitionEntries,
186 (unsigned long) secondHeader.sizeOfPartitionEntries);
187 } // if
188
189 // Now check for a few other miscellaneous problems...
190 // Check that the disk size will hold the data...
191 if (mainHeader.backupLBA > diskSize) {
192 problems++;
193 printf("\nProblem: Disk is too small to hold all the data!\n");
194 printf("(Disk size is %llu sectors, needs to be %llu sectors.)\n",
195 (unsigned long long) diskSize,
196 (unsigned long long) mainHeader.backupLBA);
197 } // if
198
199 // Check for overlapping partitions....
200 problems += FindOverlaps();
201
202 // Check for mismatched MBR and GPT partitions...
203 problems += FindHybridMismatches();
204
205 // Verify that partitions don't run into GPT data areas....
206 problems += CheckGPTSize();
207
srs56941d1448a2009-12-31 21:20:19 -0500208 // Check that partitions are aligned on proper boundaries (for WD Advanced
209 // Format and similar disks)....
210 for (i = 0; i < mainHeader.numParts; i++) {
211 if ((partitions[i].GetFirstLBA() % sectorAlignment) != 0) {
212 printf("\nCaution: Partition %d doesn't begin on a %d-sector boundary. This may\n"
213 "result in degraded performance on some modern (2010 and later) hard disks.\n",
214 i + 1, sectorAlignment);
215 } // if
216 } // for
217
srs5694e4ac11e2009-08-31 10:13:04 -0400218 // Now compute available space, but only if no problems found, since
219 // problems could affect the results
220 if (problems == 0) {
221 totalFree = FindFreeBlocks(&numSegments, &largestSegment);
222 BytesToSI(totalFree * (uint64_t) blockSize, siTotal);
223 BytesToSI(largestSegment * (uint64_t) blockSize, siLargest);
224 printf("No problems found. %llu free sectors (%s) available in %u\n"
225 "segments, the largest of which is %llu sectors (%s) in size\n",
226 (unsigned long long) totalFree,
227 siTotal, numSegments, (unsigned long long) largestSegment,
228 siLargest);
229 } else {
230 printf("\nIdentified %d problems!\n", problems);
srs5694e7b4ff92009-08-18 13:16:10 -0400231 } // if/else
srs5694e4ac11e2009-08-31 10:13:04 -0400232
233 return (problems);
234} // GPTData::Verify()
srs5694e7b4ff92009-08-18 13:16:10 -0400235
236// Checks to see if the GPT tables overrun existing partitions; if they
srs5694221e0872009-08-29 15:00:31 -0400237// do, issues a warning but takes no action. Returns number of problems
238// detected (0 if OK, 1 to 2 if problems).
srs5694e7b4ff92009-08-18 13:16:10 -0400239int GPTData::CheckGPTSize(void) {
240 uint64_t overlap, firstUsedBlock, lastUsedBlock;
241 uint32_t i;
srs5694221e0872009-08-29 15:00:31 -0400242 int numProbs = 0;
srs5694e7b4ff92009-08-18 13:16:10 -0400243
244 // first, locate the first & last used blocks
245 firstUsedBlock = UINT64_MAX;
246 lastUsedBlock = 0;
247 for (i = 0; i < mainHeader.numParts; i++) {
srs5694221e0872009-08-29 15:00:31 -0400248 if ((partitions[i].GetFirstLBA() < firstUsedBlock) &&
srs5694e4ac11e2009-08-31 10:13:04 -0400249 (partitions[i].GetFirstLBA() != 0))
srs5694221e0872009-08-29 15:00:31 -0400250 firstUsedBlock = partitions[i].GetFirstLBA();
251 if (partitions[i].GetLastLBA() > lastUsedBlock)
252 lastUsedBlock = partitions[i].GetLastLBA();
srs5694e7b4ff92009-08-18 13:16:10 -0400253 } // for
254
255 // If the disk size is 0 (the default), then it means that various
256 // variables aren't yet set, so the below tests will be useless;
257 // therefore we should skip everything
258 if (diskSize != 0) {
259 if (mainHeader.firstUsableLBA > firstUsedBlock) {
260 overlap = mainHeader.firstUsableLBA - firstUsedBlock;
srs5694221e0872009-08-29 15:00:31 -0400261 printf("Warning! Main partition table overlaps the first partition by %lu blocks!\n",
262 (unsigned long) overlap);
263 if (firstUsedBlock > 2) {
264 printf("Try reducing the partition table size by %lu entries.\n",
265 (unsigned long) (overlap * 4));
266 printf("(Use the 's' item on the experts' menu.)\n");
267 } else {
268 printf("You will need to delete this partition or resize it in another utility.\n");
269 } // if/else
270 numProbs++;
srs5694e7b4ff92009-08-18 13:16:10 -0400271 } // Problem at start of disk
272 if (mainHeader.lastUsableLBA < lastUsedBlock) {
273 overlap = lastUsedBlock - mainHeader.lastUsableLBA;
srs5694221e0872009-08-29 15:00:31 -0400274 printf("Warning! Secondary partition table overlaps the last partition by %lu blocks\n",
275 (unsigned long) overlap);
276 if (lastUsedBlock > (diskSize - 2)) {
277 printf("You will need to delete this partition or resize it in another utility.\n");
278 } else {
279 printf("Try reducing the partition table size by %lu entries.\n",
280 (unsigned long) (overlap * 4));
281 printf("(Use the 's' item on the experts' menu.)\n");
282 } // if/else
283 numProbs++;
srs5694e7b4ff92009-08-18 13:16:10 -0400284 } // Problem at end of disk
285 } // if (diskSize != 0)
srs5694221e0872009-08-29 15:00:31 -0400286 return numProbs;
srs5694e7b4ff92009-08-18 13:16:10 -0400287} // GPTData::CheckGPTSize()
288
srs5694e7b4ff92009-08-18 13:16:10 -0400289// Check the validity of the GPT header. Returns 1 if the main header
290// is valid, 2 if the backup header is valid, 3 if both are valid, and
291// 0 if neither is valid. Note that this function just checks the GPT
292// signature and revision numbers, not CRCs or other data.
293int GPTData::CheckHeaderValidity(void) {
294 int valid = 3;
295
296 if (mainHeader.signature != GPT_SIGNATURE) {
297 valid -= 1;
srs5694221e0872009-08-29 15:00:31 -0400298// printf("Main GPT signature invalid; read 0x%016llX, should be\n0x%016llX\n",
299// (unsigned long long) mainHeader.signature, (unsigned long long) GPT_SIGNATURE);
srs5694e7b4ff92009-08-18 13:16:10 -0400300 } else if ((mainHeader.revision != 0x00010000) && valid) {
301 valid -= 1;
302 printf("Unsupported GPT version in main header; read 0x%08lX, should be\n0x%08lX\n",
srs56945d58fe02010-01-03 20:57:08 -0500303 (unsigned long) mainHeader.revision, (unsigned long) UINT32_C(0x00010000));
srs5694e7b4ff92009-08-18 13:16:10 -0400304 } // if/else/if
305
306 if (secondHeader.signature != GPT_SIGNATURE) {
307 valid -= 2;
srs5694221e0872009-08-29 15:00:31 -0400308// printf("Secondary GPT signature invalid; read 0x%016llX, should be\n0x%016llX\n",
309// (unsigned long long) secondHeader.signature, (unsigned long long) GPT_SIGNATURE);
srs5694e7b4ff92009-08-18 13:16:10 -0400310 } else if ((secondHeader.revision != 0x00010000) && valid) {
311 valid -= 2;
312 printf("Unsupported GPT version in backup header; read 0x%08lX, should be\n0x%08lX\n",
srs56945d58fe02010-01-03 20:57:08 -0500313 (unsigned long) mainHeader.revision, (unsigned long) UINT32_C(0x00010000));
srs5694e7b4ff92009-08-18 13:16:10 -0400314 } // if/else/if
315
srs56942a9f5da2009-08-26 00:48:01 -0400316 // If MBR bad, check for an Apple disk signature
srs5694e35eb1b2009-09-14 00:29:34 -0400317 if ((protectiveMBR.GetValidity() == invalid) &&
srs5694e4ac11e2009-08-31 10:13:04 -0400318 (((mainHeader.signature << 32) == APM_SIGNATURE1) ||
srs56942a9f5da2009-08-26 00:48:01 -0400319 (mainHeader.signature << 32) == APM_SIGNATURE2)) {
srs5694221e0872009-08-29 15:00:31 -0400320 apmFound = 1; // Will display warning message later
srs56943f2fe992009-11-24 18:28:18 -0500321 } // if
srs56942a9f5da2009-08-26 00:48:01 -0400322
srs5694e4ac11e2009-08-31 10:13:04 -0400323 return valid;
srs5694e7b4ff92009-08-18 13:16:10 -0400324} // GPTData::CheckHeaderValidity()
325
326// Check the header CRC to see if it's OK...
srs56942a9f5da2009-08-26 00:48:01 -0400327// Note: Must be called BEFORE byte-order reversal on big-endian
328// systems!
srs5694e7b4ff92009-08-18 13:16:10 -0400329int GPTData::CheckHeaderCRC(struct GPTHeader* header) {
srs5694978041c2009-09-21 20:51:47 -0400330 uint32_t oldCRC, newCRC, hSize;
srs5694e7b4ff92009-08-18 13:16:10 -0400331
srs56942a9f5da2009-08-26 00:48:01 -0400332 // Back up old header CRC and then blank it, since it must be 0 for
srs5694e7b4ff92009-08-18 13:16:10 -0400333 // computation to be valid
334 oldCRC = header->headerCRC;
335 header->headerCRC = UINT32_C(0);
srs5694978041c2009-09-21 20:51:47 -0400336 hSize = header->headerSize;
337
338 // If big-endian system, reverse byte order
339 if (IsLittleEndian() == 0) {
340 ReverseBytes(&oldCRC, 4);
341 } // if
srs5694e7b4ff92009-08-18 13:16:10 -0400342
343 // Initialize CRC functions...
344 chksum_crc32gentab();
345
346 // Compute CRC, restore original, and return result of comparison
347 newCRC = chksum_crc32((unsigned char*) header, HEADER_SIZE);
srs5694978041c2009-09-21 20:51:47 -0400348 header->headerCRC = oldCRC;
srs5694e7b4ff92009-08-18 13:16:10 -0400349 return (oldCRC == newCRC);
350} // GPTData::CheckHeaderCRC()
351
srs56942a9f5da2009-08-26 00:48:01 -0400352// Recompute all the CRCs. Must be called before saving (but after reversing
353// byte order on big-endian systems) if any changes have been made.
srs5694e7b4ff92009-08-18 13:16:10 -0400354void GPTData::RecomputeCRCs(void) {
srs5694978041c2009-09-21 20:51:47 -0400355 uint32_t crc, hSize, trueNumParts;
srs56942a9f5da2009-08-26 00:48:01 -0400356 int littleEndian = 1;
srs5694e7b4ff92009-08-18 13:16:10 -0400357
358 // Initialize CRC functions...
359 chksum_crc32gentab();
360
srs5694978041c2009-09-21 20:51:47 -0400361 hSize = mainHeader.headerSize;
srs56942a9f5da2009-08-26 00:48:01 -0400362 littleEndian = IsLittleEndian();
363
srs5694e7b4ff92009-08-18 13:16:10 -0400364 // Compute CRC of partition tables & store in main and secondary headers
srs56942a9f5da2009-08-26 00:48:01 -0400365 trueNumParts = mainHeader.numParts;
366 if (littleEndian == 0)
srs5694221e0872009-08-29 15:00:31 -0400367 ReverseBytes(&trueNumParts, 4); // unreverse this key piece of data....
srs56942a9f5da2009-08-26 00:48:01 -0400368 crc = chksum_crc32((unsigned char*) partitions, trueNumParts * GPT_SIZE);
srs5694e7b4ff92009-08-18 13:16:10 -0400369 mainHeader.partitionEntriesCRC = crc;
370 secondHeader.partitionEntriesCRC = crc;
srs56942a9f5da2009-08-26 00:48:01 -0400371 if (littleEndian == 0) {
srs5694221e0872009-08-29 15:00:31 -0400372 ReverseBytes(&mainHeader.partitionEntriesCRC, 4);
373 ReverseBytes(&secondHeader.partitionEntriesCRC, 4);
srs56942a9f5da2009-08-26 00:48:01 -0400374 } // if
srs5694e7b4ff92009-08-18 13:16:10 -0400375
376 // Zero out GPT tables' own CRCs (required for correct computation)
377 mainHeader.headerCRC = 0;
378 secondHeader.headerCRC = 0;
379
380 // Compute & store CRCs of main & secondary headers...
srs5694978041c2009-09-21 20:51:47 -0400381 crc = chksum_crc32((unsigned char*) &mainHeader, hSize);
srs56942a9f5da2009-08-26 00:48:01 -0400382 if (littleEndian == 0)
srs5694221e0872009-08-29 15:00:31 -0400383 ReverseBytes(&crc, 4);
srs5694e7b4ff92009-08-18 13:16:10 -0400384 mainHeader.headerCRC = crc;
srs5694978041c2009-09-21 20:51:47 -0400385 crc = chksum_crc32((unsigned char*) &secondHeader, 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 secondHeader.headerCRC = crc;
389} // GPTData::RecomputeCRCs()
390
srs5694e7b4ff92009-08-18 13:16:10 -0400391// Rebuild the main GPT header, using the secondary header as a model.
392// Typically called when the main header has been found to be corrupt.
393void GPTData::RebuildMainHeader(void) {
394 int i;
395
396 mainHeader.signature = GPT_SIGNATURE;
397 mainHeader.revision = secondHeader.revision;
srs5694978041c2009-09-21 20:51:47 -0400398 mainHeader.headerSize = secondHeader.headerSize;
srs5694e7b4ff92009-08-18 13:16:10 -0400399 mainHeader.headerCRC = UINT32_C(0);
400 mainHeader.reserved = secondHeader.reserved;
401 mainHeader.currentLBA = secondHeader.backupLBA;
402 mainHeader.backupLBA = secondHeader.currentLBA;
403 mainHeader.firstUsableLBA = secondHeader.firstUsableLBA;
404 mainHeader.lastUsableLBA = secondHeader.lastUsableLBA;
405 mainHeader.diskGUID.data1 = secondHeader.diskGUID.data1;
406 mainHeader.diskGUID.data2 = secondHeader.diskGUID.data2;
407 mainHeader.partitionEntriesLBA = UINT64_C(2);
408 mainHeader.numParts = secondHeader.numParts;
409 mainHeader.sizeOfPartitionEntries = secondHeader.sizeOfPartitionEntries;
410 mainHeader.partitionEntriesCRC = secondHeader.partitionEntriesCRC;
411 for (i = 0 ; i < GPT_RESERVED; i++)
412 mainHeader.reserved2[i] = secondHeader.reserved2[i];
413} // GPTData::RebuildMainHeader()
414
415// Rebuild the secondary GPT header, using the main header as a model.
416void GPTData::RebuildSecondHeader(void) {
417 int i;
418
419 secondHeader.signature = GPT_SIGNATURE;
420 secondHeader.revision = mainHeader.revision;
srs5694978041c2009-09-21 20:51:47 -0400421 secondHeader.headerSize = mainHeader.headerSize;
srs5694e7b4ff92009-08-18 13:16:10 -0400422 secondHeader.headerCRC = UINT32_C(0);
423 secondHeader.reserved = mainHeader.reserved;
424 secondHeader.currentLBA = mainHeader.backupLBA;
425 secondHeader.backupLBA = mainHeader.currentLBA;
426 secondHeader.firstUsableLBA = mainHeader.firstUsableLBA;
427 secondHeader.lastUsableLBA = mainHeader.lastUsableLBA;
428 secondHeader.diskGUID.data1 = mainHeader.diskGUID.data1;
429 secondHeader.diskGUID.data2 = mainHeader.diskGUID.data2;
430 secondHeader.partitionEntriesLBA = secondHeader.lastUsableLBA + UINT64_C(1);
431 secondHeader.numParts = mainHeader.numParts;
432 secondHeader.sizeOfPartitionEntries = mainHeader.sizeOfPartitionEntries;
433 secondHeader.partitionEntriesCRC = mainHeader.partitionEntriesCRC;
434 for (i = 0 ; i < GPT_RESERVED; i++)
435 secondHeader.reserved2[i] = mainHeader.reserved2[i];
srs5694e4ac11e2009-08-31 10:13:04 -0400436} // GPTData::RebuildSecondHeader()
437
438// Search for hybrid MBR entries that have no corresponding GPT partition.
439// Returns number of such mismatches found
440int GPTData::FindHybridMismatches(void) {
441 int i, j, found, numFound = 0;
442 uint64_t mbrFirst, mbrLast;
443
444 for (i = 0; i < 4; i++) {
445 if ((protectiveMBR.GetType(i) != 0xEE) && (protectiveMBR.GetType(i) != 0x00)) {
446 j = 0;
447 found = 0;
448 do {
449 mbrFirst = (uint64_t) protectiveMBR.GetFirstSector(i);
450 mbrLast = mbrFirst + (uint64_t) protectiveMBR.GetLength(i) - UINT64_C(1);
451 if ((partitions[j].GetFirstLBA() == mbrFirst) &&
452 (partitions[j].GetLastLBA() == mbrLast))
453 found = 1;
454 j++;
455 } while ((!found) && (j < mainHeader.numParts));
456 if (!found) {
457 numFound++;
458 printf("\nWarning! Mismatched GPT and MBR partitions! MBR partition "
459 "%d, of type 0x%02X,\nhas no corresponding GPT partition! "
460 "You may continue, but this condition\nmight cause data loss"
461 " in the future!\a\n", i + 1, protectiveMBR.GetType(i));
462 } // if
463 } // if
464 } // for
465 return numFound;
466} // GPTData::FindHybridMismatches
467
468// Find overlapping partitions and warn user about them. Returns number of
469// overlapping partitions.
470int GPTData::FindOverlaps(void) {
471 int i, j, problems = 0;
472
473 for (i = 1; i < mainHeader.numParts; i++) {
474 for (j = 0; j < i; j++) {
475 if (partitions[i].DoTheyOverlap(&partitions[j])) {
476 problems++;
477 printf("\nProblem: partitions %d and %d overlap:\n", i + 1, j + 1);
478 printf(" Partition %d: %llu to %llu\n", i,
479 (unsigned long long) partitions[i].GetFirstLBA(),
480 (unsigned long long) partitions[i].GetLastLBA());
481 printf(" Partition %d: %llu to %llu\n", j,
482 (unsigned long long) partitions[j].GetFirstLBA(),
483 (unsigned long long) partitions[j].GetLastLBA());
484 } // if
485 } // for j...
486 } // for i...
487 return problems;
488} // GPTData::FindOverlaps()
489
490/******************************************************************
491 * *
492 * Begin functions that load data from disk or save data to disk. *
493 * *
494 ******************************************************************/
495
496// Scan for partition data. This function loads the MBR data (regular MBR or
497// protective MBR) and loads BSD disklabel data (which is probably invalid).
498// It also looks for APM data, forces a load of GPT data, and summarizes
499// the results.
500void GPTData::PartitionScan(int fd) {
501 BSDData bsdDisklabel;
502// int bsdFound;
503
504 printf("Partition table scan:\n");
505
506 // Read the MBR & check for BSD disklabel
507 protectiveMBR.ReadMBRData(fd);
508 protectiveMBR.ShowState();
509 bsdDisklabel.ReadBSDData(fd, 0, diskSize - 1);
510 bsdFound = bsdDisklabel.ShowState();
511// bsdDisklabel.DisplayBSDData();
512
513 // Load the GPT data, whether or not it's valid
514 ForceLoadGPTData(fd);
515 ShowAPMState(); // Show whether there's an Apple Partition Map present
516 ShowGPTState(); // Show GPT status
517 printf("\n");
518
519 if (apmFound) {
520 printf("\n*******************************************************************\n");
521 printf("This disk appears to contain an Apple-format (APM) partition table!\n");
srs56945d58fe02010-01-03 20:57:08 -0500522 if (!justLooking) {
523 printf("It will be destroyed if you continue!\n");
524 } // if
srs5694e4ac11e2009-08-31 10:13:04 -0400525 printf("*******************************************************************\n\n\a");
526 } // if
srs5694e4ac11e2009-08-31 10:13:04 -0400527} // GPTData::PartitionScan()
528
529// Read GPT data from a disk.
530int GPTData::LoadPartitions(char* deviceFilename) {
531 int fd, err;
532 int allOK = 1, i;
533 uint64_t firstBlock, lastBlock;
534 BSDData bsdDisklabel;
535
536 // First, do a test to see if writing will be possible later....
537 fd = OpenForWrite(deviceFilename);
srs56945d58fe02010-01-03 20:57:08 -0500538 if ((fd == -1) && (!justLooking)) {
srs5694e4ac11e2009-08-31 10:13:04 -0400539 printf("\aNOTE: Write test failed with error number %d. It will be "
540 "impossible to save\nchanges to this disk's partition table!\n\n",
541 errno);
srs56945d58fe02010-01-03 20:57:08 -0500542 justLooking = 1;
543 } // if
srs5694e4ac11e2009-08-31 10:13:04 -0400544 close(fd);
545
546 if ((fd = open(deviceFilename, O_RDONLY)) != -1) {
547 // store disk information....
548 diskSize = disksize(fd, &err);
549 blockSize = (uint32_t) GetBlockSize(fd);
srs56945d58fe02010-01-03 20:57:08 -0500550 sectorAlignment = FindAlignment(fd);
srs5694e4ac11e2009-08-31 10:13:04 -0400551 strcpy(device, deviceFilename);
552 PartitionScan(fd); // Check for partition types & print summary
553
554 switch (UseWhichPartitions()) {
555 case use_mbr:
556 XFormPartitions();
557 break;
558 case use_bsd:
559 bsdDisklabel.ReadBSDData(fd, 0, diskSize - 1);
560// bsdDisklabel.DisplayBSDData();
561 ClearGPTData();
562 protectiveMBR.MakeProtectiveMBR(1); // clear boot area (option 1)
563 XFormDisklabel(&bsdDisklabel, 0);
564 break;
565 case use_gpt:
566 break;
567 case use_new:
568 ClearGPTData();
569 protectiveMBR.MakeProtectiveMBR();
570 break;
571 } // switch
572
573 // Now find the first and last sectors used by partitions...
574 if (allOK) {
575 firstBlock = mainHeader.backupLBA; // start high
576 lastBlock = 0; // start low
577 for (i = 0; i < mainHeader.numParts; i++) {
578 if ((partitions[i].GetFirstLBA() < firstBlock) &&
579 (partitions[i].GetFirstLBA() > 0))
580 firstBlock = partitions[i].GetFirstLBA();
581 if (partitions[i].GetLastLBA() > lastBlock)
582 lastBlock = partitions[i].GetLastLBA();
583 } // for
584 } // if
585 CheckGPTSize();
586 } else {
587 allOK = 0;
588 fprintf(stderr, "Problem opening %s for reading! Error is %d\n",
589 deviceFilename, errno);
590 if (errno == EACCES) { // User is probably not running as root
591 fprintf(stderr, "You must run this program as root or use sudo!\n");
592 } // if
593 } // if/else
594 return (allOK);
595} // GPTData::LoadPartitions()
596
597// Loads the GPT, as much as possible. Returns 1 if this seems to have
598// succeeded, 0 if there are obvious problems....
599int GPTData::ForceLoadGPTData(int fd) {
600 int allOK = 1, validHeaders;
601 off_t seekTo;
602 char* storage;
603 uint32_t newCRC, sizeOfParts;
604
605 // Seek to and read the main GPT header
srs56941e093722010-01-05 00:14:19 -0500606 lseek64(fd, blockSize, SEEK_SET);
607 if (myRead(fd, (char*) &mainHeader, 512) != 512) { // read main GPT header
608 fprintf(stderr, "Warning! Error %d reading main GPT header!\n", errno);
srs56945d58fe02010-01-03 20:57:08 -0500609 } // if
srs5694e4ac11e2009-08-31 10:13:04 -0400610 mainCrcOk = CheckHeaderCRC(&mainHeader);
611 if (IsLittleEndian() == 0) // big-endian system; adjust header byte order....
612 ReverseHeaderBytes(&mainHeader);
613
srs56943f2fe992009-11-24 18:28:18 -0500614 // Load backup header, check its CRC, and store the results of the
615 // check for future reference. Load backup header using pointer in main
616 // header if possible; but if main header has a CRC error, or if it
617 // points to beyond the end of the disk, load the last sector of the
618 // disk instead.
619 if (mainCrcOk) {
620 if (mainHeader.backupLBA < diskSize) {
621 seekTo = mainHeader.backupLBA * blockSize;
622 } else {
srs56941e093722010-01-05 00:14:19 -0500623 seekTo = (diskSize * blockSize) - blockSize;
srs56943f2fe992009-11-24 18:28:18 -0500624 printf("Warning! Disk size is smaller than the main header indicates! Loading\n"
625 "secondary header from the last sector of the disk! You should use 'v' to\n"
626 "verify disk integrity, and perhaps options on the experts' menu to repair\n"
627 "the disk.\n");
628 } // else
629 } else {
srs56941e093722010-01-05 00:14:19 -0500630 seekTo = (diskSize * blockSize) - blockSize;
srs56943f2fe992009-11-24 18:28:18 -0500631 } // if/else (mainCrcOk)
632
srs5694e4ac11e2009-08-31 10:13:04 -0400633 if (lseek64(fd, seekTo, SEEK_SET) != (off_t) -1) {
srs56941e093722010-01-05 00:14:19 -0500634 if (myRead(fd, (char*) &secondHeader, 512) != 512) { // read secondary GPT header
635 fprintf(stderr, "Warning! Error %d reading secondary GPT header!\n", errno);
srs56945d58fe02010-01-03 20:57:08 -0500636 } // if
srs5694e4ac11e2009-08-31 10:13:04 -0400637 secondCrcOk = CheckHeaderCRC(&secondHeader);
638 if (IsLittleEndian() == 0) // big-endian system; adjust header byte order....
639 ReverseHeaderBytes(&secondHeader);
640 } else {
641 allOK = 0;
642 state = gpt_invalid;
srs56943f2fe992009-11-24 18:28:18 -0500643 fprintf(stderr, "Unable to seek to secondary GPT header at sector %llu!\n",
srs56945d58fe02010-01-03 20:57:08 -0500644 (unsigned long long) (diskSize - (UINT64_C(1))));
srs5694e4ac11e2009-08-31 10:13:04 -0400645 } // if/else lseek
646
647 // Return valid headers code: 0 = both headers bad; 1 = main header
648 // good, backup bad; 2 = backup header good, main header bad;
649 // 3 = both headers good. Note these codes refer to valid GPT
650 // signatures and version numbers; more subtle problems will elude
651 // this check!
652 validHeaders = CheckHeaderValidity();
653
654 // Read partitions (from primary array)
655 if (validHeaders > 0) { // if at least one header is OK....
656 // GPT appears to be valid....
657 state = gpt_valid;
658
659 // We're calling the GPT valid, but there's a possibility that one
660 // of the two headers is corrupt. If so, use the one that seems to
661 // be in better shape to regenerate the bad one
662 if (validHeaders == 2) { // valid backup header, invalid main header
663 printf("Caution: invalid main GPT header, but valid backup; regenerating main header\n"
664 "from backup!\n");
665 RebuildMainHeader();
666 mainCrcOk = secondCrcOk; // Since copied, use CRC validity of backup
667 } else if (validHeaders == 1) { // valid main header, invalid backup
668 printf("Caution: invalid backup GPT header, but valid main header; regenerating\n"
669 "backup header from main header.\n");
670 RebuildSecondHeader();
671 secondCrcOk = mainCrcOk; // Since regenerated, use CRC validity of main
672 } // if/else/if
673
674 // Load the main partition table, including storing results of its
675 // CRC check
676 if (LoadMainTable() == 0)
677 allOK = 0;
678
679 // Load backup partition table into temporary storage to check
680 // its CRC and store the results, then discard this temporary
681 // storage, since we don't use it in any but recovery operations
682 seekTo = secondHeader.partitionEntriesLBA * (off_t) blockSize;
683 if ((lseek64(fd, seekTo, SEEK_SET) != (off_t) -1) && (secondCrcOk)) {
684 sizeOfParts = secondHeader.numParts * secondHeader.sizeOfPartitionEntries;
685 storage = (char*) malloc(sizeOfParts);
srs56941e093722010-01-05 00:14:19 -0500686 if (myRead(fd, storage, sizeOfParts) != sizeOfParts) {
srs56945d58fe02010-01-03 20:57:08 -0500687 fprintf(stderr, "Warning! Error %d reading backup partition table!\n", errno);
688 } // if
srs5694e4ac11e2009-08-31 10:13:04 -0400689 newCRC = chksum_crc32((unsigned char*) storage, sizeOfParts);
690 free(storage);
691 secondPartsCrcOk = (newCRC == secondHeader.partitionEntriesCRC);
692 } // if
693
694 // Check for valid CRCs and warn if there are problems
695 if ((mainCrcOk == 0) || (secondCrcOk == 0) || (mainPartsCrcOk == 0) ||
696 (secondPartsCrcOk == 0)) {
697 printf("Warning! One or more CRCs don't match. You should repair the disk!\n");
698 state = gpt_corrupt;
699 } // if
700 } else {
701 state = gpt_invalid;
702 } // if/else
703 return allOK;
704} // GPTData::ForceLoadGPTData()
705
srs5694247657a2009-11-26 18:36:12 -0500706// Loads the partition table pointed to by the main GPT header. The
srs5694e4ac11e2009-08-31 10:13:04 -0400707// main GPT header in memory MUST be valid for this call to do anything
708// sensible!
709int GPTData::LoadMainTable(void) {
710 int fd, retval = 0;
711 uint32_t newCRC, sizeOfParts;
712
713 if ((fd = open(device, O_RDONLY)) != -1) {
714 // Set internal data structures for number of partitions on the disk
715 SetGPTSize(mainHeader.numParts);
716
717 // Load main partition table, and record whether its CRC
718 // matches the stored value
719 lseek64(fd, mainHeader.partitionEntriesLBA * blockSize, SEEK_SET);
720 sizeOfParts = mainHeader.numParts * mainHeader.sizeOfPartitionEntries;
srs56941e093722010-01-05 00:14:19 -0500721 if (myRead(fd, (char*) partitions, sizeOfParts) != sizeOfParts) {
srs56945d58fe02010-01-03 20:57:08 -0500722 fprintf(stderr, "Warning! Error %d when loading the main partition table!\n", errno);
723 } // if
srs5694e4ac11e2009-08-31 10:13:04 -0400724 newCRC = chksum_crc32((unsigned char*) partitions, sizeOfParts);
725 mainPartsCrcOk = (newCRC == mainHeader.partitionEntriesCRC);
726 if (IsLittleEndian() == 0)
727 ReversePartitionBytes();
728 retval = 1;
729 } // if
730 return retval;
731} // GPTData::LoadMainTable()
srs5694e7b4ff92009-08-18 13:16:10 -0400732
733// Load the second (backup) partition table as the primary partition
734// table. Used in repair functions
735void GPTData::LoadSecondTableAsMain(void) {
736 int fd;
737 off_t seekTo;
738 uint32_t sizeOfParts, newCRC;
739
740 if ((fd = open(device, O_RDONLY)) != -1) {
741 seekTo = secondHeader.partitionEntriesLBA * (off_t) blockSize;
742 if (lseek64(fd, seekTo, SEEK_SET) != (off_t) -1) {
743 SetGPTSize(secondHeader.numParts);
744 sizeOfParts = secondHeader.numParts * secondHeader.sizeOfPartitionEntries;
srs56941e093722010-01-05 00:14:19 -0500745 if (myRead(fd, (char*) partitions, sizeOfParts) != sizeOfParts) {
srs56945d58fe02010-01-03 20:57:08 -0500746 fprintf(stderr, "Warning! Read error %d! Misbehavior now likely!\n", errno);
747 } // if
srs5694e7b4ff92009-08-18 13:16:10 -0400748 newCRC = chksum_crc32((unsigned char*) partitions, sizeOfParts);
749 secondPartsCrcOk = (newCRC == secondHeader.partitionEntriesCRC);
srs5694e4ac11e2009-08-31 10:13:04 -0400750 mainPartsCrcOk = secondPartsCrcOk;
srs56942a9f5da2009-08-26 00:48:01 -0400751 if (IsLittleEndian() == 0)
752 ReversePartitionBytes();
srs5694e7b4ff92009-08-18 13:16:10 -0400753 if (!secondPartsCrcOk) {
754 printf("Error! After loading backup partitions, the CRC still doesn't check out!\n");
755 } // if
756 } else {
757 printf("Error! Couldn't seek to backup partition table!\n");
758 } // if/else
759 } else {
srs56941d1448a2009-12-31 21:20:19 -0500760 printf("Error! Couldn't open device %s when recovering backup partition table!\n", device);
srs5694e7b4ff92009-08-18 13:16:10 -0400761 } // if/else
762} // GPTData::LoadSecondTableAsMain()
763
srs5694e7b4ff92009-08-18 13:16:10 -0400764// Writes GPT (and protective MBR) to disk. Returns 1 on successful
765// write, 0 if there was a problem.
766int GPTData::SaveGPTData(void) {
srs5694978041c2009-09-21 20:51:47 -0400767 int allOK = 1;
srs5694e7b4ff92009-08-18 13:16:10 -0400768 char answer, line[256];
769 int fd;
770 uint64_t secondTable;
srs56942a9f5da2009-08-26 00:48:01 -0400771 uint32_t numParts;
srs5694e7b4ff92009-08-18 13:16:10 -0400772 off_t offset;
773
774 if (strlen(device) == 0) {
775 printf("Device not defined.\n");
776 } // if
777
778 // First do some final sanity checks....
srs56945d58fe02010-01-03 20:57:08 -0500779
780 // This test should only fail on read-only disks....
781 if (justLooking) {
782 printf("The justLooking flag is set. This probably means you can't write to the disk.\n");
783 allOK = 0;
784 } // if
785
srs5694e7b4ff92009-08-18 13:16:10 -0400786 // Is there enough space to hold the GPT headers and partition tables,
787 // given the partition sizes?
srs5694221e0872009-08-29 15:00:31 -0400788 if (CheckGPTSize() > 0) {
srs5694e7b4ff92009-08-18 13:16:10 -0400789 allOK = 0;
790 } // if
791
792 // Check that disk is really big enough to handle this...
793 if (mainHeader.backupLBA > diskSize) {
srs5694247657a2009-11-26 18:36:12 -0500794 fprintf(stderr, "Error! Disk is too small! The 'e' option on the experts' menu might fix the\n"
795 "problem (or it might not). Aborting!\n");
srs56945d58fe02010-01-03 20:57:08 -0500796 printf("(Disk size is %llu sectors, needs to be %llu sectors.)\n",
797 (unsigned long long) diskSize, (unsigned long long) mainHeader.backupLBA);
srs5694e7b4ff92009-08-18 13:16:10 -0400798 allOK = 0;
799 } // if
srs5694247657a2009-11-26 18:36:12 -0500800 // Check that second header is properly placed. Warn and ask if this should
801 // be corrected if the test fails....
802 if (mainHeader.backupLBA < (diskSize - UINT64_C(1))) {
803 printf("Warning! Secondary header is placed too early on the disk! Do you want to\n"
804 "correct this problem? ");
805 if (GetYN() == 'Y') {
806 MoveSecondHeaderToEnd();
807 printf("Have moved second header and partition table to correct location.\n");
808 } else {
809 printf("Have not corrected the problem. Strange problems may occur in the future!\n");
810 } // if correction requested
811 } // if
srs5694e7b4ff92009-08-18 13:16:10 -0400812
813 // Check for overlapping partitions....
srs5694e4ac11e2009-08-31 10:13:04 -0400814 if (FindOverlaps() > 0) {
815 allOK = 0;
816 fprintf(stderr, "Aborting write operation!\n");
817 } // if
818
819 // Check for mismatched MBR and GPT data, but let it pass if found
820 // (function displays warning message)
821 FindHybridMismatches();
srs5694e7b4ff92009-08-18 13:16:10 -0400822
srs56942a9f5da2009-08-26 00:48:01 -0400823 // Pull out some data that's needed before doing byte-order reversal on
824 // big-endian systems....
825 numParts = mainHeader.numParts;
826 secondTable = secondHeader.partitionEntriesLBA;
827 if (IsLittleEndian() == 0) {
828 // Reverse partition bytes first, since that function requires non-reversed
829 // data from the main header....
830 ReversePartitionBytes();
831 ReverseHeaderBytes(&mainHeader);
832 ReverseHeaderBytes(&secondHeader);
833 } // if
srs5694e7b4ff92009-08-18 13:16:10 -0400834 RecomputeCRCs();
835
836 if (allOK) {
837 printf("\nFinal checks complete. About to write GPT data. THIS WILL OVERWRITE EXISTING\n");
838 printf("MBR PARTITIONS!! THIS PROGRAM IS BETA QUALITY AT BEST. IF YOU LOSE ALL YOUR\n");
839 printf("DATA, YOU HAVE ONLY YOURSELF TO BLAME IF YOU ANSWER 'Y' BELOW!\n\n");
srs56945d58fe02010-01-03 20:57:08 -0500840 printf("Do you want to proceed, possibly destroying your data? ");
841 answer = GetYN();
842 if (answer == 'Y') {
843 printf("OK; writing new GUID partition table (GPT).\n");
srs5694e7b4ff92009-08-18 13:16:10 -0400844 } else {
845 allOK = 0;
846 } // if/else
847 } // if
848
849 // Do it!
850 if (allOK) {
srs5694e4ac11e2009-08-31 10:13:04 -0400851 fd = OpenForWrite(device);
srs5694e7b4ff92009-08-18 13:16:10 -0400852 if (fd != -1) {
853 // First, write the protective MBR...
srs5694e4ac11e2009-08-31 10:13:04 -0400854 protectiveMBR.WriteMBRData(fd);
srs5694e7b4ff92009-08-18 13:16:10 -0400855
856 // Now write the main GPT header...
srs56941e093722010-01-05 00:14:19 -0500857 if (allOK) {
858 if (lseek64(fd, blockSize, SEEK_SET) != (off_t) -1) {
859 if (myWrite(fd, (char*) &mainHeader, 512) == -1)
860 allOK = 0;
861 } else allOK = 0; // if (lseek64()...)
862 } // if (allOK)
srs5694e7b4ff92009-08-18 13:16:10 -0400863
864 // Now write the main partition tables...
srs5694e4ac11e2009-08-31 10:13:04 -0400865 if (allOK) {
srs56941e093722010-01-05 00:14:19 -0500866 offset = mainHeader.partitionEntriesLBA * blockSize;
867 if (lseek64(fd, offset, SEEK_SET) != (off_t) - 1) {
868 if (myWrite(fd, (char*) partitions, GPT_SIZE * numParts) == -1)
srs5694e7b4ff92009-08-18 13:16:10 -0400869 allOK = 0;
srs56941e093722010-01-05 00:14:19 -0500870 } else allOK = 0; // if (lseek64()...)
871 } // if (allOK)
srs5694e7b4ff92009-08-18 13:16:10 -0400872
873 // Now seek to near the end to write the secondary GPT....
srs5694e4ac11e2009-08-31 10:13:04 -0400874 if (allOK) {
srs5694e7b4ff92009-08-18 13:16:10 -0400875 offset = (off_t) secondTable * (off_t) (blockSize);
876 if (lseek64(fd, offset, SEEK_SET) == (off_t) - 1) {
877 allOK = 0;
878 printf("Unable to seek to end of disk!\n");
879 } // if
880 } // if
881
882 // Now write the secondary partition tables....
srs56941e093722010-01-05 00:14:19 -0500883 if (allOK) {
884 if (myWrite(fd, (char*) partitions, GPT_SIZE * numParts) == -1)
srs5694e7b4ff92009-08-18 13:16:10 -0400885 allOK = 0;
srs56941e093722010-01-05 00:14:19 -0500886 } // if (allOK)
srs5694e7b4ff92009-08-18 13:16:10 -0400887
888 // Now write the secondary GPT header...
srs56941e093722010-01-05 00:14:19 -0500889 if (allOK) {
890 offset = mainHeader.backupLBA * blockSize;
891 if (lseek64(fd, offset, SEEK_SET) != (off_t) - 1) {
892 if (myWrite(fd, (char*) &secondHeader, 512) == -1)
893 allOK = 0;
894 } else allOK = 0; // if (lseek64()...)
895 } // if (allOK)
srs5694e7b4ff92009-08-18 13:16:10 -0400896
897 // re-read the partition table
898 if (allOK) {
srs5694e35eb1b2009-09-14 00:29:34 -0400899 DiskSync(fd);
srs5694e7b4ff92009-08-18 13:16:10 -0400900 } // if
901
902 if (allOK) { // writes completed OK
srs5694e4ac11e2009-08-31 10:13:04 -0400903 printf("The operation has completed successfully.\n");
srs5694e7b4ff92009-08-18 13:16:10 -0400904 } else {
srs5694e4ac11e2009-08-31 10:13:04 -0400905 printf("Warning! An error was reported when writing the partition table! This error\n");
906 printf("MIGHT be harmless, but you may have trashed the disk! Use parted and, if\n");
907 printf("necessary, restore your original partition table.\n");
srs5694e7b4ff92009-08-18 13:16:10 -0400908 } // if/else
srs5694e4ac11e2009-08-31 10:13:04 -0400909 close(fd);
srs5694e7b4ff92009-08-18 13:16:10 -0400910 } else {
srs5694e4ac11e2009-08-31 10:13:04 -0400911 fprintf(stderr, "Unable to open device %s for writing! Errno is %d! Aborting write!\n",
912 device, errno);
913 allOK = 0;
srs5694e7b4ff92009-08-18 13:16:10 -0400914 } // if/else
915 } else {
916 printf("Aborting write of new partition table.\n");
917 } // if
918
srs56942a9f5da2009-08-26 00:48:01 -0400919 if (IsLittleEndian() == 0) {
920 // Reverse (normalize) header bytes first, since ReversePartitionBytes()
921 // requires non-reversed data in mainHeader...
922 ReverseHeaderBytes(&mainHeader);
923 ReverseHeaderBytes(&secondHeader);
924 ReversePartitionBytes();
925 } // if
926
srs5694e7b4ff92009-08-18 13:16:10 -0400927 return (allOK);
928} // GPTData::SaveGPTData()
929
930// Save GPT data to a backup file. This function does much less error
931// checking than SaveGPTData(). It can therefore preserve many types of
932// corruption for later analysis; however, it preserves only the MBR,
933// the main GPT header, the backup GPT header, and the main partition
934// table; it discards the backup partition table, since it should be
935// identical to the main partition table on healthy disks.
936int GPTData::SaveGPTBackup(char* filename) {
srs56942a9f5da2009-08-26 00:48:01 -0400937 int fd, allOK = 1;
938 uint32_t numParts;
srs5694e7b4ff92009-08-18 13:16:10 -0400939
940 if ((fd = open(filename, O_WRONLY | O_CREAT, S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH)) != -1) {
srs56942a9f5da2009-08-26 00:48:01 -0400941 // Reverse the byte order, if necessary....
942 numParts = mainHeader.numParts;
943 if (IsLittleEndian() == 0) {
944 ReversePartitionBytes();
945 ReverseHeaderBytes(&mainHeader);
946 ReverseHeaderBytes(&secondHeader);
947 } // if
948
srs5694978041c2009-09-21 20:51:47 -0400949 // Recomputing the CRCs is likely to alter them, which could be bad
950 // if the intent is to save a potentially bad GPT for later analysis;
951 // but if we don't do this, we get bogus errors when we load the
952 // backup. I'm favoring misses over false alarms....
953 RecomputeCRCs();
954
srs56942a9f5da2009-08-26 00:48:01 -0400955 // Now write the protective MBR...
srs5694e7b4ff92009-08-18 13:16:10 -0400956 protectiveMBR.WriteMBRData(fd);
957
958 // Now write the main GPT header...
959 if (allOK)
srs56941e093722010-01-05 00:14:19 -0500960 if (myWrite(fd, (char*) &mainHeader, 512) == -1)
srs5694e7b4ff92009-08-18 13:16:10 -0400961 allOK = 0;
962
963 // Now write the secondary GPT header...
964 if (allOK)
srs56941e093722010-01-05 00:14:19 -0500965 if (myWrite(fd, (char*) &secondHeader, 512) == -1)
srs5694e4ac11e2009-08-31 10:13:04 -0400966 allOK = 0;
srs5694e7b4ff92009-08-18 13:16:10 -0400967
968 // Now write the main partition tables...
969 if (allOK) {
srs56941e093722010-01-05 00:14:19 -0500970 if (myWrite(fd, (char*) partitions, GPT_SIZE * numParts) == -1)
srs5694e7b4ff92009-08-18 13:16:10 -0400971 allOK = 0;
972 } // if
973
974 if (allOK) { // writes completed OK
975 printf("The operation has completed successfully.\n");
976 } else {
977 printf("Warning! An error was reported when writing the backup file.\n");
srs56948bb78762009-11-24 15:43:49 -0500978 printf("It may not be usable!\n");
srs5694e7b4ff92009-08-18 13:16:10 -0400979 } // if/else
980 close(fd);
srs56942a9f5da2009-08-26 00:48:01 -0400981
982 // Now reverse the byte-order reversal, if necessary....
983 if (IsLittleEndian() == 0) {
984 ReverseHeaderBytes(&mainHeader);
985 ReverseHeaderBytes(&secondHeader);
986 ReversePartitionBytes();
987 } // if
srs5694e7b4ff92009-08-18 13:16:10 -0400988 } else {
989 fprintf(stderr, "Unable to open file %s for writing! Aborting!\n", filename);
990 allOK = 0;
991 } // if/else
992 return allOK;
993} // GPTData::SaveGPTBackup()
994
995// Load GPT data from a backup file created by SaveGPTBackup(). This function
996// does minimal error checking. It returns 1 if it completed successfully,
997// 0 if there was a problem. In the latter case, it creates a new empty
998// set of partitions.
999int GPTData::LoadGPTBackup(char* filename) {
1000 int fd, allOK = 1, val;
1001 uint32_t numParts, sizeOfEntries, sizeOfParts, newCRC;
srs56942a9f5da2009-08-26 00:48:01 -04001002 int littleEndian = 1;
srs5694e7b4ff92009-08-18 13:16:10 -04001003
1004 if ((fd = open(filename, O_RDONLY)) != -1) {
srs56942a9f5da2009-08-26 00:48:01 -04001005 if (IsLittleEndian() == 0)
1006 littleEndian = 0;
1007
srs5694e7b4ff92009-08-18 13:16:10 -04001008 // Let the MBRData class load the saved MBR...
srs5694221e0872009-08-29 15:00:31 -04001009 protectiveMBR.ReadMBRData(fd, 0); // 0 = don't check block size
srs5694e7b4ff92009-08-18 13:16:10 -04001010
1011 // Load the main GPT header, check its vaility, and set the GPT
1012 // size based on the data
srs56941e093722010-01-05 00:14:19 -05001013 if (myRead(fd, (char*) &mainHeader, 512)) {
srs56945d58fe02010-01-03 20:57:08 -05001014 fprintf(stderr, "Warning! Read error %d; strange behavior now likely!\n", errno);
1015 } // if
srs5694e7b4ff92009-08-18 13:16:10 -04001016 mainCrcOk = CheckHeaderCRC(&mainHeader);
1017
srs56942a9f5da2009-08-26 00:48:01 -04001018 // Reverse byte order, if necessary
1019 if (littleEndian == 0) {
1020 ReverseHeaderBytes(&mainHeader);
1021 } // if
1022
srs5694e7b4ff92009-08-18 13:16:10 -04001023 // Load the backup GPT header in much the same way as the main
1024 // GPT header....
srs56941e093722010-01-05 00:14:19 -05001025 if (myRead(fd, (char*) &secondHeader, 512) != 512) {
srs56945d58fe02010-01-03 20:57:08 -05001026 fprintf(stderr, "Warning! Read error %d; strange behavior now likely!\n", errno);
1027 } // if
srs5694e7b4ff92009-08-18 13:16:10 -04001028 secondCrcOk = CheckHeaderCRC(&secondHeader);
1029
srs56942a9f5da2009-08-26 00:48:01 -04001030 // Reverse byte order, if necessary
1031 if (littleEndian == 0) {
1032 ReverseHeaderBytes(&secondHeader);
1033 } // if
1034
srs5694e7b4ff92009-08-18 13:16:10 -04001035 // Return valid headers code: 0 = both headers bad; 1 = main header
1036 // good, backup bad; 2 = backup header good, main header bad;
1037 // 3 = both headers good. Note these codes refer to valid GPT
1038 // signatures and version numbers; more subtle problems will elude
1039 // this check!
1040 if ((val = CheckHeaderValidity()) > 0) {
1041 if (val == 2) { // only backup header seems to be good
1042 numParts = secondHeader.numParts;
srs5694e4ac11e2009-08-31 10:13:04 -04001043 sizeOfEntries = secondHeader.sizeOfPartitionEntries;
srs5694e7b4ff92009-08-18 13:16:10 -04001044 } else { // main header is OK
1045 numParts = mainHeader.numParts;
1046 sizeOfEntries = mainHeader.sizeOfPartitionEntries;
1047 } // if/else
1048
1049 SetGPTSize(numParts);
1050
1051 // If current disk size doesn't match that of backup....
1052 if (secondHeader.currentLBA != diskSize - UINT64_C(1)) {
1053 printf("Warning! Current disk size doesn't match that of the backup!\n"
srs5694e4ac11e2009-08-31 10:13:04 -04001054 "Adjusting sizes to match, but subsequent problems are possible!\n");
srs5694247657a2009-11-26 18:36:12 -05001055 MoveSecondHeaderToEnd();
srs5694e7b4ff92009-08-18 13:16:10 -04001056 } // if
1057
1058 // Load main partition table, and record whether its CRC
1059 // matches the stored value
1060 sizeOfParts = numParts * sizeOfEntries;
srs56941e093722010-01-05 00:14:19 -05001061 if (myRead(fd, (char*) partitions, sizeOfParts) != sizeOfParts) {
srs56945d58fe02010-01-03 20:57:08 -05001062 fprintf(stderr, "Warning! Read error %d; strange behavior now likely!\n", errno);
1063 } // if
srs5694e7b4ff92009-08-18 13:16:10 -04001064
1065 newCRC = chksum_crc32((unsigned char*) partitions, sizeOfParts);
1066 mainPartsCrcOk = (newCRC == mainHeader.partitionEntriesCRC);
1067 secondPartsCrcOk = (newCRC == secondHeader.partitionEntriesCRC);
srs56942a9f5da2009-08-26 00:48:01 -04001068 // Reverse byte order, if necessary
srs5694e4ac11e2009-08-31 10:13:04 -04001069 if (littleEndian == 0) {
1070 ReversePartitionBytes();
1071 } // if
srs56942a9f5da2009-08-26 00:48:01 -04001072
srs5694e7b4ff92009-08-18 13:16:10 -04001073 } else {
1074 allOK = 0;
1075 } // if/else
1076 } else {
1077 allOK = 0;
1078 fprintf(stderr, "Unable to open file %s for reading! Aborting!\n", filename);
1079 } // if/else
1080
1081 // Something went badly wrong, so blank out partitions
1082 if (allOK == 0) {
1083 ClearGPTData();
1084 protectiveMBR.MakeProtectiveMBR();
1085 } // if
1086 return allOK;
1087} // GPTData::LoadGPTBackup()
1088
srs5694e4ac11e2009-08-31 10:13:04 -04001089// Tell user whether Apple Partition Map (APM) was discovered....
1090void GPTData::ShowAPMState(void) {
1091 if (apmFound)
1092 printf(" APM: present\n");
1093 else
1094 printf(" APM: not present\n");
1095} // GPTData::ShowAPMState()
1096
1097// Tell user about the state of the GPT data....
1098void GPTData::ShowGPTState(void) {
1099 switch (state) {
1100 case gpt_invalid:
1101 printf(" GPT: not present\n");
1102 break;
1103 case gpt_valid:
1104 printf(" GPT: present\n");
1105 break;
1106 case gpt_corrupt:
1107 printf(" GPT: damaged\n");
1108 break;
1109 default:
1110 printf("\a GPT: unknown -- bug!\n");
1111 break;
1112 } // switch
1113} // GPTData::ShowGPTState()
1114
1115// Display the basic GPT data
1116void GPTData::DisplayGPTData(void) {
srs5694e35eb1b2009-09-14 00:29:34 -04001117 int i;
srs5694e4ac11e2009-08-31 10:13:04 -04001118 char sizeInSI[255]; // String to hold size of disk in SI units
1119 char tempStr[255];
1120 uint64_t temp, totalFree;
1121
1122 BytesToSI(diskSize * blockSize, sizeInSI);
srs5694978041c2009-09-21 20:51:47 -04001123 printf("Disk %s: %llu sectors, %s\n", device,
1124 (unsigned long long) diskSize, sizeInSI);
srs56941e093722010-01-05 00:14:19 -05001125 printf("Logical sector size: %d bytes\n", blockSize);
srs5694e4ac11e2009-08-31 10:13:04 -04001126 printf("Disk identifier (GUID): %s\n", GUIDToStr(mainHeader.diskGUID, tempStr));
1127 printf("Partition table holds up to %lu entries\n", (unsigned long) mainHeader.numParts);
srs56945d58fe02010-01-03 20:57:08 -05001128 printf("First usable sector is %llu, last usable sector is %llu\n",
1129 (unsigned long long) mainHeader.firstUsableLBA,
1130 (unsigned long long) mainHeader.lastUsableLBA);
srs5694e4ac11e2009-08-31 10:13:04 -04001131 totalFree = FindFreeBlocks(&i, &temp);
srs56945d58fe02010-01-03 20:57:08 -05001132 printf("Total free space is %llu sectors (%s)\n", (unsigned long long) totalFree,
srs5694e4ac11e2009-08-31 10:13:04 -04001133 BytesToSI(totalFree * (uint64_t) blockSize, sizeInSI));
1134 printf("\nNumber Start (sector) End (sector) Size Code Name\n");
1135 for (i = 0; i < mainHeader.numParts; i++) {
srs5694978041c2009-09-21 20:51:47 -04001136 partitions[i].ShowSummary(i, blockSize);
srs5694e4ac11e2009-08-31 10:13:04 -04001137 } // for
1138} // GPTData::DisplayGPTData()
1139
1140// Get partition number from user and then call ShowPartDetails(partNum)
1141// to show its detailed information
1142void GPTData::ShowDetails(void) {
1143 int partNum;
1144 uint32_t low, high;
1145
1146 if (GetPartRange(&low, &high) > 0) {
1147 partNum = GetPartNum();
1148 ShowPartDetails(partNum);
1149 } else {
1150 printf("No partitions\n");
1151 } // if/else
1152} // GPTData::ShowDetails()
1153
1154// Show detailed information on the specified partition
1155void GPTData::ShowPartDetails(uint32_t partNum) {
1156 if (partitions[partNum].GetFirstLBA() != 0) {
1157 partitions[partNum].ShowDetails(blockSize);
1158 } else {
1159 printf("Partition #%d does not exist.", (int) (partNum + 1));
1160 } // if
1161} // GPTData::ShowPartDetails()
1162
1163/*********************************************************************
1164 * *
1165 * Begin functions that obtain information from the users, and often *
1166 * do something with that information (call other functions) *
1167 * *
1168 *********************************************************************/
1169
1170// Prompts user for partition number and returns the result.
1171uint32_t GPTData::GetPartNum(void) {
1172 uint32_t partNum;
1173 uint32_t low, high;
1174 char prompt[255];
1175
1176 if (GetPartRange(&low, &high) > 0) {
1177 sprintf(prompt, "Partition number (%d-%d): ", low + 1, high + 1);
1178 partNum = GetNumber(low + 1, high + 1, low, prompt);
1179 } else partNum = 1;
1180 return (partNum - 1);
1181} // GPTData::GetPartNum()
1182
1183// What it says: Resize the partition table. (Default is 128 entries.)
1184void GPTData::ResizePartitionTable(void) {
1185 int newSize;
1186 char prompt[255];
1187 uint32_t curLow, curHigh;
1188
1189 printf("Current partition table size is %lu.\n",
1190 (unsigned long) mainHeader.numParts);
1191 GetPartRange(&curLow, &curHigh);
1192 curHigh++; // since GetPartRange() returns numbers starting from 0...
1193 // There's no point in having fewer than four partitions....
1194 if (curHigh < 4)
1195 curHigh = 4;
1196 sprintf(prompt, "Enter new size (%d up, default %d): ", (int) curHigh,
1197 (int) NUM_GPT_ENTRIES);
1198 newSize = GetNumber(4, 65535, 128, prompt);
1199 if (newSize < 128) {
1200 printf("Caution: The partition table size should officially be 16KB or larger,\n"
1201 "which works out to 128 entries. In practice, smaller tables seem to\n"
1202 "work with most OSes, but this practice is risky. I'm proceeding with\n"
1203 "the resize, but you may want to reconsider this action and undo it.\n\n");
1204 } // if
1205 SetGPTSize(newSize);
1206} // GPTData::ResizePartitionTable()
1207
1208// Interactively create a partition
1209void GPTData::CreatePartition(void) {
1210 uint64_t firstBlock, firstInLargest, lastBlock, sector;
1211 char prompt[255];
1212 int partNum, firstFreePart = 0;
1213
1214 // Find first free partition...
1215 while (partitions[firstFreePart].GetFirstLBA() != 0) {
1216 firstFreePart++;
1217 } // while
1218
1219 if (((firstBlock = FindFirstAvailable()) != 0) &&
1220 (firstFreePart < mainHeader.numParts)) {
1221 lastBlock = FindLastAvailable(firstBlock);
1222 firstInLargest = FindFirstInLargest();
1223
1224 // Get partition number....
1225 do {
1226 sprintf(prompt, "Partition number (%d-%d, default %d): ", firstFreePart + 1,
1227 mainHeader.numParts, firstFreePart + 1);
1228 partNum = GetNumber(firstFreePart + 1, mainHeader.numParts,
1229 firstFreePart + 1, prompt) - 1;
1230 if (partitions[partNum].GetFirstLBA() != 0)
1231 printf("partition %d is in use.\n", partNum + 1);
1232 } while (partitions[partNum].GetFirstLBA() != 0);
1233
1234 // Get first block for new partition...
srs56945d58fe02010-01-03 20:57:08 -05001235 sprintf(prompt, "First sector (%llu-%llu, default = %llu) or {+-}size{KMGT}: ",
1236 (unsigned long long) firstBlock, (unsigned long long) lastBlock,
1237 (unsigned long long) firstInLargest);
srs5694e4ac11e2009-08-31 10:13:04 -04001238 do {
1239 sector = GetSectorNum(firstBlock, lastBlock, firstInLargest, prompt);
1240 } while (IsFree(sector) == 0);
srs56941d1448a2009-12-31 21:20:19 -05001241 Align(&sector); // Align sector to correct multiple
srs5694e4ac11e2009-08-31 10:13:04 -04001242 firstBlock = sector;
1243
1244 // Get last block for new partitions...
1245 lastBlock = FindLastInFree(firstBlock);
srs56945d58fe02010-01-03 20:57:08 -05001246 sprintf(prompt, "Last sector (%llu-%llu, default = %llu) or {+-}size{KMGT}: ",
1247 (unsigned long long) firstBlock, (unsigned long long) lastBlock,
1248 (unsigned long long) lastBlock);
srs5694e4ac11e2009-08-31 10:13:04 -04001249 do {
1250 sector = GetSectorNum(firstBlock, lastBlock, lastBlock, prompt);
1251 } while (IsFree(sector) == 0);
1252 lastBlock = sector;
1253
1254 partitions[partNum].SetFirstLBA(firstBlock);
1255 partitions[partNum].SetLastLBA(lastBlock);
1256
1257 partitions[partNum].SetUniqueGUID(1);
1258 partitions[partNum].ChangeType();
1259 partitions[partNum].SetName((unsigned char*) partitions[partNum].GetNameType(prompt));
1260 } else {
1261 printf("No free sectors available\n");
1262 } // if/else
1263} // GPTData::CreatePartition()
1264
1265// Interactively delete a partition (duh!)
1266void GPTData::DeletePartition(void) {
1267 int partNum;
1268 uint32_t low, high;
1269 uint64_t startSector, length;
1270 char prompt[255];
1271
1272 if (GetPartRange(&low, &high) > 0) {
1273 sprintf(prompt, "Partition number (%d-%d): ", low + 1, high + 1);
1274 partNum = GetNumber(low + 1, high + 1, low, prompt);
1275
1276 // In case there's a protective MBR, look for & delete matching
1277 // MBR partition....
1278 startSector = partitions[partNum - 1].GetFirstLBA();
1279 length = partitions[partNum - 1].GetLengthLBA();
1280 protectiveMBR.DeleteByLocation(startSector, length);
1281
1282 // Now delete the GPT partition
1283 partitions[partNum - 1].BlankPartition();
1284 } else {
1285 printf("No partitions\n");
1286 } // if/else
1287} // GPTData::DeletePartition()
1288
1289// Prompt user for a partition number, then change its type code
1290// using ChangeGPTType(struct GPTPartition*) function.
1291void GPTData::ChangePartType(void) {
1292 int partNum;
1293 uint32_t low, high;
1294
1295 if (GetPartRange(&low, &high) > 0) {
1296 partNum = GetPartNum();
1297 partitions[partNum].ChangeType();
1298 } else {
1299 printf("No partitions\n");
1300 } // if/else
1301} // GPTData::ChangePartType()
1302
1303// Partition attributes seem to be rarely used, but I want a way to
1304// adjust them for completeness....
1305void GPTData::SetAttributes(uint32_t partNum) {
1306 Attributes theAttr;
1307
1308 theAttr.SetAttributes(partitions[partNum].GetAttributes());
1309 theAttr.DisplayAttributes();
1310 theAttr.ChangeAttributes();
1311 partitions[partNum].SetAttributes(theAttr.GetAttributes());
1312} // GPTData::SetAttributes()
1313
srs5694c0ca8f82009-08-20 21:35:25 -04001314// This function destroys the on-disk GPT structures. Returns 1 if the
1315// user confirms destruction, 0 if the user aborts.
srs5694978041c2009-09-21 20:51:47 -04001316// If prompt == 0, don't ask user about proceeding and do NOT wipe out
1317// MBR. (Set prompt == 0 when doing a GPT-to-MBR conversion.)
1318int GPTData::DestroyGPT(int prompt) {
srs56941e093722010-01-05 00:14:19 -05001319 int fd, i, sum, tableSize;
srs5694978041c2009-09-21 20:51:47 -04001320 char blankSector[512], goOn = 'Y', blank = 'N';
srs56941e093722010-01-05 00:14:19 -05001321 char* emptyTable;
srs5694c0ca8f82009-08-20 21:35:25 -04001322
1323 for (i = 0; i < 512; i++) {
1324 blankSector[i] = '\0';
1325 } // for
1326
srs5694978041c2009-09-21 20:51:47 -04001327 if (((apmFound) || (bsdFound)) && prompt) {
srs5694e35eb1b2009-09-14 00:29:34 -04001328 printf("WARNING: APM or BSD disklabel structures detected! This operation could\n"
1329 "damage any APM or BSD partitions on this disk!\n");
1330 } // if APM or BSD
srs5694978041c2009-09-21 20:51:47 -04001331 if (prompt) {
1332 printf("\a\aAbout to wipe out GPT on %s. Proceed? ", device);
1333 goOn = GetYN();
1334 } // if
srs5694c0ca8f82009-08-20 21:35:25 -04001335 if (goOn == 'Y') {
1336 fd = open(device, O_WRONLY);
1337#ifdef __APPLE__
1338 // MacOS X requires a shared lock under some circumstances....
1339 if (fd < 0) {
1340 fd = open(device, O_WRONLY|O_SHLOCK);
1341 } // if
1342#endif
1343 if (fd != -1) {
srs56941e093722010-01-05 00:14:19 -05001344 lseek64(fd, mainHeader.currentLBA * blockSize, SEEK_SET); // seek to GPT header
1345 if (myWrite(fd, blankSector, 512) != 512) { // blank it out
srs56945d58fe02010-01-03 20:57:08 -05001346 fprintf(stderr, "Warning! GPT main header not overwritten! Error is %d\n", errno);
1347 } // if
srs56941e093722010-01-05 00:14:19 -05001348 lseek64(fd, mainHeader.partitionEntriesLBA * blockSize, SEEK_SET); // seek to partition table
1349 tableSize = mainHeader.numParts * mainHeader.sizeOfPartitionEntries;
1350 emptyTable = (char*) malloc(tableSize);
1351 for (i = 0; i < tableSize; i++)
1352 emptyTable[i] = (char) 0;
1353 sum = myWrite(fd, emptyTable, tableSize);
1354 if (sum != tableSize)
srs56945d58fe02010-01-03 20:57:08 -05001355 fprintf(stderr, "Warning! GPT main partition table not overwritten! Error is %d\n", errno);
srs56941e093722010-01-05 00:14:19 -05001356 lseek64(fd, secondHeader.partitionEntriesLBA * blockSize, SEEK_SET); // seek to partition table
1357 sum = myWrite(fd, emptyTable, tableSize);
1358 if (sum != tableSize)
srs56945d58fe02010-01-03 20:57:08 -05001359 fprintf(stderr, "Warning! GPT backup partition table not overwritten! Error is %d\n", errno);
srs56941e093722010-01-05 00:14:19 -05001360 lseek64(fd, secondHeader.currentLBA * blockSize, SEEK_SET); // seek to GPT header
1361 if (myWrite(fd, blankSector, 512) != 512) { // blank it out
srs56945d58fe02010-01-03 20:57:08 -05001362 fprintf(stderr, "Warning! GPT backup header not overwritten! Error is %d\n", errno);
1363 } // if
srs5694978041c2009-09-21 20:51:47 -04001364 if (prompt) {
1365 printf("Blank out MBR? ");
1366 blank = GetYN();
1367 }// if
1368 // Note on below: Touch the MBR only if the user wants it completely
1369 // blanked out. Version 0.4.2 deleted the 0xEE partition and re-wrote
1370 // the MBR, but this could wipe out a valid MBR that the program
1371 // had subsequently discarded (say, if it conflicted with older GPT
1372 // structures).
1373 if (blank == 'Y') {
srs5694c0ca8f82009-08-20 21:35:25 -04001374 lseek64(fd, 0, SEEK_SET);
srs56941e093722010-01-05 00:14:19 -05001375 if (myWrite(fd, blankSector, 512) != 512) { // blank it out
srs56945d58fe02010-01-03 20:57:08 -05001376 fprintf(stderr, "Warning! MBR not overwritten! Error is %d!\n", errno);
1377 } // if
srs5694978041c2009-09-21 20:51:47 -04001378 } else {
1379 printf("MBR is unchanged. You may need to delete an EFI GPT (0xEE) partition\n"
1380 "with fdisk or another tool.\n");
srs5694e35eb1b2009-09-14 00:29:34 -04001381 } // if/else
1382 DiskSync(fd);
srs5694c0ca8f82009-08-20 21:35:25 -04001383 close(fd);
srs5694e4ac11e2009-08-31 10:13:04 -04001384 printf("GPT data structures destroyed! You may now partition the disk using fdisk or\n"
1385 "other utilities. Program will now terminate.\n");
srs5694c0ca8f82009-08-20 21:35:25 -04001386 } else {
srs56941d1448a2009-12-31 21:20:19 -05001387 printf("Problem opening %s for writing! Program will now terminate.\n", device);
srs5694c0ca8f82009-08-20 21:35:25 -04001388 } // if/else (fd != -1)
1389 } // if (goOn == 'Y')
1390 return (goOn == 'Y');
1391} // GPTData::DestroyGPT()
1392
srs5694e4ac11e2009-08-31 10:13:04 -04001393/**************************************************************************
1394 * *
1395 * Partition table transformation functions (MBR or BSD disklabel to GPT) *
1396 * (some of these functions may require user interaction) *
1397 * *
1398 **************************************************************************/
1399
1400// Examines the MBR & GPT data, and perhaps asks the user questions, to
1401// determine which set of data to use: the MBR (use_mbr), the GPT (use_gpt),
1402// or create a new set of partitions (use_new)
1403WhichToUse GPTData::UseWhichPartitions(void) {
1404 WhichToUse which = use_new;
1405 MBRValidity mbrState;
1406 int answer;
1407
1408 mbrState = protectiveMBR.GetValidity();
1409
1410 if ((state == gpt_invalid) && ((mbrState == mbr) || (mbrState == hybrid))) {
srs56945d58fe02010-01-03 20:57:08 -05001411 printf("\n***************************************************************\n"
1412 "Found invalid GPT and valid MBR; converting MBR to GPT format.\n");
1413 if (!justLooking) {
1414 printf("\aTHIS OPERATON IS POTENTIALLY DESTRUCTIVE! Exit by typing 'q' if\n"
1415 "you don't want to convert your MBR partitions to GPT format!\n");
1416 } // if
1417 printf("***************************************************************\n\n");
srs5694e4ac11e2009-08-31 10:13:04 -04001418 which = use_mbr;
1419 } // if
1420
1421 if ((state == gpt_invalid) && bsdFound) {
srs56945d58fe02010-01-03 20:57:08 -05001422 printf("\n**********************************************************************\n"
srs5694e4ac11e2009-08-31 10:13:04 -04001423 "Found invalid GPT and valid BSD disklabel; converting BSD disklabel\n"
srs56945d58fe02010-01-03 20:57:08 -05001424 "to GPT format.");
1425 if (!justLooking) {
1426 printf("\a THIS OPERATON IS POTENTIALLY DESTRUCTIVE! Your first\n"
srs5694e4ac11e2009-08-31 10:13:04 -04001427 "BSD partition will likely be unusable. Exit by typing 'q' if you don't\n"
srs56945d58fe02010-01-03 20:57:08 -05001428 "want to convert your BSD partitions to GPT format!");
1429 } // if
1430 printf("\n**********************************************************************\n\n");
srs5694e4ac11e2009-08-31 10:13:04 -04001431 which = use_bsd;
1432 } // if
1433
1434 if ((state == gpt_valid) && (mbrState == gpt)) {
1435 printf("Found valid GPT with protective MBR; using GPT.\n");
1436 which = use_gpt;
1437 } // if
1438 if ((state == gpt_valid) && (mbrState == hybrid)) {
1439 printf("Found valid GPT with hybrid MBR; using GPT.\n");
1440 which = use_gpt;
1441 } // if
1442 if ((state == gpt_valid) && (mbrState == invalid)) {
1443 printf("\aFound valid GPT with corrupt MBR; using GPT and will create new\nprotective MBR on save.\n");
1444 which = use_gpt;
1445 protectiveMBR.MakeProtectiveMBR();
1446 } // if
1447 if ((state == gpt_valid) && (mbrState == mbr)) {
1448 printf("Found valid MBR and GPT. Which do you want to use?\n");
1449 answer = GetNumber(1, 3, 2, (char*) " 1 - MBR\n 2 - GPT\n 3 - Create blank GPT\n\nYour answer: ");
1450 if (answer == 1) {
1451 which = use_mbr;
1452 } else if (answer == 2) {
1453 which = use_gpt;
1454 protectiveMBR.MakeProtectiveMBR();
1455 printf("Using GPT and creating fresh protective MBR.\n");
1456 } else which = use_new;
1457 } // if
1458
1459 // Nasty decisions here -- GPT is present, but corrupt (bad CRCs or other
1460 // problems)
1461 if (state == gpt_corrupt) {
1462 if ((mbrState == mbr) || (mbrState == hybrid)) {
1463 printf("Found valid MBR and corrupt GPT. Which do you want to use? (Using the\n"
1464 "GPT MAY permit recovery of GPT data.)\n");
1465 answer = GetNumber(1, 3, 2, (char*) " 1 - MBR\n 2 - GPT\n 3 - Create blank GPT\n\nYour answer: ");
1466 if (answer == 1) {
1467 which = use_mbr;
1468// protectiveMBR.MakeProtectiveMBR();
1469 } else if (answer == 2) {
1470 which = use_gpt;
1471 } else which = use_new;
1472 } else if (mbrState == invalid) {
1473 printf("Found invalid MBR and corrupt GPT. What do you want to do? (Using the\n"
1474 "GPT MAY permit recovery of GPT data.)\n");
1475 answer = GetNumber(1, 2, 1, (char*) " 1 - GPT\n 2 - Create blank GPT\n\nYour answer: ");
1476 if (answer == 1) {
1477 which = use_gpt;
1478 } else which = use_new;
1479 } else { // corrupt GPT, MBR indicates it's a GPT disk....
1480 printf("\a\a****************************************************************************\n"
1481 "Caution: Found protective or hybrid MBR and corrupt GPT. Using GPT, but disk\n"
1482 "verification and recovery are STRONGLY recommended.\n"
1483 "****************************************************************************\n");
srs5694247657a2009-11-26 18:36:12 -05001484 which = use_gpt;
srs5694e4ac11e2009-08-31 10:13:04 -04001485 } // if/else/else
1486 } // if (corrupt GPT)
1487
1488 if (which == use_new)
1489 printf("Creating new GPT entries.\n");
1490
1491 return which;
1492} // UseWhichPartitions()
1493
1494// Convert MBR partition table into GPT form
1495int GPTData::XFormPartitions(void) {
1496 int i, numToConvert;
1497 uint8_t origType;
1498 struct newGUID;
srs5694e4ac11e2009-08-31 10:13:04 -04001499
1500 // Clear out old data & prepare basics....
1501 ClearGPTData();
1502
1503 // Convert the smaller of the # of GPT or MBR partitions
srs5694978041c2009-09-21 20:51:47 -04001504 if (mainHeader.numParts > (MAX_MBR_PARTS))
1505 numToConvert = MAX_MBR_PARTS;
srs5694e4ac11e2009-08-31 10:13:04 -04001506 else
1507 numToConvert = mainHeader.numParts;
1508
1509 for (i = 0; i < numToConvert; i++) {
1510 origType = protectiveMBR.GetType(i);
1511 // don't waste CPU time trying to convert extended, hybrid protective, or
1512 // null (non-existent) partitions
srs5694e35eb1b2009-09-14 00:29:34 -04001513 if ((origType != 0x05) && (origType != 0x0f) && (origType != 0x85) &&
srs5694e4ac11e2009-08-31 10:13:04 -04001514 (origType != 0x00) && (origType != 0xEE))
1515 partitions[i] = protectiveMBR.AsGPT(i);
1516 } // for
1517
1518 // Convert MBR into protective MBR
1519 protectiveMBR.MakeProtectiveMBR();
1520
1521 // Record that all original CRCs were OK so as not to raise flags
1522 // when doing a disk verification
1523 mainCrcOk = secondCrcOk = mainPartsCrcOk = secondPartsCrcOk = 1;
1524
1525 return (1);
1526} // GPTData::XFormPartitions()
1527
1528// Transforms BSD disklabel on the specified partition (numbered from 0).
1529// If an invalid partition number is given, the program prompts for one.
1530// Returns the number of new partitions created.
1531int GPTData::XFormDisklabel(int i) {
1532 uint32_t low, high, partNum, startPart;
1533 uint16_t hexCode;
1534 int goOn = 1, numDone = 0;
1535 BSDData disklabel;
1536
1537 if (GetPartRange(&low, &high) != 0) {
1538 if ((i < low) || (i > high))
1539 partNum = GetPartNum();
1540 else
1541 partNum = (uint32_t) i;
1542
1543 // Find the partition after the last used one
1544 startPart = high + 1;
1545
1546 // Now see if the specified partition has a BSD type code....
1547 hexCode = partitions[partNum].GetHexType();
1548 if ((hexCode != 0xa500) && (hexCode != 0xa900)) {
1549 printf("Specified partition doesn't have a disklabel partition type "
1550 "code.\nContinue anyway?");
1551 goOn = (GetYN() == 'Y');
1552 } // if
1553
1554 // If all is OK, read the disklabel and convert it.
1555 if (goOn) {
1556 goOn = disklabel.ReadBSDData(device, partitions[partNum].GetFirstLBA(),
1557 partitions[partNum].GetLastLBA());
1558 if ((goOn) && (disklabel.IsDisklabel())) {
1559 numDone = XFormDisklabel(&disklabel, startPart);
1560 if (numDone == 1)
1561 printf("Converted %d BSD partition.\n", numDone);
1562 else
1563 printf("Converted %d BSD partitions.\n", numDone);
1564 } else {
1565 printf("Unable to convert partitions! Unrecognized BSD disklabel.\n");
1566 } // if/else
1567 } // if
1568 if (numDone > 0) { // converted partitions; delete carrier
1569 partitions[partNum].BlankPartition();
1570 } // if
1571 } else {
1572 printf("No partitions\n");
1573 } // if/else
1574 return numDone;
1575} // GPTData::XFormDisklable(int i)
1576
1577// Transform the partitions on an already-loaded BSD disklabel...
1578int GPTData::XFormDisklabel(BSDData* disklabel, int startPart) {
1579 int i, numDone = 0;
1580
1581 if ((disklabel->IsDisklabel()) && (startPart >= 0) &&
1582 (startPart < mainHeader.numParts)) {
1583 for (i = 0; i < disklabel->GetNumParts(); i++) {
1584 partitions[i + startPart] = disklabel->AsGPT(i);
1585 if (partitions[i + startPart].GetFirstLBA() != UINT64_C(0))
1586 numDone++;
1587 } // for
1588 } // if
1589
1590 // Record that all original CRCs were OK so as not to raise flags
1591 // when doing a disk verification
1592 mainCrcOk = secondCrcOk = mainPartsCrcOk = secondPartsCrcOk = 1;
1593
1594 return numDone;
1595} // GPTData::XFormDisklabel(BSDData* disklabel)
1596
srs5694978041c2009-09-21 20:51:47 -04001597// Add one GPT partition to MBR. Used by XFormToMBR() and MakeHybrid()
1598// functions. Returns 1 if operation was successful.
1599int GPTData::OnePartToMBR(uint32_t gptPart, int mbrPart) {
1600 int allOK = 1, typeCode, bootable;
1601 uint64_t length;
1602 char line[255];
srs56945d58fe02010-01-03 20:57:08 -05001603 char* junk;
srs5694978041c2009-09-21 20:51:47 -04001604
1605 if ((mbrPart < 0) || (mbrPart > 3)) {
1606 printf("MBR partition %d is out of range; omitting it.\n", mbrPart + 1);
1607 allOK = 0;
1608 } // if
1609 if (gptPart >= mainHeader.numParts) {
1610 printf("GPT partition %d is out of range; omitting it.\n", gptPart + 1);
1611 allOK = 0;
1612 } // if
1613 if (allOK && (partitions[gptPart].GetLastLBA() == UINT64_C(0))) {
1614 printf("GPT partition %d is undefined; omitting it.\n", gptPart + 1);
1615 allOK = 0;
1616 } // if
1617 if (allOK && (partitions[gptPart].GetFirstLBA() <= UINT32_MAX) &&
1618 (partitions[gptPart].GetLengthLBA() <= UINT32_MAX)) {
1619 if (partitions[gptPart].GetLastLBA() > UINT32_MAX) {
1620 printf("Caution: Partition end point past 32-bit pointer boundary;"
1621 " some OSes may\nreact strangely.\n");
1622 } // if partition ends past 32-bit (usually 2TiB) boundary
1623 do {
1624 printf("Enter an MBR hex code (default %02X): ",
1625 typeHelper.GUIDToID(partitions[gptPart].GetType()) / 256);
srs56945d58fe02010-01-03 20:57:08 -05001626 junk = fgets(line, 255, stdin);
srs5694978041c2009-09-21 20:51:47 -04001627 sscanf(line, "%x", &typeCode);
1628 if (line[0] == '\n')
1629 typeCode = partitions[gptPart].GetHexType() / 256;
1630 } while ((typeCode <= 0) || (typeCode > 255));
1631 printf("Set the bootable flag? ");
1632 bootable = (GetYN() == 'Y');
1633 length = partitions[gptPart].GetLengthLBA();
1634 protectiveMBR.MakePart(mbrPart, (uint32_t) partitions[gptPart].GetFirstLBA(),
1635 (uint32_t) length, typeCode, bootable);
1636 } else { // partition out of range
1637 printf("Partition %d begins beyond the 32-bit pointer limit of MBR "
1638 "partitions, or is\n too big; omitting it.\n", gptPart + 1);
1639 allOK = 0;
1640 } // if/else
1641 return allOK;
1642} // GPTData::OnePartToMBR()
1643
1644// Convert the GPT to MBR form. This function is necessarily limited; it
1645// handles at most four partitions and creates layouts that ignore CHS
1646// geometries. Returns the number of converted partitions; if this value
1647// is over 0, the calling function should call DestroyGPT() to destroy
1648// the GPT data, and then exit.
1649int GPTData::XFormToMBR(void) {
1650 char line[255];
srs56945d58fe02010-01-03 20:57:08 -05001651 char* junk;
srs5694978041c2009-09-21 20:51:47 -04001652 int i, j, numParts, numConverted = 0;
1653 uint32_t partNums[4];
1654
1655 // Get the numbers of up to four partitions to add to the
1656 // hybrid MBR....
1657 numParts = CountParts();
1658 printf("Counted %d partitions.\n", numParts);
1659
1660 // Prepare the MBR for conversion (empty it of existing partitions).
1661 protectiveMBR.EmptyMBR(0);
1662 protectiveMBR.SetDiskSize(diskSize);
1663
1664 if (numParts > 4) { // Over four partitions; engage in triage
1665 printf("Type from one to four GPT partition numbers, separated by spaces, to be\n"
1666 "used in the MBR, in sequence: ");
srs56945d58fe02010-01-03 20:57:08 -05001667 junk = fgets(line, 255, stdin);
srs5694978041c2009-09-21 20:51:47 -04001668 numParts = sscanf(line, "%d %d %d %d", &partNums[0], &partNums[1],
1669 &partNums[2], &partNums[3]);
1670 } else { // Four or fewer partitions; convert them all
1671 i = j = 0;
1672 while ((j < numParts) && (i < mainHeader.numParts)) {
1673 if (partitions[i].GetFirstLBA() > 0) { // if GPT part. is defined
1674 partNums[j++] = ++i; // flag it for conversion
1675 } else i++;
1676 } // while
1677 } // if/else
1678
1679 for (i = 0; i < numParts; i++) {
1680 j = partNums[i] - 1;
1681 printf("\nCreating entry for partition #%d\n", j + 1);
1682 numConverted += OnePartToMBR(j, i);
1683 } // for
1684 return numConverted;
1685} // GPTData::XFormToMBR()
1686
srs5694e4ac11e2009-08-31 10:13:04 -04001687// Create a hybrid MBR -- an ugly, funky thing that helps GPT work with
1688// OSes that don't understand GPT.
1689void GPTData::MakeHybrid(void) {
1690 uint32_t partNums[3];
1691 char line[255];
srs56945d58fe02010-01-03 20:57:08 -05001692 char* junk;
srs5694978041c2009-09-21 20:51:47 -04001693 int numParts, numConverted = 0, i, j, typeCode, mbrNum;
srs5694e4ac11e2009-08-31 10:13:04 -04001694 char fillItUp = 'M'; // fill extra partition entries? (Yes/No/Maybe)
srs5694978041c2009-09-21 20:51:47 -04001695 char eeFirst = 'Y'; // Whether EFI GPT (0xEE) partition comes first in table
srs5694e4ac11e2009-08-31 10:13:04 -04001696
1697 printf("\nWARNING! Hybrid MBRs are flaky and potentially dangerous! If you decide not\n"
1698 "to use one, just hit the Enter key at the below prompt and your MBR\n"
1699 "partition table will be untouched.\n\n\a");
1700
1701 // Now get the numbers of up to three partitions to add to the
1702 // hybrid MBR....
1703 printf("Type from one to three GPT partition numbers, separated by spaces, to be\n"
1704 "added to the hybrid MBR, in sequence: ");
srs56945d58fe02010-01-03 20:57:08 -05001705 junk = fgets(line, 255, stdin);
srs5694e4ac11e2009-08-31 10:13:04 -04001706 numParts = sscanf(line, "%d %d %d", &partNums[0], &partNums[1], &partNums[2]);
1707
1708 if (numParts > 0) {
1709 // Blank out the protective MBR, but leave the boot loader code
1710 // alone....
1711 protectiveMBR.EmptyMBR(0);
1712 protectiveMBR.SetDiskSize(diskSize);
1713 printf("Place EFI GPT (0xEE) partition first in MBR (good for GRUB)? ");
1714 eeFirst = GetYN();
1715 } // if
1716
1717 for (i = 0; i < numParts; i++) {
1718 j = partNums[i] - 1;
1719 printf("\nCreating entry for partition #%d\n", j + 1);
srs5694978041c2009-09-21 20:51:47 -04001720 if (eeFirst == 'Y')
1721 mbrNum = i + 1;
1722 else
1723 mbrNum = i;
1724 numConverted += OnePartToMBR(j, mbrNum);
srs5694e4ac11e2009-08-31 10:13:04 -04001725 } // for
1726
srs5694978041c2009-09-21 20:51:47 -04001727 if ((numParts > 0) && (numConverted > 0)) { // User opted to create a hybrid MBR....
srs5694e4ac11e2009-08-31 10:13:04 -04001728 // Create EFI protective partition that covers the start of the disk.
1729 // If this location (covering the main GPT data structures) is omitted,
1730 // Linux won't find any partitions on the disk. Note that this is
1731 // NUMBERED AFTER the hybrid partitions, contrary to what the
1732 // gptsync utility does. This is because Windows seems to choke on
1733 // disks with a 0xEE partition in the first slot and subsequent
1734 // additional partitions, unless it boots from the disk.
1735 if (eeFirst == 'Y')
1736 mbrNum = 0;
1737 else
1738 mbrNum = numParts;
1739 protectiveMBR.MakePart(mbrNum, 1, protectiveMBR.FindLastInFree(1), 0xEE);
srs5694978041c2009-09-21 20:51:47 -04001740 protectiveMBR.SetHybrid();
srs5694e4ac11e2009-08-31 10:13:04 -04001741
1742 // ... and for good measure, if there are any partition spaces left,
1743 // optionally create another protective EFI partition to cover as much
1744 // space as possible....
1745 for (i = 0; i < 4; i++) {
1746 if (protectiveMBR.GetType(i) == 0x00) { // unused entry....
1747 if (fillItUp == 'M') {
1748 printf("\nUnused partition space(s) found. Use one to protect more partitions? ");
1749 fillItUp = GetYN();
1750 typeCode = 0x00; // use this to flag a need to get type code
1751 } // if
1752 if (fillItUp == 'Y') {
1753 while ((typeCode <= 0) || (typeCode > 255)) {
1754 printf("Enter an MBR hex code (EE is EFI GPT, but may confuse MacOS): ");
1755 // Comment on above: Mac OS treats disks with more than one
1756 // 0xEE MBR partition as MBR disks, not as GPT disks.
srs56945d58fe02010-01-03 20:57:08 -05001757 junk = fgets(line, 255, stdin);
srs5694e4ac11e2009-08-31 10:13:04 -04001758 sscanf(line, "%x", &typeCode);
1759 if (line[0] == '\n')
1760 typeCode = 0;
1761 } // while
1762 protectiveMBR.MakeBiggestPart(i, typeCode); // make a partition
1763 } // if (fillItUp == 'Y')
1764 } // if unused entry
1765 } // for (i = 0; i < 4; i++)
1766 } // if (numParts > 0)
1767} // GPTData::MakeHybrid()
1768
1769/**********************************************************************
1770 * *
1771 * Functions that adjust GPT data structures WITHOUT user interaction *
1772 * (they may display information for the user's benefit, though) *
1773 * *
1774 **********************************************************************/
1775
1776// Resizes GPT to specified number of entries. Creates a new table if
1777// necessary, copies data if it already exists.
1778int GPTData::SetGPTSize(uint32_t numEntries) {
1779 struct GPTPart* newParts;
1780 struct GPTPart* trash;
1781 uint32_t i, high, copyNum;
1782 int allOK = 1;
1783
1784 // First, adjust numEntries upward, if necessary, to get a number
1785 // that fills the allocated sectors
1786 i = blockSize / GPT_SIZE;
1787 if ((numEntries % i) != 0) {
1788 printf("Adjusting GPT size from %lu ", (unsigned long) numEntries);
1789 numEntries = ((numEntries / i) + 1) * i;
1790 printf("to %lu to fill the sector\n", (unsigned long) numEntries);
1791 } // if
1792
srs5694247657a2009-11-26 18:36:12 -05001793 // Do the work only if the # of partitions is changing. Along with being
1794 // efficient, this prevents mucking the with location of the secondary
1795 // partition table, which causes problems when loading data from a RAID
1796 // array that's been expanded because this function is called when loading
1797 // data.
1798 if ((numEntries != mainHeader.numParts) || (partitions == NULL)) {
1799 newParts = (GPTPart*) calloc(numEntries, sizeof (GPTPart));
1800 if (newParts != NULL) {
1801 if (partitions != NULL) { // existing partitions; copy them over
1802 GetPartRange(&i, &high);
1803 if (numEntries < (high + 1)) { // Highest entry too high for new #
1804 printf("The highest-numbered partition is %lu, which is greater than the requested\n"
1805 "partition table size of %d; cannot resize. Perhaps sorting will help.\n",
1806 (unsigned long) (high + 1), numEntries);
1807 allOK = 0;
1808 } else { // go ahead with copy
1809 if (numEntries < mainHeader.numParts)
1810 copyNum = numEntries;
1811 else
1812 copyNum = mainHeader.numParts;
1813 for (i = 0; i < copyNum; i++) {
1814 newParts[i] = partitions[i];
1815 } // for
1816 trash = partitions;
1817 partitions = newParts;
1818 free(trash);
1819 } // if
1820 } else { // No existing partition table; just create it
srs5694e4ac11e2009-08-31 10:13:04 -04001821 partitions = newParts;
srs5694247657a2009-11-26 18:36:12 -05001822 } // if/else existing partitions
1823 mainHeader.numParts = numEntries;
1824 secondHeader.numParts = numEntries;
1825 mainHeader.firstUsableLBA = ((numEntries * GPT_SIZE) / blockSize) + 2 ;
1826 secondHeader.firstUsableLBA = mainHeader.firstUsableLBA;
1827 MoveSecondHeaderToEnd();
1828 if (diskSize > 0)
1829 CheckGPTSize();
1830 } else { // Bad memory allocation
1831 fprintf(stderr, "Error allocating memory for partition table!\n");
1832 allOK = 0;
1833 } // if/else
srs5694e4ac11e2009-08-31 10:13:04 -04001834 } // if/else
1835 return (allOK);
1836} // GPTData::SetGPTSize()
1837
1838// Blank the partition array
1839void GPTData::BlankPartitions(void) {
1840 uint32_t i;
1841
1842 for (i = 0; i < mainHeader.numParts; i++) {
1843 partitions[i].BlankPartition();
1844 } // for
1845} // GPTData::BlankPartitions()
1846
1847// Sort the GPT entries, eliminating gaps and making for a logical
1848// ordering. Relies on QuickSortGPT() for the bulk of the work
1849void GPTData::SortGPT(void) {
1850 int i, lastPart = 0;
1851 GPTPart temp;
1852
1853 // First, find the last partition with data, so as not to
1854 // spend needless time sorting empty entries....
1855 for (i = 0; i < mainHeader.numParts; i++) {
1856 if (partitions[i].GetFirstLBA() > 0)
1857 lastPart = i;
1858 } // for
1859
1860 // Now swap empties with the last partitions, to simplify the logic
1861 // in the Quicksort function....
1862 i = 0;
1863 while (i < lastPart) {
1864 if (partitions[i].GetFirstLBA() == 0) {
1865 temp = partitions[i];
1866 partitions[i] = partitions[lastPart];
1867 partitions[lastPart] = temp;
1868 lastPart--;
1869 } // if
1870 i++;
1871 } // while
1872
1873 // Now call the recursive quick sort routine to do the real work....
1874 QuickSortGPT(partitions, 0, lastPart);
1875} // GPTData::SortGPT()
1876
1877// Set up data structures for entirely new set of partitions on the
1878// specified device. Returns 1 if OK, 0 if there were problems.
srs5694e35eb1b2009-09-14 00:29:34 -04001879// Note that this function does NOT clear the protectiveMBR data
1880// structure, since it may hold the original MBR partitions if the
1881// program was launched on an MBR disk, and those may need to be
1882// converted to GPT format.
srs5694e4ac11e2009-08-31 10:13:04 -04001883int GPTData::ClearGPTData(void) {
srs5694e35eb1b2009-09-14 00:29:34 -04001884 int goOn = 1, i;
srs5694e4ac11e2009-08-31 10:13:04 -04001885
1886 // Set up the partition table....
1887 free(partitions);
1888 partitions = NULL;
1889 SetGPTSize(NUM_GPT_ENTRIES);
1890
1891 // Now initialize a bunch of stuff that's static....
1892 mainHeader.signature = GPT_SIGNATURE;
1893 mainHeader.revision = 0x00010000;
srs5694978041c2009-09-21 20:51:47 -04001894 mainHeader.headerSize = HEADER_SIZE;
srs5694e4ac11e2009-08-31 10:13:04 -04001895 mainHeader.reserved = 0;
1896 mainHeader.currentLBA = UINT64_C(1);
1897 mainHeader.partitionEntriesLBA = (uint64_t) 2;
1898 mainHeader.sizeOfPartitionEntries = GPT_SIZE;
1899 for (i = 0; i < GPT_RESERVED; i++) {
1900 mainHeader.reserved2[i] = '\0';
1901 } // for
1902
1903 // Now some semi-static items (computed based on end of disk)
1904 mainHeader.backupLBA = diskSize - UINT64_C(1);
1905 mainHeader.lastUsableLBA = diskSize - mainHeader.firstUsableLBA;
1906
1907 // Set a unique GUID for the disk, based on random numbers
1908 // rand() is only 32 bits, so multiply together to fill a 64-bit value
1909 mainHeader.diskGUID.data1 = (uint64_t) rand() * (uint64_t) rand();
1910 mainHeader.diskGUID.data2 = (uint64_t) rand() * (uint64_t) rand();
1911
1912 // Copy main header to backup header
1913 RebuildSecondHeader();
1914
1915 // Blank out the partitions array....
1916 BlankPartitions();
1917
1918 // Flag all CRCs as being OK....
1919 mainCrcOk = 1;
1920 secondCrcOk = 1;
1921 mainPartsCrcOk = 1;
1922 secondPartsCrcOk = 1;
1923
1924 return (goOn);
1925} // GPTData::ClearGPTData()
1926
srs5694247657a2009-11-26 18:36:12 -05001927// Set the location of the second GPT header data to the end of the disk.
1928// Used internally and called by the 'e' option on the recovery &
1929// transformation menu, to help users of RAID arrays who add disk space
1930// to their arrays.
1931void GPTData::MoveSecondHeaderToEnd() {
srs56948bb78762009-11-24 15:43:49 -05001932 mainHeader.backupLBA = secondHeader.currentLBA = diskSize - UINT64_C(1);
1933 mainHeader.lastUsableLBA = secondHeader.lastUsableLBA = diskSize - mainHeader.firstUsableLBA;
1934 secondHeader.partitionEntriesLBA = secondHeader.lastUsableLBA + UINT64_C(1);
1935} // GPTData::FixSecondHeaderLocation()
1936
srs5694e4ac11e2009-08-31 10:13:04 -04001937void GPTData::SetName(uint32_t partNum, char* theName) {
1938 if ((partNum >= 0) && (partNum < mainHeader.numParts))
1939 if (partitions[partNum].GetFirstLBA() > 0)
1940 partitions[partNum].SetName((unsigned char*) theName);
1941} // GPTData::SetName
1942
1943// Set the disk GUID to the specified value. Note that the header CRCs must
1944// be recomputed after calling this function.
1945void GPTData::SetDiskGUID(GUIDData newGUID) {
1946 mainHeader.diskGUID = newGUID;
1947 secondHeader.diskGUID = newGUID;
1948} // SetDiskGUID()
1949
1950// Set the unique GUID of the specified partition. Returns 1 on
1951// successful completion, 0 if there were problems (invalid
1952// partition number).
1953int GPTData::SetPartitionGUID(uint32_t pn, GUIDData theGUID) {
1954 int retval = 0;
1955
1956 if (pn < mainHeader.numParts) {
1957 if (partitions[pn].GetFirstLBA() != UINT64_C(0)) {
1958 partitions[pn].SetUniqueGUID(theGUID);
1959 retval = 1;
1960 } // if
1961 } // if
1962 return retval;
1963} // GPTData::SetPartitionGUID()
1964
srs56941d1448a2009-12-31 21:20:19 -05001965// Adjust sector number so that it falls on a sector boundary that's a
1966// multiple of sectorAlignment. This is done to improve the performance
1967// of Western Digital Advanced Format disks and disks with similar
1968// technology from other companies, which use 4096-byte sectors
1969// internally although they translate to 512-byte sectors for the
1970// benefit of the OS. If partitions aren't properly aligned on these
1971// disks, some filesystem data structures can span multiple physical
1972// sectors, degrading performance. This function should be called
1973// only on the FIRST sector of the partition, not the last!
1974// This function returns 1 if the alignment was altered, 0 if it
1975// was unchanged.
1976int GPTData::Align(uint64_t* sector) {
1977 int retval = 0, sectorOK = 0;
1978 uint64_t earlier, later, testSector, original;
1979
1980 if ((*sector % sectorAlignment) != 0) {
1981 original = *sector;
1982 retval = 1;
1983 earlier = (*sector / sectorAlignment) * sectorAlignment;
1984 later = earlier + (uint64_t) sectorAlignment;
1985
1986 // Check to see that every sector between the earlier one and the
1987 // requested one is clear, and that it's not too early....
1988 if (earlier >= mainHeader.firstUsableLBA) {
1989// printf("earlier is %llu, first usable is %llu\n", earlier, mainHeader.firstUsableLBA);
1990 sectorOK = 1;
1991 testSector = earlier;
1992 do {
1993 sectorOK = IsFree(testSector++);
1994 } while ((sectorOK == 1) && (testSector < *sector));
1995 if (sectorOK == 1) {
1996 *sector = earlier;
1997// printf("Moved sector earlier.\n");
1998 } // if
1999 } // if firstUsableLBA check
2000
2001 // If couldn't move the sector earlier, try to move it later instead....
2002 if ((sectorOK != 1) && (later <= mainHeader.lastUsableLBA)) {
2003 sectorOK = 1;
2004 testSector = later;
2005 do {
2006 sectorOK = IsFree(testSector--);
2007 } while ((sectorOK == 1) && (testSector > *sector));
2008 if (sectorOK == 1) {
2009 *sector = later;
2010// printf("Moved sector later\n");
2011 } // if
2012 } // if
2013
2014 // If sector was changed successfully, inform the user of this fact.
2015 // Otherwise, notify the user that it couldn't be done....
2016 if (sectorOK == 1) {
2017 printf("Information: Moved requested sector from %llu to %llu for\n"
srs56945d58fe02010-01-03 20:57:08 -05002018 "alignment purposes. Use 'l' on the experts' menu to adjust alignment.\n",
2019 (unsigned long long) original, (unsigned long long) *sector);
srs56941d1448a2009-12-31 21:20:19 -05002020 } else {
2021 printf("Information: Sector not aligned on %d-sector boundary and could not be moved.\n"
2022 "If you're using a Western Digital Advanced Format or similar disk with\n"
2023 "underlying 4096-byte sectors, performance may suffer.\n", sectorAlignment);
2024 retval = 0;
2025 } // if/else
2026 } // if
2027 return retval;
2028} // GPTData::Align()
2029
srs5694e4ac11e2009-08-31 10:13:04 -04002030/********************************************************
2031 * *
2032 * Functions that return data about GPT data structures *
2033 * (most of these are inline in gpt.h) *
2034 * *
2035 ********************************************************/
2036
2037// Find the low and high used partition numbers (numbered from 0).
2038// Return value is the number of partitions found. Note that the
2039// *low and *high values are both set to 0 when no partitions
2040// are found, as well as when a single partition in the first
2041// position exists. Thus, the return value is the only way to
2042// tell when no partitions exist.
2043int GPTData::GetPartRange(uint32_t *low, uint32_t *high) {
2044 uint32_t i;
2045 int numFound = 0;
2046
2047 *low = mainHeader.numParts + 1; // code for "not found"
2048 *high = 0;
2049 if (mainHeader.numParts > 0) { // only try if partition table exists...
2050 for (i = 0; i < mainHeader.numParts; i++) {
2051 if (partitions[i].GetFirstLBA() != UINT64_C(0)) { // it exists
2052 *high = i; // since we're counting up, set the high value
2053 // Set the low value only if it's not yet found...
2054 if (*low == (mainHeader.numParts + 1)) *low = i;
2055 numFound++;
2056 } // if
2057 } // for
2058 } // if
2059
2060 // Above will leave *low pointing to its "not found" value if no partitions
2061 // are defined, so reset to 0 if this is the case....
2062 if (*low == (mainHeader.numParts + 1))
2063 *low = 0;
2064 return numFound;
2065} // GPTData::GetPartRange()
2066
srs5694978041c2009-09-21 20:51:47 -04002067// Returns the number of defined partitions.
2068uint32_t GPTData::CountParts(void) {
2069 int i, counted = 0;
2070
2071 for (i = 0; i < mainHeader.numParts; i++) {
2072 if (partitions[i].GetFirstLBA() > 0)
2073 counted++;
2074 } // for
2075 return counted;
2076} // GPTData::CountParts()
2077
srs5694e4ac11e2009-08-31 10:13:04 -04002078/****************************************************
2079 * *
2080 * Functions that return data about disk free space *
2081 * *
2082 ****************************************************/
2083
2084// Find the first available block after the starting point; returns 0 if
2085// there are no available blocks left
2086uint64_t GPTData::FindFirstAvailable(uint64_t start) {
2087 uint64_t first;
2088 uint32_t i;
2089 int firstMoved = 0;
2090
2091 // Begin from the specified starting point or from the first usable
2092 // LBA, whichever is greater...
2093 if (start < mainHeader.firstUsableLBA)
2094 first = mainHeader.firstUsableLBA;
2095 else
2096 first = start;
2097
2098 // ...now search through all partitions; if first is within an
2099 // existing partition, move it to the next sector after that
2100 // partition and repeat. If first was moved, set firstMoved
2101 // flag; repeat until firstMoved is not set, so as to catch
2102 // cases where partitions are out of sequential order....
2103 do {
2104 firstMoved = 0;
2105 for (i = 0; i < mainHeader.numParts; i++) {
2106 if ((first >= partitions[i].GetFirstLBA()) &&
2107 (first <= partitions[i].GetLastLBA())) { // in existing part.
2108 first = partitions[i].GetLastLBA() + 1;
2109 firstMoved = 1;
2110 } // if
2111 } // for
2112 } while (firstMoved == 1);
2113 if (first > mainHeader.lastUsableLBA)
2114 first = 0;
2115 return (first);
2116} // GPTData::FindFirstAvailable()
2117
2118// Finds the first available sector in the largest block of unallocated
2119// space on the disk. Returns 0 if there are no available blocks left
2120uint64_t GPTData::FindFirstInLargest(void) {
srs5694e35eb1b2009-09-14 00:29:34 -04002121 uint64_t start, firstBlock, lastBlock, segmentSize, selectedSize = 0, selectedSegment = 0;
srs5694e4ac11e2009-08-31 10:13:04 -04002122
2123 start = 0;
2124 do {
2125 firstBlock = FindFirstAvailable(start);
2126 if (firstBlock != UINT32_C(0)) { // something's free...
2127 lastBlock = FindLastInFree(firstBlock);
2128 segmentSize = lastBlock - firstBlock + UINT32_C(1);
2129 if (segmentSize > selectedSize) {
2130 selectedSize = segmentSize;
2131 selectedSegment = firstBlock;
2132 } // if
2133 start = lastBlock + 1;
2134 } // if
2135 } while (firstBlock != 0);
2136 return selectedSegment;
2137} // GPTData::FindFirstInLargest()
2138
2139// Find the last available block on the disk at or after the start
2140// block. Returns 0 if there are no available partitions after
2141// (or including) start.
2142uint64_t GPTData::FindLastAvailable(uint64_t start) {
2143 uint64_t last;
2144 uint32_t i;
2145 int lastMoved = 0;
2146
2147 // Start by assuming the last usable LBA is available....
2148 last = mainHeader.lastUsableLBA;
2149
2150 // ...now, similar to algorithm in FindFirstAvailable(), search
2151 // through all partitions, moving last when it's in an existing
2152 // partition. Set the lastMoved flag so we repeat to catch cases
2153 // where partitions are out of logical order.
2154 do {
2155 lastMoved = 0;
2156 for (i = 0; i < mainHeader.numParts; i++) {
2157 if ((last >= partitions[i].GetFirstLBA()) &&
2158 (last <= partitions[i].GetLastLBA())) { // in existing part.
2159 last = partitions[i].GetFirstLBA() - 1;
2160 lastMoved = 1;
2161 } // if
2162 } // for
2163 } while (lastMoved == 1);
2164 if (last < mainHeader.firstUsableLBA)
2165 last = 0;
2166 return (last);
2167} // GPTData::FindLastAvailable()
2168
2169// Find the last available block in the free space pointed to by start.
2170uint64_t GPTData::FindLastInFree(uint64_t start) {
2171 uint64_t nearestStart;
2172 uint32_t i;
2173
2174 nearestStart = mainHeader.lastUsableLBA;
2175 for (i = 0; i < mainHeader.numParts; i++) {
2176 if ((nearestStart > partitions[i].GetFirstLBA()) &&
2177 (partitions[i].GetFirstLBA() > start)) {
2178 nearestStart = partitions[i].GetFirstLBA() - 1;
2179 } // if
2180 } // for
2181 return (nearestStart);
2182} // GPTData::FindLastInFree()
2183
2184// Finds the total number of free blocks, the number of segments in which
2185// they reside, and the size of the largest of those segments
2186uint64_t GPTData::FindFreeBlocks(int *numSegments, uint64_t *largestSegment) {
2187 uint64_t start = UINT64_C(0); // starting point for each search
2188 uint64_t totalFound = UINT64_C(0); // running total
2189 uint64_t firstBlock; // first block in a segment
2190 uint64_t lastBlock; // last block in a segment
2191 uint64_t segmentSize; // size of segment in blocks
2192 int num = 0;
2193
2194 *largestSegment = UINT64_C(0);
2195 do {
2196 firstBlock = FindFirstAvailable(start);
2197 if (firstBlock != UINT64_C(0)) { // something's free...
2198 lastBlock = FindLastInFree(firstBlock);
2199 segmentSize = lastBlock - firstBlock + UINT64_C(1);
2200 if (segmentSize > *largestSegment) {
2201 *largestSegment = segmentSize;
2202 } // if
2203 totalFound += segmentSize;
2204 num++;
2205 start = lastBlock + 1;
2206 } // if
2207 } while (firstBlock != 0);
2208 *numSegments = num;
2209 return totalFound;
2210} // GPTData::FindFreeBlocks()
2211
2212// Returns 1 if sector is unallocated, 0 if it's allocated to a partition
2213int GPTData::IsFree(uint64_t sector) {
2214 int isFree = 1;
2215 uint32_t i;
2216
2217 for (i = 0; i < mainHeader.numParts; i++) {
2218 if ((sector >= partitions[i].GetFirstLBA()) &&
2219 (sector <= partitions[i].GetLastLBA())) {
2220 isFree = 0;
2221 } // if
2222 } // for
srs5694e35eb1b2009-09-14 00:29:34 -04002223 if ((sector < mainHeader.firstUsableLBA) ||
srs5694e4ac11e2009-08-31 10:13:04 -04002224 (sector > mainHeader.lastUsableLBA)) {
2225 isFree = 0;
2226 } // if
2227 return (isFree);
2228} // GPTData::IsFree()
2229
2230/********************************
2231 * *
2232 * Endianness support functions *
2233 * *
2234 ********************************/
2235
srs56942a9f5da2009-08-26 00:48:01 -04002236void GPTData::ReverseHeaderBytes(struct GPTHeader* header) {
srs5694221e0872009-08-29 15:00:31 -04002237 ReverseBytes(&header->signature, 8);
2238 ReverseBytes(&header->revision, 4);
2239 ReverseBytes(&header->headerSize, 4);
2240 ReverseBytes(&header->headerCRC, 4);
2241 ReverseBytes(&header->reserved, 4);
2242 ReverseBytes(&header->currentLBA, 8);
2243 ReverseBytes(&header->backupLBA, 8);
2244 ReverseBytes(&header->firstUsableLBA, 8);
2245 ReverseBytes(&header->lastUsableLBA, 8);
2246 ReverseBytes(&header->partitionEntriesLBA, 8);
2247 ReverseBytes(&header->numParts, 4);
2248 ReverseBytes(&header->sizeOfPartitionEntries, 4);
2249 ReverseBytes(&header->partitionEntriesCRC, 4);
2250 ReverseBytes(&header->reserved2, GPT_RESERVED);
2251 ReverseBytes(&header->diskGUID.data1, 8);
2252 ReverseBytes(&header->diskGUID.data2, 8);
srs56942a9f5da2009-08-26 00:48:01 -04002253} // GPTData::ReverseHeaderBytes()
2254
2255// IMPORTANT NOTE: This function requires non-reversed mainHeader
2256// structure!
2257void GPTData::ReversePartitionBytes() {
2258 uint32_t i;
2259
2260 // Check GPT signature on big-endian systems; this will mismatch
2261 // if the function is called out of order. Unfortunately, it'll also
2262 // mismatch if there's data corruption.
2263 if ((mainHeader.signature != GPT_SIGNATURE) && (IsLittleEndian() == 0)) {
2264 printf("GPT signature mismatch in GPTData::ReversePartitionBytes(). This indicates\n"
srs5694e4ac11e2009-08-31 10:13:04 -04002265 "data corruption or a misplaced call to this function.\n");
srs56942a9f5da2009-08-26 00:48:01 -04002266 } // if signature mismatch....
2267 for (i = 0; i < mainHeader.numParts; i++) {
srs5694221e0872009-08-29 15:00:31 -04002268 partitions[i].ReversePartBytes();
srs56942a9f5da2009-08-26 00:48:01 -04002269 } // for
2270} // GPTData::ReversePartitionBytes()
2271
2272/******************************************
2273 * *
2274 * Additional non-class support functions *
2275 * *
2276 ******************************************/
2277
srs5694e7b4ff92009-08-18 13:16:10 -04002278// Check to be sure that data type sizes are correct. The basic types (uint*_t) should
2279// never fail these tests, but the struct types may fail depending on compile options.
2280// Specifically, the -fpack-struct option to gcc may be required to ensure proper structure
2281// sizes.
2282int SizesOK(void) {
2283 int allOK = 1;
srs5694e7b4ff92009-08-18 13:16:10 -04002284
2285 if (sizeof(uint8_t) != 1) {
srs56941d1448a2009-12-31 21:20:19 -05002286 fprintf(stderr, "uint8_t is %lu bytes, should be 1 byte; aborting!\n", sizeof(uint8_t));
srs5694e7b4ff92009-08-18 13:16:10 -04002287 allOK = 0;
2288 } // if
2289 if (sizeof(uint16_t) != 2) {
srs56941d1448a2009-12-31 21:20:19 -05002290 fprintf(stderr, "uint16_t is %lu bytes, should be 2 bytes; aborting!\n", sizeof(uint16_t));
srs5694e7b4ff92009-08-18 13:16:10 -04002291 allOK = 0;
2292 } // if
2293 if (sizeof(uint32_t) != 4) {
srs56941d1448a2009-12-31 21:20:19 -05002294 fprintf(stderr, "uint32_t is %lu bytes, should be 4 bytes; aborting!\n", sizeof(uint32_t));
srs5694e7b4ff92009-08-18 13:16:10 -04002295 allOK = 0;
2296 } // if
2297 if (sizeof(uint64_t) != 8) {
srs56941d1448a2009-12-31 21:20:19 -05002298 fprintf(stderr, "uint64_t is %lu bytes, should be 8 bytes; aborting!\n", sizeof(uint64_t));
srs5694e7b4ff92009-08-18 13:16:10 -04002299 allOK = 0;
2300 } // if
2301 if (sizeof(struct MBRRecord) != 16) {
srs56941d1448a2009-12-31 21:20:19 -05002302 fprintf(stderr, "MBRRecord is %lu bytes, should be 16 bytes; aborting!\n", sizeof(MBRRecord));
srs5694e7b4ff92009-08-18 13:16:10 -04002303 allOK = 0;
2304 } // if
srs5694978041c2009-09-21 20:51:47 -04002305 if (sizeof(struct TempMBR) != 512) {
srs56941d1448a2009-12-31 21:20:19 -05002306 fprintf(stderr, "TempMBR is %lu bytes, should be 512 bytes; aborting!\n", sizeof(TempMBR));
srs5694e7b4ff92009-08-18 13:16:10 -04002307 allOK = 0;
2308 } // if
2309 if (sizeof(struct GPTHeader) != 512) {
srs56941d1448a2009-12-31 21:20:19 -05002310 fprintf(stderr, "GPTHeader is %lu bytes, should be 512 bytes; aborting!\n", sizeof(GPTHeader));
srs5694e7b4ff92009-08-18 13:16:10 -04002311 allOK = 0;
2312 } // if
srs5694221e0872009-08-29 15:00:31 -04002313 if (sizeof(GPTPart) != 128) {
srs56941d1448a2009-12-31 21:20:19 -05002314 fprintf(stderr, "GPTPart is %lu bytes, should be 128 bytes; aborting!\n", sizeof(GPTPart));
srs5694221e0872009-08-29 15:00:31 -04002315 allOK = 0;
2316 } // if
2317// Determine endianness; set allOK = 0 if running on big-endian hardware
srs56942a9f5da2009-08-26 00:48:01 -04002318 if (IsLittleEndian() == 0) {
2319 fprintf(stderr, "\aRunning on big-endian hardware. Big-endian support is new and poorly"
srs5694e4ac11e2009-08-31 10:13:04 -04002320 " tested!\nBeware!\n");
srs56942a9f5da2009-08-26 00:48:01 -04002321 // allOK = 0;
srs5694e7b4ff92009-08-18 13:16:10 -04002322 } // if
2323 return (allOK);
2324} // SizesOK()
srs5694e4ac11e2009-08-31 10:13:04 -04002325