| srs5694 | e7b4ff9 | 2009-08-18 13:16:10 -0400 | [diff] [blame] | 1 | /* gpt.cc -- Functions for loading, saving, and manipulating legacy MBR and GPT partition | 
 | 2 |    data. */ | 
 | 3 |  | 
| srs5694 | e4ac11e | 2009-08-31 10:13:04 -0400 | [diff] [blame] | 4 | /* By Rod Smith, initial coding January to February, 2009 */ | 
| srs5694 | e7b4ff9 | 2009-08-18 13:16:10 -0400 | [diff] [blame] | 5 |  | 
| srs5694 | 221e087 | 2009-08-29 15:00:31 -0400 | [diff] [blame] | 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 |  | 
| srs5694 | e7b4ff9 | 2009-08-18 13:16:10 -0400 | [diff] [blame] | 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" | 
| srs5694 | 221e087 | 2009-08-29 15:00:31 -0400 | [diff] [blame] | 23 | #include "bsd.h" | 
| srs5694 | e7b4ff9 | 2009-08-18 13:16:10 -0400 | [diff] [blame] | 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 |  | 
| srs5694 | e4ac11e | 2009-08-31 10:13:04 -0400 | [diff] [blame] | 36 | // Default constructor | 
| srs5694 | e7b4ff9 | 2009-08-18 13:16:10 -0400 | [diff] [blame] | 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; | 
| srs5694 | 221e087 | 2009-08-29 15:00:31 -0400 | [diff] [blame] | 47 |    apmFound = 0; | 
 | 48 |    bsdFound = 0; | 
| srs5694 | e7b4ff9 | 2009-08-18 13:16:10 -0400 | [diff] [blame] | 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; | 
| srs5694 | 221e087 | 2009-08-29 15:00:31 -0400 | [diff] [blame] | 64 |    apmFound = 0; | 
 | 65 |    bsdFound = 0; | 
| srs5694 | e7b4ff9 | 2009-08-18 13:16:10 -0400 | [diff] [blame] | 66 |    srand((unsigned int) time(NULL)); | 
 | 67 |    LoadPartitions(filename); | 
 | 68 | } // GPTData(char* filename) constructor | 
 | 69 |  | 
| srs5694 | e4ac11e | 2009-08-31 10:13:04 -0400 | [diff] [blame] | 70 | // Destructor | 
| srs5694 | e7b4ff9 | 2009-08-18 13:16:10 -0400 | [diff] [blame] | 71 | GPTData::~GPTData(void) { | 
 | 72 |    free(partitions); | 
 | 73 | } // GPTData destructor | 
 | 74 |  | 
| srs5694 | e4ac11e | 2009-08-31 10:13:04 -0400 | [diff] [blame] | 75 | /********************************************************************* | 
 | 76 |  *                                                                   * | 
 | 77 |  * Begin functions that verify data, or that adjust the verification * | 
 | 78 |  * information (compute CRCs, rebuild headers)                       * | 
 | 79 |  *                                                                   * | 
 | 80 |  *********************************************************************/ | 
| srs5694 | e7b4ff9 | 2009-08-18 13:16:10 -0400 | [diff] [blame] | 81 |  | 
| srs5694 | e4ac11e | 2009-08-31 10:13:04 -0400 | [diff] [blame] | 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) { | 
| srs5694 | e35eb1b | 2009-09-14 00:29:34 -0400 | [diff] [blame] | 86 |    int problems = 0, numSegments; | 
| srs5694 | e4ac11e | 2009-08-31 10:13:04 -0400 | [diff] [blame] | 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"); | 
| srs5694 | e7b4ff9 | 2009-08-18 13:16:10 -0400 | [diff] [blame] | 113 |    } // if | 
 | 114 |  | 
| srs5694 | 978041c | 2009-09-21 20:51:47 -0400 | [diff] [blame] | 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++; | 
| srs5694 | 3f2fe99 | 2009-11-24 18:28:18 -0500 | [diff] [blame] | 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" | 
| srs5694 | 247657a | 2009-11-26 18:36:12 -0500 | [diff] [blame^] | 127 |              "option on the experts' menu to adjust the secondary header's and partition\n" | 
 | 128 |              "table's locations.\n"); | 
| srs5694 | 978041c | 2009-09-21 20:51:47 -0400 | [diff] [blame] | 129 |    } // if | 
 | 130 |  | 
 | 131 |    // Now check that critical main and backup GPT entries match each other | 
| srs5694 | e4ac11e | 2009-08-31 10:13:04 -0400 | [diff] [blame] | 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); | 
| srs5694 | e7b4ff9 | 2009-08-18 13:16:10 -0400 | [diff] [blame] | 215 |    } // if/else | 
| srs5694 | e4ac11e | 2009-08-31 10:13:04 -0400 | [diff] [blame] | 216 |  | 
 | 217 |    return (problems); | 
 | 218 | } // GPTData::Verify() | 
| srs5694 | e7b4ff9 | 2009-08-18 13:16:10 -0400 | [diff] [blame] | 219 |  | 
 | 220 | // Checks to see if the GPT tables overrun existing partitions; if they | 
| srs5694 | 221e087 | 2009-08-29 15:00:31 -0400 | [diff] [blame] | 221 | // do, issues a warning but takes no action. Returns number of problems | 
 | 222 | // detected (0 if OK, 1 to 2 if problems). | 
