OLD | NEW |
(Empty) | |
| 1 /* Copyright (c) 2010 The Chromium OS Authors. All rights reserved. |
| 2 * Use of this source code is governed by a BSD-style license that can be |
| 3 * found in the LICENSE file. |
| 4 */ |
| 5 |
| 6 #include "cgptlib.h" |
| 7 #include "cgptlib_internal.h" |
| 8 #include "crc32.h" |
| 9 #include "gpt.h" |
| 10 #include "utility.h" |
| 11 |
| 12 |
| 13 int CheckParameters(GptData *gpt) { |
| 14 /* Currently, we only support 512-byte sector. In the future, we may support |
| 15 * larger sector. */ |
| 16 if (gpt->sector_bytes != 512) |
| 17 return GPT_ERROR_INVALID_SECTOR_SIZE; |
| 18 |
| 19 /* The sector number of a drive should be reasonable. If the given value is |
| 20 * too small to contain basic GPT structure (PMBR + Headers + Entries), |
| 21 * the value is wrong. */ |
| 22 if (gpt->drive_sectors < (1 + 2 * (1 + GPT_ENTRIES_SECTORS))) |
| 23 return GPT_ERROR_INVALID_SECTOR_NUMBER; |
| 24 |
| 25 return GPT_SUCCESS; |
| 26 } |
| 27 |
| 28 |
| 29 uint32_t HeaderCrc(GptHeader* h) { |
| 30 uint32_t crc32, original_crc32; |
| 31 |
| 32 /* Original CRC is calculated with the CRC field 0. */ |
| 33 original_crc32 = h->header_crc32; |
| 34 h->header_crc32 = 0; |
| 35 crc32 = Crc32((const uint8_t *)h, h->size); |
| 36 h->header_crc32 = original_crc32; |
| 37 |
| 38 return crc32; |
| 39 } |
| 40 |
| 41 |
| 42 int CheckHeader(GptHeader *h, int is_secondary, uint64_t drive_sectors) { |
| 43 if (!h) |
| 44 return 1; |
| 45 |
| 46 /* Make sure we're looking at a header of reasonable size before |
| 47 * attempting to calculate CRC. */ |
| 48 if (Memcmp(h->signature, GPT_HEADER_SIGNATURE, GPT_HEADER_SIGNATURE_SIZE)) |
| 49 return 1; |
| 50 if (h->revision != GPT_HEADER_REVISION) |
| 51 return 1; |
| 52 if (h->size < MIN_SIZE_OF_HEADER || h->size > MAX_SIZE_OF_HEADER) |
| 53 return 1; |
| 54 |
| 55 /* Check CRC before looking at remaining fields */ |
| 56 if (HeaderCrc(h) != h->header_crc32) |
| 57 return 1; |
| 58 |
| 59 /* Reserved fields must be zero. */ |
| 60 if (h->reserved) |
| 61 return 1; |
| 62 |
| 63 /* TODO: Padding must be set to zero. */ |
| 64 |
| 65 /* If entry size is different than our struct, we won't be able to |
| 66 * parse it. Technically, any size 2^N where N>=7 is valid. */ |
| 67 if (h->size_of_entry != sizeof(GptEntry)) |
| 68 return 1; |
| 69 if ((h->number_of_entries < MIN_NUMBER_OF_ENTRIES) || |
| 70 (h->number_of_entries > MAX_NUMBER_OF_ENTRIES) || |
| 71 (h->number_of_entries * h->size_of_entry != TOTAL_ENTRIES_SIZE)) |
| 72 return 1; |
| 73 |
| 74 /* Check locations for the header and its entries. The primary |
| 75 * immediately follows the PMBR, and is followed by its entries. |
| 76 * The secondary is at the end of the drive, preceded by its |
| 77 * entries. */ |
| 78 if (is_secondary) { |
| 79 if ((h->my_lba != drive_sectors - 1) || (h->alternate_lba != 1)) |
| 80 return 1; |
| 81 if (h->entries_lba != h->my_lba - GPT_ENTRIES_SECTORS) |
| 82 return 1; |
| 83 } else { |
| 84 if ((h->my_lba != 1) || (h->alternate_lba != drive_sectors - 1)) |
| 85 return 1; |
| 86 if (h->entries_lba != h->my_lba + 1) |
| 87 return 1; |
| 88 } |
| 89 |
| 90 /* FirstUsableLBA must be after the end of the primary GPT table |
| 91 * array. LastUsableLBA must be before the start of the secondary |
| 92 * GPT table array. FirstUsableLBA <= LastUsableLBA. */ |
| 93 if (h->first_usable_lba < 2 + GPT_ENTRIES_SECTORS) |
| 94 return 1; |
| 95 if (h->last_usable_lba >= drive_sectors - 1 - GPT_ENTRIES_SECTORS) |
| 96 return 1; |
| 97 if (h->first_usable_lba > h->last_usable_lba) |
| 98 return 1; |
| 99 |
| 100 /* Success */ |
| 101 return 0; |
| 102 } |
| 103 |
| 104 |
| 105 /* Return 1 if the entry is unused, 0 if it is used. */ |
| 106 int IsUnusedEntry(const GptEntry* e) { |
| 107 static Guid zero = {{{0, 0, 0, 0, 0, {0, 0, 0, 0, 0, 0}}}}; |
| 108 return (Memcmp(&zero, (const uint8_t*)(&e->type), sizeof(zero)) ? 0 : 1); |
| 109 } |
| 110 |
| 111 /* Returns 1 if the entry is a Chrome OS kernel partition, else 0. */ |
| 112 int IsKernelEntry(const GptEntry* e) { |
| 113 static Guid chromeos_kernel = GPT_ENT_TYPE_CHROMEOS_KERNEL; |
| 114 return (Memcmp(&e->type, &chromeos_kernel, sizeof(Guid)) ? 0 : 1); |
| 115 } |
| 116 |
| 117 |
| 118 int CheckEntries(GptEntry* entries, GptHeader* h, uint64_t drive_sectors) { |
| 119 |
| 120 GptEntry* entry; |
| 121 uint32_t crc32; |
| 122 int i; |
| 123 |
| 124 /* Check CRC before examining entries. */ |
| 125 crc32 = Crc32((const uint8_t *)entries, |
| 126 h->size_of_entry * h->number_of_entries); |
| 127 if (crc32 != h->entries_crc32) |
| 128 return 1; |
| 129 |
| 130 /* Check all entries. */ |
| 131 for (i = 0, entry = entries; i < h->number_of_entries; i++, entry++) { |
| 132 GptEntry* e2; |
| 133 int i2; |
| 134 |
| 135 if (IsUnusedEntry(entry)) |
| 136 continue; |
| 137 |
| 138 /* Entry must be in valid region. */ |
| 139 if ((entry->starting_lba < h->first_usable_lba) || |
| 140 (entry->ending_lba > h->last_usable_lba) || |
| 141 (entry->ending_lba < entry->starting_lba)) |
| 142 return 1; |
| 143 |
| 144 /* Entry must not overlap other entries. */ |
| 145 for (i2 = 0, e2 = entries; i2 < h->number_of_entries; i2++, e2++) { |
| 146 if (i2 == i || IsUnusedEntry(e2)) |
| 147 continue; |
| 148 |
| 149 if ((entry->starting_lba >= e2->starting_lba) && |
| 150 (entry->starting_lba <= e2->ending_lba)) |
| 151 return 1; |
| 152 if ((entry->ending_lba >= e2->starting_lba) && |
| 153 (entry->ending_lba <= e2->ending_lba)) |
| 154 return 1; |
| 155 } |
| 156 } |
| 157 |
| 158 /* Success */ |
| 159 return 0; |
| 160 } |
| 161 |
| 162 |
| 163 /* Returns 0 if the GptHeaders are the same for all fields which don't |
| 164 * differ between the primary and secondary headers - that is, all |
| 165 * fields other than: |
| 166 * |
| 167 * my_lba |
| 168 * alternate_lba |
| 169 * entries_lba */ |
| 170 int HeaderFieldsSame(GptHeader *h1, GptHeader *h2) { |
| 171 if (Memcmp(h1->signature, h2->signature, sizeof(h1->signature))) |
| 172 return 1; |
| 173 if (h1->revision != h2->revision) |
| 174 return 1; |
| 175 if (h1->size != h2->size) |
| 176 return 1; |
| 177 if (h1->reserved != h2->reserved) |
| 178 return 1; |
| 179 if (h1->first_usable_lba != h2->first_usable_lba) |
| 180 return 1; |
| 181 if (h1->last_usable_lba != h2->last_usable_lba) |
| 182 return 1; |
| 183 if (Memcmp(&h1->disk_uuid, &h2->disk_uuid, sizeof(Guid))) |
| 184 return 1; |
| 185 if (h1->number_of_entries != h2->number_of_entries) |
| 186 return 1; |
| 187 if (h1->size_of_entry != h2->size_of_entry) |
| 188 return 1; |
| 189 if (h1->entries_crc32 != h2->entries_crc32) |
| 190 return 1; |
| 191 |
| 192 return 0; |
| 193 } |
| 194 |
| 195 |
| 196 int GptSanityCheck(GptData *gpt) { |
| 197 int retval; |
| 198 GptHeader* header1 = (GptHeader*)(gpt->primary_header); |
| 199 GptHeader* header2 = (GptHeader*)(gpt->secondary_header); |
| 200 GptEntry* entries1 = (GptEntry*)(gpt->primary_entries); |
| 201 GptEntry* entries2 = (GptEntry*)(gpt->secondary_entries); |
| 202 GptHeader* goodhdr = NULL; |
| 203 |
| 204 gpt->valid_headers = 0; |
| 205 gpt->valid_entries = 0; |
| 206 |
| 207 retval = CheckParameters(gpt); |
| 208 if (retval != GPT_SUCCESS) |
| 209 return retval; |
| 210 |
| 211 /* Check both headers; we need at least one valid header. */ |
| 212 if (0 == CheckHeader(header1, 0, gpt->drive_sectors)) { |
| 213 gpt->valid_headers |= MASK_PRIMARY; |
| 214 goodhdr = header1; |
| 215 } |
| 216 if (0 == CheckHeader(header2, 1, gpt->drive_sectors)) { |
| 217 gpt->valid_headers |= MASK_SECONDARY; |
| 218 if (!goodhdr) |
| 219 goodhdr = header2; |
| 220 } |
| 221 |
| 222 if (!gpt->valid_headers) |
| 223 return GPT_ERROR_INVALID_HEADERS; |
| 224 |
| 225 /* Checks if entries are valid. |
| 226 * |
| 227 * Note that we use the same header in both checks. This way we'll |
| 228 * catch the case where (header1,entries1) and (header2,entries2) |
| 229 * are both valid, but (entries1 != entries2). */ |
| 230 if (0 == CheckEntries(entries1, goodhdr, gpt->drive_sectors)) |
| 231 gpt->valid_entries |= MASK_PRIMARY; |
| 232 if (0 == CheckEntries(entries2, goodhdr, gpt->drive_sectors)) |
| 233 gpt->valid_entries |= MASK_SECONDARY; |
| 234 |
| 235 /* If both headers are good but neither entries were good, check the |
| 236 * entries with the secondary header. */ |
| 237 if (MASK_BOTH == gpt->valid_headers && !gpt->valid_entries) { |
| 238 if (0 == CheckEntries(entries1, header2, gpt->drive_sectors)) |
| 239 gpt->valid_entries |= MASK_PRIMARY; |
| 240 if (0 == CheckEntries(entries2, header2, gpt->drive_sectors)) |
| 241 gpt->valid_entries |= MASK_SECONDARY; |
| 242 if (gpt->valid_entries) { |
| 243 /* Sure enough, header2 had a good CRC for one of the entries. Mark |
| 244 * header1 invalid, so we'll update its entries CRC. */ |
| 245 gpt->valid_headers &= ~MASK_PRIMARY; |
| 246 goodhdr = header2; |
| 247 } |
| 248 } |
| 249 |
| 250 if (!gpt->valid_entries) |
| 251 return GPT_ERROR_INVALID_ENTRIES; |
| 252 |
| 253 /* Now that we've determined which header contains a good CRC for |
| 254 * the entries, make sure the headers are otherwise identical. */ |
| 255 if (MASK_BOTH == gpt->valid_headers && |
| 256 0 != HeaderFieldsSame(header1, header2)) |
| 257 gpt->valid_headers &= ~MASK_SECONDARY; |
| 258 |
| 259 return GPT_SUCCESS; |
| 260 } |
| 261 |
| 262 |
| 263 void GptRepair(GptData *gpt) { |
| 264 GptHeader* header1 = (GptHeader*)(gpt->primary_header); |
| 265 GptHeader* header2 = (GptHeader*)(gpt->secondary_header); |
| 266 GptEntry* entries1 = (GptEntry*)(gpt->primary_entries); |
| 267 GptEntry* entries2 = (GptEntry*)(gpt->secondary_entries); |
| 268 int entries_size; |
| 269 |
| 270 /* Need at least one good header and one good set of entries. */ |
| 271 if (MASK_NONE == gpt->valid_headers || MASK_NONE == gpt->valid_entries) |
| 272 return; |
| 273 |
| 274 /* Repair headers if necessary */ |
| 275 if (MASK_PRIMARY == gpt->valid_headers) { |
| 276 /* Primary is good, secondary is bad */ |
| 277 Memcpy(header2, header1, sizeof(GptHeader)); |
| 278 header2->my_lba = gpt->drive_sectors - 1; |
| 279 header2->alternate_lba = 1; |
| 280 header2->entries_lba = header2->my_lba - GPT_ENTRIES_SECTORS; |
| 281 header2->header_crc32 = HeaderCrc(header2); |
| 282 gpt->modified |= GPT_MODIFIED_HEADER2; |
| 283 } |
| 284 else if (MASK_SECONDARY == gpt->valid_headers) { |
| 285 /* Secondary is good, primary is bad */ |
| 286 Memcpy(header1, header2, sizeof(GptHeader)); |
| 287 header1->my_lba = 1; |
| 288 header1->alternate_lba = gpt->drive_sectors - 1; |
| 289 header1->entries_lba = header1->my_lba + 1; |
| 290 header1->header_crc32 = HeaderCrc(header1); |
| 291 gpt->modified |= GPT_MODIFIED_HEADER1; |
| 292 } |
| 293 gpt->valid_headers = MASK_BOTH; |
| 294 |
| 295 /* Repair entries if necessary */ |
| 296 entries_size = header1->size_of_entry * header1->number_of_entries; |
| 297 if (MASK_PRIMARY == gpt->valid_entries) { |
| 298 /* Primary is good, secondary is bad */ |
| 299 Memcpy(entries2, entries1, entries_size); |
| 300 gpt->modified |= GPT_MODIFIED_ENTRIES2; |
| 301 } |
| 302 else if (MASK_SECONDARY == gpt->valid_entries) { |
| 303 /* Secondary is good, primary is bad */ |
| 304 Memcpy(entries1, entries2, entries_size); |
| 305 gpt->modified |= GPT_MODIFIED_ENTRIES1; |
| 306 } |
| 307 gpt->valid_entries = MASK_BOTH; |
| 308 } |
| 309 |
| 310 |
| 311 int GetEntrySuccessful(const GptEntry* e) { |
| 312 return (e->attributes & CGPT_ATTRIBUTE_SUCCESSFUL_MASK) >> |
| 313 CGPT_ATTRIBUTE_SUCCESSFUL_OFFSET; |
| 314 } |
| 315 |
| 316 |
| 317 int GetEntryPriority(const GptEntry* e) { |
| 318 return (e->attributes & CGPT_ATTRIBUTE_PRIORITY_MASK) >> |
| 319 CGPT_ATTRIBUTE_PRIORITY_OFFSET; |
| 320 } |
| 321 |
| 322 |
| 323 int GetEntryTries(const GptEntry* e) { |
| 324 return (e->attributes & CGPT_ATTRIBUTE_TRIES_MASK) >> |
| 325 CGPT_ATTRIBUTE_TRIES_OFFSET; |
| 326 } |
| 327 |
| 328 |
| 329 void SetEntrySuccessful(GptEntry* e, int successful) { |
| 330 if (successful) |
| 331 e->attributes |= CGPT_ATTRIBUTE_SUCCESSFUL_MASK; |
| 332 else |
| 333 e->attributes &= ~CGPT_ATTRIBUTE_SUCCESSFUL_MASK; |
| 334 } |
| 335 |
| 336 |
| 337 void SetEntryPriority(GptEntry* e, int priority) { |
| 338 e->attributes &= ~CGPT_ATTRIBUTE_PRIORITY_MASK; |
| 339 e->attributes |= ((uint64_t)priority << CGPT_ATTRIBUTE_PRIORITY_OFFSET) & |
| 340 CGPT_ATTRIBUTE_PRIORITY_MASK; |
| 341 } |
| 342 |
| 343 |
| 344 void SetEntryTries(GptEntry* e, int tries) { |
| 345 e->attributes &= ~CGPT_ATTRIBUTE_TRIES_MASK; |
| 346 e->attributes |= ((uint64_t)tries << CGPT_ATTRIBUTE_TRIES_OFFSET) & |
| 347 CGPT_ATTRIBUTE_TRIES_MASK; |
| 348 } |
OLD | NEW |