[e16e8f2] | 1 | /* ----------------------------------------------------------------------- * |
---|
| 2 | * |
---|
| 3 | * Copyright 2003-2009 H. Peter Anvin - All Rights Reserved |
---|
| 4 | * Copyright 2009-2010 Intel Corporation; author: H. Peter Anvin |
---|
| 5 | * Copyright 2010 Shao Miller |
---|
| 6 | * Copyright 2010-2012 Michal Soltys |
---|
| 7 | * |
---|
| 8 | * Permission is hereby granted, free of charge, to any person |
---|
| 9 | * obtaining a copy of this software and associated documentation |
---|
| 10 | * files (the "Software"), to deal in the Software without |
---|
| 11 | * restriction, including without limitation the rights to use, |
---|
| 12 | * copy, modify, merge, publish, distribute, sublicense, and/or |
---|
| 13 | * sell copies of the Software, and to permit persons to whom |
---|
| 14 | * the Software is furnished to do so, subject to the following |
---|
| 15 | * conditions: |
---|
| 16 | * |
---|
| 17 | * The above copyright notice and this permission notice shall |
---|
| 18 | * be included in all copies or substantial portions of the Software. |
---|
| 19 | * |
---|
| 20 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
---|
| 21 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES |
---|
| 22 | * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
---|
| 23 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT |
---|
| 24 | * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, |
---|
| 25 | * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
---|
| 26 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR |
---|
| 27 | * OTHER DEALINGS IN THE SOFTWARE. |
---|
| 28 | * |
---|
| 29 | * ----------------------------------------------------------------------- */ |
---|
| 30 | |
---|
| 31 | /* |
---|
| 32 | * partiter.c |
---|
| 33 | * |
---|
| 34 | * Provides disk / partition iteration. |
---|
| 35 | */ |
---|
| 36 | |
---|
| 37 | #include <stdlib.h> |
---|
| 38 | #include <stdio.h> |
---|
| 39 | #include <string.h> |
---|
| 40 | #include <stdarg.h> |
---|
| 41 | #include <zlib.h> |
---|
| 42 | #include <syslinux/disk.h> |
---|
| 43 | #include "partiter.h" |
---|
| 44 | #include "utility.h" |
---|
| 45 | |
---|
| 46 | #define ost_is_ext(type) ((type) == 0x05 || (type) == 0x0F || (type) == 0x85) |
---|
| 47 | #define ost_is_nondata(type) (ost_is_ext(type) || (type) == 0x00) |
---|
| 48 | #define sane(s,l) ((s)+(l) > (s)) |
---|
| 49 | |
---|
| 50 | /* virtual forwards */ |
---|
| 51 | |
---|
| 52 | static void pi_dtor_(struct part_iter *); |
---|
| 53 | static int pi_next_(struct part_iter *); |
---|
| 54 | static int pi_dos_next(struct part_iter *); |
---|
| 55 | static int pi_gpt_next(struct part_iter *); |
---|
| 56 | |
---|
| 57 | /* vtab and types */ |
---|
| 58 | |
---|
| 59 | static struct itertype types[] = { |
---|
| 60 | [0] = { |
---|
| 61 | .dtor = &pi_dtor_, |
---|
| 62 | .next = &pi_dos_next, |
---|
| 63 | }, [1] = { |
---|
| 64 | .dtor = &pi_dtor_, |
---|
| 65 | .next = &pi_gpt_next, |
---|
| 66 | }, [2] = { |
---|
| 67 | .dtor = &pi_dtor_, |
---|
| 68 | .next = &pi_next_, |
---|
| 69 | }}; |
---|
| 70 | |
---|
| 71 | const struct itertype * const typedos = types; |
---|
| 72 | const struct itertype * const typegpt = types+1; |
---|
| 73 | const struct itertype * const typeraw = types+2; |
---|
| 74 | |
---|
| 75 | /* pi_dtor_() - common/raw iterator cleanup */ |
---|
| 76 | static void pi_dtor_(struct part_iter *iter) |
---|
| 77 | { |
---|
| 78 | /* syslinux's free is null resilient */ |
---|
| 79 | free(iter->data); |
---|
| 80 | } |
---|
| 81 | |
---|
| 82 | /* pi_ctor() - common/raw iterator initialization */ |
---|
| 83 | static int pi_ctor(struct part_iter *iter, |
---|
| 84 | const struct disk_info *di, int flags |
---|
| 85 | ) |
---|
| 86 | { |
---|
| 87 | memcpy(&iter->di, di, sizeof *di); |
---|
| 88 | iter->flags = flags; |
---|
| 89 | iter->index0 = -1; |
---|
| 90 | iter->length = di->lbacnt; |
---|
| 91 | |
---|
| 92 | iter->type = typeraw; |
---|
| 93 | return 0; |
---|
| 94 | } |
---|
| 95 | |
---|
| 96 | /* pi_dos_ctor() - MBR/EBR iterator specific initialization */ |
---|
| 97 | static int pi_dos_ctor(struct part_iter *iter, |
---|
| 98 | const struct disk_info *di, int flags, |
---|
| 99 | const struct disk_dos_mbr *mbr |
---|
| 100 | ) |
---|
| 101 | { |
---|
| 102 | if (pi_ctor(iter, di, flags)) |
---|
| 103 | return -1; |
---|
| 104 | |
---|
| 105 | if (!(iter->data = malloc(sizeof *mbr))) { |
---|
| 106 | critm(); |
---|
| 107 | goto bail; |
---|
| 108 | } |
---|
| 109 | |
---|
| 110 | memcpy(iter->data, mbr, sizeof *mbr); |
---|
| 111 | |
---|
| 112 | iter->dos.bebr_index0 = -1; |
---|
| 113 | iter->dos.disk_sig = mbr->disk_sig; |
---|
| 114 | |
---|
| 115 | iter->type = typedos; |
---|
| 116 | return 0; |
---|
| 117 | bail: |
---|
| 118 | pi_dtor_(iter); |
---|
| 119 | return -1; |
---|
| 120 | } |
---|
| 121 | |
---|
| 122 | /* pi_gpt_ctor() - GPT iterator specific initialization */ |
---|
| 123 | static int pi_gpt_ctor(struct part_iter *iter, |
---|
| 124 | const struct disk_info *di, int flags, |
---|
| 125 | const struct disk_gpt_header *gpth, const struct disk_gpt_part_entry *gptl |
---|
| 126 | ) |
---|
| 127 | { |
---|
| 128 | uint64_t siz; |
---|
| 129 | |
---|
| 130 | if (pi_ctor(iter, di, flags)) |
---|
| 131 | return -1; |
---|
| 132 | |
---|
| 133 | siz = (uint64_t)gpth->part_count * gpth->part_size; |
---|
| 134 | |
---|
| 135 | if (!(iter->data = malloc((size_t)siz))) { |
---|
| 136 | critm(); |
---|
| 137 | goto bail; |
---|
| 138 | } |
---|
| 139 | |
---|
| 140 | memcpy(iter->data, gptl, (size_t)siz); |
---|
| 141 | |
---|
| 142 | iter->gpt.pe_count = (int)gpth->part_count; |
---|
| 143 | iter->gpt.pe_size = (int)gpth->part_size; |
---|
| 144 | iter->gpt.ufirst = gpth->lba_first_usable; |
---|
| 145 | iter->gpt.ulast = gpth->lba_last_usable; |
---|
| 146 | |
---|
| 147 | memcpy(&iter->gpt.disk_guid, &gpth->disk_guid, sizeof gpth->disk_guid); |
---|
| 148 | memcpy(&iter->gpt.part_guid, &gpth->disk_guid, sizeof gpth->disk_guid); |
---|
| 149 | |
---|
| 150 | iter->type = typegpt; |
---|
| 151 | return 0; |
---|
| 152 | bail: |
---|
| 153 | pi_dtor_(iter); |
---|
| 154 | return -1; |
---|
| 155 | } |
---|
| 156 | |
---|
| 157 | /* Logical partition must be sane, meaning: |
---|
| 158 | * - must be data or empty |
---|
| 159 | * - must have non-0 start and length |
---|
| 160 | * - values must not wrap around 32bit |
---|
| 161 | * - must be inside current EBR frame |
---|
| 162 | */ |
---|
| 163 | |
---|
| 164 | static int notsane_logical(const struct part_iter *iter) |
---|
| 165 | { |
---|
| 166 | const struct disk_dos_part_entry *dp; |
---|
| 167 | uint32_t end_log; |
---|
| 168 | |
---|
| 169 | dp = ((struct disk_dos_mbr *)iter->data)->table; |
---|
| 170 | |
---|
| 171 | if (!dp[0].ostype) |
---|
| 172 | return 0; |
---|
| 173 | |
---|
| 174 | if (ost_is_ext(dp[0].ostype)) { |
---|
| 175 | error("The 1st EBR entry must be data or empty."); |
---|
| 176 | return -1; |
---|
| 177 | } |
---|
| 178 | |
---|
| 179 | if (!(iter->flags & PIF_STRICT)) |
---|
| 180 | return 0; |
---|
| 181 | |
---|
| 182 | end_log = dp[0].start_lba + dp[0].length; |
---|
| 183 | |
---|
| 184 | if (!dp[0].start_lba || |
---|
| 185 | !dp[0].length || |
---|
| 186 | !sane(dp[0].start_lba, dp[0].length) || |
---|
| 187 | end_log > iter->dos.nebr_siz) { |
---|
| 188 | |
---|
| 189 | error("Logical partition (in EBR) with invalid offset and/or length."); |
---|
| 190 | return -1; |
---|
| 191 | } |
---|
| 192 | |
---|
| 193 | return 0; |
---|
| 194 | } |
---|
| 195 | |
---|
| 196 | /* Extended partition must be sane, meaning: |
---|
| 197 | * - must be extended or empty |
---|
| 198 | * - must have non-0 start and length |
---|
| 199 | * - values must not wrap around 32bit |
---|
| 200 | * - must be inside base EBR frame |
---|
| 201 | */ |
---|
| 202 | |
---|
| 203 | static int notsane_extended(const struct part_iter *iter) |
---|
| 204 | { |
---|
| 205 | const struct disk_dos_part_entry *dp; |
---|
| 206 | uint32_t end_ebr; |
---|
| 207 | |
---|
| 208 | dp = ((struct disk_dos_mbr *)iter->data)->table; |
---|
| 209 | |
---|
| 210 | if (!dp[1].ostype) |
---|
| 211 | return 0; |
---|
| 212 | |
---|
| 213 | if (!ost_is_nondata(dp[1].ostype)) { |
---|
| 214 | error("The 2nd EBR entry must be extended or empty."); |
---|
| 215 | return -1; |
---|
| 216 | } |
---|
| 217 | |
---|
| 218 | if (!(iter->flags & PIF_STRICT)) |
---|
| 219 | return 0; |
---|
| 220 | |
---|
| 221 | end_ebr = dp[1].start_lba + dp[1].length; |
---|
| 222 | |
---|
| 223 | if (!dp[1].start_lba || |
---|
| 224 | !dp[1].length || |
---|
| 225 | !sane(dp[1].start_lba, dp[1].length) || |
---|
| 226 | end_ebr > iter->dos.bebr_siz) { |
---|
| 227 | |
---|
| 228 | error("Extended partition (EBR) with invalid offset and/or length."); |
---|
| 229 | return -1; |
---|
| 230 | } |
---|
| 231 | |
---|
| 232 | return 0; |
---|
| 233 | } |
---|
| 234 | |
---|
| 235 | /* Primary partition must be sane, meaning: |
---|
| 236 | * - must have non-0 start and length |
---|
| 237 | * - values must not wrap around 32bit |
---|
| 238 | */ |
---|
| 239 | |
---|
| 240 | static int notsane_primary(const struct part_iter *iter) |
---|
| 241 | { |
---|
| 242 | const struct disk_dos_part_entry *dp; |
---|
| 243 | dp = ((struct disk_dos_mbr *)iter->data)->table + iter->index0; |
---|
| 244 | |
---|
| 245 | if (!dp->ostype) |
---|
| 246 | return 0; |
---|
| 247 | |
---|
| 248 | if (!(iter->flags & PIF_STRICT)) |
---|
| 249 | return 0; |
---|
| 250 | |
---|
| 251 | if (!dp->start_lba || |
---|
| 252 | !dp->length || |
---|
| 253 | !sane(dp->start_lba, dp->length) || |
---|
| 254 | ((iter->flags & PIF_STRICTER) && (dp->start_lba + dp->length > iter->di.lbacnt))) { |
---|
| 255 | error("Primary partition (in MBR) with invalid offset and/or length."); |
---|
| 256 | return -1; |
---|
| 257 | } |
---|
| 258 | |
---|
| 259 | return 0; |
---|
| 260 | } |
---|
| 261 | |
---|
| 262 | static int notsane_gpt(const struct part_iter *iter) |
---|
| 263 | { |
---|
| 264 | const struct disk_gpt_part_entry *gp; |
---|
| 265 | gp = (const struct disk_gpt_part_entry *) |
---|
| 266 | (iter->data + iter->index0 * iter->gpt.pe_size); |
---|
| 267 | |
---|
| 268 | if (guid_is0(&gp->type)) |
---|
| 269 | return 0; |
---|
| 270 | |
---|
| 271 | if (!(iter->flags & PIF_STRICT)) |
---|
| 272 | return 0; |
---|
| 273 | |
---|
| 274 | if (gp->lba_first < iter->gpt.ufirst || |
---|
| 275 | gp->lba_last > iter->gpt.ulast) { |
---|
| 276 | error("LBA sectors of GPT partition are beyond the range allowed in GPT header."); |
---|
| 277 | return -1; |
---|
| 278 | } |
---|
| 279 | |
---|
| 280 | return 0; |
---|
| 281 | } |
---|
| 282 | |
---|
| 283 | static int dos_next_mbr(struct part_iter *iter, uint32_t *lba, |
---|
| 284 | struct disk_dos_part_entry **_dp) |
---|
| 285 | { |
---|
| 286 | struct disk_dos_part_entry *dp; |
---|
| 287 | |
---|
| 288 | while (++iter->index0 < 4) { |
---|
| 289 | dp = ((struct disk_dos_mbr *)iter->data)->table + iter->index0; |
---|
| 290 | |
---|
| 291 | if (notsane_primary(iter)) { |
---|
| 292 | iter->status = PI_INSANE; |
---|
| 293 | return -1; |
---|
| 294 | } |
---|
| 295 | |
---|
| 296 | if (ost_is_ext(dp->ostype)) { |
---|
| 297 | if (iter->dos.bebr_index0 >= 0) { |
---|
| 298 | error("More than 1 extended partition."); |
---|
| 299 | iter->status = PI_INSANE; |
---|
| 300 | return -1; |
---|
| 301 | } |
---|
| 302 | /* record base EBR index */ |
---|
| 303 | iter->dos.bebr_index0 = iter->index0; |
---|
| 304 | } |
---|
| 305 | if (!ost_is_nondata(dp->ostype) || (iter->flags & PIF_STEPALL)) { |
---|
| 306 | *lba = dp->start_lba; |
---|
| 307 | *_dp = dp; |
---|
| 308 | break; |
---|
| 309 | } |
---|
| 310 | } |
---|
| 311 | |
---|
| 312 | return 0; |
---|
| 313 | } |
---|
| 314 | |
---|
| 315 | static int prep_base_ebr(struct part_iter *iter) |
---|
| 316 | { |
---|
| 317 | struct disk_dos_part_entry *dp; |
---|
| 318 | |
---|
| 319 | if (iter->dos.bebr_index0 < 0) /* if we don't have base extended partition at all */ |
---|
| 320 | return -1; |
---|
| 321 | else if (!iter->dos.bebr_lba) { /* if not initialized yet */ |
---|
| 322 | dp = ((struct disk_dos_mbr *)iter->data)->table + iter->dos.bebr_index0; |
---|
| 323 | |
---|
| 324 | iter->dos.bebr_lba = dp->start_lba; |
---|
| 325 | iter->dos.bebr_siz = dp->length; |
---|
| 326 | |
---|
| 327 | iter->dos.nebr_lba = dp->start_lba; |
---|
| 328 | iter->dos.nebr_siz = dp->length; |
---|
| 329 | |
---|
| 330 | iter->index0--; |
---|
| 331 | } |
---|
| 332 | return 0; |
---|
| 333 | } |
---|
| 334 | |
---|
| 335 | static int dos_next_ebr(struct part_iter *iter, uint32_t *lba, |
---|
| 336 | struct disk_dos_part_entry **_dp) |
---|
| 337 | { |
---|
| 338 | struct disk_dos_part_entry *dp; |
---|
| 339 | |
---|
| 340 | if (prep_base_ebr(iter) < 0) { |
---|
| 341 | iter->status = PI_DONE; |
---|
| 342 | return -1; |
---|
| 343 | } |
---|
| 344 | |
---|
| 345 | while (++iter->index0 < 1024 && iter->dos.nebr_lba) { |
---|
| 346 | free(iter->data); |
---|
| 347 | if (!(iter->data = |
---|
| 348 | disk_read_sectors(&iter->di, iter->dos.nebr_lba, 1))) { |
---|
| 349 | error("Couldn't load EBR."); |
---|
| 350 | iter->status = PI_ERRLOAD; |
---|
| 351 | return -1; |
---|
| 352 | } |
---|
| 353 | |
---|
| 354 | /* check sanity of loaded data */ |
---|
| 355 | if (notsane_logical(iter) || notsane_extended(iter)) { |
---|
| 356 | iter->status = PI_INSANE; |
---|
| 357 | return -1; |
---|
| 358 | } |
---|
| 359 | |
---|
| 360 | dp = ((struct disk_dos_mbr *)iter->data)->table; |
---|
| 361 | |
---|
| 362 | iter->dos.cebr_lba = iter->dos.nebr_lba; |
---|
| 363 | iter->dos.cebr_siz = iter->dos.nebr_siz; |
---|
| 364 | |
---|
| 365 | /* setup next frame values */ |
---|
| 366 | if (dp[1].ostype) { |
---|
| 367 | iter->dos.nebr_lba = iter->dos.bebr_lba + dp[1].start_lba; |
---|
| 368 | iter->dos.nebr_siz = dp[1].length; |
---|
| 369 | } else { |
---|
| 370 | iter->dos.nebr_lba = 0; |
---|
| 371 | iter->dos.nebr_siz = 0; |
---|
| 372 | } |
---|
| 373 | |
---|
| 374 | if (!dp[0].ostype) |
---|
| 375 | iter->dos.logskipcnt++; |
---|
| 376 | |
---|
| 377 | if (dp[0].ostype || (iter->flags & PIF_STEPALL)) { |
---|
| 378 | *lba = dp[0].start_lba ? iter->dos.cebr_lba + dp[0].start_lba : 0; |
---|
| 379 | *_dp = dp; |
---|
| 380 | return 0; |
---|
| 381 | } |
---|
| 382 | /* |
---|
| 383 | * This way it's possible to continue, if some crazy soft left a "hole" |
---|
| 384 | * - EBR with a valid extended partition without a logical one. In |
---|
| 385 | * such case, linux will not reserve a number for such hole - so we |
---|
| 386 | * don't increase index0. If PIF_STEPALL flag is set, we will never |
---|
| 387 | * reach this place. |
---|
| 388 | */ |
---|
| 389 | } |
---|
| 390 | iter->status = PI_DONE; |
---|
| 391 | return -1; |
---|
| 392 | } |
---|
| 393 | |
---|
| 394 | static void gpt_conv_label(struct part_iter *iter) |
---|
| 395 | { |
---|
| 396 | const struct disk_gpt_part_entry *gp; |
---|
| 397 | const int16_t *orig_lab; |
---|
| 398 | |
---|
| 399 | gp = (const struct disk_gpt_part_entry *) |
---|
| 400 | (iter->data + iter->index0 * iter->gpt.pe_size); |
---|
| 401 | orig_lab = (const int16_t *)gp->name; |
---|
| 402 | |
---|
| 403 | /* caveat: this is very crude conversion */ |
---|
| 404 | for (int i = 0; i < PI_GPTLABSIZE/2; i++) { |
---|
| 405 | iter->gpt.part_label[i] = (char)orig_lab[i]; |
---|
| 406 | } |
---|
| 407 | iter->gpt.part_label[PI_GPTLABSIZE/2] = 0; |
---|
| 408 | } |
---|
| 409 | |
---|
| 410 | static inline int valid_crc(uint32_t crc, const uint8_t *buf, unsigned int siz) |
---|
| 411 | { |
---|
| 412 | return crc == crc32(crc32(0, NULL, 0), buf, siz); |
---|
| 413 | } |
---|
| 414 | |
---|
| 415 | static int valid_crc_hdr(void *buf) |
---|
| 416 | { |
---|
| 417 | struct disk_gpt_header *gh = buf; |
---|
| 418 | uint32_t crc = gh->chksum; |
---|
| 419 | int valid; |
---|
| 420 | |
---|
| 421 | gh->chksum = 0; |
---|
| 422 | valid = crc == crc32(crc32(0, NULL, 0), buf, gh->hdr_size); |
---|
| 423 | gh->chksum = crc; |
---|
| 424 | return valid; |
---|
| 425 | } |
---|
| 426 | |
---|
| 427 | static int pi_next_(struct part_iter *iter) |
---|
| 428 | { |
---|
| 429 | iter->status = PI_DONE; |
---|
| 430 | return iter->status; |
---|
| 431 | } |
---|
| 432 | |
---|
| 433 | static int pi_dos_next(struct part_iter *iter) |
---|
| 434 | { |
---|
| 435 | uint32_t abs_lba = 0; |
---|
| 436 | struct disk_dos_part_entry *dos_part = NULL; |
---|
| 437 | |
---|
| 438 | if (iter->status) |
---|
| 439 | return iter->status; |
---|
| 440 | |
---|
| 441 | /* look for primary partitions */ |
---|
| 442 | if (iter->index0 < 4 && |
---|
| 443 | dos_next_mbr(iter, &abs_lba, &dos_part) < 0) |
---|
| 444 | return iter->status; |
---|
| 445 | |
---|
| 446 | /* look for logical partitions */ |
---|
| 447 | if (iter->index0 >= 4 && |
---|
| 448 | dos_next_ebr(iter, &abs_lba, &dos_part) < 0) |
---|
| 449 | return iter->status; |
---|
| 450 | |
---|
| 451 | /* |
---|
| 452 | * note special index handling: |
---|
| 453 | * in case PIF_STEPALL is set - this makes the index consistent with |
---|
| 454 | * non-PIF_STEPALL iterators |
---|
| 455 | */ |
---|
| 456 | |
---|
| 457 | if (!dos_part->ostype) |
---|
| 458 | iter->index = -1; |
---|
| 459 | else |
---|
| 460 | iter->index = iter->index0 + 1 - iter->dos.logskipcnt; |
---|
| 461 | iter->abs_lba = abs_lba; |
---|
| 462 | iter->length = dos_part->length; |
---|
| 463 | iter->record = (char *)dos_part; |
---|
| 464 | |
---|
| 465 | #ifdef DEBUG |
---|
| 466 | disk_dos_part_dump(dos_part); |
---|
| 467 | #endif |
---|
| 468 | |
---|
| 469 | return iter->status; |
---|
| 470 | } |
---|
| 471 | |
---|
| 472 | static int pi_gpt_next(struct part_iter *iter) |
---|
| 473 | { |
---|
| 474 | const struct disk_gpt_part_entry *gpt_part = NULL; |
---|
| 475 | |
---|
| 476 | if (iter->status) |
---|
| 477 | return iter->status; |
---|
| 478 | |
---|
| 479 | while (++iter->index0 < iter->gpt.pe_count) { |
---|
| 480 | gpt_part = (const struct disk_gpt_part_entry *) |
---|
| 481 | (iter->data + iter->index0 * iter->gpt.pe_size); |
---|
| 482 | |
---|
| 483 | if (notsane_gpt(iter)) { |
---|
| 484 | iter->status = PI_INSANE; |
---|
| 485 | return iter->status; |
---|
| 486 | } |
---|
| 487 | |
---|
| 488 | if (!guid_is0(&gpt_part->type) || (iter->flags & PIF_STEPALL)) |
---|
| 489 | break; |
---|
| 490 | } |
---|
| 491 | /* no more partitions ? */ |
---|
| 492 | if (iter->index0 == iter->gpt.pe_count) { |
---|
| 493 | iter->status = PI_DONE; |
---|
| 494 | return iter->status; |
---|
| 495 | } |
---|
| 496 | /* gpt_part is guaranteed to be valid here */ |
---|
| 497 | iter->index = iter->index0 + 1; |
---|
| 498 | iter->abs_lba = gpt_part->lba_first; |
---|
| 499 | iter->length = gpt_part->lba_last - gpt_part->lba_first + 1; |
---|
| 500 | iter->record = (char *)gpt_part; |
---|
| 501 | memcpy(&iter->gpt.part_guid, &gpt_part->uid, sizeof(struct guid)); |
---|
| 502 | gpt_conv_label(iter); |
---|
| 503 | |
---|
| 504 | #ifdef DEBUG |
---|
| 505 | disk_gpt_part_dump(gpt_part); |
---|
| 506 | #endif |
---|
| 507 | |
---|
| 508 | return iter->status; |
---|
| 509 | } |
---|
| 510 | |
---|
| 511 | static struct part_iter *pi_alloc(void) |
---|
| 512 | { |
---|
| 513 | struct part_iter *iter; |
---|
| 514 | if (!(iter = malloc(sizeof *iter))) |
---|
| 515 | critm(); |
---|
| 516 | else |
---|
| 517 | memset(iter, 0, sizeof *iter); |
---|
| 518 | return iter; |
---|
| 519 | } |
---|
| 520 | |
---|
| 521 | /* pi_del() - delete iterator */ |
---|
| 522 | void pi_del(struct part_iter **_iter) |
---|
| 523 | { |
---|
| 524 | if(!_iter || !*_iter) |
---|
| 525 | return; |
---|
| 526 | pi_dtor(*_iter); |
---|
| 527 | free(*_iter); |
---|
| 528 | *_iter = NULL; |
---|
| 529 | } |
---|
| 530 | |
---|
| 531 | static void try_gpt_we(const char *str, int sec) |
---|
| 532 | { |
---|
| 533 | if (sec) |
---|
| 534 | error(str); |
---|
| 535 | else |
---|
| 536 | warn(str); |
---|
| 537 | } |
---|
| 538 | |
---|
| 539 | static struct disk_gpt_header *try_gpt_hdr(const struct disk_info *di, int sec) |
---|
| 540 | { |
---|
| 541 | const char *desc = sec ? "backup" : "primary"; |
---|
| 542 | uint64_t gpt_cur = sec ? di->lbacnt - 1 : 1; |
---|
| 543 | struct disk_gpt_header *gpth; |
---|
| 544 | char errbuf[64]; |
---|
| 545 | |
---|
| 546 | gpth = disk_read_sectors(di, gpt_cur, 1); |
---|
| 547 | if (!gpth) { |
---|
| 548 | sprintf(errbuf, "Unable to read %s GPT header.", desc); |
---|
| 549 | try_gpt_we(errbuf, sec); |
---|
| 550 | return NULL; |
---|
| 551 | } |
---|
| 552 | if(!valid_crc_hdr(gpth)) { |
---|
| 553 | sprintf(errbuf, "Invalid checksum of %s GPT header.", desc); |
---|
| 554 | try_gpt_we(errbuf, sec); |
---|
| 555 | free(gpth); |
---|
| 556 | return NULL; |
---|
| 557 | } |
---|
| 558 | return gpth; |
---|
| 559 | } |
---|
| 560 | |
---|
| 561 | static struct disk_gpt_part_entry *try_gpt_list(const struct disk_info *di, const struct disk_gpt_header *gpth, int alt) |
---|
| 562 | { |
---|
| 563 | int pri = gpth->lba_cur < gpth->lba_alt; |
---|
| 564 | const char *desc = alt ? "alternative" : "main"; |
---|
| 565 | struct disk_gpt_part_entry *gptl; |
---|
| 566 | char errbuf[64]; |
---|
| 567 | uint64_t gpt_lsiz; /* size of GPT partition list in bytes */ |
---|
| 568 | uint64_t gpt_lcnt; /* size of GPT partition in sectors */ |
---|
| 569 | uint64_t gpt_loff; /* offset to GPT partition list in sectors */ |
---|
| 570 | |
---|
| 571 | gpt_lsiz = (uint64_t)gpth->part_size * gpth->part_count; |
---|
| 572 | gpt_lcnt = (gpt_lsiz + di->bps - 1) / di->bps; |
---|
| 573 | if (!alt) { |
---|
| 574 | /* prefer header value for partition table if not asking for alternative */ |
---|
| 575 | gpt_loff = gpth->lba_table; |
---|
| 576 | } else { |
---|
| 577 | /* try to read alternative, we have to calculate its position */ |
---|
| 578 | if (!pri) |
---|
| 579 | gpt_loff = gpth->lba_alt + 1; |
---|
| 580 | else |
---|
| 581 | gpt_loff = gpth->lba_alt - gpt_lcnt; |
---|
| 582 | } |
---|
| 583 | |
---|
| 584 | gptl = disk_read_sectors(di, gpt_loff, gpt_lcnt); |
---|
| 585 | if (!gptl) { |
---|
| 586 | sprintf(errbuf, "Unable to read %s GPT partition list.", desc); |
---|
| 587 | try_gpt_we(errbuf, alt); |
---|
| 588 | return NULL; |
---|
| 589 | } |
---|
| 590 | if (!valid_crc(gpth->table_chksum, (const uint8_t *)gptl, gpt_lsiz)) { |
---|
| 591 | sprintf(errbuf, "Invalid checksum of %s GPT partition list.", desc); |
---|
| 592 | try_gpt_we(errbuf, alt); |
---|
| 593 | free(gptl); |
---|
| 594 | return NULL; |
---|
| 595 | } |
---|
| 596 | return gptl; |
---|
| 597 | } |
---|
| 598 | |
---|
| 599 | static int notsane_gpt_hdr(const struct disk_info *di, const struct disk_gpt_header *gpth, int flags) |
---|
| 600 | { |
---|
| 601 | uint64_t gpt_loff; /* offset to GPT partition list in sectors */ |
---|
| 602 | uint64_t gpt_lsiz; /* size of GPT partition list in bytes */ |
---|
| 603 | uint64_t gpt_lcnt; /* size of GPT partition in sectors */ |
---|
| 604 | uint64_t gpt_sec; /* secondary gpt header */ |
---|
| 605 | |
---|
| 606 | if (!(flags & PIF_STRICT)) |
---|
| 607 | return 0; |
---|
| 608 | |
---|
| 609 | if (gpth->lba_alt < gpth->lba_cur) |
---|
| 610 | gpt_sec = gpth->lba_cur; |
---|
| 611 | else |
---|
| 612 | gpt_sec = gpth->lba_alt; |
---|
| 613 | gpt_loff = gpth->lba_table; |
---|
| 614 | gpt_lsiz = (uint64_t)gpth->part_size * gpth->part_count; |
---|
| 615 | gpt_lcnt = (gpt_lsiz + di->bps - 1) / di->bps; |
---|
| 616 | |
---|
| 617 | /* |
---|
| 618 | * disk_read_sectors allows reading of max 255 sectors, so we use |
---|
| 619 | * it as a sanity check base. EFI doesn't specify max (AFAIK). |
---|
| 620 | */ |
---|
| 621 | if (gpt_loff < 2 || !gpt_lsiz || gpt_lcnt > 255u || |
---|
| 622 | gpth->lba_first_usable > gpth->lba_last_usable || |
---|
| 623 | !sane(gpt_loff, gpt_lcnt) || |
---|
| 624 | (gpt_loff + gpt_lcnt > gpth->lba_first_usable && gpt_loff <= gpth->lba_last_usable) || |
---|
| 625 | gpt_loff + gpt_lcnt > gpt_sec || |
---|
| 626 | ((flags & PIF_STRICTER) && (gpt_sec >= di->lbacnt)) || |
---|
| 627 | gpth->part_size < sizeof(struct disk_gpt_part_entry)) |
---|
| 628 | return -1; |
---|
| 629 | |
---|
| 630 | return 0; |
---|
| 631 | } |
---|
| 632 | |
---|
| 633 | /* pi_begin() - validate and and get proper iterator for a disk described by di */ |
---|
| 634 | struct part_iter *pi_begin(const struct disk_info *di, int flags) |
---|
| 635 | { |
---|
| 636 | int isgpt = 0, ret = -1; |
---|
| 637 | struct part_iter *iter; |
---|
| 638 | struct disk_dos_mbr *mbr = NULL; |
---|
| 639 | struct disk_gpt_header *gpth = NULL; |
---|
| 640 | struct disk_gpt_part_entry *gptl = NULL; |
---|
| 641 | |
---|
| 642 | /* Preallocate iterator */ |
---|
| 643 | if (!(iter = pi_alloc())) |
---|
| 644 | goto out; |
---|
| 645 | |
---|
| 646 | /* Read MBR */ |
---|
| 647 | if (!(mbr = disk_read_sectors(di, 0, 1))) { |
---|
| 648 | error("Unable to read the first disk sector."); |
---|
| 649 | goto out; |
---|
| 650 | } |
---|
| 651 | |
---|
| 652 | /* Check for MBR magic */ |
---|
| 653 | if (mbr->sig != disk_mbr_sig_magic) { |
---|
| 654 | warn("No MBR magic, treating disk as raw."); |
---|
| 655 | /* looks like RAW */ |
---|
| 656 | ret = pi_ctor(iter, di, flags); |
---|
| 657 | goto out; |
---|
| 658 | } |
---|
| 659 | |
---|
| 660 | /* Check for GPT protective MBR */ |
---|
| 661 | for (size_t i = 0; i < 4; i++) |
---|
| 662 | isgpt |= (mbr->table[i].ostype == 0xEE); |
---|
| 663 | isgpt = isgpt && !(flags & PIF_PREFMBR); |
---|
| 664 | |
---|
| 665 | /* Try to read GPT header */ |
---|
| 666 | if (isgpt) { |
---|
| 667 | gpth = try_gpt_hdr(di, 0); |
---|
| 668 | if (!gpth) |
---|
| 669 | /* |
---|
| 670 | * this read might fail if bios reports different disk size (different vm/pc) |
---|
| 671 | * not much we can do here to avoid it |
---|
| 672 | */ |
---|
| 673 | gpth = try_gpt_hdr(di, 1); |
---|
| 674 | if (!gpth) |
---|
| 675 | goto out; |
---|
| 676 | } |
---|
| 677 | |
---|
| 678 | if (gpth && gpth->rev.uint32 == 0x00010000 && |
---|
| 679 | !memcmp(gpth->sig, disk_gpt_sig_magic, sizeof gpth->sig)) { |
---|
| 680 | /* looks like GPT v1.0 */ |
---|
| 681 | #ifdef DEBUG |
---|
| 682 | dprintf("Looks like a GPT v1.0 disk.\n"); |
---|
| 683 | disk_gpt_header_dump(gpth); |
---|
| 684 | #endif |
---|
| 685 | if (notsane_gpt_hdr(di, gpth, flags)) { |
---|
| 686 | error("GPT header values are corrupted."); |
---|
| 687 | goto out; |
---|
| 688 | } |
---|
| 689 | |
---|
| 690 | gptl = try_gpt_list(di, gpth, 0); |
---|
| 691 | if (!gptl) |
---|
| 692 | gptl = try_gpt_list(di, gpth, 1); |
---|
| 693 | if (!gptl) |
---|
| 694 | goto out; |
---|
| 695 | |
---|
| 696 | /* looks like GPT */ |
---|
| 697 | ret = pi_gpt_ctor(iter, di, flags, gpth, gptl); |
---|
| 698 | } else { |
---|
| 699 | /* looks like MBR */ |
---|
| 700 | ret = pi_dos_ctor(iter, di, flags, mbr); |
---|
| 701 | } |
---|
| 702 | out: |
---|
| 703 | if (ret < 0) { |
---|
| 704 | free(iter); |
---|
| 705 | iter = NULL; |
---|
| 706 | } |
---|
| 707 | free(mbr); |
---|
| 708 | free(gpth); |
---|
| 709 | free(gptl); |
---|
| 710 | |
---|
| 711 | return iter; |
---|
| 712 | } |
---|
| 713 | |
---|
| 714 | /* vim: set ts=8 sts=4 sw=4 noet: */ |
---|