| srs5694 | e7b4ff9 | 2009-08-18 13:16:10 -0400 | [diff] [blame] | 223 | int GPTData::CheckGPTSize(void) { | 
 | 224 |    uint64_t overlap, firstUsedBlock, lastUsedBlock; | 
 | 225 |    uint32_t i; | 
| srs5694 | 221e087 | 2009-08-29 15:00:31 -0400 | [diff] [blame] | 226 |    int numProbs = 0; | 
| srs5694 | e7b4ff9 | 2009-08-18 13:16:10 -0400 | [diff] [blame] | 227 |  | 
 | 228 |    // first, locate the first & last used blocks | 
 | 229 |    firstUsedBlock = UINT64_MAX; | 
 | 230 |    lastUsedBlock = 0; | 
 | 231 |    for (i = 0; i < mainHeader.numParts; i++) { | 
| srs5694 | 221e087 | 2009-08-29 15:00:31 -0400 | [diff] [blame] | 232 |       if ((partitions[i].GetFirstLBA() < firstUsedBlock) && | 
| srs5694 | e4ac11e | 2009-08-31 10:13:04 -0400 | [diff] [blame] | 233 |            (partitions[i].GetFirstLBA() != 0)) | 
| srs5694 | 221e087 | 2009-08-29 15:00:31 -0400 | [diff] [blame] | 234 |          firstUsedBlock = partitions[i].GetFirstLBA(); | 
 | 235 |       if (partitions[i].GetLastLBA() > lastUsedBlock) | 
 | 236 |          lastUsedBlock = partitions[i].GetLastLBA(); | 
| srs5694 | e7b4ff9 | 2009-08-18 13:16:10 -0400 | [diff] [blame] | 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; | 
| srs5694 | 221e087 | 2009-08-29 15:00:31 -0400 | [diff] [blame] | 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++; | 
| srs5694 | e7b4ff9 | 2009-08-18 13:16:10 -0400 | [diff] [blame] | 255 |       } // Problem at start of disk | 
 | 256 |       if (mainHeader.lastUsableLBA < lastUsedBlock) { | 
 | 257 |          overlap = lastUsedBlock - mainHeader.lastUsableLBA; | 
| srs5694 | 221e087 | 2009-08-29 15:00:31 -0400 | [diff] [blame] | 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++; | 
| srs5694 | e7b4ff9 | 2009-08-18 13:16:10 -0400 | [diff] [blame] | 268 |       } // Problem at end of disk | 
 | 269 |    } // if (diskSize != 0) | 
| srs5694 | 221e087 | 2009-08-29 15:00:31 -0400 | [diff] [blame] | 270 |    return numProbs; | 
| srs5694 | e7b4ff9 | 2009-08-18 13:16:10 -0400 | [diff] [blame] | 271 | } // GPTData::CheckGPTSize() | 
 | 272 |  | 
| srs5694 | e7b4ff9 | 2009-08-18 13:16:10 -0400 | [diff] [blame] | 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; | 
| srs5694 | 221e087 | 2009-08-29 15:00:31 -0400 | [diff] [blame] | 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); | 
| srs5694 | e7b4ff9 | 2009-08-18 13:16:10 -0400 | [diff] [blame] | 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; | 
| srs5694 | 221e087 | 2009-08-29 15:00:31 -0400 | [diff] [blame] | 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); | 
| srs5694 | e7b4ff9 | 2009-08-18 13:16:10 -0400 | [diff] [blame] | 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 |  | 
| srs5694 | 2a9f5da | 2009-08-26 00:48:01 -0400 | [diff] [blame] | 300 |    // If MBR bad, check for an Apple disk signature | 
| srs5694 | e35eb1b | 2009-09-14 00:29:34 -0400 | [diff] [blame] | 301 |    if ((protectiveMBR.GetValidity() == invalid) && | 
| srs5694 | e4ac11e | 2009-08-31 10:13:04 -0400 | [diff] [blame] | 302 |         (((mainHeader.signature << 32) == APM_SIGNATURE1) || | 
| srs5694 | 2a9f5da | 2009-08-26 00:48:01 -0400 | [diff] [blame] | 303 |         (mainHeader.signature << 32) == APM_SIGNATURE2)) { | 
| srs5694 | 221e087 | 2009-08-29 15:00:31 -0400 | [diff] [blame] | 304 |       apmFound = 1; // Will display warning message later | 
| srs5694 | 3f2fe99 | 2009-11-24 18:28:18 -0500 | [diff] [blame] | 305 |    } // if | 
| srs5694 | 2a9f5da | 2009-08-26 00:48:01 -0400 | [diff] [blame] | 306 |  | 
| srs5694 | e4ac11e | 2009-08-31 10:13:04 -0400 | [diff] [blame] | 307 |         return valid; | 
| srs5694 | e7b4ff9 | 2009-08-18 13:16:10 -0400 | [diff] [blame] | 308 | } // GPTData::CheckHeaderValidity() | 
 | 309 |  | 
 | 310 | // Check the header CRC to see if it's OK... | 
| srs5694 | 2a9f5da | 2009-08-26 00:48:01 -0400 | [diff] [blame] | 311 | // Note: Must be called BEFORE byte-order reversal on big-endian | 
 | 312 | // systems! | 
| srs5694 | e7b4ff9 | 2009-08-18 13:16:10 -0400 | [diff] [blame] | 313 | int GPTData::CheckHeaderCRC(struct GPTHeader* header) { | 
| srs5694 | 978041c | 2009-09-21 20:51:47 -0400 | [diff] [blame] | 314 |    uint32_t oldCRC, newCRC, hSize; | 
| srs5694 | e7b4ff9 | 2009-08-18 13:16:10 -0400 | [diff] [blame] | 315 |  | 
| srs5694 | 2a9f5da | 2009-08-26 00:48:01 -0400 | [diff] [blame] | 316 |    // Back up old header CRC and then blank it, since it must be 0 for | 
| srs5694 | e7b4ff9 | 2009-08-18 13:16:10 -0400 | [diff] [blame] | 317 |    // computation to be valid | 
 | 318 |    oldCRC = header->headerCRC; | 
 | 319 |    header->headerCRC = UINT32_C(0); | 
| srs5694 | 978041c | 2009-09-21 20:51:47 -0400 | [diff] [blame] | 320 |    hSize = header->headerSize; | 
 | 321 |  | 
 | 322 |    // If big-endian system, reverse byte order | 
 | 323 |    if (IsLittleEndian() == 0) { | 
 | 324 |       ReverseBytes(&oldCRC, 4); | 
 | 325 |    } // if | 
| srs5694 | e7b4ff9 | 2009-08-18 13:16:10 -0400 | [diff] [blame] | 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); | 
| srs5694 | 978041c | 2009-09-21 20:51:47 -0400 | [diff] [blame] | 332 |    header->headerCRC = oldCRC; | 
| srs5694 | e7b4ff9 | 2009-08-18 13:16:10 -0400 | [diff] [blame] | 333 |    return (oldCRC == newCRC); | 
 | 334 | } // GPTData::CheckHeaderCRC() | 
 | 335 |  | 
