Randall Spangler | cefe12c | 2013-01-30 12:52:02 -0800 | [diff] [blame] | 1 | /* Copyright (c) 2013 The Chromium OS Authors. All rights reserved. |
Randall Spangler | 3dcf9dc | 2010-06-02 12:46:17 -0700 | [diff] [blame] | 2 | * Use of this source code is governed by a BSD-style license that can be |
| 3 | * found in the LICENSE file. |
| 4 | */ |
| 5 | |
Joel Kitching | 0b3ce46 | 2019-06-14 14:52:41 +0800 | [diff] [blame] | 6 | #include "2sysincludes.h" |
Randall Spangler | 3dcf9dc | 2010-06-02 12:46:17 -0700 | [diff] [blame] | 7 | #include "cgptlib.h" |
| 8 | #include "cgptlib_internal.h" |
| 9 | #include "crc32.h" |
| 10 | #include "gpt.h" |
Dan Ehrenberg | 7c2beb0 | 2014-10-21 16:15:54 -0700 | [diff] [blame] | 11 | #include "gpt_misc.h" |
Randall Spangler | 3dcf9dc | 2010-06-02 12:46:17 -0700 | [diff] [blame] | 12 | |
Sam Hurst | d6f52a0 | 2018-04-11 08:50:58 -0700 | [diff] [blame] | 13 | const static int MIN_SECTOR_SIZE = 512; |
Nam T. Nguyen | 3200401 | 2014-12-12 09:38:35 -0800 | [diff] [blame] | 14 | |
Sam Hurst | d6f52a0 | 2018-04-11 08:50:58 -0700 | [diff] [blame] | 15 | size_t CalculateEntriesSectors(GptHeader* h, uint32_t sector_bytes) |
Randall Spangler | fb26715 | 2016-10-11 15:28:16 -0700 | [diff] [blame] | 16 | { |
| 17 | size_t bytes = h->number_of_entries * h->size_of_entry; |
Sam Hurst | d6f52a0 | 2018-04-11 08:50:58 -0700 | [diff] [blame] | 18 | size_t ret = (bytes + sector_bytes - 1) / sector_bytes; |
Randall Spangler | fb26715 | 2016-10-11 15:28:16 -0700 | [diff] [blame] | 19 | return ret; |
Nam T. Nguyen | 3200401 | 2014-12-12 09:38:35 -0800 | [diff] [blame] | 20 | } |
Randall Spangler | 3dcf9dc | 2010-06-02 12:46:17 -0700 | [diff] [blame] | 21 | |
Randall Spangler | cefe12c | 2013-01-30 12:52:02 -0800 | [diff] [blame] | 22 | int CheckParameters(GptData *gpt) |
Vadim Bendebury | 65d3c27 | 2012-09-24 17:41:18 -0700 | [diff] [blame] | 23 | { |
Sam Hurst | d6f52a0 | 2018-04-11 08:50:58 -0700 | [diff] [blame] | 24 | /* Only support 512-byte or larger sectors that are a power of 2 */ |
| 25 | if (gpt->sector_bytes < MIN_SECTOR_SIZE || |
| 26 | (gpt->sector_bytes & (gpt->sector_bytes - 1)) != 0) |
Randall Spangler | cefe12c | 2013-01-30 12:52:02 -0800 | [diff] [blame] | 27 | return GPT_ERROR_INVALID_SECTOR_SIZE; |
Vadim Bendebury | 65d3c27 | 2012-09-24 17:41:18 -0700 | [diff] [blame] | 28 | |
Randall Spangler | cefe12c | 2013-01-30 12:52:02 -0800 | [diff] [blame] | 29 | /* |
Dan Ehrenberg | b3d38f5 | 2014-12-09 13:42:15 -0800 | [diff] [blame] | 30 | * gpt_drive_sectors should be reasonable. It cannot be unset, and it |
| 31 | * cannot differ from streaming_drive_sectors if the GPT structs are |
| 32 | * stored on same device. |
Nam T. Nguyen | 6ee52d9 | 2014-10-24 13:20:39 -0700 | [diff] [blame] | 33 | */ |
| 34 | if (gpt->gpt_drive_sectors == 0 || |
Dan Ehrenberg | b3d38f5 | 2014-12-09 13:42:15 -0800 | [diff] [blame] | 35 | (!(gpt->flags & GPT_FLAG_EXTERNAL) && |
| 36 | gpt->gpt_drive_sectors != gpt->streaming_drive_sectors)) { |
Nam T. Nguyen | 6ee52d9 | 2014-10-24 13:20:39 -0700 | [diff] [blame] | 37 | return GPT_ERROR_INVALID_SECTOR_NUMBER; |
| 38 | } |
| 39 | |
| 40 | /* |
Randall Spangler | cefe12c | 2013-01-30 12:52:02 -0800 | [diff] [blame] | 41 | * Sector count of a drive should be reasonable. If the given value is |
| 42 | * too small to contain basic GPT structure (PMBR + Headers + Entries), |
Dan Ehrenberg | f3f7fca | 2015-01-02 14:39:38 -0800 | [diff] [blame] | 43 | * the value is wrong. |
Randall Spangler | cefe12c | 2013-01-30 12:52:02 -0800 | [diff] [blame] | 44 | */ |
Dan Ehrenberg | f3f7fca | 2015-01-02 14:39:38 -0800 | [diff] [blame] | 45 | if (gpt->gpt_drive_sectors < |
| 46 | (1 + 2 * (1 + MIN_NUMBER_OF_ENTRIES / |
Sam Hurst | d6f52a0 | 2018-04-11 08:50:58 -0700 | [diff] [blame] | 47 | (gpt->sector_bytes / sizeof(GptEntry))))) |
Randall Spangler | cefe12c | 2013-01-30 12:52:02 -0800 | [diff] [blame] | 48 | return GPT_ERROR_INVALID_SECTOR_NUMBER; |
Vadim Bendebury | 65d3c27 | 2012-09-24 17:41:18 -0700 | [diff] [blame] | 49 | |
Randall Spangler | cefe12c | 2013-01-30 12:52:02 -0800 | [diff] [blame] | 50 | return GPT_SUCCESS; |
| 51 | } |
Vadim Bendebury | 65d3c27 | 2012-09-24 17:41:18 -0700 | [diff] [blame] | 52 | |
Randall Spangler | cefe12c | 2013-01-30 12:52:02 -0800 | [diff] [blame] | 53 | uint32_t HeaderCrc(GptHeader *h) |
| 54 | { |
| 55 | uint32_t crc32, original_crc32; |
Vadim Bendebury | 65d3c27 | 2012-09-24 17:41:18 -0700 | [diff] [blame] | 56 | |
Randall Spangler | cefe12c | 2013-01-30 12:52:02 -0800 | [diff] [blame] | 57 | /* Original CRC is calculated with the CRC field 0. */ |
| 58 | original_crc32 = h->header_crc32; |
| 59 | h->header_crc32 = 0; |
| 60 | crc32 = Crc32((const uint8_t *)h, h->size); |
| 61 | h->header_crc32 = original_crc32; |
Vadim Bendebury | 65d3c27 | 2012-09-24 17:41:18 -0700 | [diff] [blame] | 62 | |
Randall Spangler | cefe12c | 2013-01-30 12:52:02 -0800 | [diff] [blame] | 63 | return crc32; |
| 64 | } |
Vadim Bendebury | 65d3c27 | 2012-09-24 17:41:18 -0700 | [diff] [blame] | 65 | |
Dan Ehrenberg | b3d38f5 | 2014-12-09 13:42:15 -0800 | [diff] [blame] | 66 | int CheckHeader(GptHeader *h, int is_secondary, |
| 67 | uint64_t streaming_drive_sectors, |
Sam Hurst | d6f52a0 | 2018-04-11 08:50:58 -0700 | [diff] [blame] | 68 | uint64_t gpt_drive_sectors, uint32_t flags, |
| 69 | uint32_t sector_bytes) |
Randall Spangler | cefe12c | 2013-01-30 12:52:02 -0800 | [diff] [blame] | 70 | { |
| 71 | if (!h) |
| 72 | return 1; |
Vadim Bendebury | 65d3c27 | 2012-09-24 17:41:18 -0700 | [diff] [blame] | 73 | |
Randall Spangler | cefe12c | 2013-01-30 12:52:02 -0800 | [diff] [blame] | 74 | /* |
| 75 | * Make sure we're looking at a header of reasonable size before |
| 76 | * attempting to calculate CRC. |
| 77 | */ |
Randall Spangler | 664096b | 2016-10-13 16:16:41 -0700 | [diff] [blame] | 78 | if (memcmp(h->signature, GPT_HEADER_SIGNATURE, |
Randall Spangler | cefe12c | 2013-01-30 12:52:02 -0800 | [diff] [blame] | 79 | GPT_HEADER_SIGNATURE_SIZE) && |
Randall Spangler | 664096b | 2016-10-13 16:16:41 -0700 | [diff] [blame] | 80 | memcmp(h->signature, GPT_HEADER_SIGNATURE2, |
Randall Spangler | cefe12c | 2013-01-30 12:52:02 -0800 | [diff] [blame] | 81 | GPT_HEADER_SIGNATURE_SIZE)) |
| 82 | return 1; |
| 83 | if (h->revision != GPT_HEADER_REVISION) |
| 84 | return 1; |
| 85 | if (h->size < MIN_SIZE_OF_HEADER || h->size > MAX_SIZE_OF_HEADER) |
| 86 | return 1; |
Vadim Bendebury | 65d3c27 | 2012-09-24 17:41:18 -0700 | [diff] [blame] | 87 | |
Randall Spangler | cefe12c | 2013-01-30 12:52:02 -0800 | [diff] [blame] | 88 | /* Check CRC before looking at remaining fields */ |
| 89 | if (HeaderCrc(h) != h->header_crc32) |
| 90 | return 1; |
Vadim Bendebury | 65d3c27 | 2012-09-24 17:41:18 -0700 | [diff] [blame] | 91 | |
Randall Spangler | cefe12c | 2013-01-30 12:52:02 -0800 | [diff] [blame] | 92 | /* Reserved fields must be zero. */ |
| 93 | if (h->reserved_zero) |
| 94 | return 1; |
Vadim Bendebury | 65d3c27 | 2012-09-24 17:41:18 -0700 | [diff] [blame] | 95 | |
Randall Spangler | cefe12c | 2013-01-30 12:52:02 -0800 | [diff] [blame] | 96 | /* Could check that padding is zero, but that doesn't matter to us. */ |
Vadim Bendebury | 65d3c27 | 2012-09-24 17:41:18 -0700 | [diff] [blame] | 97 | |
Randall Spangler | cefe12c | 2013-01-30 12:52:02 -0800 | [diff] [blame] | 98 | /* |
| 99 | * If entry size is different than our struct, we won't be able to |
| 100 | * parse it. Technically, any size 2^N where N>=7 is valid. |
| 101 | */ |
| 102 | if (h->size_of_entry != sizeof(GptEntry)) |
| 103 | return 1; |
| 104 | if ((h->number_of_entries < MIN_NUMBER_OF_ENTRIES) || |
| 105 | (h->number_of_entries > MAX_NUMBER_OF_ENTRIES) || |
Dan Ehrenberg | b3d38f5 | 2014-12-09 13:42:15 -0800 | [diff] [blame] | 106 | (!(flags & GPT_FLAG_EXTERNAL) && |
Dan Ehrenberg | f3f7fca | 2015-01-02 14:39:38 -0800 | [diff] [blame] | 107 | h->number_of_entries != MAX_NUMBER_OF_ENTRIES)) |
Randall Spangler | cefe12c | 2013-01-30 12:52:02 -0800 | [diff] [blame] | 108 | return 1; |
Vadim Bendebury | 65d3c27 | 2012-09-24 17:41:18 -0700 | [diff] [blame] | 109 | |
Randall Spangler | cefe12c | 2013-01-30 12:52:02 -0800 | [diff] [blame] | 110 | /* |
| 111 | * Check locations for the header and its entries. The primary |
| 112 | * immediately follows the PMBR, and is followed by its entries. The |
| 113 | * secondary is at the end of the drive, preceded by its entries. |
| 114 | */ |
| 115 | if (is_secondary) { |
Dan Ehrenberg | a524a3a | 2014-11-06 16:22:24 -0800 | [diff] [blame] | 116 | if (h->my_lba != gpt_drive_sectors - GPT_HEADER_SECTORS) |
Randall Spangler | cefe12c | 2013-01-30 12:52:02 -0800 | [diff] [blame] | 117 | return 1; |
Sam Hurst | d6f52a0 | 2018-04-11 08:50:58 -0700 | [diff] [blame] | 118 | if (h->entries_lba != h->my_lba - CalculateEntriesSectors(h, |
| 119 | sector_bytes)) |
Randall Spangler | cefe12c | 2013-01-30 12:52:02 -0800 | [diff] [blame] | 120 | return 1; |
| 121 | } else { |
Nam T. Nguyen | 88458d9 | 2014-08-28 10:58:47 -0700 | [diff] [blame] | 122 | if (h->my_lba != GPT_PMBR_SECTORS) |
Randall Spangler | cefe12c | 2013-01-30 12:52:02 -0800 | [diff] [blame] | 123 | return 1; |
Nam T. Nguyen | a2d72f7 | 2014-08-22 15:01:38 -0700 | [diff] [blame] | 124 | if (h->entries_lba < h->my_lba + 1) |
Randall Spangler | cefe12c | 2013-01-30 12:52:02 -0800 | [diff] [blame] | 125 | return 1; |
| 126 | } |
| 127 | |
Nam T. Nguyen | 6ee52d9 | 2014-10-24 13:20:39 -0700 | [diff] [blame] | 128 | /* FirstUsableLBA <= LastUsableLBA. */ |
| 129 | if (h->first_usable_lba > h->last_usable_lba) |
| 130 | return 1; |
| 131 | |
Dan Ehrenberg | b3d38f5 | 2014-12-09 13:42:15 -0800 | [diff] [blame] | 132 | if (flags & GPT_FLAG_EXTERNAL) { |
| 133 | if (h->last_usable_lba >= streaming_drive_sectors) { |
Nam T. Nguyen | 6ee52d9 | 2014-10-24 13:20:39 -0700 | [diff] [blame] | 134 | return 1; |
| 135 | } |
| 136 | return 0; |
| 137 | } |
| 138 | |
Randall Spangler | cefe12c | 2013-01-30 12:52:02 -0800 | [diff] [blame] | 139 | /* |
| 140 | * FirstUsableLBA must be after the end of the primary GPT table array. |
| 141 | * LastUsableLBA must be before the start of the secondary GPT table |
Nam T. Nguyen | 6ee52d9 | 2014-10-24 13:20:39 -0700 | [diff] [blame] | 142 | * array. |
Randall Spangler | cefe12c | 2013-01-30 12:52:02 -0800 | [diff] [blame] | 143 | */ |
Nam T. Nguyen | a2d72f7 | 2014-08-22 15:01:38 -0700 | [diff] [blame] | 144 | /* TODO(namnguyen): Also check for padding between header & entries. */ |
Sam Hurst | d6f52a0 | 2018-04-11 08:50:58 -0700 | [diff] [blame] | 145 | if (h->first_usable_lba < 2 + CalculateEntriesSectors(h, sector_bytes)) |
Randall Spangler | cefe12c | 2013-01-30 12:52:02 -0800 | [diff] [blame] | 146 | return 1; |
Dan Ehrenberg | b3d38f5 | 2014-12-09 13:42:15 -0800 | [diff] [blame] | 147 | if (h->last_usable_lba >= |
Sam Hurst | d6f52a0 | 2018-04-11 08:50:58 -0700 | [diff] [blame] | 148 | streaming_drive_sectors - 1 - CalculateEntriesSectors(h, |
| 149 | sector_bytes)) |
Randall Spangler | cefe12c | 2013-01-30 12:52:02 -0800 | [diff] [blame] | 150 | return 1; |
Randall Spangler | cefe12c | 2013-01-30 12:52:02 -0800 | [diff] [blame] | 151 | |
| 152 | /* Success */ |
| 153 | return 0; |
| 154 | } |
| 155 | |
Randall Spangler | cefe12c | 2013-01-30 12:52:02 -0800 | [diff] [blame] | 156 | int IsKernelEntry(const GptEntry *e) |
| 157 | { |
| 158 | static Guid chromeos_kernel = GPT_ENT_TYPE_CHROMEOS_KERNEL; |
Randall Spangler | 664096b | 2016-10-13 16:16:41 -0700 | [diff] [blame] | 159 | return !memcmp(&e->type, &chromeos_kernel, sizeof(Guid)); |
Randall Spangler | cefe12c | 2013-01-30 12:52:02 -0800 | [diff] [blame] | 160 | } |
| 161 | |
| 162 | int CheckEntries(GptEntry *entries, GptHeader *h) |
| 163 | { |
Nam T. Nguyen | 5ce8325 | 2014-10-30 15:09:43 -0700 | [diff] [blame] | 164 | if (!entries) |
| 165 | return GPT_ERROR_INVALID_ENTRIES; |
Randall Spangler | cefe12c | 2013-01-30 12:52:02 -0800 | [diff] [blame] | 166 | GptEntry *entry; |
| 167 | uint32_t crc32; |
| 168 | uint32_t i; |
| 169 | |
| 170 | /* Check CRC before examining entries. */ |
| 171 | crc32 = Crc32((const uint8_t *)entries, |
| 172 | h->size_of_entry * h->number_of_entries); |
| 173 | if (crc32 != h->entries_crc32) |
| 174 | return GPT_ERROR_CRC_CORRUPTED; |
| 175 | |
| 176 | /* Check all entries. */ |
| 177 | for (i = 0, entry = entries; i < h->number_of_entries; i++, entry++) { |
| 178 | GptEntry *e2; |
| 179 | uint32_t i2; |
| 180 | |
| 181 | if (IsUnusedEntry(entry)) |
| 182 | continue; |
| 183 | |
| 184 | /* Entry must be in valid region. */ |
| 185 | if ((entry->starting_lba < h->first_usable_lba) || |
| 186 | (entry->ending_lba > h->last_usable_lba) || |
| 187 | (entry->ending_lba < entry->starting_lba)) |
| 188 | return GPT_ERROR_OUT_OF_REGION; |
| 189 | |
| 190 | /* Entry must not overlap other entries. */ |
| 191 | for (i2 = 0, e2 = entries; i2 < h->number_of_entries; |
| 192 | i2++, e2++) { |
| 193 | if (i2 == i || IsUnusedEntry(e2)) |
| 194 | continue; |
| 195 | |
| 196 | if ((entry->starting_lba >= e2->starting_lba) && |
| 197 | (entry->starting_lba <= e2->ending_lba)) |
| 198 | return GPT_ERROR_START_LBA_OVERLAP; |
| 199 | if ((entry->ending_lba >= e2->starting_lba) && |
| 200 | (entry->ending_lba <= e2->ending_lba)) |
| 201 | return GPT_ERROR_END_LBA_OVERLAP; |
| 202 | |
| 203 | /* UniqueGuid field must be unique. */ |
Randall Spangler | 664096b | 2016-10-13 16:16:41 -0700 | [diff] [blame] | 204 | if (0 == memcmp(&entry->unique, &e2->unique, |
Randall Spangler | cefe12c | 2013-01-30 12:52:02 -0800 | [diff] [blame] | 205 | sizeof(Guid))) |
| 206 | return GPT_ERROR_DUP_GUID; |
| 207 | } |
| 208 | } |
| 209 | |
| 210 | /* Success */ |
| 211 | return 0; |
| 212 | } |
| 213 | |
| 214 | int HeaderFieldsSame(GptHeader *h1, GptHeader *h2) |
| 215 | { |
Randall Spangler | 664096b | 2016-10-13 16:16:41 -0700 | [diff] [blame] | 216 | if (memcmp(h1->signature, h2->signature, sizeof(h1->signature))) |
Randall Spangler | cefe12c | 2013-01-30 12:52:02 -0800 | [diff] [blame] | 217 | return 1; |
| 218 | if (h1->revision != h2->revision) |
| 219 | return 1; |
| 220 | if (h1->size != h2->size) |
| 221 | return 1; |
| 222 | if (h1->reserved_zero != h2->reserved_zero) |
| 223 | return 1; |
| 224 | if (h1->first_usable_lba != h2->first_usable_lba) |
| 225 | return 1; |
| 226 | if (h1->last_usable_lba != h2->last_usable_lba) |
| 227 | return 1; |
Randall Spangler | 664096b | 2016-10-13 16:16:41 -0700 | [diff] [blame] | 228 | if (memcmp(&h1->disk_uuid, &h2->disk_uuid, sizeof(Guid))) |
Randall Spangler | cefe12c | 2013-01-30 12:52:02 -0800 | [diff] [blame] | 229 | return 1; |
| 230 | if (h1->number_of_entries != h2->number_of_entries) |
| 231 | return 1; |
| 232 | if (h1->size_of_entry != h2->size_of_entry) |
| 233 | return 1; |
| 234 | if (h1->entries_crc32 != h2->entries_crc32) |
| 235 | return 1; |
| 236 | |
| 237 | return 0; |
| 238 | } |
| 239 | |
Daisuke Nojiri | 053592b | 2020-08-12 15:46:30 -0700 | [diff] [blame] | 240 | int GptValidityCheck(GptData *gpt) |
Randall Spangler | cefe12c | 2013-01-30 12:52:02 -0800 | [diff] [blame] | 241 | { |
| 242 | int retval; |
| 243 | GptHeader *header1 = (GptHeader *)(gpt->primary_header); |
| 244 | GptHeader *header2 = (GptHeader *)(gpt->secondary_header); |
| 245 | GptEntry *entries1 = (GptEntry *)(gpt->primary_entries); |
| 246 | GptEntry *entries2 = (GptEntry *)(gpt->secondary_entries); |
| 247 | GptHeader *goodhdr = NULL; |
| 248 | |
| 249 | gpt->valid_headers = 0; |
| 250 | gpt->valid_entries = 0; |
Julius Werner | 39910d0 | 2016-04-19 16:55:36 -0700 | [diff] [blame] | 251 | gpt->ignored = 0; |
Randall Spangler | cefe12c | 2013-01-30 12:52:02 -0800 | [diff] [blame] | 252 | |
| 253 | retval = CheckParameters(gpt); |
| 254 | if (retval != GPT_SUCCESS) |
| 255 | return retval; |
| 256 | |
| 257 | /* Check both headers; we need at least one valid header. */ |
Dan Ehrenberg | b3d38f5 | 2014-12-09 13:42:15 -0800 | [diff] [blame] | 258 | if (0 == CheckHeader(header1, 0, gpt->streaming_drive_sectors, |
Sam Hurst | d6f52a0 | 2018-04-11 08:50:58 -0700 | [diff] [blame] | 259 | gpt->gpt_drive_sectors, gpt->flags, |
| 260 | gpt->sector_bytes)) { |
Randall Spangler | cefe12c | 2013-01-30 12:52:02 -0800 | [diff] [blame] | 261 | gpt->valid_headers |= MASK_PRIMARY; |
| 262 | goodhdr = header1; |
Julius Werner | 8e8f4b9 | 2019-10-28 16:26:18 -0700 | [diff] [blame] | 263 | if (0 == CheckEntries(entries1, goodhdr)) |
| 264 | gpt->valid_entries |= MASK_PRIMARY; |
Randall Spangler | 664096b | 2016-10-13 16:16:41 -0700 | [diff] [blame] | 265 | } else if (header1 && !memcmp(header1->signature, |
Julius Werner | 39910d0 | 2016-04-19 16:55:36 -0700 | [diff] [blame] | 266 | GPT_HEADER_SIGNATURE_IGNORED, GPT_HEADER_SIGNATURE_SIZE)) { |
| 267 | gpt->ignored |= MASK_PRIMARY; |
Randall Spangler | cefe12c | 2013-01-30 12:52:02 -0800 | [diff] [blame] | 268 | } |
Dan Ehrenberg | b3d38f5 | 2014-12-09 13:42:15 -0800 | [diff] [blame] | 269 | if (0 == CheckHeader(header2, 1, gpt->streaming_drive_sectors, |
Sam Hurst | d6f52a0 | 2018-04-11 08:50:58 -0700 | [diff] [blame] | 270 | gpt->gpt_drive_sectors, gpt->flags, |
| 271 | gpt->sector_bytes)) { |
Randall Spangler | cefe12c | 2013-01-30 12:52:02 -0800 | [diff] [blame] | 272 | gpt->valid_headers |= MASK_SECONDARY; |
| 273 | if (!goodhdr) |
| 274 | goodhdr = header2; |
Julius Werner | 8e8f4b9 | 2019-10-28 16:26:18 -0700 | [diff] [blame] | 275 | /* Check header1+entries2 if it was good, to catch mismatch. */ |
| 276 | if (0 == CheckEntries(entries2, goodhdr)) |
| 277 | gpt->valid_entries |= MASK_SECONDARY; |
Randall Spangler | 664096b | 2016-10-13 16:16:41 -0700 | [diff] [blame] | 278 | } else if (header2 && !memcmp(header2->signature, |
Julius Werner | 39910d0 | 2016-04-19 16:55:36 -0700 | [diff] [blame] | 279 | GPT_HEADER_SIGNATURE_IGNORED, GPT_HEADER_SIGNATURE_SIZE)) { |
| 280 | gpt->ignored |= MASK_SECONDARY; |
Randall Spangler | cefe12c | 2013-01-30 12:52:02 -0800 | [diff] [blame] | 281 | } |
| 282 | |
| 283 | if (!gpt->valid_headers) |
| 284 | return GPT_ERROR_INVALID_HEADERS; |
| 285 | |
| 286 | /* |
Randall Spangler | cefe12c | 2013-01-30 12:52:02 -0800 | [diff] [blame] | 287 | * If both headers are good but neither entries were good, check the |
| 288 | * entries with the secondary header. |
| 289 | */ |
| 290 | if (MASK_BOTH == gpt->valid_headers && !gpt->valid_entries) { |
| 291 | if (0 == CheckEntries(entries1, header2)) |
| 292 | gpt->valid_entries |= MASK_PRIMARY; |
| 293 | if (0 == CheckEntries(entries2, header2)) |
| 294 | gpt->valid_entries |= MASK_SECONDARY; |
| 295 | if (gpt->valid_entries) { |
| 296 | /* |
| 297 | * Sure enough, header2 had a good CRC for one of the |
| 298 | * entries. Mark header1 invalid, so we'll update its |
| 299 | * entries CRC. |
| 300 | */ |
| 301 | gpt->valid_headers &= ~MASK_PRIMARY; |
| 302 | goodhdr = header2; |
| 303 | } |
| 304 | } |
| 305 | |
| 306 | if (!gpt->valid_entries) |
| 307 | return GPT_ERROR_INVALID_ENTRIES; |
| 308 | |
| 309 | /* |
| 310 | * Now that we've determined which header contains a good CRC for |
| 311 | * the entries, make sure the headers are otherwise identical. |
| 312 | */ |
| 313 | if (MASK_BOTH == gpt->valid_headers && |
| 314 | 0 != HeaderFieldsSame(header1, header2)) |
| 315 | gpt->valid_headers &= ~MASK_SECONDARY; |
| 316 | |
Julius Werner | 39910d0 | 2016-04-19 16:55:36 -0700 | [diff] [blame] | 317 | /* |
| 318 | * When we're ignoring a GPT, make it look in memory like the other one |
| 319 | * and pretend that everything is fine (until we try to save). |
| 320 | */ |
| 321 | if (MASK_NONE != gpt->ignored) { |
| 322 | GptRepair(gpt); |
| 323 | gpt->modified = 0; |
| 324 | } |
| 325 | |
Randall Spangler | cefe12c | 2013-01-30 12:52:02 -0800 | [diff] [blame] | 326 | return GPT_SUCCESS; |
| 327 | } |
| 328 | |
| 329 | void GptRepair(GptData *gpt) |
| 330 | { |
| 331 | GptHeader *header1 = (GptHeader *)(gpt->primary_header); |
| 332 | GptHeader *header2 = (GptHeader *)(gpt->secondary_header); |
| 333 | GptEntry *entries1 = (GptEntry *)(gpt->primary_entries); |
| 334 | GptEntry *entries2 = (GptEntry *)(gpt->secondary_entries); |
| 335 | int entries_size; |
| 336 | |
| 337 | /* Need at least one good header and one good set of entries. */ |
| 338 | if (MASK_NONE == gpt->valid_headers || MASK_NONE == gpt->valid_entries) |
| 339 | return; |
| 340 | |
| 341 | /* Repair headers if necessary */ |
| 342 | if (MASK_PRIMARY == gpt->valid_headers) { |
| 343 | /* Primary is good, secondary is bad */ |
Randall Spangler | 664096b | 2016-10-13 16:16:41 -0700 | [diff] [blame] | 344 | memcpy(header2, header1, sizeof(GptHeader)); |
Dan Ehrenberg | a524a3a | 2014-11-06 16:22:24 -0800 | [diff] [blame] | 345 | header2->my_lba = gpt->gpt_drive_sectors - GPT_HEADER_SECTORS; |
Nam T. Nguyen | 88458d9 | 2014-08-28 10:58:47 -0700 | [diff] [blame] | 346 | header2->alternate_lba = GPT_PMBR_SECTORS; /* Second sector. */ |
Sam Hurst | d6f52a0 | 2018-04-11 08:50:58 -0700 | [diff] [blame] | 347 | header2->entries_lba = header2->my_lba - |
| 348 | CalculateEntriesSectors(header1, gpt->sector_bytes); |
Randall Spangler | cefe12c | 2013-01-30 12:52:02 -0800 | [diff] [blame] | 349 | header2->header_crc32 = HeaderCrc(header2); |
| 350 | gpt->modified |= GPT_MODIFIED_HEADER2; |
| 351 | } |
| 352 | else if (MASK_SECONDARY == gpt->valid_headers) { |
| 353 | /* Secondary is good, primary is bad */ |
Randall Spangler | 664096b | 2016-10-13 16:16:41 -0700 | [diff] [blame] | 354 | memcpy(header1, header2, sizeof(GptHeader)); |
Nam T. Nguyen | 88458d9 | 2014-08-28 10:58:47 -0700 | [diff] [blame] | 355 | header1->my_lba = GPT_PMBR_SECTORS; /* Second sector. */ |
Dan Ehrenberg | b3d38f5 | 2014-12-09 13:42:15 -0800 | [diff] [blame] | 356 | header1->alternate_lba = |
| 357 | gpt->streaming_drive_sectors - GPT_HEADER_SECTORS; |
Nam T. Nguyen | a2d72f7 | 2014-08-22 15:01:38 -0700 | [diff] [blame] | 358 | /* TODO (namnguyen): Preserve (header, entries) padding. */ |
Randall Spangler | cefe12c | 2013-01-30 12:52:02 -0800 | [diff] [blame] | 359 | header1->entries_lba = header1->my_lba + 1; |
| 360 | header1->header_crc32 = HeaderCrc(header1); |
| 361 | gpt->modified |= GPT_MODIFIED_HEADER1; |
| 362 | } |
| 363 | gpt->valid_headers = MASK_BOTH; |
| 364 | |
| 365 | /* Repair entries if necessary */ |
| 366 | entries_size = header1->size_of_entry * header1->number_of_entries; |
| 367 | if (MASK_PRIMARY == gpt->valid_entries) { |
| 368 | /* Primary is good, secondary is bad */ |
Randall Spangler | 664096b | 2016-10-13 16:16:41 -0700 | [diff] [blame] | 369 | memcpy(entries2, entries1, entries_size); |
Randall Spangler | cefe12c | 2013-01-30 12:52:02 -0800 | [diff] [blame] | 370 | gpt->modified |= GPT_MODIFIED_ENTRIES2; |
| 371 | } |
| 372 | else if (MASK_SECONDARY == gpt->valid_entries) { |
| 373 | /* Secondary is good, primary is bad */ |
Randall Spangler | 664096b | 2016-10-13 16:16:41 -0700 | [diff] [blame] | 374 | memcpy(entries1, entries2, entries_size); |
Randall Spangler | cefe12c | 2013-01-30 12:52:02 -0800 | [diff] [blame] | 375 | gpt->modified |= GPT_MODIFIED_ENTRIES1; |
| 376 | } |
| 377 | gpt->valid_entries = MASK_BOTH; |
| 378 | } |
| 379 | |
Ben Chan | a4a8c02 | 2018-01-31 11:39:14 -0800 | [diff] [blame] | 380 | int GetEntryRequired(const GptEntry *e) |
| 381 | { |
| 382 | return e->attrs.fields.required; |
| 383 | } |
| 384 | |
Mike Frysinger | 6c18af5 | 2016-09-07 16:45:48 -0400 | [diff] [blame] | 385 | int GetEntryLegacyBoot(const GptEntry *e) |
| 386 | { |
| 387 | return e->attrs.fields.legacy_boot; |
| 388 | } |
| 389 | |
Randall Spangler | cefe12c | 2013-01-30 12:52:02 -0800 | [diff] [blame] | 390 | int GetEntrySuccessful(const GptEntry *e) |
| 391 | { |
| 392 | return (e->attrs.fields.gpt_att & CGPT_ATTRIBUTE_SUCCESSFUL_MASK) >> |
| 393 | CGPT_ATTRIBUTE_SUCCESSFUL_OFFSET; |
| 394 | } |
| 395 | |
| 396 | int GetEntryPriority(const GptEntry *e) |
| 397 | { |
| 398 | return (e->attrs.fields.gpt_att & CGPT_ATTRIBUTE_PRIORITY_MASK) >> |
| 399 | CGPT_ATTRIBUTE_PRIORITY_OFFSET; |
| 400 | } |
| 401 | |
| 402 | int GetEntryTries(const GptEntry *e) |
| 403 | { |
| 404 | return (e->attrs.fields.gpt_att & CGPT_ATTRIBUTE_TRIES_MASK) >> |
| 405 | CGPT_ATTRIBUTE_TRIES_OFFSET; |
| 406 | } |
| 407 | |
Ben Chan | a4a8c02 | 2018-01-31 11:39:14 -0800 | [diff] [blame] | 408 | void SetEntryRequired(GptEntry *e, int required) |
| 409 | { |
| 410 | e->attrs.fields.required = required; |
| 411 | } |
| 412 | |
Mike Frysinger | 6c18af5 | 2016-09-07 16:45:48 -0400 | [diff] [blame] | 413 | void SetEntryLegacyBoot(GptEntry *e, int legacy_boot) |
| 414 | { |
| 415 | e->attrs.fields.legacy_boot = legacy_boot; |
| 416 | } |
| 417 | |
Randall Spangler | cefe12c | 2013-01-30 12:52:02 -0800 | [diff] [blame] | 418 | void SetEntrySuccessful(GptEntry *e, int successful) |
| 419 | { |
| 420 | if (successful) |
| 421 | e->attrs.fields.gpt_att |= CGPT_ATTRIBUTE_SUCCESSFUL_MASK; |
| 422 | else |
| 423 | e->attrs.fields.gpt_att &= ~CGPT_ATTRIBUTE_SUCCESSFUL_MASK; |
| 424 | } |
| 425 | |
| 426 | void SetEntryPriority(GptEntry *e, int priority) |
| 427 | { |
| 428 | e->attrs.fields.gpt_att &= ~CGPT_ATTRIBUTE_PRIORITY_MASK; |
| 429 | e->attrs.fields.gpt_att |= |
| 430 | (priority << CGPT_ATTRIBUTE_PRIORITY_OFFSET) & |
| 431 | CGPT_ATTRIBUTE_PRIORITY_MASK; |
| 432 | } |
| 433 | |
| 434 | void SetEntryTries(GptEntry *e, int tries) |
| 435 | { |
| 436 | e->attrs.fields.gpt_att &= ~CGPT_ATTRIBUTE_TRIES_MASK; |
| 437 | e->attrs.fields.gpt_att |= (tries << CGPT_ATTRIBUTE_TRIES_OFFSET) & |
| 438 | CGPT_ATTRIBUTE_TRIES_MASK; |
| 439 | } |
| 440 | |
| 441 | void GetCurrentKernelUniqueGuid(GptData *gpt, void *dest) |
| 442 | { |
| 443 | GptEntry *entries = (GptEntry *)gpt->primary_entries; |
| 444 | GptEntry *e = entries + gpt->current_kernel; |
Randall Spangler | 664096b | 2016-10-13 16:16:41 -0700 | [diff] [blame] | 445 | memcpy(dest, &e->unique, sizeof(Guid)); |
Randall Spangler | cefe12c | 2013-01-30 12:52:02 -0800 | [diff] [blame] | 446 | } |
| 447 | |
Albert Chaulk | 5c9e453 | 2013-03-20 16:03:49 -0700 | [diff] [blame] | 448 | void GptModified(GptData *gpt) { |
| 449 | GptHeader *header = (GptHeader *)gpt->primary_header; |
| 450 | |
| 451 | /* Update the CRCs */ |
| 452 | header->entries_crc32 = Crc32(gpt->primary_entries, |
| 453 | header->size_of_entry * |
| 454 | header->number_of_entries); |
| 455 | header->header_crc32 = HeaderCrc(header); |
| 456 | gpt->modified |= GPT_MODIFIED_HEADER1 | GPT_MODIFIED_ENTRIES1; |
| 457 | |
| 458 | /* |
| 459 | * Use the repair function to update the other copy of the GPT. This |
| 460 | * is a tad inefficient, but is much faster than the disk I/O to update |
| 461 | * the GPT on disk so it doesn't matter. |
| 462 | */ |
| 463 | gpt->valid_headers = MASK_PRIMARY; |
| 464 | gpt->valid_entries = MASK_PRIMARY; |
| 465 | GptRepair(gpt); |
| 466 | } |
| 467 | |
| 468 | |
Randall Spangler | cefe12c | 2013-01-30 12:52:02 -0800 | [diff] [blame] | 469 | const char *GptErrorText(int error_code) |
| 470 | { |
| 471 | switch(error_code) { |
| 472 | case GPT_SUCCESS: |
| 473 | return "none"; |
| 474 | |
| 475 | case GPT_ERROR_NO_VALID_KERNEL: |
| 476 | return "Invalid kernel"; |
| 477 | |
| 478 | case GPT_ERROR_INVALID_HEADERS: |
| 479 | return "Invalid headers"; |
| 480 | |
| 481 | case GPT_ERROR_INVALID_ENTRIES: |
| 482 | return "Invalid entries"; |
| 483 | |
| 484 | case GPT_ERROR_INVALID_SECTOR_SIZE: |
| 485 | return "Invalid sector size"; |
| 486 | |
| 487 | case GPT_ERROR_INVALID_SECTOR_NUMBER: |
| 488 | return "Invalid sector number"; |
| 489 | |
| 490 | case GPT_ERROR_INVALID_UPDATE_TYPE: |
| 491 | return "Invalid update type"; |
| 492 | |
| 493 | case GPT_ERROR_CRC_CORRUPTED: |
| 494 | return "Entries' crc corrupted"; |
| 495 | |
| 496 | case GPT_ERROR_OUT_OF_REGION: |
| 497 | return "Entry outside of valid region"; |
| 498 | |
| 499 | case GPT_ERROR_START_LBA_OVERLAP: |
| 500 | return "Starting LBA overlaps"; |
| 501 | |
| 502 | case GPT_ERROR_END_LBA_OVERLAP: |
| 503 | return "Ending LBA overlaps"; |
| 504 | |
| 505 | case GPT_ERROR_DUP_GUID: |
| 506 | return "Duplicated GUID"; |
| 507 | |
Albert Chaulk | 5c9e453 | 2013-03-20 16:03:49 -0700 | [diff] [blame] | 508 | case GPT_ERROR_INVALID_FLASH_GEOMETRY: |
| 509 | return "Invalid flash geometry"; |
| 510 | |
| 511 | case GPT_ERROR_NO_SUCH_ENTRY: |
| 512 | return "No entry found"; |
| 513 | |
Randall Spangler | cefe12c | 2013-01-30 12:52:02 -0800 | [diff] [blame] | 514 | default: |
| 515 | break; |
| 516 | }; |
| 517 | return "Unknown"; |
Vadim Bendebury | 65d3c27 | 2012-09-24 17:41:18 -0700 | [diff] [blame] | 518 | } |