~ubuntu-branches/ubuntu/utopic/gdisk/utopic-proposed

« back to all changes in this revision

Viewing changes to gpt.cc

  • Committer: Bazaar Package Importer
  • Author(s): Guillaume Delacour
  • Date: 2009-12-01 21:04:25 UTC
  • Revision ID: james.westby@ubuntu.com-20091201210425-lznlvi764r2dwkf8
Tags: upstream-0.5.1
ImportĀ upstreamĀ versionĀ 0.5.1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* gpt.cc -- Functions for loading, saving, and manipulating legacy MBR and GPT partition
 
2
   data. */
 
3
 
 
4
/* By Rod Smith, initial coding January to February, 2009 */
 
5
 
 
6
/* 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
 
 
9
#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"
 
23
#include "bsd.h"
 
24
#include "support.h"
 
25
#include "parttypes.h"
 
26
#include "attributes.h"
 
27
 
 
28
using namespace std;
 
29
 
 
30
/****************************************
 
31
 *                                      *
 
32
 * GPTData class and related structures *
 
33
 *                                      *
 
34
 ****************************************/
 
35
 
 
36
// Default constructor
 
37
GPTData::GPTData(void) {
 
38
   blockSize = SECTOR_SIZE; // set a default
 
39
   diskSize = 0;
 
40
   partitions = NULL;
 
41
   state = gpt_valid;
 
42
   strcpy(device, "");
 
43
   mainCrcOk = 0;
 
44
   secondCrcOk = 0;
 
45
   mainPartsCrcOk = 0;
 
46
   secondPartsCrcOk = 0;
 
47
   apmFound = 0;
 
48
   bsdFound = 0;
 
49
   srand((unsigned int) time(NULL));
 
50
   SetGPTSize(NUM_GPT_ENTRIES);
 
51
} // GPTData default constructor
 
52
 
 
53
// The following constructor loads GPT data from a device file
 
54
GPTData::GPTData(char* filename) {
 
55
   blockSize = SECTOR_SIZE; // set a default
 
56
   diskSize = 0;
 
57
   partitions = NULL;
 
58
   state = gpt_invalid;
 
59
   strcpy(device, "");
 
60
   mainCrcOk = 0;
 
61
   secondCrcOk = 0;
 
62
   mainPartsCrcOk = 0;
 
63
   secondPartsCrcOk = 0;
 
64
   apmFound = 0;
 
65
   bsdFound = 0;
 
66
   srand((unsigned int) time(NULL));
 
67
   LoadPartitions(filename);
 
68
} // GPTData(char* filename) constructor
 
69
 
 
70
// Destructor
 
71
GPTData::~GPTData(void) {
 
72
   free(partitions);
 
73
} // GPTData destructor
 
74
 
 
75
/*********************************************************************
 
76
 *                                                                   *
 
77
 * Begin functions that verify data, or that adjust the verification *
 
78
 * information (compute CRCs, rebuild headers)                       *
 
79
 *                                                                   *
 
80
 *********************************************************************/
 
81
 
 
82
// Perform detailed verification, reporting on any problems found, but
 
83
// do *NOT* recover from these problems. Returns the total number of
 
84
// problems identified.
 
85
int GPTData::Verify(void) {
 
86
   int problems = 0, numSegments;
 
87
   uint64_t totalFree, largestSegment;
 
88
   char tempStr[255], siTotal[255], siLargest[255];
 
89
 
 
90
   // First, check for CRC errors in the GPT data....
 
91
   if (!mainCrcOk) {
 
92
      problems++;
 
93
      printf("\nProblem: The CRC for the main GPT header is invalid. The main GPT header may\n"
 
94
            "be corrupt. Consider loading the backup GPT header to rebuild the main GPT\n"
 
95
            "header\n");
 
96
   } // if
 
97
   if (!mainPartsCrcOk) {
 
98
      problems++;
 
99
      printf("\nProblem: The CRC for the main partition table is invalid. This table may be\n"
 
100
            "corrupt. Consider loading the backup partition table.\n");
 
101
   } // if
 
102
   if (!secondCrcOk) {
 
103
      problems++;
 
104
      printf("\nProblem: The CRC for the backup GPT header is invalid. The backup GPT header\n"
 
105
            "may be corrupt. Consider using the main GPT header to rebuild the backup GPT\n"
 
106
            "header.\n");
 
107
   } // if
 
108
   if (!secondPartsCrcOk) {
 
109
      problems++;
 
110
      printf("\nCaution: The CRC for the backup partition table is invalid. This table may\n"
 
111
            "be corrupt. This program will automatically create a new backup partition\n"
 
112
            "table when you save your partitions.\n");
 
113
   } // if
 
114
 
 
115
   // Now check that the main and backup headers both point to themselves....
 
116
   if (mainHeader.currentLBA != 1) {
 
117
      problems++;
 
118
      printf("\nProblem: The main header's self-pointer doesn't point to itself. This problem\n"
 
119
             "is being automatically corrected, but it may be a symptom of more serious\n"
 
120
             "problems. Think carefully before saving changes with 'w' or using this disk.\n");
 
121
      mainHeader.currentLBA = 1;
 
122
   } // if
 
123
   if (secondHeader.currentLBA != (diskSize - UINT64_C(1))) {
 
124
      problems++;
 
125
      printf("\nProblem: The secondary header's self-pointer indicates that it doesn't reside\n"
 
126
             "at the end of the disk. If you've added a disk to a RAID array, use the 'e'\n"
 
127
             "option on the experts' menu to adjust the secondary header's and partition\n"
 
128
             "table's locations.\n");
 
129
   } // if
 
130
 
 
131
   // Now check that critical main and backup GPT entries match each other
 
132
   if (mainHeader.currentLBA != secondHeader.backupLBA) {
 
133
      problems++;
 
134
      printf("\nProblem: main GPT header's current LBA pointer (%llu) doesn't\n"
 
135
            "match the backup GPT header's LBA pointer(%llu)\n",
 
136
            (unsigned long long) mainHeader.currentLBA,
 
137
             (unsigned long long) secondHeader.backupLBA);
 
138
   } // if
 
139
   if (mainHeader.backupLBA != secondHeader.currentLBA) {
 
140
      problems++;
 
141
      printf("\nProblem: main GPT header's backup LBA pointer (%llu) doesn't\n"
 
142
            "match the backup GPT header's current LBA pointer (%llu)\n",
 
143
            (unsigned long long) mainHeader.backupLBA,
 
144
             (unsigned long long) secondHeader.currentLBA);
 
145
   } // if
 
146
   if (mainHeader.firstUsableLBA != secondHeader.firstUsableLBA) {
 
147
      problems++;
 
148
      printf("\nProblem: main GPT header's first usable LBA pointer (%llu) doesn't\n"
 
149
            "match the backup GPT header's first usable LBA pointer (%llu)\n",
 
150
            (unsigned long long) mainHeader.firstUsableLBA,
 
151
             (unsigned long long) secondHeader.firstUsableLBA);
 
152
   } // if
 
153
   if (mainHeader.lastUsableLBA != secondHeader.lastUsableLBA) {
 
154
      problems++;
 
155
      printf("\nProblem: main GPT header's last usable LBA pointer (%llu) doesn't\n"
 
156
            "match the backup GPT header's last usable LBA pointer (%llu)\n",
 
157
            (unsigned long long) mainHeader.lastUsableLBA,
 
158
             (unsigned long long) secondHeader.lastUsableLBA);
 
159
   } // if
 
160
   if ((mainHeader.diskGUID.data1 != secondHeader.diskGUID.data1) ||
 
161
        (mainHeader.diskGUID.data2 != secondHeader.diskGUID.data2)) {
 
162
      problems++;
 
163
      printf("\nProblem: main header's disk GUID (%s) doesn't\n",
 
164
             GUIDToStr(mainHeader.diskGUID, tempStr));
 
165
      printf("match the backup GPT header's disk GUID (%s)\n",
 
166
             GUIDToStr(secondHeader.diskGUID, tempStr));
 
167
   } // if
 
168
   if (mainHeader.numParts != secondHeader.numParts) {
 
169
      problems++;
 
170
      printf("\nProblem: main GPT header's number of partitions (%lu) doesn't\n"
 
171
            "match the backup GPT header's number of partitions (%lu)\n",
 
172
            (unsigned long) mainHeader.numParts,
 
173
            (unsigned long) secondHeader.numParts);
 
174
   } // if
 
175
   if (mainHeader.sizeOfPartitionEntries != secondHeader.sizeOfPartitionEntries) {
 
176
      problems++;
 
177
      printf("\nProblem: main GPT header's size of partition entries (%lu) doesn't\n"
 
178
            "match the backup GPT header's size of partition entries (%lu)\n",
 
179
            (unsigned long) mainHeader.sizeOfPartitionEntries,
 
180
            (unsigned long) secondHeader.sizeOfPartitionEntries);
 
181
   } // if
 
182
 
 
183
   // Now check for a few other miscellaneous problems...
 
184
   // Check that the disk size will hold the data...
 
185
   if (mainHeader.backupLBA > diskSize) {
 
186
      problems++;
 
187
      printf("\nProblem: Disk is too small to hold all the data!\n");
 
188
      printf("(Disk size is %llu sectors, needs to be %llu sectors.)\n",
 
189
            (unsigned long long) diskSize,
 
190
               (unsigned long long) mainHeader.backupLBA);
 
191
   } // if
 
192
 
 
193
   // Check for overlapping partitions....
 
194
   problems += FindOverlaps();
 
195
 
 
196
   // Check for mismatched MBR and GPT partitions...
 
197
   problems += FindHybridMismatches();
 
198
 
 
199
   // Verify that partitions don't run into GPT data areas....
 
200
   problems += CheckGPTSize();
 
201
 
 
202
   // Now compute available space, but only if no problems found, since
 
203
   // problems could affect the results
 
204
   if (problems == 0) {
 
205
      totalFree = FindFreeBlocks(&numSegments, &largestSegment);
 
206
      BytesToSI(totalFree * (uint64_t) blockSize, siTotal);
 
207
      BytesToSI(largestSegment * (uint64_t) blockSize, siLargest);
 
208
      printf("No problems found. %llu free sectors (%s) available in %u\n"
 
209
             "segments, the largest of which is %llu sectors (%s) in size\n",
 
210
             (unsigned long long) totalFree,
 
211
              siTotal, numSegments, (unsigned long long) largestSegment,
 
212
                                     siLargest);
 
213
   } else {
 
214
      printf("\nIdentified %d problems!\n", problems);
 
215
   } // if/else
 
216
 
 
217
   return (problems);
 
218
} // GPTData::Verify()
 
219
 
 
220
// Checks to see if the GPT tables overrun existing partitions; if they
 
221
// do, issues a warning but takes no action. Returns number of problems
 
222
// detected (0 if OK, 1 to 2 if problems).
 
223
int GPTData::CheckGPTSize(void) {
 
224
   uint64_t overlap, firstUsedBlock, lastUsedBlock;
 
225
   uint32_t i;
 
226
   int numProbs = 0;
 
227
 
 
228
   // first, locate the first & last used blocks
 
229
   firstUsedBlock = UINT64_MAX;
 
230
   lastUsedBlock = 0;
 
231
   for (i = 0; i < mainHeader.numParts; i++) {
 
232
      if ((partitions[i].GetFirstLBA() < firstUsedBlock) &&
 
233
           (partitions[i].GetFirstLBA() != 0))
 
234
         firstUsedBlock = partitions[i].GetFirstLBA();
 
235
      if (partitions[i].GetLastLBA() > lastUsedBlock)
 
236
         lastUsedBlock = partitions[i].GetLastLBA();
 
237
   } // for
 
238
 
 
239
   // If the disk size is 0 (the default), then it means that various
 
240
   // variables aren't yet set, so the below tests will be useless;
 
241
   // therefore we should skip everything
 
242
   if (diskSize != 0) {
 
243
      if (mainHeader.firstUsableLBA > firstUsedBlock) {
 
244
         overlap = mainHeader.firstUsableLBA - firstUsedBlock;
 
245
         printf("Warning! Main partition table overlaps the first partition by %lu blocks!\n",
 
246
                (unsigned long) overlap);
 
247
         if (firstUsedBlock > 2) {
 
248
            printf("Try reducing the partition table size by %lu entries.\n",
 
249
                   (unsigned long) (overlap * 4));
 
250
            printf("(Use the 's' item on the experts' menu.)\n");
 
251
         } else {
 
252
            printf("You will need to delete this partition or resize it in another utility.\n");
 
253
         } // if/else
 
254
         numProbs++;
 
255
      } // Problem at start of disk
 
256
      if (mainHeader.lastUsableLBA < lastUsedBlock) {
 
257
         overlap = lastUsedBlock - mainHeader.lastUsableLBA;
 
258
         printf("Warning! Secondary partition table overlaps the last partition by %lu blocks\n",
 
259
                (unsigned long) overlap);
 
260
         if (lastUsedBlock > (diskSize - 2)) {
 
261
            printf("You will need to delete this partition or resize it in another utility.\n");
 
262
         } else {
 
263
            printf("Try reducing the partition table size by %lu entries.\n",
 
264
                   (unsigned long) (overlap * 4));
 
265
            printf("(Use the 's' item on the experts' menu.)\n");
 
266
         } // if/else
 
267
         numProbs++;
 
268
      } // Problem at end of disk
 
269
   } // if (diskSize != 0)
 
270
   return numProbs;
 
271
} // GPTData::CheckGPTSize()
 
272
 
 
273
// Check the validity of the GPT header. Returns 1 if the main header
 
274
// is valid, 2 if the backup header is valid, 3 if both are valid, and
 
275
// 0 if neither is valid. Note that this function just checks the GPT
 
276
// signature and revision numbers, not CRCs or other data.
 