| srs5694 | 2a9f5da | 2009-08-26 00:48:01 -0400 | [diff] [blame] | 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. | 
| srs5694 | e7b4ff9 | 2009-08-18 13:16:10 -0400 | [diff] [blame] | 338 | void GPTData::RecomputeCRCs(void) { | 
| srs5694 | 978041c | 2009-09-21 20:51:47 -0400 | [diff] [blame] | 339 |    uint32_t crc, hSize, trueNumParts; | 
| srs5694 | 2a9f5da | 2009-08-26 00:48:01 -0400 | [diff] [blame] | 340 |    int littleEndian = 1; | 
| srs5694 | e7b4ff9 | 2009-08-18 13:16:10 -0400 | [diff] [blame] | 341 |  | 
 | 342 |    // Initialize CRC functions... | 
 | 343 |    chksum_crc32gentab(); | 
 | 344 |  | 
| srs5694 | 978041c | 2009-09-21 20:51:47 -0400 | [diff] [blame] | 345 |    hSize = mainHeader.headerSize; | 
| srs5694 | 2a9f5da | 2009-08-26 00:48:01 -0400 | [diff] [blame] | 346 |    littleEndian = IsLittleEndian(); | 
 | 347 |  | 
| srs5694 | e7b4ff9 | 2009-08-18 13:16:10 -0400 | [diff] [blame] | 348 |    // Compute CRC of partition tables & store in main and secondary headers | 
| srs5694 | 2a9f5da | 2009-08-26 00:48:01 -0400 | [diff] [blame] | 349 |    trueNumParts = mainHeader.numParts; | 
 | 350 |    if (littleEndian == 0) | 
| srs5694 | 221e087 | 2009-08-29 15:00:31 -0400 | [diff] [blame] | 351 |       ReverseBytes(&trueNumParts, 4); // unreverse this key piece of data.... | 
| srs5694 | 2a9f5da | 2009-08-26 00:48:01 -0400 | [diff] [blame] | 352 |    crc = chksum_crc32((unsigned char*) partitions, trueNumParts * GPT_SIZE); | 
| srs5694 | e7b4ff9 | 2009-08-18 13:16:10 -0400 | [diff] [blame] | 353 |    mainHeader.partitionEntriesCRC = crc; | 
 | 354 |    secondHeader.partitionEntriesCRC = crc; | 
| srs5694 | 2a9f5da | 2009-08-26 00:48:01 -0400 | [diff] [blame] | 355 |    if (littleEndian == 0) { | 
| srs5694 | 221e087 | 2009-08-29 15:00:31 -0400 | [diff] [blame] | 356 |       ReverseBytes(&mainHeader.partitionEntriesCRC, 4); | 
 | 357 |       ReverseBytes(&secondHeader.partitionEntriesCRC, 4); | 
| srs5694 | 2a9f5da | 2009-08-26 00:48:01 -0400 | [diff] [blame] | 358 |    } // if | 
| srs5694 | e7b4ff9 | 2009-08-18 13:16:10 -0400 | [diff] [blame] | 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... | 
| srs5694 | 978041c | 2009-09-21 20:51:47 -0400 | [diff] [blame] | 365 |    crc = chksum_crc32((unsigned char*) &mainHeader, hSize); | 
| srs5694 | 2a9f5da | 2009-08-26 00:48:01 -0400 | [diff] [blame] | 366 |    if (littleEndian == 0) | 
| srs5694 | 221e087 | 2009-08-29 15:00:31 -0400 | [diff] [blame] | 367 |       ReverseBytes(&crc, 4); | 
| srs5694 | e7b4ff9 | 2009-08-18 13:16:10 -0400 | [diff] [blame] | 368 |    mainHeader.headerCRC = crc; | 
| srs5694 | 978041c | 2009-09-21 20:51:47 -0400 | [diff] [blame] | 369 |    crc = chksum_crc32((unsigned char*) &secondHeader, hSize); | 
| srs5694 | 2a9f5da | 2009-08-26 00:48:01 -0400 | [diff] [blame] | 370 |    if (littleEndian == 0) | 
| srs5694 | 221e087 | 2009-08-29 15:00:31 -0400 | [diff] [blame] | 371 |       ReverseBytes(&crc, 4); | 
| srs5694 | e7b4ff9 | 2009-08-18 13:16:10 -0400 | [diff] [blame] | 372 |    secondHeader.headerCRC = crc; | 
 | 373 | } // GPTData::RecomputeCRCs() | 
 | 374 |  | 
