1 /* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #include <stdlib.h> 18 #include <sys/ioctl.h> 19 #include <sys/stat.h> 20 21 #include <ext4_utils/ext4_sb.h> 22 #include <squashfs_utils.h> 23 24 #if defined(__linux__) 25 #include <linux/fs.h> 26 #elif defined(__APPLE__) 27 #include <sys/disk.h> 28 #define BLKGETSIZE64 DKIOCGETBLOCKCOUNT 29 #define fdatasync(fd) fcntl((fd), F_FULLFSYNC) 30 #endif 31 32 #include "avb_utils.h" 33 #include "fec_private.h" 34 35 /* used by `find_offset'; returns metadata size for a file size `size' and 36 `roots' Reed-Solomon parity bytes */ 37 using size_func = uint64_t (*)(uint64_t size, int roots); 38 39 /* performs a binary search to find a metadata offset from a file so that 40 the metadata size matches function `get_real_size(size, roots)', using 41 the approximate size returned by `get_appr_size' as a starting point */ 42 static int find_offset(uint64_t file_size, int roots, uint64_t *offset, 43 size_func get_appr_size, size_func get_real_size) 44 { 45 check(offset); 46 check(get_appr_size); 47 check(get_real_size); 48 49 if (file_size % FEC_BLOCKSIZE) { 50 /* must be a multiple of block size */ 51 error("file size not multiple of " stringify(FEC_BLOCKSIZE)); 52 errno = EINVAL; 53 return -1; 54 } 55 56 uint64_t mi = get_appr_size(file_size, roots); 57 uint64_t lo = file_size - mi * 2; 58 uint64_t hi = file_size - mi / 2; 59 60 while (lo < hi) { 61 mi = ((hi + lo) / (2 * FEC_BLOCKSIZE)) * FEC_BLOCKSIZE; 62 uint64_t total = mi + get_real_size(mi, roots); 63 64 if (total < file_size) { 65 lo = mi + FEC_BLOCKSIZE; 66 } else if (total > file_size) { 67 hi = mi; 68 } else { 69 *offset = mi; 70 debug("file_size = %" PRIu64 " -> offset = %" PRIu64, file_size, 71 mi); 72 return 0; 73 } 74 } 75 76 warn("could not determine offset"); 77 errno = ERANGE; 78 return -1; 79 } 80 81 /* returns verity metadata size for a `size' byte file */ 82 static uint64_t get_verity_size(uint64_t size, int) 83 { 84 return VERITY_METADATA_SIZE + 85 verity_get_size(size, NULL, NULL, SHA256_DIGEST_LENGTH); 86 } 87 88 /* computes the verity metadata offset for a file with size `f->size' */ 89 static int find_verity_offset(fec_handle *f, uint64_t *offset) 90 { 91 check(f); 92 check(offset); 93 94 return find_offset(f->data_size, 0, offset, get_verity_size, 95 get_verity_size); 96 } 97 98 /* attempts to read and validate an ecc header from file position `offset' */ 99 static int parse_ecc_header(fec_handle *f, uint64_t offset) 100 { 101 check(f); 102 check(f->ecc.rsn > 0 && f->ecc.rsn < FEC_RSM); 103 check(f->size > sizeof(fec_header)); 104 105 debug("offset = %" PRIu64, offset); 106 107 if (offset > f->size - sizeof(fec_header)) { 108 return -1; 109 } 110 111 fec_header header; 112 113 /* there's obviously no ecc data at this point, so there is no need to 114 call fec_pread to access this data */ 115 if (!raw_pread(f->fd, &header, sizeof(fec_header), offset)) { 116 error("failed to read: %s", strerror(errno)); 117 return -1; 118 } 119 120 /* move offset back to the beginning of the block for validating header */ 121 offset -= offset % FEC_BLOCKSIZE; 122 123 if (header.magic != FEC_MAGIC) { 124 return -1; 125 } 126 if (header.version != FEC_VERSION) { 127 error("unsupported ecc version: %u", header.version); 128 return -1; 129 } 130 if (header.size != sizeof(fec_header)) { 131 error("unexpected ecc header size: %u", header.size); 132 return -1; 133 } 134 if (header.roots == 0 || header.roots >= FEC_RSM) { 135 error("invalid ecc roots: %u", header.roots); 136 return -1; 137 } 138 if (f->ecc.roots != (int)header.roots) { 139 error("unexpected number of roots: %d vs %u", f->ecc.roots, 140 header.roots); 141 return -1; 142 } 143 if (header.fec_size % header.roots || 144 header.fec_size % FEC_BLOCKSIZE) { 145 error("inconsistent ecc size %u", header.fec_size); 146 return -1; 147 } 148 149 f->data_size = header.inp_size; 150 f->ecc.blocks = fec_div_round_up(f->data_size, FEC_BLOCKSIZE); 151 f->ecc.rounds = fec_div_round_up(f->ecc.blocks, f->ecc.rsn); 152 153 if (header.fec_size != 154 (uint32_t)f->ecc.rounds * f->ecc.roots * FEC_BLOCKSIZE) { 155 error("inconsistent ecc size %u", header.fec_size); 156 return -1; 157 } 158 159 f->ecc.size = header.fec_size; 160 f->ecc.start = header.inp_size; 161 162 /* validate encoding data; caller may opt not to use it if invalid */ 163 SHA256_CTX ctx; 164 SHA256_Init(&ctx); 165 166 uint8_t buf[FEC_BLOCKSIZE]; 167 uint32_t n = 0; 168 uint32_t len = FEC_BLOCKSIZE; 169 170 while (n < f->ecc.size) { 171 if (len > f->ecc.size - n) { 172 len = f->ecc.size - n; 173 } 174 175 if (!raw_pread(f->fd, buf, len, f->ecc.start + n)) { 176 error("failed to read ecc: %s", strerror(errno)); 177 return -1; 178 } 179 180 SHA256_Update(&ctx, buf, len); 181 n += len; 182 } 183 184 uint8_t hash[SHA256_DIGEST_LENGTH]; 185 SHA256_Final(hash, &ctx); 186 187 f->ecc.valid = !memcmp(hash, header.hash, SHA256_DIGEST_LENGTH); 188 189 if (!f->ecc.valid) { 190 warn("ecc data not valid"); 191 } 192 193 return 0; 194 } 195 196 /* attempts to read an ecc header from `offset', and checks for a backup copy 197 at the end of the block if the primary header is not valid */ 198 static int parse_ecc(fec_handle *f, uint64_t offset) 199 { 200 check(f); 201 check(offset % FEC_BLOCKSIZE == 0); 202 check(offset < UINT64_MAX - FEC_BLOCKSIZE); 203 204 /* check the primary header at the beginning of the block */ 205 if (parse_ecc_header(f, offset) == 0) { 206 return 0; 207 } 208 209 /* check the backup header at the end of the block */ 210 if (parse_ecc_header(f, offset + FEC_BLOCKSIZE - sizeof(fec_header)) == 0) { 211 warn("using backup ecc header"); 212 return 0; 213 } 214 215 return -1; 216 } 217 218 /* reads the squashfs superblock and returns the size of the file system in 219 `offset' */ 220 static int get_squashfs_size(fec_handle *f, uint64_t *offset) 221 { 222 check(f); 223 check(offset); 224 225 size_t sb_size = squashfs_get_sb_size(); 226 check(sb_size <= SSIZE_MAX); 227 228 uint8_t buffer[sb_size]; 229 230 if (fec_pread(f, buffer, sizeof(buffer), 0) != (ssize_t)sb_size) { 231 error("failed to read superblock: %s", strerror(errno)); 232 return -1; 233 } 234 235 squashfs_info sq; 236 237 if (squashfs_parse_sb_buffer(buffer, &sq) < 0) { 238 error("failed to parse superblock: %s", strerror(errno)); 239 return -1; 240 } 241 242 *offset = sq.bytes_used_4K_padded; 243 return 0; 244 } 245 246 /* reads the ext4 superblock and returns the size of the file system in 247 `offset' */ 248 static int get_ext4_size(fec_handle *f, uint64_t *offset) 249 { 250 check(f); 251 check(f->size > 1024 + sizeof(ext4_super_block)); 252 check(offset); 253 254 ext4_super_block sb; 255 256 if (fec_pread(f, &sb, sizeof(sb), 1024) != sizeof(sb)) { 257 error("failed to read superblock: %s", strerror(errno)); 258 return -1; 259 } 260 261 fs_info info; 262 info.len = 0; /* only len is set to 0 to ask the device for real size. */ 263 264 if (ext4_parse_sb(&sb, &info) != 0) { 265 errno = EINVAL; 266 return -1; 267 } 268 269 *offset = info.len; 270 return 0; 271 } 272 273 /* attempts to determine file system size, if no fs type is specified in 274 `f->flags', tries all supported types, and returns the size in `offset' */ 275 static int get_fs_size(fec_handle *f, uint64_t *offset) 276 { 277 check(f); 278 check(offset); 279 280 if (f->flags & FEC_FS_EXT4) { 281 return get_ext4_size(f, offset); 282 } else if (f->flags & FEC_FS_SQUASH) { 283 return get_squashfs_size(f, offset); 284 } else { 285 /* try all alternatives */ 286 int rc = get_ext4_size(f, offset); 287 288 if (rc == 0) { 289 debug("found ext4fs"); 290 return rc; 291 } 292 293 rc = get_squashfs_size(f, offset); 294 295 if (rc == 0) { 296 debug("found squashfs"); 297 } 298 299 return rc; 300 } 301 } 302 303 /* locates, validates, and loads verity metadata from `f->fd' */ 304 static int load_verity(fec_handle *f) 305 { 306 check(f); 307 debug("size = %" PRIu64 ", flags = %d", f->data_size, f->flags); 308 309 uint64_t offset = f->data_size - VERITY_METADATA_SIZE; 310 311 /* verity header is at the end of the data area */ 312 if (verity_parse_header(f, offset) == 0) { 313 debug("found at %" PRIu64 " (start %" PRIu64 ")", offset, 314 f->verity.hashtree.hash_start); 315 return 0; 316 } 317 318 debug("trying legacy formats"); 319 320 /* legacy format at the end of the partition */ 321 if (find_verity_offset(f, &offset) == 0 && 322 verity_parse_header(f, offset) == 0) { 323 debug("found at %" PRIu64 " (start %" PRIu64 ")", offset, 324 f->verity.hashtree.hash_start); 325 return 0; 326 } 327 328 /* legacy format after the file system, but not at the end */ 329 int rc = get_fs_size(f, &offset); 330 if (rc == 0) { 331 debug("file system size = %" PRIu64, offset); 332 /* Jump over the verity tree appended to the filesystem */ 333 offset += verity_get_size(offset, NULL, NULL, SHA256_DIGEST_LENGTH); 334 rc = verity_parse_header(f, offset); 335 336 if (rc == 0) { 337 debug("found at %" PRIu64 " (start %" PRIu64 ")", offset, 338 f->verity.hashtree.hash_start); 339 } 340 } 341 342 return rc; 343 } 344 345 /* locates, validates, and loads ecc data from `f->fd' */ 346 static int load_ecc(fec_handle *f) 347 { 348 check(f); 349 debug("size = %" PRIu64, f->data_size); 350 351 uint64_t offset = f->data_size - FEC_BLOCKSIZE; 352 353 if (parse_ecc(f, offset) == 0) { 354 debug("found at %" PRIu64 " (start %" PRIu64 ")", offset, 355 f->ecc.start); 356 return 0; 357 } 358 359 return -1; 360 } 361 362 /* sets `f->size' to the size of the file or block device */ 363 static int get_size(fec_handle *f) 364 { 365 check(f); 366 367 struct stat st; 368 369 if (fstat(f->fd, &st) == -1) { 370 error("fstat failed: %s", strerror(errno)); 371 return -1; 372 } 373 374 if (S_ISBLK(st.st_mode)) { 375 debug("block device"); 376 377 if (ioctl(f->fd, BLKGETSIZE64, &f->size) == -1) { 378 error("ioctl failed: %s", strerror(errno)); 379 return -1; 380 } 381 } else if (S_ISREG(st.st_mode)) { 382 debug("file"); 383 f->size = st.st_size; 384 } else { 385 error("unsupported type %d", (int)st.st_mode); 386 errno = EACCES; 387 return -1; 388 } 389 390 return 0; 391 } 392 393 /* clears fec_handle fiels to safe values */ 394 static void reset_handle(fec_handle *f) 395 { 396 f->fd = -1; 397 f->flags = 0; 398 f->mode = 0; 399 f->errors = 0; 400 f->data_size = 0; 401 f->pos = 0; 402 f->size = 0; 403 404 f->ecc = {}; 405 f->verity = {}; 406 } 407 408 /* closes and flushes `f->fd' and releases any memory allocated for `f' */ 409 int fec_close(struct fec_handle *f) 410 { 411 check(f); 412 413 if (f->fd != -1) { 414 if (f->mode & O_RDWR && fdatasync(f->fd) == -1) { 415 warn("fdatasync failed: %s", strerror(errno)); 416 } 417 418 close(f->fd); 419 } 420 421 pthread_mutex_destroy(&f->mutex); 422 423 reset_handle(f); 424 delete f; 425 426 return 0; 427 } 428 429 /* populates `data' from the internal data in `f', returns a value <0 if verity 430 metadata is not available in `f->fd' */ 431 int fec_verity_get_metadata(struct fec_handle *f, struct fec_verity_metadata *data) 432 { 433 check(f); 434 check(data); 435 436 if (!f->verity.metadata_start) { 437 return -1; 438 } 439 440 check(f->data_size < f->size); 441 check(f->data_size <= f->verity.hashtree.hash_start); 442 check(f->data_size <= f->verity.metadata_start); 443 check(!f->verity.table.empty()); 444 445 data->disabled = f->verity.disabled; 446 data->data_size = f->data_size; 447 memcpy(data->signature, f->verity.header.signature, 448 sizeof(data->signature)); 449 memcpy(data->ecc_signature, f->verity.ecc_header.signature, 450 sizeof(data->ecc_signature)); 451 data->table = f->verity.table.c_str(); 452 data->table_length = f->verity.header.length; 453 454 return 0; 455 } 456 457 /* populates `data' from the internal data in `f', returns a value <0 if ecc 458 metadata is not available in `f->fd' */ 459 int fec_ecc_get_metadata(struct fec_handle *f, struct fec_ecc_metadata *data) 460 { 461 check(f); 462 check(data); 463 464 if (!f->ecc.start) { 465 return -1; 466 } 467 468 check(f->data_size < f->size); 469 check(f->ecc.start >= f->data_size); 470 check(f->ecc.start < f->size); 471 check(f->ecc.start % FEC_BLOCKSIZE == 0) 472 473 data->valid = f->ecc.valid; 474 data->roots = f->ecc.roots; 475 data->blocks = f->ecc.blocks; 476 data->rounds = f->ecc.rounds; 477 data->start = f->ecc.start; 478 479 return 0; 480 } 481 482 /* populates `data' from the internal status in `f' */ 483 int fec_get_status(struct fec_handle *f, struct fec_status *s) 484 { 485 check(f); 486 check(s); 487 488 s->flags = f->flags; 489 s->mode = f->mode; 490 s->errors = f->errors; 491 s->data_size = f->data_size; 492 s->size = f->size; 493 494 return 0; 495 } 496 497 /* opens `path' using given options and returns a fec_handle in `handle' if 498 successful */ 499 int fec_open(struct fec_handle **handle, const char *path, int mode, int flags, 500 int roots) 501 { 502 check(path); 503 check(handle); 504 check(roots > 0 && roots < FEC_RSM); 505 506 debug("path = %s, mode = %d, flags = %d, roots = %d", path, mode, flags, 507 roots); 508 509 if (mode & (O_CREAT | O_TRUNC | O_EXCL | O_WRONLY)) { 510 /* only reading and updating existing files is supported */ 511 error("failed to open '%s': (unsupported mode %d)", path, mode); 512 errno = EACCES; 513 return -1; 514 } 515 516 fec::handle f(new (std::nothrow) fec_handle, fec_close); 517 518 if (unlikely(!f)) { 519 error("failed to allocate file handle"); 520 errno = ENOMEM; 521 return -1; 522 } 523 524 reset_handle(f.get()); 525 526 f->mode = mode; 527 f->ecc.roots = roots; 528 f->ecc.rsn = FEC_RSM - roots; 529 f->flags = flags; 530 531 if (unlikely(pthread_mutex_init(&f->mutex, NULL) != 0)) { 532 error("failed to create a mutex: %s", strerror(errno)); 533 return -1; 534 } 535 536 f->fd = TEMP_FAILURE_RETRY(open(path, mode | O_CLOEXEC)); 537 538 if (f->fd == -1) { 539 error("failed to open '%s': %s", path, strerror(errno)); 540 return -1; 541 } 542 543 if (get_size(f.get()) == -1) { 544 error("failed to get size for '%s': %s", path, strerror(errno)); 545 return -1; 546 } 547 548 f->data_size = f->size; /* until ecc and/or verity are loaded */ 549 550 // Don't parse the avb image if FEC_NO_AVB is set. It's used when libavb is 551 // not supported on mac. 552 std::vector<uint8_t> vbmeta; 553 if (parse_vbmeta_from_footer(f.get(), &vbmeta) == 0) { 554 if (parse_avb_image(f.get(), vbmeta) != 0) { 555 error("failed to parse avb image."); 556 return -1; 557 } 558 559 *handle = f.release(); 560 return 0; 561 } 562 // TODO(xunchang) For android, handle the case when vbmeta is in a separate 563 // image. We could use avb_slot_verify() && AvbOps from libavb_user. 564 565 // Fall back to use verity format. 566 567 if (load_ecc(f.get()) == -1) { 568 debug("error-correcting codes not found from '%s'", path); 569 } 570 571 if (load_verity(f.get()) == -1) { 572 debug("verity metadata not found from '%s'", path); 573 } 574 575 *handle = f.release(); 576 return 0; 577 } 578