277
int GPTData::CheckHeaderValidity(void) {
 
278
   int valid = 3;
 
279
 
 
280
   if (mainHeader.signature != GPT_SIGNATURE) {
 
281
      valid -= 1;
 
282
//      printf("Main GPT signature invalid; read 0x%016llX, should be\n0x%016llX\n",
 
283
//             (unsigned long long) mainHeader.signature, (unsigned long long) GPT_SIGNATURE);
 
284
   } else if ((mainHeader.revision != 0x00010000) && valid) {
 
285
      valid -= 1;
 
286
      printf("Unsupported GPT version in main header; read 0x%08lX, should be\n0x%08lX\n",
 
287
             (unsigned long) mainHeader.revision, UINT32_C(0x00010000));
 
288
   } // if/else/if
 
289
 
 
290
   if (secondHeader.signature != GPT_SIGNATURE) {
 
291
      valid -= 2;
 
292
//      printf("Secondary GPT signature invalid; read 0x%016llX, should be\n0x%016llX\n",
 
293
//             (unsigned long long) secondHeader.signature, (unsigned long long) GPT_SIGNATURE);
 
294
   } else if ((secondHeader.revision != 0x00010000) && valid) {
 
295
      valid -= 2;
 
296
      printf("Unsupported GPT version in backup header; read 0x%08lX, should be\n0x%08lX\n",
 
297
             (unsigned long) mainHeader.revision, UINT32_C(0x00010000));
 
298
   } // if/else/if
 
299
 
 
300
   // If MBR bad, check for an Apple disk signature
 
301
   if ((protectiveMBR.GetValidity() == invalid) &&
 
302
        (((mainHeader.signature << 32) == APM_SIGNATURE1) ||
 
303
        (mainHeader.signature << 32) == APM_SIGNATURE2)) {
 
304
      apmFound = 1; // Will display warning message later
 
305
   } // if
 
306
 
 
307
        return valid;
 
308
} // GPTData::CheckHeaderValidity()
 
309
 
 
310
// Check the header CRC to see if it's OK...
 
311
// Note: Must be called BEFORE byte-order reversal on big-endian
 
312
// systems!
 
313
int GPTData::CheckHeaderCRC(struct GPTHeader* header) {
 
314
   uint32_t oldCRC, newCRC, hSize;
 
315
 
 
316
   // Back up old header CRC and then blank it, since it must be 0 for
 
317
   // computation to be valid
 
318
   oldCRC = header->headerCRC;
 
319
   header->headerCRC = UINT32_C(0);
 
320
   hSize = header->headerSize;
 
321
 
 
322
   // If big-endian system, reverse byte order
 
323
   if (IsLittleEndian() == 0) {
 
324
      ReverseBytes(&oldCRC, 4);
 
325
   } // if
 
326
 
 
327
   // Initialize CRC functions...
 
328
   chksum_crc32gentab();
 
329
 
 
330
   // Compute CRC, restore original, and return result of comparison
 
331
   newCRC = chksum_crc32((unsigned char*) header, HEADER_SIZE);
 
332
   header->headerCRC = oldCRC;
 
333
   return (oldCRC == newCRC);
 
334
} // GPTData::CheckHeaderCRC()
 
335
 
 
336
// Recompute all the CRCs. Must be called before saving (but after reversing
 
337
// byte order on big-endian systems) if any changes have been made.
 
338
void GPTData::RecomputeCRCs(void) {
 
339
   uint32_t crc, hSize, trueNumParts;
 
340
   int littleEndian = 1;
 
341
 
 
342
   // Initialize CRC functions...
 
343
   chksum_crc32gentab();
 
344
 
 
345
   hSize = mainHeader.headerSize;
 
346
   littleEndian = IsLittleEndian();
 
347
 
 
348
   // Compute CRC of partition tables & store in main and secondary headers
 
349
   trueNumParts = mainHeader.numParts;
 
350
   if (littleEndian == 0)
 
351
      ReverseBytes(&trueNumParts, 4); // unreverse this key piece of data....
 
352
   crc = chksum_crc32((unsigned char*) partitions, trueNumParts * GPT_SIZE);
 
353
   mainHeader.partitionEntriesCRC = crc;
 
354
   secondHeader.partitionEntriesCRC = crc;
 
355
   if (littleEndian == 0) {
 
356
      ReverseBytes(&mainHeader.partitionEntriesCRC, 4);
 
357
      ReverseBytes(&secondHeader.partitionEntriesCRC, 4);
 
358
   } // if
 
359
 
 
360
   // Zero out GPT tables' own CRCs (required for correct computation)
 
361
   mainHeader.headerCRC = 0;
 
362
   secondHeader.headerCRC = 0;
 
363
 
 
364
   // Compute & store CRCs of main & secondary headers...
 
365
   crc = chksum_crc32((unsigned char*) &mainHeader, hSize);
 
366
   if (littleEndian == 0)
 
367
      ReverseBytes(&crc, 4);
 
368
   mainHeader.headerCRC = crc;
 
369
   crc = chksum_crc32((unsigned char*) &secondHeader, hSize);
 
370
   if (littleEndian == 0)
 
371
      ReverseBytes(&crc, 4);
 
372
   secondHeader.headerCRC = crc;
 
373
} // GPTData::RecomputeCRCs()
 
374
 
 
375
// Rebuild the main GPT header, using the secondary header as a model.
 
376
// Typically called when the main header has been found to be corrupt.
 
377
void GPTData::RebuildMainHeader(void) {
 
378
   int i;
 
379
 
 
380
   mainHeader.signature = GPT_SIGNATURE;
 
381
   mainHeader.revision = secondHeader.revision;
 
382
   mainHeader.headerSize = secondHeader.headerSize;
 
383
   mainHeader.headerCRC = UINT32_C(0);
 
384
   mainHeader.reserved = secondHeader.reserved;
 
385
   mainHeader.currentLBA = secondHeader.backupLBA;
 
386
   mainHeader.backupLBA = secondHeader.currentLBA;
 
387
   mainHeader.firstUsableLBA = secondHeader.firstUsableLBA;
 
388
   mainHeader.lastUsableLBA = secondHeader.lastUsableLBA;
 
389
   mainHeader.diskGUID.data1 = secondHeader.diskGUID.data1;
 
390
   mainHeader.diskGUID.data2 = secondHeader.diskGUID.data2;
 
391
   mainHeader.partitionEntriesLBA = UINT64_C(2);
 
392
   mainHeader.numParts = secondHeader.numParts;
 
393
   mainHeader.sizeOfPartitionEntries = secondHeader.sizeOfPartitionEntries;
 
394
   mainHeader.partitionEntriesCRC = secondHeader.partitionEntriesCRC;
 
395
   for (i = 0 ; i < GPT_RESERVED; i++)
 
396
      mainHeader.reserved2[i] = secondHeader.reserved2[i];
 
397
} // GPTData::RebuildMainHeader()
 
398
 
 
399
// Rebuild the secondary GPT header, using the main header as a model.
 
400
void GPTData::RebuildSecondHeader(void) {
 
401
   int i;
 
402
 
 
403
   secondHeader.signature = GPT_SIGNATURE;
 
404
   secondHeader.revision = mainHeader.revision;
 
405
   secondHeader.headerSize = mainHeader.headerSize;
 
406
   secondHeader.headerCRC = UINT32_C(0);
 
407
   secondHeader.reserved = mainHeader.reserved;
 
408
   secondHeader.currentLBA = mainHeader.backupLBA;
 
409
   secondHeader.backupLBA = mainHeader.currentLBA;
 
410
   secondHeader.firstUsableLBA = mainHeader.firstUsableLBA;
 
411
   secondHeader.lastUsableLBA = mainHeader.lastUsableLBA;
 
412
   secondHeader.diskGUID.data1 = mainHeader.diskGUID.data1;
 
413
   secondHeader.diskGUID.data2 = mainHeader.diskGUID.data2;
 
414
   secondHeader.partitionEntriesLBA = secondHeader.lastUsableLBA + UINT64_C(1);
 
415
   secondHeader.numParts = mainHeader.numParts;
 
416
   secondHeader.sizeOfPartitionEntries = mainHeader.sizeOfPartitionEntries;
 
417
   secondHeader.partitionEntriesCRC = mainHeader.partitionEntriesCRC;
 
418
   for (i = 0 ; i < GPT_RESERVED; i++)
 
419
      secondHeader.reserved2[i] = mainHeader.reserved2[i];
 
420
} // GPTData::RebuildSecondHeader()
 
421
 
 
422
// Search for hybrid MBR entries that have no corresponding GPT partition.
 
423
// Returns number of such mismatches found
 
424
int GPTData::FindHybridMismatches(void) {
 
425
   int i, j, found, numFound = 0;
 
426
   uint64_t mbrFirst, mbrLast;
 
427
 
 
428
   for (i = 0; i < 4; i++) {
 
429
      if ((protectiveMBR.GetType(i) != 0xEE) && (protectiveMBR.GetType(i) != 0x00)) {
 
430
         j = 0;
 
431
         found = 0;
 
432
         do {
 
433
            mbrFirst = (uint64_t) protectiveMBR.GetFirstSector(i);
 
434
            mbrLast = mbrFirst + (uint64_t) protectiveMBR.GetLength(i) - UINT64_C(1);
 
435
            if ((partitions[j].GetFirstLBA() == mbrFirst) &&
 
436
                (partitions[j].GetLastLBA() == mbrLast))
 
437
               found = 1;
 
438
            j++;
 
439
         } while ((!found) && (j < mainHeader.numParts));
 
440
         if (!found) {
 
441
            numFound++;
 
442
            printf("\nWarning! Mismatched GPT and MBR partitions! MBR partition "
 
443
                   "%d, of type 0x%02X,\nhas no corresponding GPT partition! "
 
444
                   "You may continue, but this condition\nmight cause data loss"
 
445
                   " in the future!\a\n", i + 1, protectiveMBR.GetType(i));
 
446
         } // if
 
447
      } // if
 
448
   } // for
 
449
   return numFound;
 
450
} // GPTData::FindHybridMismatches
 
451
 
 
452
// Find overlapping partitions and warn user about them. Returns number of
 
453
// overlapping partitions.
 
454
int GPTData::FindOverlaps(void) {
 
455
   int i, j, problems = 0;
 
456
 
 
457
   for (i = 1; i < mainHeader.numParts; i++) {
 
458
      for (j = 0; j < i; j++) {
 
459
         if (partitions[i].DoTheyOverlap(&partitions[j])) {
 
460
            problems++;
 
461
            printf("\nProblem: partitions %d and %d overlap:\n", i + 1, j + 1);
 
462
            printf("  Partition %d: %llu to %llu\n", i,
 
463
                   (unsigned long long) partitions[i].GetFirstLBA(),
 
464
                    (unsigned long long) partitions[i].GetLastLBA());
 
465
            printf("  Partition %d: %llu to %llu\n", j,
 
466
                   (unsigned long long) partitions[j].GetFirstLBA(),
 
467
                    (unsigned long long) partitions[j].GetLastLBA());
 
468
         } // if
 
469
      } // for j...
 
470
   } // for i...
 
471
   return problems;
 
472
} // GPTData::FindOverlaps()
 
473
 
 
474
/******************************************************************
 
475
 *                                                                *
 
476
 * Begin functions that load data from disk or save data to disk. *
 
477
 *                                                                *
 
478
 ******************************************************************/
 
479
 
 
480
// Scan for partition data. This function loads the MBR data (regular MBR or
 
481
// protective MBR) and loads BSD disklabel data (which is probably invalid).
 
482
// It also looks for APM data, forces a load of GPT data, and summarizes
 
483
// the results.
 
484
void GPTData::PartitionScan(int fd) {
 
485
   BSDData bsdDisklabel;
 
486
//   int bsdFound;
 
487
 
 
488
   printf("Partition table scan:\n");
 
489
 
 
490
   // Read the MBR & check for BSD disklabel
 
491
   protectiveMBR.ReadMBRData(fd);
 
492
   protectiveMBR.ShowState();
 
493
   bsdDisklabel.ReadBSDData(fd, 0, diskSize - 1);
 
494
   bsdFound = bsdDisklabel.ShowState();
 
495
//   bsdDisklabel.DisplayBSDData();
 
496
 
 
497
   // Load the GPT data, whether or not it's valid
 
498
   ForceLoadGPTData(fd);
 
499
   ShowAPMState(); // Show whether there's an Apple Partition Map present
 
500
   ShowGPTState(); // Show GPT status
 
501
   printf("\n");
 
502
 
 
503
   if (apmFound) {
 
504
      printf("\n*******************************************************************\n");
 
505
      printf("This disk appears to contain an Apple-format (APM) partition table!\n");
 
506
      printf("It will be destroyed if you continue!\n");
 
507
      printf("*******************************************************************\n\n\a");
 
508
   } // if
 
509
} // GPTData::PartitionScan()
 
510
 
 
511
// Read GPT data from a disk.
 
512
int GPTData::LoadPartitions(char* deviceFilename) {
 
513
   int fd, err;
 
514
   int allOK = 1, i;
 
515
   uint64_t firstBlock, lastBlock;
 
516
   BSDData bsdDisklabel;
 
517
 
 
518
   // First, do a test to see if writing will be possible later....
 
519
   fd = OpenForWrite(deviceFilename);
 
520
   if (fd == -1)
 
521
      printf("\aNOTE: Write test failed with error number %d. It will be "
 
522
             "impossible to save\nchanges to this disk's partition table!\n\n",
 
523
             errno);
 
524
   close(fd);
 
525
 
 
526
   if ((fd = open(deviceFilename, O_RDONLY)) != -1) {
 
527
      // store disk information....
 
528
      diskSize = disksize(fd, &err);
 
529
      blockSize = (uint32_t) GetBlockSize(fd);
 
530
      strcpy(device, deviceFilename);
 
531
      PartitionScan(fd); // Check for partition types & print summary
 
532
 
 
533
      switch (UseWhichPartitions()) {
 
534
         case use_mbr:
 
535
            XFormPartitions();
 
536
            break;
 
537
         case use_bsd:
 
538
            bsdDisklabel.ReadBSDData(fd, 0, diskSize - 1);
 
539
//            bsdDisklabel.DisplayBSDData();
 
540
            ClearGPTData();
 
541
            protectiveMBR.MakeProtectiveMBR(1); // clear boot area (option 1)
 
542
            XFormDisklabel(&bsdDisklabel, 0);
 
543
            break;
 
544
         case use_gpt:
 
545
            break;
 
546
         case use_new:
 
547
            ClearGPTData();
 
548
            protectiveMBR.MakeProtectiveMBR();
 
549
            break;
 
550
      } // switch
 
551
 
 
552
      // Now find the first and last sectors used by partitions...
 
553
      if (allOK) {
 
554
         firstBlock = mainHeader.backupLBA; // start high
 
555
         lastBlock = 0; // start low
 
556
         for (i = 0; i < mainHeader.numParts; i++) {
 
557
            if ((partitions[i].GetFirstLBA() < firstBlock) &&
 
558
                 (partitions[i].GetFirstLBA() > 0))
 
559
               firstBlock = partitions[i].GetFirstLBA();
 
560
            if (partitions[i].GetLastLBA() > lastBlock)
 
561
               lastBlock = partitions[i].GetLastLBA();
 
562
         } // for
 
563
      } // if
 
564
      CheckGPTSize();
 
565
   } else {
 
566
      allOK = 0;
 
567
      fprintf(stderr, "Problem opening %s for reading! Error is %d\n",
 
568
              deviceFilename, errno);
 
569
      if (errno == EACCES) { // User is probably not running as root
 
570
         fprintf(stderr, "You must run this program as root or use sudo!\n");
 
571
      } // if
 
572
   } // if/else
 
573
   return (allOK);
 
574
} // GPTData::LoadPartitions()
 