| srs5694 | e7b4ff9 | 2009-08-18 13:16:10 -0400 | [diff] [blame] | 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; | 
| srs5694 | 978041c | 2009-09-21 20:51:47 -0400 | [diff] [blame] | 382 |    mainHeader.headerSize = secondHeader.headerSize; | 
| srs5694 | e7b4ff9 | 2009-08-18 13:16:10 -0400 | [diff] [blame] | 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; | 
| srs5694 | 978041c | 2009-09-21 20:51:47 -0400 | [diff] [blame] | 405 |    secondHeader.headerSize = mainHeader.headerSize; | 
| srs5694 | e7b4ff9 | 2009-08-18 13:16:10 -0400 | [diff] [blame] | 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]; | 
| srs5694 | e4ac11e | 2009-08-31 10:13:04 -0400 | [diff] [blame] | 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 | 
| srs5694 | e4ac11e | 2009-08-31 10:13:04 -0400 | [diff] [blame] | 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 |  | 
| srs5694 | 3f2fe99 | 2009-11-24 18:28:18 -0500 | [diff] [blame] | 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 |  | 
| srs5694 | e4ac11e | 2009-08-31 10:13:04 -0400 | [diff] [blame] | 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; | 
| srs5694 | 3f2fe99 | 2009-11-24 18:28:18 -0500 | [diff] [blame] | 618 |       fprintf(stderr, "Unable to seek to secondary GPT header at sector %llu!\n", | 
| srs5694 | e4ac11e | 2009-08-31 10:13:04 -0400 | [diff] [blame] | 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 |  | 
| srs5694 | 247657a | 2009-11-26 18:36:12 -0500 | [diff] [blame^] | 679 | // Loads the partition table pointed to by the main GPT header. The | 
| srs5694 | e4ac11e | 2009-08-31 10:13:04 -0400 | [diff] [blame] | 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() | 
| srs5694 | e7b4ff9 | 2009-08-18 13:16:10 -0400 | [diff] [blame] | 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); | 
| srs5694 | e4ac11e | 2009-08-31 10:13:04 -0400 | [diff] [blame] | 719 |          mainPartsCrcOk = secondPartsCrcOk; | 
| srs5694 | 2a9f5da | 2009-08-26 00:48:01 -0400 | [diff] [blame] | 720 |          if (IsLittleEndian() == 0) | 
 | 721 |             ReversePartitionBytes(); | 