575
 
 
576
// Loads the GPT, as much as possible. Returns 1 if this seems to have
 
577
// succeeded, 0 if there are obvious problems....
 
578
int GPTData::ForceLoadGPTData(int fd) {
 
579
   int allOK = 1, validHeaders;
 
580
   off_t seekTo;
 
581
   char* storage;
 
582
   uint32_t newCRC, sizeOfParts;
 
583
 
 
584
   // Seek to and read the main GPT header
 
585
   lseek64(fd, 512, SEEK_SET);
 
586
   read(fd, &mainHeader, 512); // read main GPT header
 
587
   mainCrcOk = CheckHeaderCRC(&mainHeader);
 
588
   if (IsLittleEndian() == 0) // big-endian system; adjust header byte order....
 
589
      ReverseHeaderBytes(&mainHeader);
 
590
 
 
591
   // Load backup header, check its CRC, and store the results of the
 
592
   // check for future reference. Load backup header using pointer in main
 
593
   // header if possible; but if main header has a CRC error, or if it
 
594
   // points to beyond the end of the disk, load the last sector of the
 
595
   // disk instead.
 
596
   if (mainCrcOk) {
 
597
      if (mainHeader.backupLBA < diskSize) {
 
598
         seekTo = mainHeader.backupLBA * blockSize;
 
599
      } else {
 
600
         seekTo = (diskSize * blockSize) - UINT64_C(512);
 
601
         printf("Warning! Disk size is smaller than the main header indicates! Loading\n"
 
602
                "secondary header from the last sector of the disk! You should use 'v' to\n"
 
603
                "verify disk integrity, and perhaps options on the experts' menu to repair\n"
 
604
                "the disk.\n");
 
605
      } // else
 
606
   } else {
 
607
      seekTo = (diskSize * blockSize) - UINT64_C(512);
 
608
   } // if/else (mainCrcOk)
 
609
 
 
610
   if (lseek64(fd, seekTo, SEEK_SET) != (off_t) -1) {
 
611
      read(fd, &secondHeader, 512); // read secondary GPT header
 
612
      secondCrcOk = CheckHeaderCRC(&secondHeader);
 
613
      if (IsLittleEndian() == 0) // big-endian system; adjust header byte order....
 
614
         ReverseHeaderBytes(&secondHeader);
 
615
   } else {
 
616
      allOK = 0;
 
617
      state = gpt_invalid;
 
618
      fprintf(stderr, "Unable to seek to secondary GPT header at sector %llu!\n",
 
619
              diskSize - (UINT64_C(1)));
 
620
   } // if/else lseek
 
621
 
 
622
   // Return valid headers code: 0 = both headers bad; 1 = main header
 
623
   // good, backup bad; 2 = backup header good, main header bad;
 
624
   // 3 = both headers good. Note these codes refer to valid GPT
 
625
   // signatures and version numbers; more subtle problems will elude
 
626
   // this check!
 
627
   validHeaders = CheckHeaderValidity();
 
628
 
 
629
   // Read partitions (from primary array)
 
630
   if (validHeaders > 0) { // if at least one header is OK....
 
631
      // GPT appears to be valid....
 
632
      state = gpt_valid;
 
633
 
 
634
      // We're calling the GPT valid, but there's a possibility that one
 
635
      // of the two headers is corrupt. If so, use the one that seems to
 
636
      // be in better shape to regenerate the bad one
 
637
      if (validHeaders == 2) { // valid backup header, invalid main header
 
638
         printf("Caution: invalid main GPT header, but valid backup; regenerating main header\n"
 
639
               "from backup!\n");
 
640
         RebuildMainHeader();
 
641
         mainCrcOk = secondCrcOk; // Since copied, use CRC validity of backup
 
642
      } else if (validHeaders == 1) { // valid main header, invalid backup
 
643
         printf("Caution: invalid backup GPT header, but valid main header; regenerating\n"
 
644
               "backup header from main header.\n");
 
645
         RebuildSecondHeader();
 
646
         secondCrcOk = mainCrcOk; // Since regenerated, use CRC validity of main
 
647
      } // if/else/if
 
648
 
 
649
      // Load the main partition table, including storing results of its
 
650
      // CRC check
 
651
      if (LoadMainTable() == 0)
 
652
         allOK = 0;
 
653
 
 
654
      // Load backup partition table into temporary storage to check
 
655
      // its CRC and store the results, then discard this temporary
 
656
      // storage, since we don't use it in any but recovery operations
 
657
      seekTo = secondHeader.partitionEntriesLBA * (off_t) blockSize;
 
658
      if ((lseek64(fd, seekTo, SEEK_SET) != (off_t) -1) && (secondCrcOk)) {
 
659
         sizeOfParts = secondHeader.numParts * secondHeader.sizeOfPartitionEntries;
 
660
         storage = (char*) malloc(sizeOfParts);
 
661
         read(fd, storage, sizeOfParts);
 
662
         newCRC = chksum_crc32((unsigned char*) storage,  sizeOfParts);
 
663
         free(storage);
 
664
         secondPartsCrcOk = (newCRC == secondHeader.partitionEntriesCRC);
 
665
      } // if
 
666
 
 
667
      // Check for valid CRCs and warn if there are problems
 
668
      if ((mainCrcOk == 0) || (secondCrcOk == 0) || (mainPartsCrcOk == 0) ||
 
669
           (secondPartsCrcOk == 0)) {
 
670
         printf("Warning! One or more CRCs don't match. You should repair the disk!\n");
 
671
         state = gpt_corrupt;
 
672
           } // if
 
673
   } else {
 
674
      state = gpt_invalid;
 
675
   } // if/else
 
676
   return allOK;
 
677
} // GPTData::ForceLoadGPTData()
 
678
 
 
679
// Loads the partition table pointed to by the main GPT header. The
 
680
// main GPT header in memory MUST be valid for this call to do anything
 
681
// sensible!
 
682
int GPTData::LoadMainTable(void) {
 
683
   int fd, retval = 0;
 
684
   uint32_t newCRC, sizeOfParts;
 
685
 
 
686
   if ((fd = open(device, O_RDONLY)) != -1) {
 
687
      // Set internal data structures for number of partitions on the disk
 
688
      SetGPTSize(mainHeader.numParts);
 
689
 
 
690
      // Load main partition table, and record whether its CRC
 
691
      // matches the stored value
 
692
      lseek64(fd, mainHeader.partitionEntriesLBA * blockSize, SEEK_SET);
 
693
      sizeOfParts = mainHeader.numParts * mainHeader.sizeOfPartitionEntries;
 
694
      read(fd, partitions, sizeOfParts);
 
695
      newCRC = chksum_crc32((unsigned char*) partitions, sizeOfParts);
 
696
      mainPartsCrcOk = (newCRC == mainHeader.partitionEntriesCRC);
 
697
      if (IsLittleEndian() == 0)
 
698
         ReversePartitionBytes();
 
699
      retval = 1;
 
700
   } // if
 
701
   return retval;
 
702
} // GPTData::LoadMainTable()
 
703
 
 
704
// Load the second (backup) partition table as the primary partition
 
705
// table. Used in repair functions
 
706
void GPTData::LoadSecondTableAsMain(void) {
 
707
   int fd;
 
708
   off_t seekTo;
 
709
   uint32_t sizeOfParts, newCRC;
 
710
 
 
711
   if ((fd = open(device, O_RDONLY)) != -1) {
 
712
      seekTo = secondHeader.partitionEntriesLBA * (off_t) blockSize;
 
713
      if (lseek64(fd, seekTo, SEEK_SET) != (off_t) -1) {
 
714
         SetGPTSize(secondHeader.numParts);
 
715
         sizeOfParts = secondHeader.numParts * secondHeader.sizeOfPartitionEntries;
 
716
         read(fd, partitions, sizeOfParts);
 
717
         newCRC = chksum_crc32((unsigned char*) partitions, sizeOfParts);
 
718
         secondPartsCrcOk = (newCRC == secondHeader.partitionEntriesCRC);
 
719
         mainPartsCrcOk = secondPartsCrcOk;
 
720
         if (IsLittleEndian() == 0)
 
721
            ReversePartitionBytes();
 
722
         if (!secondPartsCrcOk) {
 
723
            printf("Error! After loading backup partitions, the CRC still doesn't check out!\n");
 
724
         } // if
 
725
      } else {
 
726
         printf("Error! Couldn't seek to backup partition table!\n");
 
727
      } // if/else
 
728
   } else {
 
729
      printf("Error! Couldn't open device %s when recovering backup partition table!\n");
 
730
   } // if/else
 
731
} // GPTData::LoadSecondTableAsMain()
 
732
 
 
733
// Writes GPT (and protective MBR) to disk. Returns 1 on successful
 
734
// write, 0 if there was a problem.
 
735
int GPTData::SaveGPTData(void) {
 
736
   int allOK = 1;
 
737
   char answer, line[256];
 
738
   int fd;
 
739
   uint64_t secondTable;
 
740
   uint32_t numParts;
 
741
   off_t offset;
 
742
 
 
743
   if (strlen(device) == 0) {
 
744
      printf("Device not defined.\n");
 
745
   } // if
 
746
 
 
747
   // First do some final sanity checks....
 
748
   // Is there enough space to hold the GPT headers and partition tables,
 
749
   // given the partition sizes?
 
750
   if (CheckGPTSize() > 0) {
 
751
      allOK = 0;
 
752
   } // if
 
753
 
 
754
   // Check that disk is really big enough to handle this...
 
755
   if (mainHeader.backupLBA > diskSize) {
 
756
      fprintf(stderr, "Error! Disk is too small! The 'e' option on the experts' menu might fix the\n"
 
757
              "problem (or it might not). Aborting!\n");
 
758
      printf("(Disk size is %ld sectors, needs to be %ld sectors.)\n", diskSize,
 
759
             mainHeader.backupLBA);
 
760
      allOK = 0;
 
761
   } // if
 
762
   // Check that second header is properly placed. Warn and ask if this should
 
763
   // be corrected if the test fails....
 
764
   if (mainHeader.backupLBA < (diskSize - UINT64_C(1))) {
 
765
      printf("Warning! Secondary header is placed too early on the disk! Do you want to\n"
 
766
             "correct this problem? ");
 
767
      if (GetYN() == 'Y') {
 
768
         MoveSecondHeaderToEnd();
 
769
         printf("Have moved second header and partition table to correct location.\n");
 
770
      } else {
 
771
         printf("Have not corrected the problem. Strange problems may occur in the future!\n");
 
772
      } // if correction requested
 
773
   } // if
 
774
 
 
775
   // Check for overlapping partitions....
 
776
   if (FindOverlaps() > 0) {
 
777
      allOK = 0;
 
778
      fprintf(stderr, "Aborting write operation!\n");
 
779
   } // if
 
780
 
 
781
   // Check for mismatched MBR and GPT data, but let it pass if found
 
782
   // (function displays warning message)
 
783
   FindHybridMismatches();
 
784
 
 
785
   // Pull out some data that's needed before doing byte-order reversal on
 
786
   // big-endian systems....
 
787
   numParts = mainHeader.numParts;
 
788
   secondTable = secondHeader.partitionEntriesLBA;
 
789
   if (IsLittleEndian() == 0) {
 
790
      // Reverse partition bytes first, since that function requires non-reversed
 
791
      // data from the main header....
 
792
      ReversePartitionBytes();
 
793
      ReverseHeaderBytes(&mainHeader);
 
794
      ReverseHeaderBytes(&secondHeader);
 
795
   } // if
 
796
   RecomputeCRCs();
 
797
 
 
798
   if (allOK) {
 
799
      printf("\nFinal checks complete. About to write GPT data. THIS WILL OVERWRITE EXISTING\n");
 
800
      printf("MBR PARTITIONS!! THIS PROGRAM IS BETA QUALITY AT BEST. IF YOU LOSE ALL YOUR\n");
 
801
      printf("DATA, YOU HAVE ONLY YOURSELF TO BLAME IF YOU ANSWER 'Y' BELOW!\n\n");
 
802
      printf("Do you want to proceed, possibly destroying your data? (Y/N) ");
 
803
      fgets(line, 255, stdin);
 
804
      sscanf(line, "%c", &answer);
 
805
      if ((answer == 'Y') || (answer == 'y')) {
 
806
         printf("OK; writing new GPT partition table.\n");
 
807
      } else {
 
808
         allOK = 0;
 
809
      } // if/else
 
810
   } // if
 
811
 
 
812
   // Do it!
 
813
   if (allOK) {
 
814
      fd = OpenForWrite(device);
 
815
      if (fd != -1) {
 
816
         // First, write the protective MBR...
 
817
         protectiveMBR.WriteMBRData(fd);
 
818
 
 
819
         // Now write the main GPT header...
 
820
         if (allOK)
 
821
            if (write(fd, &mainHeader, 512) == -1)
 
822
               allOK = 0;
 
823
 
 
824
         // Now write the main partition tables...
 
825
         if (allOK) {
 
826
            if (write(fd, partitions, GPT_SIZE * numParts) == -1)
 
827
               allOK = 0;
 
828
         } // if
 
829
 
 
830
         // Now seek to near the end to write the secondary GPT....
 
831
         if (allOK) {
 
832
            offset = (off_t) secondTable * (off_t) (blockSize);
 
833
            if (lseek64(fd, offset, SEEK_SET) == (off_t) - 1) {
 
834
               allOK = 0;
 
835
               printf("Unable to seek to end of disk!\n");
 
836
            } // if
 
837
         } // if
 
838
 
 
839
         // Now write the secondary partition tables....
 
840
         if (allOK)
 
841
            if (write(fd, partitions, GPT_SIZE * numParts) == -1)
 
842
               allOK = 0;
 
843
 
 
844
         // Now write the secondary GPT header...
 
845
         if (allOK)
 
846
            if (write(fd, &secondHeader, 512) == -1)
 
847
               allOK = 0;
 
848
 
 
849
         // re-read the partition table
 
850
         if (allOK) {
 
851
            DiskSync(fd);
 
852
         } // if
 
853
 
 
854
         if (allOK) { // writes completed OK
 
855
            printf("The operation has completed successfully.\n");
 
856
         } else {
 
857
            printf("Warning! An error was reported when writing the partition table! This error\n");
 
858
            printf("MIGHT be harmless, but you may have trashed the disk! Use parted and, if\n");
 
859
            printf("necessary, restore your original partition table.\n");
 
860
         } // if/else
 
861
         close(fd);
 
862
      } else {
 
863
         fprintf(stderr, "Unable to open device %s for writing! Errno is %d! Aborting write!\n",
 
864
                 device, errno);
 
865
         allOK = 0;
 
866
      } // if/else
 
867
   } else {
 
868
      printf("Aborting write of new partition table.\n");
 
869
   } // if
 
870
 
 
871
   if (IsLittleEndian() == 0) {
 
872
      // Reverse (normalize) header bytes first, since ReversePartitionBytes()
 
873
      // requires non-reversed data in mainHeader...
 
874
      ReverseHeaderBytes(&mainHeader);
 
875
      ReverseHeaderBytes(&secondHeader);
 
876
      ReversePartitionBytes();
 
877
   } // if
 
878
 
 
879
   return (allOK);
 
880
} // GPTData::SaveGPTData()
 
881
 
 
882
// Save GPT data to a backup file. This function does much less error
 
883
// checking than SaveGPTData(). It can therefore preserve many types of
 
884
// corruption for later analysis; however, it preserves only the MBR,
 
885
// the main GPT header, the backup GPT header, and the main partition
 
886
// table; it discards the backup partition table, since it should be
 
887
// identical to the main partition table on healthy disks.
 
888
int GPTData::SaveGPTBackup(char* filename) {
 
889
   int fd, allOK = 1;
 
890
   uint32_t numParts;
 
891
 
 
892
   if ((fd = open(filename, O_WRONLY | O_CREAT, S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH)) != -1) {
 
893
      // Reverse the byte order, if necessary....
 
894
      numParts = mainHeader.numParts;
 
895
      if (IsLittleEndian() == 0) {
 
896
         ReversePartitionBytes();
 
897
         ReverseHeaderBytes(&mainHeader);
 
898
         ReverseHeaderBytes(&secondHeader);
 
899
      } // if
 
900
 
 
901
      // Recomputing the CRCs is likely to alter them, which could be bad
 
902
      // if the intent is to save a potentially bad GPT for later analysis;
 
903
      // but if we don't do this, we get bogus errors when we load the
 
904
      // backup. I'm favoring misses over false alarms....
 
905
      RecomputeCRCs();
 
906
 
 
907
      // Now write the protective MBR...
 
908
      protectiveMBR.WriteMBRData(fd);
 
909
 
 
910
      // Now write the main GPT header...
 
911
      if (allOK)
 
912
         if (write(fd, &mainHeader, 512) == -1)
 
913
            allOK = 0;
 
914
 
 
915
      // Now write the secondary GPT header...
 
916
      if (allOK)
 
917
         if (write(fd, &secondHeader, 512) == -1)
 
918
            allOK = 0;
 
919
 
 
920
      // Now write the main partition tables...
 
921
      if (allOK) {
 
922
         if (write(fd, partitions, GPT_SIZE * numParts) == -1)
 
923
            allOK = 0;
 
924
      } // if
 
925
 
 
926
      if (allOK) { // writes completed OK
 
927
         printf("The operation has completed successfully.\n");
 
928
      } else {
 
929
         printf("Warning! An error was reported when writing the backup file.\n");
 
930
         printf("It may not be usable!\n");
 
931
      } // if/else
 
932
      close(fd);
 
933
 
 
934
      // Now reverse the byte-order reversal, if necessary....
 
935
      if (IsLittleEndian() == 0) {
 
936
         ReverseHeaderBytes(&mainHeader);
 
937
         ReverseHeaderBytes(&secondHeader);
 
938
         ReversePartitionBytes();
 
939
      } // if
 
940
   } else {
 
941
      fprintf(stderr, "Unable to open file %s for writing! Aborting!\n", filename);
 
942
      allOK = 0;
 
943
   } // if/else
 
944
   return allOK;
 
945
} // GPTData::SaveGPTBackup()
 
946
 
 
947
// Load GPT data from a backup file created by SaveGPTBackup(). This function
 
948
// does minimal error checking. It returns 1 if it completed successfully,
 
949
// 0 if there was a problem. In the latter case, it creates a new empty
 
950
// set of partitions.
 
951
int GPTData::LoadGPTBackup(char* filename) {
 
952
   int fd, allOK = 1, val;
 
953
   uint32_t numParts, sizeOfEntries, sizeOfParts, newCRC;
 
954
   int littleEndian = 1;
 
955
 
 
956
   if ((fd = open(filename, O_RDONLY)) != -1) {
 
957
      if (IsLittleEndian() == 0)
 
958
         littleEndian = 0;
 
959
 
 
960
      // Let the MBRData class load the saved MBR...
 
961
      protectiveMBR.ReadMBRData(fd, 0); // 0 = don't check block size
 
962
 
 
963
      // Load the main GPT header, check its vaility, and set the GPT
 
964
      // size based on the data
 
965
      read(fd, &mainHeader, 512);
 
966
      mainCrcOk = CheckHeaderCRC(&mainHeader);
 
967
 
 
968
      // Reverse byte order, if necessary
 
969
      if (littleEndian == 0) {
 
970
         ReverseHeaderBytes(&mainHeader);
 
971
      } // if
 
972
 
 
973
      // Load the backup GPT header in much the same way as the main
 
974
      // GPT header....
 
975
      read(fd, &secondHeader, 512);
 
976
      secondCrcOk = CheckHeaderCRC(&secondHeader);
 
977
 
 
978
      // Reverse byte order, if necessary
 
979
      if (littleEndian == 0) {
 
980
         ReverseHeaderBytes(&secondHeader);
 
981
      } // if
 
982
 
 
983
      // Return valid headers code: 0 = both headers bad; 1 = main header
 
984
      // good, backup bad; 2 = backup header good, main header bad;
 
985
      // 3 = both headers good. Note these codes refer to valid GPT
 
986
      // signatures and version numbers; more subtle problems will elude
 
987
      // this check!
 
988
      if ((val = CheckHeaderValidity()) > 0) {
 
989
         if (val == 2) { // only backup header seems to be good
 
990
            numParts = secondHeader.numParts;
 
991
            sizeOfEntries = secondHeader.sizeOfPartitionEntries;
 
992
         } else { // main header is OK
 
993
            numParts = mainHeader.numParts;
 
994
            sizeOfEntries = mainHeader.sizeOfPartitionEntries;
 
995
         } // if/else
 
996
 
 
997
         SetGPTSize(numParts);
 
998
 
 
999
         // If current disk size doesn't match that of backup....
 
1000
         if (secondHeader.currentLBA != diskSize - UINT64_C(1)) {
 
1001
            printf("Warning! Current disk size doesn't match that of the backup!\n"
 
1002
                  "Adjusting sizes to match, but subsequent problems are possible!\n");
 
1003
            MoveSecondHeaderToEnd();
 
1004
         } // if
 
1005
 
 
1006
         // Load main partition table, and record whether its CRC
 
1007
         // matches the stored value
 
1008
         sizeOfParts = numParts * sizeOfEntries;
 
1009
         read(fd, partitions, sizeOfParts);
 
1010
 
 
1011
         newCRC = chksum_crc32((unsigned char*) partitions, sizeOfParts);
 
1012
         mainPartsCrcOk = (newCRC == mainHeader.partitionEntriesCRC);
 
1013
         secondPartsCrcOk = (newCRC == secondHeader.partitionEntriesCRC);
 
1014
         // Reverse byte order, if necessary
 
1015
         if (littleEndian == 0) {
 
1016
            ReversePartitionBytes();
 
1017
         } // if
 
1018
 
 
1019
      } else {
 
1020
         allOK = 0;
 
1021
      } // if/else
 
1022
   } else {
 
1023
      allOK = 0;
 
1024
      fprintf(stderr, "Unable to open file %s for reading! Aborting!\n", filename);
 
1025
   } // if/else
 
1026
 
 
1027
   // Something went badly wrong, so blank out partitions
 
1028
   if (allOK == 0) {
 
1029
      ClearGPTData();
 
1030
      protectiveMBR.MakeProtectiveMBR();
 
1031
   } // if
 
1032
   return allOK;
 
1033
} // GPTData::LoadGPTBackup()
 
1034
 
 
1035
// Tell user whether Apple Partition Map (APM) was discovered....
 
1036
void GPTData::ShowAPMState(void) {
 
1037
   if (apmFound)
 
1038
      printf("  APM: present\n");
 
1039
   else
 
1040
      printf("  APM: not present\n");
 
1041
} // GPTData::ShowAPMState()
 
1042
 
 
1043
// Tell user about the state of the GPT data....
 
1044
void GPTData::ShowGPTState(void) {
 
1045
   switch (state) {
 
1046
      case gpt_invalid:
 
1047
         printf("  GPT: not present\n");
 
1048
         break;
 
1049
      case gpt_valid:
 
1050
         printf("  GPT: present\n");
 
1051
         break;
 
1052
      case gpt_corrupt:
 
1053
         printf("  GPT: damaged\n");
 
1054
         break;
 
1055
      default:
 
1056
         printf("\a  GPT: unknown -- bug!\n");
 
1057
         break;
 
1058
   } // switch
 
1059
} // GPTData::ShowGPTState()
 
1060
 
 
1061
// Display the basic GPT data
 
1062
void GPTData::DisplayGPTData(void) {
 
1063
   int i;
 
1064
   char sizeInSI[255]; // String to hold size of disk in SI units
 
1065
   char tempStr[255];
 
1066
   uint64_t temp, totalFree;
 
1067
 
 
1068
   BytesToSI(diskSize * blockSize, sizeInSI);
 
1069
   printf("Disk %s: %llu sectors, %s\n", device,
 
1070
          (unsigned long long) diskSize, sizeInSI);
 
1071
   printf("Disk identifier (GUID): %s\n", GUIDToStr(mainHeader.diskGUID, tempStr));
 
1072
   printf("Partition table holds up to %lu entries\n", (unsigned long) mainHeader.numParts);
 
1073
   printf("First usable sector is %lu, last usable sector is %lu\n",
 
1074
          (unsigned long) mainHeader.firstUsableLBA,
 
1075
           (unsigned long) mainHeader.lastUsableLBA);
 
1076
   totalFree = FindFreeBlocks(&i, &temp);
 
1077
   printf("Total free space is %llu sectors (%s)\n", totalFree,
 
1078
          BytesToSI(totalFree * (uint64_t) blockSize, sizeInSI));
 
1079
   printf("\nNumber  Start (sector)    End (sector)  Size       Code  Name\n");
 
1080
   for (i = 0; i < mainHeader.numParts; i++) {
 
1081
      partitions[i].ShowSummary(i, blockSize);
 
1082
   } // for
 
1083
} // GPTData::DisplayGPTData()
 
1084
 
 
1085
// Get partition number from user and then call ShowPartDetails(partNum)
 
1086
// to show its detailed information
 
1087
void GPTData::ShowDetails(void) {
 
1088
   int partNum;
 
1089
   uint32_t low, high;
 
1090
 
 
1091
   if (GetPartRange(&low, &high) > 0) {
 
1092
      partNum = GetPartNum();
 
1093
      ShowPartDetails(partNum);
 
1094
   } else {
 
1095
      printf("No partitions\n");
 
1096
   } // if/else
 
1097
} // GPTData::ShowDetails()
 
1098
 
 
1099
// Show detailed information on the specified partition
 
1100
void GPTData::ShowPartDetails(uint32_t partNum) {
 
1101
   if (partitions[partNum].GetFirstLBA() != 0) {
 
1102
      partitions[partNum].ShowDetails(blockSize);
 
1103
   } else {
 
1104
      printf("Partition #%d does not exist.", (int) (partNum + 1));
 
1105
   } // if
 
1106
} // GPTData::ShowPartDetails()
 
1107
 
 
1108
/*********************************************************************
 
1109
 *                                                                   *
 
1110
 * Begin functions that obtain information from the users, and often *
 
1111
 * do something with that information (call other functions)         *
 
1112
 *                                                                   *
 
1113
 *********************************************************************/
 
1114
 
 
1115
// Prompts user for partition number and returns the result.
 
1116
uint32_t GPTData::GetPartNum(void) {
 
1117
   uint32_t partNum;
 
1118
   uint32_t low, high;
 
1119
   char prompt[255];
 
1120
 
 
1121
   if (GetPartRange(&low, &high) > 0) {
 
1122
      sprintf(prompt, "Partition number (%d-%d): ", low + 1, high + 1);
 
1123
      partNum = GetNumber(low + 1, high + 1, low, prompt);
 
1124
   } else partNum = 1;
 
1125
   return (partNum - 1);
 
1126
} // GPTData::GetPartNum()
 