| srs5694 | e7b4ff9 | 2009-08-18 13:16:10 -0400 | [diff] [blame] | 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 |  | 
| srs5694 | e7b4ff9 | 2009-08-18 13:16:10 -0400 | [diff] [blame] | 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) { | 
| srs5694 | 978041c | 2009-09-21 20:51:47 -0400 | [diff] [blame] | 736 |    int allOK = 1; | 
| srs5694 | e7b4ff9 | 2009-08-18 13:16:10 -0400 | [diff] [blame] | 737 |    char answer, line[256]; | 
 | 738 |    int fd; | 
 | 739 |    uint64_t secondTable; | 
| srs5694 | 2a9f5da | 2009-08-26 00:48:01 -0400 | [diff] [blame] | 740 |    uint32_t numParts; | 
| srs5694 | e7b4ff9 | 2009-08-18 13:16:10 -0400 | [diff] [blame] | 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? | 
| srs5694 | 221e087 | 2009-08-29 15:00:31 -0400 | [diff] [blame] | 750 |    if (CheckGPTSize() > 0) { | 
| srs5694 | e7b4ff9 | 2009-08-18 13:16:10 -0400 | [diff] [blame] | 751 |       allOK = 0; | 
 | 752 |    } // if | 
 | 753 |  | 
 | 754 |    // Check that disk is really big enough to handle this... | 
 | 755 |    if (mainHeader.backupLBA > diskSize) { | 
| srs5694 | 247657a | 2009-11-26 18:36:12 -0500 | [diff] [blame^] | 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"); | 
| srs5694 | e7b4ff9 | 2009-08-18 13:16:10 -0400 | [diff] [blame] | 758 |       printf("(Disk size is %ld sectors, needs to be %ld sectors.)\n", diskSize, | 
 | 759 |              mainHeader.backupLBA); | 
 | 760 |       allOK = 0; | 
 | 761 |    } // if | 
| srs5694 | 247657a | 2009-11-26 18:36:12 -0500 | [diff] [blame^] | 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 | 
| srs5694 | e7b4ff9 | 2009-08-18 13:16:10 -0400 | [diff] [blame] | 774 |  | 
 | 775 |    // Check for overlapping partitions.... | 
| srs5694 | e4ac11e | 2009-08-31 10:13:04 -0400 | [diff] [blame] | 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(); | 
| srs5694 | e7b4ff9 | 2009-08-18 13:16:10 -0400 | [diff] [blame] | 784 |  | 
| srs5694 | 2a9f5da | 2009-08-26 00:48:01 -0400 | [diff] [blame] | 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 | 
| srs5694 | e7b4ff9 | 2009-08-18 13:16:10 -0400 | [diff] [blame] | 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) { | 
| srs5694 | e4ac11e | 2009-08-31 10:13:04 -0400 | [diff] [blame] | 814 |       fd = OpenForWrite(device); | 
| srs5694 | e7b4ff9 | 2009-08-18 13:16:10 -0400 | [diff] [blame] | 815 |       if (fd != -1) { | 
 | 816 |          // First, write the protective MBR... | 
| srs5694 | e4ac11e | 2009-08-31 10:13:04 -0400 | [diff] [blame] | 817 |          protectiveMBR.WriteMBRData(fd); | 
| srs5694 | e7b4ff9 | 2009-08-18 13:16:10 -0400 | [diff] [blame] | 818 |  | 
 | 819 |          // Now write the main GPT header... | 
 | 820 |          if (allOK) | 
 | 821 |             if (write(fd, &mainHeader, 512) == -1) | 
| srs5694 | e4ac11e | 2009-08-31 10:13:04 -0400 | [diff] [blame] | 822 |                allOK = 0; | 
| srs5694 | e7b4ff9 | 2009-08-18 13:16:10 -0400 | [diff] [blame] | 823 |  | 
 | 824 |          // Now write the main partition tables... | 
| srs5694 | e4ac11e | 2009-08-31 10:13:04 -0400 | [diff] [blame] | 825 |          if (allOK) { | 
 | 826 |             if (write(fd, partitions, GPT_SIZE * numParts) == -1) | 
| srs5694 | e7b4ff9 | 2009-08-18 13:16:10 -0400 | [diff] [blame] | 827 |                allOK = 0; | 
 | 828 |          } // if | 
 | 829 |  | 
 | 830 |          // Now seek to near the end to write the secondary GPT.... | 
| srs5694 | e4ac11e | 2009-08-31 10:13:04 -0400 | [diff] [blame] | 831 |          if (allOK) { | 
| srs5694 | e7b4ff9 | 2009-08-18 13:16:10 -0400 | [diff] [blame] | 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.... | 
| srs5694 | e4ac11e | 2009-08-31 10:13:04 -0400 | [diff] [blame] | 840 |          if (allOK) | 
| srs5694 | 2a9f5da | 2009-08-26 00:48:01 -0400 | [diff] [blame] | 841 |             if (write(fd, partitions, GPT_SIZE * numParts) == -1) | 
| srs5694 | e7b4ff9 | 2009-08-18 13:16:10 -0400 | [diff] [blame] | 842 |                allOK = 0; | 
 | 843 |  | 
 | 844 |          // Now write the secondary GPT header... | 
| srs5694 | e4ac11e | 2009-08-31 10:13:04 -0400 | [diff] [blame] | 845 |          if (allOK) | 
| srs5694 | e7b4ff9 | 2009-08-18 13:16:10 -0400 | [diff] [blame] | 846 |             if (write(fd, &secondHeader, 512) == -1) | 
| srs5694 | e4ac11e | 2009-08-31 10:13:04 -0400 | [diff] [blame] | 847 |                allOK = 0; | 
| srs5694 | e7b4ff9 | 2009-08-18 13:16:10 -0400 | [diff] [blame] | 848 |  | 
 | 849 |          // re-read the partition table | 
 | 850 |          if (allOK) { | 
| srs5694 | e35eb1b | 2009-09-14 00:29:34 -0400 | [diff] [blame] | 851 |             DiskSync(fd); | 
| srs5694 | e7b4ff9 | 2009-08-18 13:16:10 -0400 | [diff] [blame] | 852 |          } // if | 
 | 853 |  | 
 | 854 |          if (allOK) { // writes completed OK | 
| srs5694 | e4ac11e | 2009-08-31 10:13:04 -0400 | [diff] [blame] | 855 |             printf("The operation has completed successfully.\n"); | 
| srs5694 | e7b4ff9 | 2009-08-18 13:16:10 -0400 | [diff] [blame] | 856 |          } else { | 
| srs5694 | e4ac11e | 2009-08-31 10:13:04 -0400 | [diff] [blame] | 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"); | 
| srs5694 | e7b4ff9 | 2009-08-18 13:16:10 -0400 | [diff] [blame] | 860 |          } // if/else | 
| srs5694 | e4ac11e | 2009-08-31 10:13:04 -0400 | [diff] [blame] | 861 |          close(fd); | 
| srs5694 | e7b4ff9 | 2009-08-18 13:16:10 -0400 | [diff] [blame] | 862 |       } else { | 
| srs5694 | e4ac11e | 2009-08-31 10:13:04 -0400 | [diff] [blame] | 863 |          fprintf(stderr, "Unable to open device %s for writing! Errno is %d! Aborting write!\n", | 
 | 864 |                  device, errno); | 
 | 865 |          allOK = 0; | 
| srs5694 | e7b4ff9 | 2009-08-18 13:16:10 -0400 | [diff] [blame] | 866 |       } // if/else | 
 | 867 |    } else { | 
 | 868 |       printf("Aborting write of new partition table.\n"); | 
 | 869 |    } // if | 
 | 870 |  | 
| srs5694 | 2a9f5da | 2009-08-26 00:48:01 -0400 | [diff] [blame] | 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 |  | 
| srs5694 | e7b4ff9 | 2009-08-18 13:16:10 -0400 | [diff] [blame] | 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) { | 
| srs5694 | 2a9f5da | 2009-08-26 00:48:01 -0400 | [diff] [blame] | 889 |    int fd, allOK = 1; | 
 | 890 |    uint32_t numParts; | 
| srs5694 | e7b4ff9 | 2009-08-18 13:16:10 -0400 | [diff] [blame] | 891 |  | 
 | 892 |    if ((fd = open(filename, O_WRONLY | O_CREAT, S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH)) != -1) { | 
| srs5694 | 2a9f5da | 2009-08-26 00:48:01 -0400 | [diff] [blame] | 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 |  | 
| srs5694 | 978041c | 2009-09-21 20:51:47 -0400 | [diff] [blame] | 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 |  | 
| srs5694 | 2a9f5da | 2009-08-26 00:48:01 -0400 | [diff] [blame] | 907 |       // Now write the protective MBR... | 
| srs5694 | e7b4ff9 | 2009-08-18 13:16:10 -0400 | [diff] [blame] | 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) | 
| srs5694 | e4ac11e | 2009-08-31 10:13:04 -0400 | [diff] [blame] | 918 |             allOK = 0; | 
| srs5694 | e7b4ff9 | 2009-08-18 13:16:10 -0400 | [diff] [blame] | 919 |  | 
 | 920 |       // Now write the main partition tables... | 
 | 921 |       if (allOK) { | 
| srs5694 | 2a9f5da | 2009-08-26 00:48:01 -0400 | [diff] [blame] | 922 |          if (write(fd, partitions, GPT_SIZE * numParts) == -1) | 
| srs5694 | e7b4ff9 | 2009-08-18 13:16:10 -0400 | [diff] [blame] | 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"); | 
| srs5694 | 8bb7876 | 2009-11-24 15:43:49 -0500 | [diff] [blame] | 930 |          printf("It may not be usable!\n"); | 
| srs5694 | e7b4ff9 | 2009-08-18 13:16:10 -0400 | [diff] [blame] | 931 |       } // if/else | 
 | 932 |       close(fd); | 
| srs5694 | 2a9f5da | 2009-08-26 00:48:01 -0400 | [diff] [blame] | 933 |  | 
 | 934 |       // Now reverse the byte-order reversal, if necessary.... | 
 | 935 |       if (IsLittleEndian() == 0) { | 
 | 936 |          ReverseHeaderBytes(&mainHeader); | 
 | 937 |          ReverseHeaderBytes(&secondHeader); | 
 | 938 |          ReversePartitionBytes(); | 
 | 939 |       } // if | 
| srs5694 | e7b4ff9 | 2009-08-18 13:16:10 -0400 | [diff] [blame] | 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; | 
| srs5694 | 2a9f5da | 2009-08-26 00:48:01 -0400 | [diff] [blame] | 954 |    int littleEndian = 1; | 
| srs5694 | e7b4ff9 | 2009-08-18 13:16:10 -0400 | [diff] [blame] | 955 |  | 
 | 956 |    if ((fd = open(filename, O_RDONLY)) != -1) { | 
| srs5694 | 2a9f5da | 2009-08-26 00:48:01 -0400 | [diff] [blame] | 957 |       if (IsLittleEndian() == 0) | 
 | 958 |          littleEndian = 0; | 
 | 959 |  | 
| srs5694 | e7b4ff9 | 2009-08-18 13:16:10 -0400 | [diff] [blame] | 960 |       // Let the MBRData class load the saved MBR... | 
| srs5694 | 221e087 | 2009-08-29 15:00:31 -0400 | [diff] [blame] | 961 |       protectiveMBR.ReadMBRData(fd, 0); // 0 = don't check block size | 
| srs5694 | e7b4ff9 | 2009-08-18 13:16:10 -0400 | [diff] [blame] | 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 |  | 
| srs5694 | 2a9f5da | 2009-08-26 00:48:01 -0400 | [diff] [blame] | 968 |       // Reverse byte order, if necessary | 
 | 969 |       if (littleEndian == 0) { | 
 | 970 |          ReverseHeaderBytes(&mainHeader); | 
 | 971 |       } // if | 
 | 972 |  | 
| srs5694 | e7b4ff9 | 2009-08-18 13:16:10 -0400 | [diff] [blame] | 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 |  | 
| srs5694 | 2a9f5da | 2009-08-26 00:48:01 -0400 | [diff] [blame] | 978 |       // Reverse byte order, if necessary | 
 | 979 |       if (littleEndian == 0) { | 
 | 980 |          ReverseHeaderBytes(&secondHeader); | 
 | 981 |       } // if | 
 | 982 |  | 
| srs5694 | e7b4ff9 | 2009-08-18 13:16:10 -0400 | [diff] [blame] | 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; | 
| srs5694 | e4ac11e | 2009-08-31 10:13:04 -0400 | [diff] [blame] | 991 |             sizeOfEntries = secondHeader.sizeOfPartitionEntries; | 
| srs5694 | e7b4ff9 | 2009-08-18 13:16:10 -0400 | [diff] [blame] | 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" | 
| srs5694 | e4ac11e | 2009-08-31 10:13:04 -0400 | [diff] [blame] | 1002 |                   "Adjusting sizes to match, but subsequent problems are possible!\n"); | 
| srs5694 | 247657a | 2009-11-26 18:36:12 -0500 | [diff] [blame^] | 1003 |             MoveSecondHeaderToEnd(); | 
| srs5694 | e7b4ff9 | 2009-08-18 13:16:10 -0400 | [diff] [blame] | 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); | 
| srs5694 | 2a9f5da | 2009-08-26 00:48:01 -0400 | [diff] [blame] | 1014 |          // Reverse byte order, if necessary | 
| srs5694 | e4ac11e | 2009-08-31 10:13:04 -0400 | [diff] [blame] | 1015 |          if (littleEndian == 0) { | 
 | 1016 |             ReversePartitionBytes(); | 
 | 1017 |          } // if | 