1127
 
 
1128
// What it says: Resize the partition table. (Default is 128 entries.)
 
1129
void GPTData::ResizePartitionTable(void) {
 
1130
   int newSize;
 
1131
   char prompt[255];
 
1132
   uint32_t curLow, curHigh;
 
1133
 
 
1134
   printf("Current partition table size is %lu.\n",
 
1135
          (unsigned long) mainHeader.numParts);
 
1136
   GetPartRange(&curLow, &curHigh);
 
1137
   curHigh++; // since GetPartRange() returns numbers starting from 0...
 
1138
   // There's no point in having fewer than four partitions....
 
1139
   if (curHigh < 4)
 
1140
      curHigh = 4;
 
1141
   sprintf(prompt, "Enter new size (%d up, default %d): ", (int) curHigh,
 
1142
           (int) NUM_GPT_ENTRIES);
 
1143
   newSize = GetNumber(4, 65535, 128, prompt);
 
1144
   if (newSize < 128) {
 
1145
      printf("Caution: The partition table size should officially be 16KB or larger,\n"
 
1146
            "which works out to 128 entries. In practice, smaller tables seem to\n"
 
1147
            "work with most OSes, but this practice is risky. I'm proceeding with\n"
 
1148
            "the resize, but you may want to reconsider this action and undo it.\n\n");
 
1149
   } // if
 
1150
   SetGPTSize(newSize);
 
1151
} // GPTData::ResizePartitionTable()
 
1152
 
 
1153
// Interactively create a partition
 
1154
void GPTData::CreatePartition(void) {
 
1155
   uint64_t firstBlock, firstInLargest, lastBlock, sector;
 
1156
   char prompt[255];
 
1157
   int partNum, firstFreePart = 0;
 
1158
 
 
1159
   // Find first free partition...
 
1160
   while (partitions[firstFreePart].GetFirstLBA() != 0) {
 
1161
      firstFreePart++;
 
1162
   } // while
 
1163
 
 
1164
   if (((firstBlock = FindFirstAvailable()) != 0) &&
 
1165
         (firstFreePart < mainHeader.numParts)) {
 
1166
      lastBlock = FindLastAvailable(firstBlock);
 
1167
      firstInLargest = FindFirstInLargest();
 
1168
 
 
1169
      // Get partition number....
 
1170
      do {
 
1171
         sprintf(prompt, "Partition number (%d-%d, default %d): ", firstFreePart + 1,
 
1172
                 mainHeader.numParts, firstFreePart + 1);
 
1173
         partNum = GetNumber(firstFreePart + 1, mainHeader.numParts,
 
1174
                             firstFreePart + 1, prompt) - 1;
 
1175
         if (partitions[partNum].GetFirstLBA() != 0)
 
1176
            printf("partition %d is in use.\n", partNum + 1);
 
1177
      } while (partitions[partNum].GetFirstLBA() != 0);
 
1178
 
 
1179
      // Get first block for new partition...
 
1180
      sprintf(prompt,
 
1181
              "First sector (%llu-%llu, default = %llu) or {+-}size{KMGT}: ",
 
1182
              firstBlock, lastBlock, firstInLargest);
 
1183
      do {
 
1184
         sector = GetSectorNum(firstBlock, lastBlock, firstInLargest, prompt);
 
1185
      } while (IsFree(sector) == 0);
 
1186
      firstBlock = sector;
 
1187
 
 
1188
      // Get last block for new partitions...
 
1189
      lastBlock = FindLastInFree(firstBlock);
 
1190
      sprintf(prompt,
 
1191
              "Last sector (%llu-%llu, default = %llu) or {+-}size{KMGT}: ",
 
1192
              firstBlock, lastBlock, lastBlock);
 
1193
      do {
 
1194
         sector = GetSectorNum(firstBlock, lastBlock, lastBlock, prompt);
 
1195
      } while (IsFree(sector) == 0);
 
1196
      lastBlock = sector;
 
1197
 
 
1198
      partitions[partNum].SetFirstLBA(firstBlock);
 
1199
      partitions[partNum].SetLastLBA(lastBlock);
 
1200
 
 
1201
      partitions[partNum].SetUniqueGUID(1);
 
1202
      partitions[partNum].ChangeType();
 
1203
      partitions[partNum].SetName((unsigned char*) partitions[partNum].GetNameType(prompt));
 
1204
         } else {
 
1205
            printf("No free sectors available\n");
 
1206
         } // if/else
 
1207
} // GPTData::CreatePartition()
 
1208
 
 
1209
// Interactively delete a partition (duh!)
 
1210
void GPTData::DeletePartition(void) {
 
1211
   int partNum;
 
1212
   uint32_t low, high;
 
1213
   uint64_t startSector, length;
 
1214
   char prompt[255];
 
1215
 
 
1216
   if (GetPartRange(&low, &high) > 0) {
 
1217
      sprintf(prompt, "Partition number (%d-%d): ", low + 1, high + 1);
 
1218
      partNum = GetNumber(low + 1, high + 1, low, prompt);
 
1219
 
 
1220
      // In case there's a protective MBR, look for & delete matching
 
1221
      // MBR partition....
 
1222
      startSector = partitions[partNum - 1].GetFirstLBA();
 
1223
      length = partitions[partNum - 1].GetLengthLBA();
 
1224
      protectiveMBR.DeleteByLocation(startSector, length);
 
1225
 
 
1226
      // Now delete the GPT partition
 
1227
      partitions[partNum - 1].BlankPartition();
 
1228
   } else {
 
1229
      printf("No partitions\n");
 
1230
   } // if/else
 
1231
} // GPTData::DeletePartition()
 
1232
 
 
1233
// Prompt user for a partition number, then change its type code
 
1234
// using ChangeGPTType(struct GPTPartition*) function.
 
1235
void GPTData::ChangePartType(void) {
 
1236
   int partNum;
 
1237
   uint32_t low, high;
 
1238
 
 
1239
   if (GetPartRange(&low, &high) > 0) {
 
1240
      partNum = GetPartNum();
 
1241
      partitions[partNum].ChangeType();
 
1242
   } else {
 
1243
      printf("No partitions\n");
 
1244
   } // if/else
 
1245
} // GPTData::ChangePartType()
 
1246
 
 
1247
// Partition attributes seem to be rarely used, but I want a way to
 
1248
// adjust them for completeness....
 
1249
void GPTData::SetAttributes(uint32_t partNum) {
 
1250
   Attributes theAttr;
 
1251
 
 
1252
   theAttr.SetAttributes(partitions[partNum].GetAttributes());
 
1253
   theAttr.DisplayAttributes();
 
1254
   theAttr.ChangeAttributes();
 
1255
   partitions[partNum].SetAttributes(theAttr.GetAttributes());
 
1256
} // GPTData::SetAttributes()
 
1257
 
 
1258
// This function destroys the on-disk GPT structures. Returns 1 if the
 
1259
// user confirms destruction, 0 if the user aborts.
 
1260
// If prompt == 0, don't ask user about proceeding and do NOT wipe out
 
1261
// MBR. (Set prompt == 0 when doing a GPT-to-MBR conversion.)
 
1262
int GPTData::DestroyGPT(int prompt) {
 
1263
   int fd, i;
 
1264
   char blankSector[512], goOn = 'Y', blank = 'N';
 
1265
 
 
1266
   for (i = 0; i < 512; i++) {
 
1267
      blankSector[i] = '\0';
 
1268
   } // for
 
1269
 
 
1270
   if (((apmFound) || (bsdFound)) && prompt) {
 
1271
      printf("WARNING: APM or BSD disklabel structures detected! This operation could\n"
 
1272
             "damage any APM or BSD partitions on this disk!\n");
 
1273
   } // if APM or BSD
 
1274
   if (prompt) {
 
1275
      printf("\a\aAbout to wipe out GPT on %s. Proceed? ", device);
 
1276
      goOn = GetYN();
 
1277
   } // if
 
1278
   if (goOn == 'Y') {
 
1279
      fd = open(device, O_WRONLY);
 
1280
#ifdef __APPLE__
 
1281
      // MacOS X requires a shared lock under some circumstances....
 
1282
      if (fd < 0) {
 
1283
         fd = open(device, O_WRONLY|O_SHLOCK);
 
1284
      } // if
 
1285
#endif
 
1286
      if (fd != -1) {
 
1287
         lseek64(fd, mainHeader.currentLBA * 512, SEEK_SET); // seek to GPT header
 
1288
         write(fd, blankSector, 512); // blank it out
 
1289
         lseek64(fd, mainHeader.partitionEntriesLBA * 512, SEEK_SET); // seek to partition table
 
1290
         for (i = 0; i < GetBlocksInPartTable(); i++)
 
1291
            write(fd, blankSector, 512);
 
1292
         lseek64(fd, secondHeader.partitionEntriesLBA * 512, SEEK_SET); // seek to partition table
 
1293
         for (i = 0; i < GetBlocksInPartTable(); i++)
 
1294
            write(fd, blankSector, 512);
 
1295
         lseek64(fd, secondHeader.currentLBA * 512, SEEK_SET); // seek to GPT header
 
1296
         write(fd, blankSector, 512); // blank it out
 
1297
         if (prompt) {
 
1298
            printf("Blank out MBR? ");
 
1299
            blank = GetYN();
 
1300
         }// if
 
1301
         // Note on below: Touch the MBR only if the user wants it completely
 
1302
         // blanked out. Version 0.4.2 deleted the 0xEE partition and re-wrote
 
1303
         // the MBR, but this could wipe out a valid MBR that the program
 
1304
         // had subsequently discarded (say, if it conflicted with older GPT
 
1305
         // structures).
 
1306
         if (blank == 'Y') {
 
1307
            lseek64(fd, 0, SEEK_SET);
 
1308
            write(fd, blankSector, 512); // blank it out
 
1309
         } else {
 
1310
            printf("MBR is unchanged. You may need to delete an EFI GPT (0xEE) partition\n"
 
1311
                   "with fdisk or another tool.\n");
 
1312
         } // if/else
 
1313
         DiskSync(fd);
 
1314
         close(fd);
 
1315
         printf("GPT data structures destroyed! You may now partition the disk using fdisk or\n"
 
1316
               "other utilities. Program will now terminate.\n");
 
1317
      } else {
 
1318
         printf("Problem opening %s for writing! Program will now terminate.\n");
 
1319
      } // if/else (fd != -1)
 
1320
   } // if (goOn == 'Y')
 
1321
   return (goOn == 'Y');
 
1322
} // GPTData::DestroyGPT()
 
1323
 
 
1324
/**************************************************************************
 
1325
 *                                                                        *
 
1326
 * Partition table transformation functions (MBR or BSD disklabel to GPT) *
 
1327
 * (some of these functions may require user interaction)                 *
 
1328
 *                                                                        *
 
1329
 **************************************************************************/
 
1330
 
 
1331
// Examines the MBR & GPT data, and perhaps asks the user questions, to
 
1332
// determine which set of data to use: the MBR (use_mbr), the GPT (use_gpt),
 
1333
// or create a new set of partitions (use_new)
 
1334
WhichToUse GPTData::UseWhichPartitions(void) {
 
1335
   WhichToUse which = use_new;
 
1336
   MBRValidity mbrState;
 
1337
   int answer;
 
1338
 
 
1339
   mbrState = protectiveMBR.GetValidity();
 
1340
 
 
1341
   if ((state == gpt_invalid) && ((mbrState == mbr) || (mbrState == hybrid))) {
 
1342
      printf("\n\a***************************************************************\n"
 
1343
            "Found invalid GPT and valid MBR; converting MBR to GPT format.\n"
 
1344
            "THIS OPERATON IS POTENTIALLY DESTRUCTIVE! Exit by typing 'q' if\n"
 
1345
            "you don't want to convert your MBR partitions to GPT format!\n"
 
1346
            "***************************************************************\n\n");
 
1347
      which = use_mbr;
 
1348
   } // if
 
1349
 
 
1350
   if ((state == gpt_invalid) && bsdFound) {
 
1351
      printf("\n\a**********************************************************************\n"
 
1352
            "Found invalid GPT and valid BSD disklabel; converting BSD disklabel\n"
 
1353
            "to GPT format. THIS OPERATON IS POTENTIALLY DESTRUCTIVE! Your first\n"
 
1354
            "BSD partition will likely be unusable. Exit by typing 'q' if you don't\n"
 
1355
            "want to convert your BSD partitions to GPT format!\n"
 
1356
            "**********************************************************************\n\n");
 
1357
      which = use_bsd;
 
1358
   } // if
 
1359
 
 
1360
   if ((state == gpt_valid) && (mbrState == gpt)) {
 
1361
      printf("Found valid GPT with protective MBR; using GPT.\n");
 
1362
      which = use_gpt;
 
1363
   } // if
 
1364
   if ((state == gpt_valid) && (mbrState == hybrid)) {
 
1365
      printf("Found valid GPT with hybrid MBR; using GPT.\n");
 
1366
      which = use_gpt;
 
1367
   } // if
 
1368
   if ((state == gpt_valid) && (mbrState == invalid)) {
 
1369
      printf("\aFound valid GPT with corrupt MBR; using GPT and will create new\nprotective MBR on save.\n");
 
1370
      which = use_gpt;
 
1371
      protectiveMBR.MakeProtectiveMBR();
 
1372
   } // if
 
1373
   if ((state == gpt_valid) && (mbrState == mbr)) {
 
1374
      printf("Found valid MBR and GPT. Which do you want to use?\n");
 
1375
      answer = GetNumber(1, 3, 2, (char*) " 1 - MBR\n 2 - GPT\n 3 - Create blank GPT\n\nYour answer: ");
 
1376
      if (answer == 1) {
 
1377
         which = use_mbr;
 
1378
      } else if (answer == 2) {
 
1379
         which = use_gpt;
 
1380
         protectiveMBR.MakeProtectiveMBR();
 
1381
         printf("Using GPT and creating fresh protective MBR.\n");
 
1382
      } else which = use_new;
 
1383
   } // if
 
1384
 
 
1385
   // Nasty decisions here -- GPT is present, but corrupt (bad CRCs or other
 
1386
   // problems)
 
1387
   if (state == gpt_corrupt) {
 
1388
      if ((mbrState == mbr) || (mbrState == hybrid)) {
 
1389
         printf("Found valid MBR and corrupt GPT. Which do you want to use? (Using the\n"
 
1390
               "GPT MAY permit recovery of GPT data.)\n");
 
1391
         answer = GetNumber(1, 3, 2, (char*) " 1 - MBR\n 2 - GPT\n 3 - Create blank GPT\n\nYour answer: ");
 
1392
         if (answer == 1) {
 
1393
            which = use_mbr;
 
1394
//            protectiveMBR.MakeProtectiveMBR();
 
1395
         } else if (answer == 2) {
 
1396
            which = use_gpt;
 
1397
         } else which = use_new;
 
1398
      } else if (mbrState == invalid) {
 
1399
         printf("Found invalid MBR and corrupt GPT. What do you want to do? (Using the\n"
 
1400
               "GPT MAY permit recovery of GPT data.)\n");
 
1401
         answer = GetNumber(1, 2, 1, (char*) " 1 - GPT\n 2 - Create blank GPT\n\nYour answer: ");
 
1402
         if (answer == 1) {
 
1403
            which = use_gpt;
 
1404
         } else which = use_new;
 
1405
      } else { // corrupt GPT, MBR indicates it's a GPT disk....
 
1406
         printf("\a\a****************************************************************************\n"
 
1407
               "Caution: Found protective or hybrid MBR and corrupt GPT. Using GPT, but disk\n"
 
1408
               "verification and recovery are STRONGLY recommended.\n"
 
1409
               "****************************************************************************\n");
 
1410
         which = use_gpt;
 
1411
      } // if/else/else
 
1412
   } // if (corrupt GPT)
 
1413
 
 
1414
   if (which == use_new)
 
1415
      printf("Creating new GPT entries.\n");
 
1416
 
 
1417
   return which;
 
1418
} // UseWhichPartitions()
 
1419
 
 
1420
// Convert MBR partition table into GPT form
 
1421
int GPTData::XFormPartitions(void) {
 
1422
   int i, numToConvert;
 
1423
   uint8_t origType;
 
1424
   struct newGUID;
 
1425
 
 
1426
   // Clear out old data & prepare basics....
 
1427
   ClearGPTData();
 
1428
 
 
1429
   // Convert the smaller of the # of GPT or MBR partitions
 
1430
   if (mainHeader.numParts > (MAX_MBR_PARTS))
 
1431
      numToConvert = MAX_MBR_PARTS;
 
1432
   else
 
1433
      numToConvert = mainHeader.numParts;
 
1434
 
 
1435
   for (i = 0; i < numToConvert; i++) {
 
1436
      origType = protectiveMBR.GetType(i);
 
1437
      // don't waste CPU time trying to convert extended, hybrid protective, or
 
1438
      // null (non-existent) partitions
 
1439
      if ((origType != 0x05) && (origType != 0x0f) && (origType != 0x85) &&
 
1440
           (origType != 0x00) && (origType != 0xEE))
 
1441
         partitions[i] = protectiveMBR.AsGPT(i);
 
1442
   } // for
 
1443
 
 
1444
   // Convert MBR into protective MBR
 
1445
   protectiveMBR.MakeProtectiveMBR();
 
1446
 
 
1447
   // Record that all original CRCs were OK so as not to raise flags
 
1448
   // when doing a disk verification
 
1449
   mainCrcOk = secondCrcOk = mainPartsCrcOk = secondPartsCrcOk = 1;
 
1450
 
 
1451
   return (1);
 
1452
} // GPTData::XFormPartitions()
 
1453
 
 
1454
// Transforms BSD disklabel on the specified partition (numbered from 0).
 
1455
// If an invalid partition number is given, the program prompts for one.
 
1456
// Returns the number of new partitions created.
 
1457
int GPTData::XFormDisklabel(int i) {
 
1458
   uint32_t low, high, partNum, startPart;
 
1459
   uint16_t hexCode;
 
1460
   int goOn = 1, numDone = 0;
 
1461
   BSDData disklabel;
 
1462
 
 
1463
   if (GetPartRange(&low, &high) != 0) {
 
1464
      if ((i < low) || (i > high))
 
1465
         partNum = GetPartNum();
 
1466
      else
 
1467
         partNum = (uint32_t) i;
 
1468
 
 
1469
      // Find the partition after the last used one
 
1470
      startPart = high + 1;
 
1471
 
 
1472
      // Now see if the specified partition has a BSD type code....
 
1473
      hexCode = partitions[partNum].GetHexType();
 
1474
      if ((hexCode != 0xa500) && (hexCode != 0xa900)) {
 
1475
         printf("Specified partition doesn't have a disklabel partition type "
 
1476
               "code.\nContinue anyway?");
 
1477
         goOn = (GetYN() == 'Y');
 
1478
      } // if
 
1479
 
 
1480
      // If all is OK, read the disklabel and convert it.
 
1481
      if (goOn) {
 
1482
         goOn = disklabel.ReadBSDData(device, partitions[partNum].GetFirstLBA(),
 
1483
                                      partitions[partNum].GetLastLBA());
 
1484
         if ((goOn) && (disklabel.IsDisklabel())) {
 
1485
            numDone = XFormDisklabel(&disklabel, startPart);
 
1486
            if (numDone == 1)
 
1487
               printf("Converted %d BSD partition.\n", numDone);
 
1488
            else
 
1489
               printf("Converted %d BSD partitions.\n", numDone);
 
1490
         } else {
 
1491
            printf("Unable to convert partitions! Unrecognized BSD disklabel.\n");
 
1492
         } // if/else
 
1493
      } // if
 
1494
      if (numDone > 0) { // converted partitions; delete carrier
 
1495
         partitions[partNum].BlankPartition();
 
1496
      } // if
 
1497
   } else {
 
1498
      printf("No partitions\n");
 
1499
   } // if/else
 
1500
   return numDone;
 
1501
} // GPTData::XFormDisklable(int i)
 
1502
 
 
1503
// Transform the partitions on an already-loaded BSD disklabel...
 
1504
int GPTData::XFormDisklabel(BSDData* disklabel, int startPart) {
 
1505
   int i, numDone = 0;
 
1506
 
 
1507
   if ((disklabel->IsDisklabel()) && (startPart >= 0) &&
 
1508
        (startPart < mainHeader.numParts)) {
 
1509
      for (i = 0; i < disklabel->GetNumParts(); i++) {
 
1510
         partitions[i + startPart] = disklabel->AsGPT(i);
 
1511
         if (partitions[i + startPart].GetFirstLBA() != UINT64_C(0))
 
1512
            numDone++;
 
1513
      } // for
 
1514
   } // if
 
1515
 
 
1516
   // Record that all original CRCs were OK so as not to raise flags
 
1517
   // when doing a disk verification
 
1518
   mainCrcOk = secondCrcOk = mainPartsCrcOk = secondPartsCrcOk = 1;
 
1519
 
 
1520
   return numDone;
 
1521
} // GPTData::XFormDisklabel(BSDData* disklabel)
 
1522
 
 
1523
// Add one GPT partition to MBR. Used by XFormToMBR() and MakeHybrid()
 
1524
// functions. Returns 1 if operation was successful.
 
1525
int GPTData::OnePartToMBR(uint32_t gptPart, int mbrPart) {
 
1526
   int allOK = 1, typeCode, bootable;
 
1527
   uint64_t length;
 
1528
   char line[255];
 
1529
 
 
1530
   if ((mbrPart < 0) || (mbrPart > 3)) {
 
1531
      printf("MBR partition %d is out of range; omitting it.\n", mbrPart + 1);
 
1532
      allOK = 0;
 
1533
   } // if
 
1534
   if (gptPart >= mainHeader.numParts) {
 
1535
      printf("GPT partition %d is out of range; omitting it.\n", gptPart + 1);
 
1536
      allOK = 0;
 
1537
   } // if
 
1538
   if (allOK && (partitions[gptPart].GetLastLBA() == UINT64_C(0))) {
 
1539
      printf("GPT partition %d is undefined; omitting it.\n", gptPart + 1);
 
1540
      allOK = 0;
 
1541
   } // if
 
1542
   if (allOK && (partitions[gptPart].GetFirstLBA() <= UINT32_MAX) &&
 
1543
       (partitions[gptPart].GetLengthLBA() <= UINT32_MAX)) {
 
1544
      if (partitions[gptPart].GetLastLBA() > UINT32_MAX) {
 
1545
         printf("Caution: Partition end point past 32-bit pointer boundary;"
 
1546
                " some OSes may\nreact strangely.\n");
 
1547
      } // if partition ends past 32-bit (usually 2TiB) boundary
 
1548
      do {
 
1549
         printf("Enter an MBR hex code (default %02X): ",
 
1550
                  typeHelper.GUIDToID(partitions[gptPart].GetType()) / 256);
 
1551
         fgets(line, 255, stdin);
 
1552
         sscanf(line, "%x", &typeCode);
 
1553
         if (line[0] == '\n')
 
1554
            typeCode = partitions[gptPart].GetHexType() / 256;
 
1555
      } while ((typeCode <= 0) || (typeCode > 255));
 
1556
      printf("Set the bootable flag? ");
 
1557
      bootable = (GetYN() == 'Y');
 
1558
      length = partitions[gptPart].GetLengthLBA();
 
1559
      protectiveMBR.MakePart(mbrPart, (uint32_t) partitions[gptPart].GetFirstLBA(),
 
1560
                             (uint32_t) length, typeCode, bootable);
 
1561
   } else { // partition out of range
 
1562
      printf("Partition %d begins beyond the 32-bit pointer limit of MBR "
 
1563
             "partitions, or is\n too big; omitting it.\n", gptPart + 1);
 
1564
      allOK = 0;
 
1565
   } // if/else
 
1566
   return allOK;
 
1567
} // GPTData::OnePartToMBR()
 
1568
 
 
1569
// Convert the GPT to MBR form. This function is necessarily limited; it
 
1570
// handles at most four partitions and creates layouts that ignore CHS
 
1571
// geometries. Returns the number of converted partitions; if this value
 
1572
// is over 0, the calling function should call DestroyGPT() to destroy
 
1573
// the GPT data, and then exit.
 
1574
int GPTData::XFormToMBR(void) {
 
1575
   char line[255];
 
1576
   int i, j, numParts, numConverted = 0;
 
1577
   uint32_t partNums[4];
 
1578
 
 
1579
   // Get the numbers of up to four partitions to add to the
 
1580
   // hybrid MBR....
 
1581
   numParts = CountParts();
 
1582
   printf("Counted %d partitions.\n", numParts);
 
1583
 
 
1584
   // Prepare the MBR for conversion (empty it of existing partitions).
 
1585
   protectiveMBR.EmptyMBR(0);
 
1586
   protectiveMBR.SetDiskSize(diskSize);
 
1587
 
 
1588
   if (numParts > 4) { // Over four partitions; engage in triage
 
1589
      printf("Type from one to four GPT partition numbers, separated by spaces, to be\n"
 
1590
            "used in the MBR, in sequence: ");
 
1591
      fgets(line, 255, stdin);
 
1592
      numParts = sscanf(line, "%d %d %d %d", &partNums[0], &partNums[1],
 
1593
                        &partNums[2], &partNums[3]);
 
1594
   } else { // Four or fewer partitions; convert them all
 
1595
      i = j = 0;
 
1596
      while ((j < numParts) && (i < mainHeader.numParts)) {
 
1597
         if (partitions[i].GetFirstLBA() > 0) { // if GPT part. is defined
 
1598
            partNums[j++] = ++i; // flag it for conversion
 
1599
         } else i++;
 
1600
      } // while
 
1601
   } // if/else
 
1602
 
 
1603
   for (i = 0; i < numParts; i++) {
 
1604
      j = partNums[i] - 1;
 
1605
      printf("\nCreating entry for partition #%d\n", j + 1);
 
1606
      numConverted += OnePartToMBR(j, i);
 
1607
   } // for
 
1608
   return numConverted;
 
1609
} // GPTData::XFormToMBR()
 
1610
 
 
1611
// Create a hybrid MBR -- an ugly, funky thing that helps GPT work with
 
1612
// OSes that don't understand GPT.
 