| srs5694 | 2a9f5da | 2009-08-26 00:48:01 -0400 | [diff] [blame] | 1018 |  | 
| srs5694 | e7b4ff9 | 2009-08-18 13:16:10 -0400 | [diff] [blame] | 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 |  | 
| srs5694 | e4ac11e | 2009-08-31 10:13:04 -0400 | [diff] [blame] | 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) { | 
| srs5694 | e35eb1b | 2009-09-14 00:29:34 -0400 | [diff] [blame] | 1063 |    int i; | 
| srs5694 | e4ac11e | 2009-08-31 10:13:04 -0400 | [diff] [blame] | 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); | 
| srs5694 | 978041c | 2009-09-21 20:51:47 -0400 | [diff] [blame] | 1069 |    printf("Disk %s: %llu sectors, %s\n", device, | 
 | 1070 |           (unsigned long long) diskSize, sizeInSI); | 
| srs5694 | e4ac11e | 2009-08-31 10:13:04 -0400 | [diff] [blame] | 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++) { | 
| srs5694 | 978041c | 2009-09-21 20:51:47 -0400 | [diff] [blame] | 1081 |       partitions[i].ShowSummary(i, blockSize); | 
| srs5694 | e4ac11e | 2009-08-31 10:13:04 -0400 | [diff] [blame] | 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 |  | 
| srs5694 | c0ca8f8 | 2009-08-20 21:35:25 -0400 | [diff] [blame] | 1258 | // This function destroys the on-disk GPT structures. Returns 1 if the | 
 | 1259 | // user confirms destruction, 0 if the user aborts. | 
| srs5694 | 978041c | 2009-09-21 20:51:47 -0400 | [diff] [blame] | 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) { | 
| srs5694 | e35eb1b | 2009-09-14 00:29:34 -0400 | [diff] [blame] | 1263 |    int fd, i; | 
| srs5694 | 978041c | 2009-09-21 20:51:47 -0400 | [diff] [blame] | 1264 |    char blankSector[512], goOn = 'Y', blank = 'N'; | 
| srs5694 | c0ca8f8 | 2009-08-20 21:35:25 -0400 | [diff] [blame] | 1265 |  | 
 | 1266 |    for (i = 0; i < 512; i++) { | 
 | 1267 |       blankSector[i] = '\0'; | 
 | 1268 |    } // for | 
 | 1269 |  | 
| srs5694 | 978041c | 2009-09-21 20:51:47 -0400 | [diff] [blame] | 1270 |    if (((apmFound) || (bsdFound)) && prompt) { | 
| srs5694 | e35eb1b | 2009-09-14 00:29:34 -0400 | [diff] [blame] | 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 | 
| srs5694 | 978041c | 2009-09-21 20:51:47 -0400 | [diff] [blame] | 1274 |    if (prompt) { | 
 | 1275 |       printf("\a\aAbout to wipe out GPT on %s. Proceed? ", device); | 
 | 1276 |       goOn = GetYN(); | 
 | 1277 |    } // if | 
| srs5694 | c0ca8f8 | 2009-08-20 21:35:25 -0400 | [diff] [blame] | 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 | 
| srs5694 | 978041c | 2009-09-21 20:51:47 -0400 | [diff] [blame] | 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') { | 
| srs5694 | c0ca8f8 | 2009-08-20 21:35:25 -0400 | [diff] [blame] | 1307 |             lseek64(fd, 0, SEEK_SET); | 
 | 1308 |             write(fd, blankSector, 512); // blank it out | 
| srs5694 | 978041c | 2009-09-21 20:51:47 -0400 | [diff] [blame] | 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"); | 
| srs5694 | e35eb1b | 2009-09-14 00:29:34 -0400 | [diff] [blame] | 1312 |          } // if/else | 
 | 1313 |          DiskSync(fd); | 
| srs5694 | c0ca8f8 | 2009-08-20 21:35:25 -0400 | [diff] [blame] | 1314 |          close(fd); | 
| srs5694 | e4ac11e | 2009-08-31 10:13:04 -0400 | [diff] [blame] | 1315 |          printf("GPT data structures destroyed! You may now partition the disk using fdisk or\n" | 
 | 1316 |                "other utilities. Program will now terminate.\n"); | 
| srs5694 | c0ca8f8 | 2009-08-20 21:35:25 -0400 | [diff] [blame] | 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 |  | 
| srs5694 | e4ac11e | 2009-08-31 10:13:04 -0400 | [diff] [blame] | 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"); | 
| srs5694 | 247657a | 2009-11-26 18:36:12 -0500 | [diff] [blame^] | 1410 |          which = use_gpt; | 
| srs5694 | e4ac11e | 2009-08-31 10:13:04 -0400 | [diff] [blame] | 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; | 
| srs5694 | e4ac11e | 2009-08-31 10:13:04 -0400 | [diff] [blame] | 1425 |  | 
 | 1426 |    // Clear out old data & prepare basics.... | 
 | 1427 |    ClearGPTData(); | 
 | 1428 |  | 
 | 1429 |    // Convert the smaller of the # of GPT or MBR partitions | 
| srs5694 | 978041c | 2009-09-21 20:51:47 -0400 | [diff] [blame] | 1430 |    if (mainHeader.numParts > (MAX_MBR_PARTS)) | 
 | 1431 |       numToConvert = MAX_MBR_PARTS; | 
| srs5694 | e4ac11e | 2009-08-31 10:13:04 -0400 | [diff] [blame] | 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 | 
| srs5694 | e35eb1b | 2009-09-14 00:29:34 -0400 | [diff] [blame] | 1439 |       if ((origType != 0x05) && (origType != 0x0f) && (origType != 0x85) && | 
| srs5694 | e4ac11e | 2009-08-31 10:13:04 -0400 | [diff] [blame] | 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 |  | 
| srs5694 | 978041c | 2009-09-21 20:51:47 -0400 | [diff] [blame] | 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 |  | 
| srs5694 | e4ac11e | 2009-08-31 10:13:04 -0400 | [diff] [blame] | 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]; | 
| srs5694 | 978041c | 2009-09-21 20:51:47 -0400 | [diff] [blame] | 1616 |    int numParts, numConverted = 0, i, j, typeCode, mbrNum; | 
| srs5694 | e4ac11e | 2009-08-31 10:13:04 -0400 | [diff] [blame] | 1617 |    char fillItUp = 'M'; // fill extra partition entries? (Yes/No/Maybe) | 
| srs5694 | 978041c | 2009-09-21 20:51:47 -0400 | [diff] [blame] | 1618 |    char eeFirst = 'Y'; // Whether EFI GPT (0xEE) partition comes first in table | 
| srs5694 | e4ac11e | 2009-08-31 10:13:04 -0400 | [diff] [blame] | 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); | 
| srs5694 | 978041c | 2009-09-21 20:51:47 -0400 | [diff] [blame] | 1643 |       if (eeFirst == 'Y') | 
 | 1644 |          mbrNum = i + 1; | 
 | 1645 |       else | 
 | 1646 |          mbrNum = i; | 
 | 1647 |       numConverted += OnePartToMBR(j, mbrNum); | 