1613
void GPTData::MakeHybrid(void) {
 
1614
   uint32_t partNums[3];
 
1615
   char line[255];
 
1616
   int numParts, numConverted = 0, i, j, typeCode, mbrNum;
 
1617
   char fillItUp = 'M'; // fill extra partition entries? (Yes/No/Maybe)
 
1618
   char eeFirst = 'Y'; // Whether EFI GPT (0xEE) partition comes first in table
 
1619
 
 
1620
   printf("\nWARNING! Hybrid MBRs are flaky and potentially dangerous! If you decide not\n"
 
1621
         "to use one, just hit the Enter key at the below prompt and your MBR\n"
 
1622
         "partition table will be untouched.\n\n\a");
 
1623
 
 
1624
   // Now get the numbers of up to three partitions to add to the
 
1625
   // hybrid MBR....
 
1626
   printf("Type from one to three GPT partition numbers, separated by spaces, to be\n"
 
1627
         "added to the hybrid MBR, in sequence: ");
 
1628
   fgets(line, 255, stdin);
 
1629
   numParts = sscanf(line, "%d %d %d", &partNums[0], &partNums[1], &partNums[2]);
 
1630
 
 
1631
   if (numParts > 0) {
 
1632
      // Blank out the protective MBR, but leave the boot loader code
 
1633
      // alone....
 
1634
      protectiveMBR.EmptyMBR(0);
 
1635
      protectiveMBR.SetDiskSize(diskSize);
 
1636
      printf("Place EFI GPT (0xEE) partition first in MBR (good for GRUB)? ");
 
1637
      eeFirst = GetYN();
 
1638
   } // if
 
1639
 
 
1640
   for (i = 0; i < numParts; i++) {
 
1641
      j = partNums[i] - 1;
 
1642
      printf("\nCreating entry for partition #%d\n", j + 1);
 
1643
      if (eeFirst == 'Y')
 
1644
         mbrNum = i + 1;
 
1645
      else
 
1646
         mbrNum = i;
 
1647
      numConverted += OnePartToMBR(j, mbrNum);
 
1648
   } // for
 
1649
 
 
1650
   if ((numParts > 0) && (numConverted > 0)) { // User opted to create a hybrid MBR....
 
1651
      // Create EFI protective partition that covers the start of the disk.
 
1652
      // If this location (covering the main GPT data structures) is omitted,
 
1653
      // Linux won't find any partitions on the disk. Note that this is
 
1654
      // NUMBERED AFTER the hybrid partitions, contrary to what the
 
1655
      // gptsync utility does. This is because Windows seems to choke on
 
1656
      // disks with a 0xEE partition in the first slot and subsequent
 
1657
      // additional partitions, unless it boots from the disk.
 
1658
      if (eeFirst == 'Y')
 
1659
         mbrNum = 0;
 
1660
      else
 
1661
         mbrNum = numParts;
 
1662
      protectiveMBR.MakePart(mbrNum, 1, protectiveMBR.FindLastInFree(1), 0xEE);
 
1663
      protectiveMBR.SetHybrid();
 
1664
 
 
1665
      // ... and for good measure, if there are any partition spaces left,
 
1666
      // optionally create another protective EFI partition to cover as much
 
1667
      // space as possible....
 
1668
      for (i = 0; i < 4; i++) {
 
1669
         if (protectiveMBR.GetType(i) == 0x00) { // unused entry....
 
1670
            if (fillItUp == 'M') {
 
1671
               printf("\nUnused partition space(s) found. Use one to protect more partitions? ");
 
1672
               fillItUp = GetYN();
 
1673
               typeCode = 0x00; // use this to flag a need to get type code
 
1674
            } // if
 
1675
            if (fillItUp == 'Y') {
 
1676
               while ((typeCode <= 0) || (typeCode > 255)) {
 
1677
                  printf("Enter an MBR hex code (EE is EFI GPT, but may confuse MacOS): ");
 
1678
                  // Comment on above: Mac OS treats disks with more than one
 
1679
                  // 0xEE MBR partition as MBR disks, not as GPT disks.
 
1680
                  fgets(line, 255, stdin);
 
1681
                  sscanf(line, "%x", &typeCode);
 
1682
                  if (line[0] == '\n')
 
1683
                     typeCode = 0;
 
1684
               } // while
 
1685
               protectiveMBR.MakeBiggestPart(i, typeCode); // make a partition
 
1686
            } // if (fillItUp == 'Y')
 
1687
         } // if unused entry
 
1688
      } // for (i = 0; i < 4; i++)
 
1689
   } // if (numParts > 0)
 
1690
} // GPTData::MakeHybrid()
 
1691
 
 
1692
/**********************************************************************
 
1693
 *                                                                    *
 
1694
 * Functions that adjust GPT data structures WITHOUT user interaction *
 
1695
 * (they may display information for the user's benefit, though)      *
 
1696
 *                                                                    *
 
1697
 **********************************************************************/
 
1698
 
 
1699
// Resizes GPT to specified number of entries. Creates a new table if
 
1700
// necessary, copies data if it already exists.
 
1701
int GPTData::SetGPTSize(uint32_t numEntries) {
 
1702
   struct GPTPart* newParts;
 
1703
   struct GPTPart* trash;
 
1704
   uint32_t i, high, copyNum;
 
1705
   int allOK = 1;
 
1706
 
 
1707
   // First, adjust numEntries upward, if necessary, to get a number
 
1708
   // that fills the allocated sectors
 
1709
   i = blockSize / GPT_SIZE;
 
1710
   if ((numEntries % i) != 0) {
 
1711
      printf("Adjusting GPT size from %lu ", (unsigned long) numEntries);
 
1712
      numEntries = ((numEntries / i) + 1) * i;
 
1713
      printf("to %lu to fill the sector\n", (unsigned long) numEntries);
 
1714
   } // if
 
1715
 
 
1716
   // Do the work only if the # of partitions is changing. Along with being
 
1717
   // efficient, this prevents mucking the with location of the secondary
 
1718
   // partition table, which causes problems when loading data from a RAID
 
1719
   // array that's been expanded because this function is called when loading
 
1720
   // data.
 
1721
   if ((numEntries != mainHeader.numParts) || (partitions == NULL)) {
 
1722
      newParts = (GPTPart*) calloc(numEntries, sizeof (GPTPart));
 
1723
      if (newParts != NULL) {
 
1724
         if (partitions != NULL) { // existing partitions; copy them over
 
1725
            GetPartRange(&i, &high);
 
1726
            if (numEntries < (high + 1)) { // Highest entry too high for new #
 
1727
               printf("The highest-numbered partition is %lu, which is greater than the requested\n"
 
1728
                     "partition table size of %d; cannot resize. Perhaps sorting will help.\n",
 
1729
                     (unsigned long) (high + 1), numEntries);
 
1730
               allOK = 0;
 
1731
            } else { // go ahead with copy
 
1732
               if (numEntries < mainHeader.numParts)
 
1733
                  copyNum = numEntries;
 
1734
               else
 
1735
                  copyNum = mainHeader.numParts;
 
1736
               for (i = 0; i < copyNum; i++) {
 
1737
                  newParts[i] = partitions[i];
 
1738
               } // for
 
1739
               trash = partitions;
 
1740
               partitions = newParts;
 
1741
               free(trash);
 
1742
            } // if
 
1743
         } else { // No existing partition table; just create it
 
1744
            partitions = newParts;
 
1745
         } // if/else existing partitions
 
1746
         mainHeader.numParts = numEntries;
 
1747
         secondHeader.numParts = numEntries;
 
1748
         mainHeader.firstUsableLBA = ((numEntries * GPT_SIZE) / blockSize) + 2 ;
 
1749
         secondHeader.firstUsableLBA = mainHeader.firstUsableLBA;
 
1750
         MoveSecondHeaderToEnd();
 
1751
         if (diskSize > 0)
 
1752
            CheckGPTSize();
 
1753
      } else { // Bad memory allocation
 
1754
         fprintf(stderr, "Error allocating memory for partition table!\n");
 
1755
         allOK = 0;
 
1756
      } // if/else
 
1757
   } // if/else
 
1758
   return (allOK);
 
1759
} // GPTData::SetGPTSize()
 
1760
 
 
1761
// Blank the partition array
 
1762
void GPTData::BlankPartitions(void) {
 
1763
   uint32_t i;
 
1764
 
 
1765
   for (i = 0; i < mainHeader.numParts; i++) {
 
1766
      partitions[i].BlankPartition();
 
1767
   } // for
 
1768
} // GPTData::BlankPartitions()
 
1769
 
 
1770
// Sort the GPT entries, eliminating gaps and making for a logical
 
1771
// ordering. Relies on QuickSortGPT() for the bulk of the work
 
1772
void GPTData::SortGPT(void) {
 
1773
   int i, lastPart = 0;
 
1774
   GPTPart temp;
 
1775
 
 
1776
   // First, find the last partition with data, so as not to
 
1777
   // spend needless time sorting empty entries....
 
1778
   for (i = 0; i < mainHeader.numParts; i++) {
 
1779
      if (partitions[i].GetFirstLBA() > 0)
 
1780
         lastPart = i;
 
1781
   } // for
 
1782
 
 
1783
   // Now swap empties with the last partitions, to simplify the logic
 
1784
   // in the Quicksort function....
 
1785
   i = 0;
 
1786
   while (i < lastPart) {
 
1787
      if (partitions[i].GetFirstLBA() == 0) {
 
1788
         temp = partitions[i];
 
1789
         partitions[i] = partitions[lastPart];
 
1790
         partitions[lastPart] = temp;
 
1791
         lastPart--;
 
1792
      } // if
 
1793
      i++;
 
1794
   } // while
 
1795
 
 
1796
   // Now call the recursive quick sort routine to do the real work....
 
1797
   QuickSortGPT(partitions, 0, lastPart);
 
1798
} // GPTData::SortGPT()
 
1799
 
 
1800
// Set up data structures for entirely new set of partitions on the
 
1801
// specified device. Returns 1 if OK, 0 if there were problems.
 
1802
// Note that this function does NOT clear the protectiveMBR data
 
1803
// structure, since it may hold the original MBR partitions if the
 
1804
// program was launched on an MBR disk, and those may need to be
 
1805
// converted to GPT format.
 
1806
int GPTData::ClearGPTData(void) {
 
1807
   int goOn = 1, i;
 
1808
 
 
1809
   // Set up the partition table....
 
1810
   free(partitions);
 
1811
   partitions = NULL;
 
1812
   SetGPTSize(NUM_GPT_ENTRIES);
 
1813
 
 
1814
   // Now initialize a bunch of stuff that's static....
 
1815
   mainHeader.signature = GPT_SIGNATURE;
 
1816
   mainHeader.revision = 0x00010000;
 
1817
   mainHeader.headerSize = HEADER_SIZE;
 
1818
   mainHeader.reserved = 0;
 
1819
   mainHeader.currentLBA = UINT64_C(1);
 
1820
   mainHeader.partitionEntriesLBA = (uint64_t) 2;
 
1821
   mainHeader.sizeOfPartitionEntries = GPT_SIZE;
 
1822
   for (i = 0; i < GPT_RESERVED; i++) {
 
1823
      mainHeader.reserved2[i] = '\0';
 
1824
   } // for
 
1825
 
 
1826
   // Now some semi-static items (computed based on end of disk)
 
1827
   mainHeader.backupLBA = diskSize - UINT64_C(1);
 
1828
   mainHeader.lastUsableLBA = diskSize - mainHeader.firstUsableLBA;
 
1829
 
 
1830
   // Set a unique GUID for the disk, based on random numbers
 
1831
   // rand() is only 32 bits, so multiply together to fill a 64-bit value
 
1832
   mainHeader.diskGUID.data1 = (uint64_t) rand() * (uint64_t) rand();
 
1833
   mainHeader.diskGUID.data2 = (uint64_t) rand() * (uint64_t) rand();
 
1834
 
 
1835
   // Copy main header to backup header
 
1836
   RebuildSecondHeader();
 
1837
 
 
1838
   // Blank out the partitions array....
 
1839
   BlankPartitions();
 
1840
 
 
1841
   // Flag all CRCs as being OK....
 
1842
   mainCrcOk = 1;
 
1843
   secondCrcOk = 1;
 
1844
   mainPartsCrcOk = 1;
 
1845
   secondPartsCrcOk = 1;
 
1846
 
 
1847
   return (goOn);
 
1848
} // GPTData::ClearGPTData()
 
1849
 
 
1850
// Set the location of the second GPT header data to the end of the disk.
 
1851
// Used internally and called by the 'e' option on the recovery &
 
1852
// transformation menu, to help users of RAID arrays who add disk space
 
1853
// to their arrays.
 
1854
void GPTData::MoveSecondHeaderToEnd() {
 
1855
   mainHeader.backupLBA = secondHeader.currentLBA = diskSize - UINT64_C(1);
 
1856
   mainHeader.lastUsableLBA = secondHeader.lastUsableLBA = diskSize - mainHeader.firstUsableLBA;
 
1857
   secondHeader.partitionEntriesLBA = secondHeader.lastUsableLBA + UINT64_C(1);
 
1858
} // GPTData::FixSecondHeaderLocation()
 
1859
 
 
1860
void GPTData::SetName(uint32_t partNum, char* theName) {
 
1861
   if ((partNum >= 0) && (partNum < mainHeader.numParts))
 
1862
      if (partitions[partNum].GetFirstLBA() > 0)
 
1863
         partitions[partNum].SetName((unsigned char*) theName);
 
1864
} // GPTData::SetName
 
1865
 
 
1866
// Set the disk GUID to the specified value. Note that the header CRCs must
 
1867
// be recomputed after calling this function.
 
1868
void GPTData::SetDiskGUID(GUIDData newGUID) {
 
1869
   mainHeader.diskGUID = newGUID;
 
1870
   secondHeader.diskGUID = newGUID;
 
1871
} // SetDiskGUID()
 
1872
 
 
1873
// Set the unique GUID of the specified partition. Returns 1 on
 
1874
// successful completion, 0 if there were problems (invalid
 
1875
// partition number).
 
1876
int GPTData::SetPartitionGUID(uint32_t pn, GUIDData theGUID) {
 
1877
   int retval = 0;
 
1878
 
 
1879
   if (pn < mainHeader.numParts) {
 
1880
      if (partitions[pn].GetFirstLBA() != UINT64_C(0)) {
 
1881
         partitions[pn].SetUniqueGUID(theGUID);
 
1882
         retval = 1;
 
1883
      } // if
 
1884
   } // if
 
1885
   return retval;
 
1886
} // GPTData::SetPartitionGUID()
 
1887
 
 
1888
/********************************************************
 
1889
 *                                                      *
 
1890
 * Functions that return data about GPT data structures *
 
1891
 * (most of these are inline in gpt.h)                  *
 
1892
 *                                                      *
 
1893
 ********************************************************/
 
1894
 
 
1895
// Find the low and high used partition numbers (numbered from 0).
 
1896
// Return value is the number of partitions found. Note that the
 
1897
// *low and *high values are both set to 0 when no partitions
 
1898
// are found, as well as when a single partition in the first
 
1899
// position exists. Thus, the return value is the only way to
 
1900
// tell when no partitions exist.
 