| srs5694 | e4ac11e | 2009-08-31 10:13:04 -0400 | [diff] [blame] | 1648 |    } // for | 
 | 1649 |  | 
| srs5694 | 978041c | 2009-09-21 20:51:47 -0400 | [diff] [blame] | 1650 |    if ((numParts > 0) && (numConverted > 0)) { // User opted to create a hybrid MBR.... | 
| srs5694 | e4ac11e | 2009-08-31 10:13:04 -0400 | [diff] [blame] | 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); | 
| srs5694 | 978041c | 2009-09-21 20:51:47 -0400 | [diff] [blame] | 1663 |       protectiveMBR.SetHybrid(); | 
| srs5694 | e4ac11e | 2009-08-31 10:13:04 -0400 | [diff] [blame] | 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 |  | 
| srs5694 | 247657a | 2009-11-26 18:36:12 -0500 | [diff] [blame^] | 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 | 
| srs5694 | e4ac11e | 2009-08-31 10:13:04 -0400 | [diff] [blame] | 1744 |             partitions = newParts; | 
| srs5694 | 247657a | 2009-11-26 18:36:12 -0500 | [diff] [blame^] | 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 | 
| srs5694 | e4ac11e | 2009-08-31 10:13:04 -0400 | [diff] [blame] | 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. | 
| srs5694 | e35eb1b | 2009-09-14 00:29:34 -0400 | [diff] [blame] | 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. | 
| srs5694 | e4ac11e | 2009-08-31 10:13:04 -0400 | [diff] [blame] | 1806 | int GPTData::ClearGPTData(void) { | 
| srs5694 | e35eb1b | 2009-09-14 00:29:34 -0400 | [diff] [blame] | 1807 |    int goOn = 1, i; | 
| srs5694 | e4ac11e | 2009-08-31 10:13:04 -0400 | [diff] [blame] | 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; | 
| srs5694 | 978041c | 2009-09-21 20:51:47 -0400 | [diff] [blame] | 1817 |    mainHeader.headerSize = HEADER_SIZE; | 
| srs5694 | e4ac11e | 2009-08-31 10:13:04 -0400 | [diff] [blame] | 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 |  | 
| srs5694 | 247657a | 2009-11-26 18:36:12 -0500 | [diff] [blame^] | 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() { | 
| srs5694 | 8bb7876 | 2009-11-24 15:43:49 -0500 | [diff] [blame] | 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 |  | 
| srs5694 | e4ac11e | 2009-08-31 10:13:04 -0400 | [diff] [blame] | 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 |  | 
| srs5694 | 978041c | 2009-09-21 20:51:47 -0400 | [diff] [blame] | 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 |  | 
| srs5694 | e4ac11e | 2009-08-31 10:13:04 -0400 | [diff] [blame] | 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) { | 
| srs5694 | e35eb1b | 2009-09-14 00:29:34 -0400 | [diff] [blame] | 1979 |    uint64_t start, firstBlock, lastBlock, segmentSize, selectedSize = 0, selectedSegment = 0; | 
| srs5694 | e4ac11e | 2009-08-31 10:13:04 -0400 | [diff] [blame] | 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 | 
| srs5694 | e35eb1b | 2009-09-14 00:29:34 -0400 | [diff] [blame] | 2081 |    if ((sector < mainHeader.firstUsableLBA) || | 
| srs5694 | e4ac11e | 2009-08-31 10:13:04 -0400 | [diff] [blame] | 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 |  | 
| srs5694 | 2a9f5da | 2009-08-26 00:48:01 -0400 | [diff] [blame] | 2094 | void GPTData::ReverseHeaderBytes(struct GPTHeader* header) { | 
| srs5694 | 221e087 | 2009-08-29 15:00:31 -0400 | [diff] [blame] | 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); | 
| srs5694 | 2a9f5da | 2009-08-26 00:48:01 -0400 | [diff] [blame] | 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" | 
| srs5694 | e4ac11e | 2009-08-31 10:13:04 -0400 | [diff] [blame] | 2123 |             "data corruption or a misplaced call to this function.\n"); | 
| srs5694 | 2a9f5da | 2009-08-26 00:48:01 -0400 | [diff] [blame] | 2124 |    } // if signature mismatch.... | 
 | 2125 |    for (i = 0; i < mainHeader.numParts; i++) { | 
| srs5694 | 221e087 | 2009-08-29 15:00:31 -0400 | [diff] [blame] | 2126 |       partitions[i].ReversePartBytes(); | 
| srs5694 | 2a9f5da | 2009-08-26 00:48:01 -0400 | [diff] [blame] | 2127 |    } // for | 
 | 2128 | } // GPTData::ReversePartitionBytes() | 
 | 2129 |  | 
 | 2130 | /****************************************** | 
 | 2131 |  *                                        * | 
 | 2132 |  * Additional non-class support functions * | 
 | 2133 |  *                                        * | 
 | 2134 |  ******************************************/ | 
 | 2135 |  | 