1901
int GPTData::GetPartRange(uint32_t *low, uint32_t *high) {
 
1902
   uint32_t i;
 
1903
   int numFound = 0;
 
1904
 
 
1905
   *low = mainHeader.numParts + 1; // code for "not found"
 
1906
   *high = 0;
 
1907
   if (mainHeader.numParts > 0) { // only try if partition table exists...
 
1908
      for (i = 0; i < mainHeader.numParts; i++) {
 
1909
         if (partitions[i].GetFirstLBA() != UINT64_C(0)) { // it exists
 
1910
            *high = i; // since we're counting up, set the high value
 
1911
            // Set the low value only if it's not yet found...
 
1912
            if (*low == (mainHeader.numParts + 1)) *low = i;
 
1913
            numFound++;
 
1914
         } // if
 
1915
      } // for
 
1916
   } // if
 
1917
 
 
1918
   // Above will leave *low pointing to its "not found" value if no partitions
 
1919
   // are defined, so reset to 0 if this is the case....
 
1920
   if (*low == (mainHeader.numParts + 1))
 
1921
      *low = 0;
 
1922
   return numFound;
 
1923
} // GPTData::GetPartRange()
 
1924
 
 
1925
// Returns the number of defined partitions.
 
1926
uint32_t GPTData::CountParts(void) {
 
1927
   int i, counted = 0;
 
1928
 
 
1929
   for (i = 0; i < mainHeader.numParts; i++) {
 
1930
      if (partitions[i].GetFirstLBA() > 0)
 
1931
         counted++;
 
1932
   } // for
 
1933
   return counted;
 
1934
} // GPTData::CountParts()
 
1935
 
 
1936
/****************************************************
 
1937
 *                                                  *
 
1938
 * Functions that return data about disk free space *
 
1939
 *                                                  *
 
1940
 ****************************************************/
 
1941
 
 
1942
// Find the first available block after the starting point; returns 0 if
 
1943
// there are no available blocks left
 
1944
uint64_t GPTData::FindFirstAvailable(uint64_t start) {
 
1945
   uint64_t first;
 
1946
   uint32_t i;
 
1947
   int firstMoved = 0;
 
1948
 
 
1949
   // Begin from the specified starting point or from the first usable
 
1950
   // LBA, whichever is greater...
 
1951
   if (start < mainHeader.firstUsableLBA)
 
1952
      first = mainHeader.firstUsableLBA;
 
1953
   else
 
1954
      first = start;
 
1955
 
 
1956
   // ...now search through all partitions; if first is within an
 
1957
   // existing partition, move it to the next sector after that
 
1958
   // partition and repeat. If first was moved, set firstMoved
 
1959
   // flag; repeat until firstMoved is not set, so as to catch
 
1960
   // cases where partitions are out of sequential order....
 
1961
   do {
 
1962
      firstMoved = 0;
 
1963
      for (i = 0; i < mainHeader.numParts; i++) {
 
1964
         if ((first >= partitions[i].GetFirstLBA()) &&
 
1965
              (first <= partitions[i].GetLastLBA())) { // in existing part.
 
1966
            first = partitions[i].GetLastLBA() + 1;
 
1967
            firstMoved = 1;
 
1968
              } // if
 
1969
      } // for
 
1970
   } while (firstMoved == 1);
 
1971
   if (first > mainHeader.lastUsableLBA)
 
1972
      first = 0;
 
1973
   return (first);
 
1974
} // GPTData::FindFirstAvailable()
 
1975
 
 
1976
// Finds the first available sector in the largest block of unallocated
 
1977
// space on the disk. Returns 0 if there are no available blocks left
 
1978
uint64_t GPTData::FindFirstInLargest(void) {
 
1979
   uint64_t start, firstBlock, lastBlock, segmentSize, selectedSize = 0, selectedSegment = 0;
 
1980
 
 
1981
   start = 0;
 
1982
   do {
 
1983
      firstBlock = FindFirstAvailable(start);
 
1984
      if (firstBlock != UINT32_C(0)) { // something's free...
 
1985
         lastBlock = FindLastInFree(firstBlock);
 
1986
         segmentSize = lastBlock - firstBlock + UINT32_C(1);
 
1987
         if (segmentSize > selectedSize) {
 
1988
            selectedSize = segmentSize;
 
1989
            selectedSegment = firstBlock;
 
1990
         } // if
 
1991
         start = lastBlock + 1;
 
1992
      } // if
 
1993
   } while (firstBlock != 0);
 
1994
   return selectedSegment;
 
1995
} // GPTData::FindFirstInLargest()
 
1996
 
 
1997
// Find the last available block on the disk at or after the start
 
1998
// block. Returns 0 if there are no available partitions after
 
1999
// (or including) start.
 
2000
uint64_t GPTData::FindLastAvailable(uint64_t start) {
 
2001
   uint64_t last;
 
2002
   uint32_t i;
 
2003
   int lastMoved = 0;
 
2004
 
 
2005
   // Start by assuming the last usable LBA is available....
 
2006
   last = mainHeader.lastUsableLBA;
 
2007
 
 
2008
   // ...now, similar to algorithm in FindFirstAvailable(), search
 
2009
   // through all partitions, moving last when it's in an existing
 
2010
   // partition. Set the lastMoved flag so we repeat to catch cases
 
2011
   // where partitions are out of logical order.
 
2012
   do {
 
2013
      lastMoved = 0;
 
2014
      for (i = 0; i < mainHeader.numParts; i++) {
 
2015
         if ((last >= partitions[i].GetFirstLBA()) &&
 
2016
              (last <= partitions[i].GetLastLBA())) { // in existing part.
 
2017
            last = partitions[i].GetFirstLBA() - 1;
 
2018
            lastMoved = 1;
 
2019
              } // if
 
2020
      } // for
 
2021
   } while (lastMoved == 1);
 
2022
   if (last < mainHeader.firstUsableLBA)
 
2023
      last = 0;
 
2024
   return (last);
 
2025
} // GPTData::FindLastAvailable()
 
2026
 
 
2027
// Find the last available block in the free space pointed to by start.
 
2028
uint64_t GPTData::FindLastInFree(uint64_t start) {
 
2029
   uint64_t nearestStart;
 
2030
   uint32_t i;
 
2031
 
 
2032
   nearestStart = mainHeader.lastUsableLBA;
 
2033
   for (i = 0; i < mainHeader.numParts; i++) {
 
2034
      if ((nearestStart > partitions[i].GetFirstLBA()) &&
 
2035
           (partitions[i].GetFirstLBA() > start)) {
 
2036
         nearestStart = partitions[i].GetFirstLBA() - 1;
 
2037
           } // if
 
2038
   } // for
 
2039
   return (nearestStart);
 
2040
} // GPTData::FindLastInFree()
 
2041
 
 
2042
// Finds the total number of free blocks, the number of segments in which
 
2043
// they reside, and the size of the largest of those segments
 
2044
uint64_t GPTData::FindFreeBlocks(int *numSegments, uint64_t *largestSegment) {
 
2045
   uint64_t start = UINT64_C(0); // starting point for each search
 
2046
   uint64_t totalFound = UINT64_C(0); // running total
 
2047
   uint64_t firstBlock; // first block in a segment
 
2048
   uint64_t lastBlock; // last block in a segment
 
2049
   uint64_t segmentSize; // size of segment in blocks
 
2050
   int num = 0;
 
2051
 
 
2052
   *largestSegment = UINT64_C(0);
 
2053
   do {
 
2054
      firstBlock = FindFirstAvailable(start);
 
2055
      if (firstBlock != UINT64_C(0)) { // something's free...
 
2056
         lastBlock = FindLastInFree(firstBlock);
 
2057
         segmentSize = lastBlock - firstBlock + UINT64_C(1);
 
2058
         if (segmentSize > *largestSegment) {
 
2059
            *largestSegment = segmentSize;
 
2060
         } // if
 
2061
         totalFound += segmentSize;
 
2062
         num++;
 
2063
         start = lastBlock + 1;
 
2064
      } // if
 
2065
   } while (firstBlock != 0);
 
2066
   *numSegments = num;
 
2067
   return totalFound;
 
2068
} // GPTData::FindFreeBlocks()
 
2069
 
 
2070
// Returns 1 if sector is unallocated, 0 if it's allocated to a partition
 
2071
int GPTData::IsFree(uint64_t sector) {
 
2072
   int isFree = 1;
 
2073
   uint32_t i;
 
2074
 
 
2075
   for (i = 0; i < mainHeader.numParts; i++) {
 
2076
      if ((sector >= partitions[i].GetFirstLBA()) &&
 
2077
           (sector <= partitions[i].GetLastLBA())) {
 
2078
         isFree = 0;
 
2079
           } // if
 
2080
   } // for
 
2081
   if ((sector < mainHeader.firstUsableLBA) ||
 
2082
        (sector > mainHeader.lastUsableLBA)) {
 
2083
      isFree = 0;
 
2084
        } // if
 
2085
        return (isFree);
 
2086
} // GPTData::IsFree()
 
2087
 
 
2088
/********************************
 
2089
 *                              *
 
2090
 * Endianness support functions *
 
2091
 *                              *
 
2092
 ********************************/
 
2093
 
 
2094
void GPTData::ReverseHeaderBytes(struct GPTHeader* header) {
 
2095
   ReverseBytes(&header->signature, 8);
 
2096
   ReverseBytes(&header->revision, 4);
 
2097
   ReverseBytes(&header->headerSize, 4);
 
2098
   ReverseBytes(&header->headerCRC, 4);
 
2099
   ReverseBytes(&header->reserved, 4);
 
2100
   ReverseBytes(&header->currentLBA, 8);
 
2101
   ReverseBytes(&header->backupLBA, 8);
 
2102
   ReverseBytes(&header->firstUsableLBA, 8);
 
2103
   ReverseBytes(&header->lastUsableLBA, 8);
 
2104
   ReverseBytes(&header->partitionEntriesLBA, 8);
 
2105
   ReverseBytes(&header->numParts, 4);
 
2106
   ReverseBytes(&header->sizeOfPartitionEntries, 4);
 
2107
   ReverseBytes(&header->partitionEntriesCRC, 4);
 
2108
   ReverseBytes(&header->reserved2, GPT_RESERVED);
 
2109
   ReverseBytes(&header->diskGUID.data1, 8);
 
2110
   ReverseBytes(&header->diskGUID.data2, 8);
 
2111
} // GPTData::ReverseHeaderBytes()
 
2112
 
 
2113
// IMPORTANT NOTE: This function requires non-reversed mainHeader
 
2114
// structure!
 
2115
void GPTData::ReversePartitionBytes() {
 
2116
   uint32_t i;
 
2117
 
 
2118
   // Check GPT signature on big-endian systems; this will mismatch
 
2119
   // if the function is called out of order. Unfortunately, it'll also
 
2120
   // mismatch if there's data corruption.
 
2121
   if ((mainHeader.signature != GPT_SIGNATURE) && (IsLittleEndian() == 0)) {
 
2122
      printf("GPT signature mismatch in GPTData::ReversePartitionBytes(). This indicates\n"
 
2123
            "data corruption or a misplaced call to this function.\n");
 
2124
   } // if signature mismatch....
 
2125
   for (i = 0; i < mainHeader.numParts; i++) {
 
2126
      partitions[i].ReversePartBytes();
 
2127
   } // for
 
2128
} // GPTData::ReversePartitionBytes()
 
2129
 
 
2130
/******************************************
 
2131
 *                                        *
 
2132
 * Additional non-class support functions *
 
2133
 *                                        *
 
2134
 ******************************************/
 
2135
 
 
2136
// Check to be sure that data type sizes are correct. The basic types (uint*_t) should
 
2137
// never fail these tests, but the struct types may fail depending on compile options.
 
2138
// Specifically, the -fpack-struct option to gcc may be required to ensure proper structure
 
2139
// sizes.
 
2140
int SizesOK(void) {
 
2141
   int allOK = 1;
 
2142
 
 
2143
   if (sizeof(uint8_t) != 1) {
 
2144
      fprintf(stderr, "uint8_t is %d bytes, should be 1 byte; aborting!\n", sizeof(uint8_t));
 
2145
      allOK = 0;
 
2146
   } // if
 
2147
   if (sizeof(uint16_t) != 2) {
 
2148
      fprintf(stderr, "uint16_t is %d bytes, should be 2 bytes; aborting!\n", sizeof(uint16_t));
 
2149
      allOK = 0;
 
2150
   } // if
 
2151
   if (sizeof(uint32_t) != 4) {
 
2152
      fprintf(stderr, "uint32_t is %d bytes, should be 4 bytes; aborting!\n", sizeof(uint32_t));
 
2153
      allOK = 0;
 
2154
   } // if
 
2155
   if (sizeof(uint64_t) != 8) {
 
2156
      fprintf(stderr, "uint64_t is %d bytes, should be 8 bytes; aborting!\n", sizeof(uint64_t));
 
2157
      allOK = 0;
 
2158
   } // if
 
2159
   if (sizeof(struct MBRRecord) != 16) {
 
2160
      fprintf(stderr, "MBRRecord is %d bytes, should be 16 bytes; aborting!\n", sizeof(MBRRecord));
 
2161
      allOK = 0;
 
2162
   } // if
 
2163
   if (sizeof(struct TempMBR) != 512) {
 
2164
      fprintf(stderr, "TempMBR is %d bytes, should be 512 bytes; aborting!\n", sizeof(TempMBR));
 
2165
      allOK = 0;
 
2166
   } // if
 
2167
   if (sizeof(struct GPTHeader) != 512) {
 
2168
      fprintf(stderr, "GPTHeader is %d bytes, should be 512 bytes; aborting!\n", sizeof(GPTHeader));
 
2169
      allOK = 0;
 
2170
   } // if
 
2171
   if (sizeof(GPTPart) != 128) {
 
2172
      fprintf(stderr, "GPTPart is %d bytes, should be 128 bytes; aborting!\n", sizeof(GPTPart));
 
2173
      allOK = 0;
 
2174
   } // if
 
2175
// Determine endianness; set allOK = 0 if running on big-endian hardware
 
2176
   if (IsLittleEndian() == 0) {
 
2177
      fprintf(stderr, "\aRunning on big-endian hardware. Big-endian support is new and poorly"
 
2178
            " tested!\nBeware!\n");
 
2179
      // allOK = 0;
 
2180
   } // if
 
2181
   return (allOK);
 
2182
} // SizesOK()
 
2183