| srs5694 | e7b4ff9 | 2009-08-18 13:16:10 -0400 | [diff] [blame] | 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; | 
| srs5694 | e7b4ff9 | 2009-08-18 13:16:10 -0400 | [diff] [blame] | 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) { | 
| srs5694 | 221e087 | 2009-08-29 15:00:31 -0400 | [diff] [blame] | 2160 |       fprintf(stderr, "MBRRecord is %d bytes, should be 16 bytes; aborting!\n", sizeof(MBRRecord)); | 
| srs5694 | e7b4ff9 | 2009-08-18 13:16:10 -0400 | [diff] [blame] | 2161 |       allOK = 0; | 
 | 2162 |    } // if | 
| srs5694 | 978041c | 2009-09-21 20:51:47 -0400 | [diff] [blame] | 2163 |    if (sizeof(struct TempMBR) != 512) { | 
 | 2164 |       fprintf(stderr, "TempMBR is %d bytes, should be 512 bytes; aborting!\n", sizeof(TempMBR)); | 
| srs5694 | e7b4ff9 | 2009-08-18 13:16:10 -0400 | [diff] [blame] | 2165 |       allOK = 0; | 
 | 2166 |    } // if | 
 | 2167 |    if (sizeof(struct GPTHeader) != 512) { | 
| srs5694 | 221e087 | 2009-08-29 15:00:31 -0400 | [diff] [blame] | 2168 |       fprintf(stderr, "GPTHeader is %d bytes, should be 512 bytes; aborting!\n", sizeof(GPTHeader)); | 
| srs5694 | e7b4ff9 | 2009-08-18 13:16:10 -0400 | [diff] [blame] | 2169 |       allOK = 0; | 
 | 2170 |    } // if | 
| srs5694 | 221e087 | 2009-08-29 15:00:31 -0400 | [diff] [blame] | 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 | 
| srs5694 | 2a9f5da | 2009-08-26 00:48:01 -0400 | [diff] [blame] | 2176 |    if (IsLittleEndian() == 0) { | 
 | 2177 |       fprintf(stderr, "\aRunning on big-endian hardware. Big-endian support is new and poorly" | 
| srs5694 | e4ac11e | 2009-08-31 10:13:04 -0400 | [diff] [blame] | 2178 |             " tested!\nBeware!\n"); | 
| srs5694 | 2a9f5da | 2009-08-26 00:48:01 -0400 | [diff] [blame] | 2179 |       // allOK = 0; | 
| srs5694 | e7b4ff9 | 2009-08-18 13:16:10 -0400 | [diff] [blame] | 2180 |    } // if | 
 | 2181 |    return (allOK); | 
 | 2182 | } // SizesOK() | 
| srs5694 | e4ac11e | 2009-08-31 10:13:04 -0400 | [diff] [blame] | 2183 |  |