1 /* 2 * Copyright (C) 2014 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 <ctype.h> 18 #include <errno.h> 19 #include <fcntl.h> 20 #include <inttypes.h> 21 #include <pthread.h> 22 #include <stdarg.h> 23 #include <stdio.h> 24 #include <stdlib.h> 25 #include <string.h> 26 #include <sys/types.h> 27 #include <sys/wait.h> 28 #include <sys/ioctl.h> 29 #include <time.h> 30 #include <unistd.h> 31 32 #include "applypatch/applypatch.h" 33 #include "edify/expr.h" 34 #include "mincrypt/sha.h" 35 #include "minzip/DirUtil.h" 36 #include "updater.h" 37 38 #define BLOCKSIZE 4096 39 40 // Set this to 0 to interpret 'erase' transfers to mean do a 41 // BLKDISCARD ioctl (the normal behavior). Set to 1 to interpret 42 // erase to mean fill the region with zeroes. 43 #define DEBUG_ERASE 0 44 45 #ifndef BLKDISCARD 46 #define BLKDISCARD _IO(0x12,119) 47 #endif 48 49 char* PrintSha1(const uint8_t* digest); 50 51 typedef struct { 52 int count; 53 int size; 54 int pos[0]; 55 } RangeSet; 56 57 static RangeSet* parse_range(char* text) { 58 char* save; 59 int num; 60 num = strtol(strtok_r(text, ",", &save), NULL, 0); 61 62 RangeSet* out = malloc(sizeof(RangeSet) + num * sizeof(int)); 63 if (out == NULL) { 64 fprintf(stderr, "failed to allocate range of %zu bytes\n", 65 sizeof(RangeSet) + num * sizeof(int)); 66 exit(1); 67 } 68 out->count = num / 2; 69 out->size = 0; 70 int i; 71 for (i = 0; i < num; ++i) { 72 out->pos[i] = strtol(strtok_r(NULL, ",", &save), NULL, 0); 73 if (i%2) { 74 out->size += out->pos[i]; 75 } else { 76 out->size -= out->pos[i]; 77 } 78 } 79 80 return out; 81 } 82 83 static void readblock(int fd, uint8_t* data, size_t size) { 84 size_t so_far = 0; 85 while (so_far < size) { 86 ssize_t r = read(fd, data+so_far, size-so_far); 87 if (r < 0 && errno != EINTR) { 88 fprintf(stderr, "read failed: %s\n", strerror(errno)); 89 return; 90 } else { 91 so_far += r; 92 } 93 } 94 } 95 96 static void writeblock(int fd, const uint8_t* data, size_t size) { 97 size_t written = 0; 98 while (written < size) { 99 ssize_t w = write(fd, data+written, size-written); 100 if (w < 0 && errno != EINTR) { 101 fprintf(stderr, "write failed: %s\n", strerror(errno)); 102 return; 103 } else { 104 written += w; 105 } 106 } 107 } 108 109 static void check_lseek(int fd, off64_t offset, int whence) { 110 while (true) { 111 off64_t ret = lseek64(fd, offset, whence); 112 if (ret < 0) { 113 if (errno != EINTR) { 114 fprintf(stderr, "lseek64 failed: %s\n", strerror(errno)); 115 exit(1); 116 } 117 } else { 118 break; 119 } 120 } 121 } 122 123 static void allocate(size_t size, uint8_t** buffer, size_t* buffer_alloc) { 124 // if the buffer's big enough, reuse it. 125 if (size <= *buffer_alloc) return; 126 127 free(*buffer); 128 129 *buffer = (uint8_t*) malloc(size); 130 if (*buffer == NULL) { 131 fprintf(stderr, "failed to allocate %zu bytes\n", size); 132 exit(1); 133 } 134 *buffer_alloc = size; 135 } 136 137 typedef struct { 138 int fd; 139 RangeSet* tgt; 140 int p_block; 141 size_t p_remain; 142 } RangeSinkState; 143 144 static ssize_t RangeSinkWrite(const uint8_t* data, ssize_t size, void* token) { 145 RangeSinkState* rss = (RangeSinkState*) token; 146 147 if (rss->p_remain <= 0) { 148 fprintf(stderr, "range sink write overrun"); 149 exit(1); 150 } 151 152 ssize_t written = 0; 153 while (size > 0) { 154 size_t write_now = size; 155 if (rss->p_remain < write_now) write_now = rss->p_remain; 156 writeblock(rss->fd, data, write_now); 157 data += write_now; 158 size -= write_now; 159 160 rss->p_remain -= write_now; 161 written += write_now; 162 163 if (rss->p_remain == 0) { 164 // move to the next block 165 ++rss->p_block; 166 if (rss->p_block < rss->tgt->count) { 167 rss->p_remain = (rss->tgt->pos[rss->p_block*2+1] - rss->tgt->pos[rss->p_block*2]) * BLOCKSIZE; 168 check_lseek(rss->fd, (off64_t)rss->tgt->pos[rss->p_block*2] * BLOCKSIZE, SEEK_SET); 169 } else { 170 // we can't write any more; return how many bytes have 171 // been written so far. 172 return written; 173 } 174 } 175 } 176 177 return written; 178 } 179 180 // All of the data for all the 'new' transfers is contained in one 181 // file in the update package, concatenated together in the order in 182 // which transfers.list will need it. We want to stream it out of the 183 // archive (it's compressed) without writing it to a temp file, but we 184 // can't write each section until it's that transfer's turn to go. 185 // 186 // To achieve this, we expand the new data from the archive in a 187 // background thread, and block that threads 'receive uncompressed 188 // data' function until the main thread has reached a point where we 189 // want some new data to be written. We signal the background thread 190 // with the destination for the data and block the main thread, 191 // waiting for the background thread to complete writing that section. 192 // Then it signals the main thread to wake up and goes back to 193 // blocking waiting for a transfer. 194 // 195 // NewThreadInfo is the struct used to pass information back and forth 196 // between the two threads. When the main thread wants some data 197 // written, it sets rss to the destination location and signals the 198 // condition. When the background thread is done writing, it clears 199 // rss and signals the condition again. 200 201 typedef struct { 202 ZipArchive* za; 203 const ZipEntry* entry; 204 205 RangeSinkState* rss; 206 207 pthread_mutex_t mu; 208 pthread_cond_t cv; 209 } NewThreadInfo; 210 211 static bool receive_new_data(const unsigned char* data, int size, void* cookie) { 212 NewThreadInfo* nti = (NewThreadInfo*) cookie; 213 214 while (size > 0) { 215 // Wait for nti->rss to be non-NULL, indicating some of this 216 // data is wanted. 217 pthread_mutex_lock(&nti->mu); 218 while (nti->rss == NULL) { 219 pthread_cond_wait(&nti->cv, &nti->mu); 220 } 221 pthread_mutex_unlock(&nti->mu); 222 223 // At this point nti->rss is set, and we own it. The main 224 // thread is waiting for it to disappear from nti. 225 ssize_t written = RangeSinkWrite(data, size, nti->rss); 226 data += written; 227 size -= written; 228 229 if (nti->rss->p_block == nti->rss->tgt->count) { 230 // we have written all the bytes desired by this rss. 231 232 pthread_mutex_lock(&nti->mu); 233 nti->rss = NULL; 234 pthread_cond_broadcast(&nti->cv); 235 pthread_mutex_unlock(&nti->mu); 236 } 237 } 238 239 return true; 240 } 241 242 static void* unzip_new_data(void* cookie) { 243 NewThreadInfo* nti = (NewThreadInfo*) cookie; 244 mzProcessZipEntryContents(nti->za, nti->entry, receive_new_data, nti); 245 return NULL; 246 } 247 248 // Do a source/target load for move/bsdiff/imgdiff in version 1. 249 // 'wordsave' is the save_ptr of a strtok_r()-in-progress. We expect 250 // to parse the remainder of the string as: 251 // 252 // <src_range> <tgt_range> 253 // 254 // The source range is loaded into the provided buffer, reallocating 255 // it to make it larger if necessary. The target ranges are returned 256 // in *tgt, if tgt is non-NULL. 257 258 static void LoadSrcTgtVersion1(char* wordsave, RangeSet** tgt, int* src_blocks, 259 uint8_t** buffer, size_t* buffer_alloc, int fd) { 260 char* word; 261 262 word = strtok_r(NULL, " ", &wordsave); 263 RangeSet* src = parse_range(word); 264 265 if (tgt != NULL) { 266 word = strtok_r(NULL, " ", &wordsave); 267 *tgt = parse_range(word); 268 } 269 270 allocate(src->size * BLOCKSIZE, buffer, buffer_alloc); 271 size_t p = 0; 272 int i; 273 for (i = 0; i < src->count; ++i) { 274 check_lseek(fd, (off64_t)src->pos[i*2] * BLOCKSIZE, SEEK_SET); 275 size_t sz = (src->pos[i*2+1] - src->pos[i*2]) * BLOCKSIZE; 276 readblock(fd, *buffer+p, sz); 277 p += sz; 278 } 279 280 *src_blocks = src->size; 281 free(src); 282 } 283 284 static void MoveRange(uint8_t* dest, RangeSet* locs, const uint8_t* source) { 285 // source contains packed data, which we want to move to the 286 // locations given in *locs in the dest buffer. source and dest 287 // may be the same buffer. 288 289 int start = locs->size; 290 int i; 291 for (i = locs->count-1; i >= 0; --i) { 292 int blocks = locs->pos[i*2+1] - locs->pos[i*2]; 293 start -= blocks; 294 memmove(dest + (locs->pos[i*2] * BLOCKSIZE), source + (start * BLOCKSIZE), 295 blocks * BLOCKSIZE); 296 } 297 } 298 299 // Do a source/target load for move/bsdiff/imgdiff in version 2. 300 // 'wordsave' is the save_ptr of a strtok_r()-in-progress. We expect 301 // to parse the remainder of the string as one of: 302 // 303 // <tgt_range> <src_block_count> <src_range> 304 // (loads data from source image only) 305 // 306 // <tgt_range> <src_block_count> - <[stash_id:stash_range] ...> 307 // (loads data from stashes only) 308 // 309 // <tgt_range> <src_block_count> <src_range> <src_loc> <[stash_id:stash_range] ...> 310 // (loads data from both source image and stashes) 311 // 312 // On return, buffer is filled with the loaded source data (rearranged 313 // and combined with stashed data as necessary). buffer may be 314 // reallocated if needed to accommodate the source data. *tgt is the 315 // target RangeSet. Any stashes required are taken from stash_table 316 // and free()'d after being used. 317 318 static void LoadSrcTgtVersion2(char* wordsave, RangeSet** tgt, int* src_blocks, 319 uint8_t** buffer, size_t* buffer_alloc, int fd, 320 uint8_t** stash_table) { 321 char* word; 322 323 if (tgt != NULL) { 324 word = strtok_r(NULL, " ", &wordsave); 325 *tgt = parse_range(word); 326 } 327 328 word = strtok_r(NULL, " ", &wordsave); 329 *src_blocks = strtol(word, NULL, 0); 330 331 allocate(*src_blocks * BLOCKSIZE, buffer, buffer_alloc); 332 333 word = strtok_r(NULL, " ", &wordsave); 334 if (word[0] == '-' && word[1] == '\0') { 335 // no source ranges, only stashes 336 } else { 337 RangeSet* src = parse_range(word); 338 339 size_t p = 0; 340 int i; 341 for (i = 0; i < src->count; ++i) { 342 check_lseek(fd, (off64_t)src->pos[i*2] * BLOCKSIZE, SEEK_SET); 343 size_t sz = (src->pos[i*2+1] - src->pos[i*2]) * BLOCKSIZE; 344 readblock(fd, *buffer+p, sz); 345 p += sz; 346 } 347 free(src); 348 349 word = strtok_r(NULL, " ", &wordsave); 350 if (word == NULL) { 351 // no stashes, only source range 352 return; 353 } 354 355 RangeSet* locs = parse_range(word); 356 MoveRange(*buffer, locs, *buffer); 357 } 358 359 while ((word = strtok_r(NULL, " ", &wordsave)) != NULL) { 360 // Each word is a an index into the stash table, a colon, and 361 // then a rangeset describing where in the source block that 362 // stashed data should go. 363 char* colonsave = NULL; 364 char* colon = strtok_r(word, ":", &colonsave); 365 int stash_id = strtol(colon, NULL, 0); 366 colon = strtok_r(NULL, ":", &colonsave); 367 RangeSet* locs = parse_range(colon); 368 MoveRange(*buffer, locs, stash_table[stash_id]); 369 free(stash_table[stash_id]); 370 stash_table[stash_id] = NULL; 371 free(locs); 372 } 373 } 374 375 // args: 376 // - block device (or file) to modify in-place 377 // - transfer list (blob) 378 // - new data stream (filename within package.zip) 379 // - patch stream (filename within package.zip, must be uncompressed) 380 381 Value* BlockImageUpdateFn(const char* name, State* state, int argc, Expr* argv[]) { 382 Value* blockdev_filename; 383 Value* transfer_list_value; 384 char* transfer_list = NULL; 385 Value* new_data_fn; 386 Value* patch_data_fn; 387 bool success = false; 388 389 if (ReadValueArgs(state, argv, 4, &blockdev_filename, &transfer_list_value, 390 &new_data_fn, &patch_data_fn) < 0) { 391 return NULL; 392 } 393 394 if (blockdev_filename->type != VAL_STRING) { 395 ErrorAbort(state, "blockdev_filename argument to %s must be string", name); 396 goto done; 397 } 398 if (transfer_list_value->type != VAL_BLOB) { 399 ErrorAbort(state, "transfer_list argument to %s must be blob", name); 400 goto done; 401 } 402 if (new_data_fn->type != VAL_STRING) { 403 ErrorAbort(state, "new_data_fn argument to %s must be string", name); 404 goto done; 405 } 406 if (patch_data_fn->type != VAL_STRING) { 407 ErrorAbort(state, "patch_data_fn argument to %s must be string", name); 408 goto done; 409 } 410 411 UpdaterInfo* ui = (UpdaterInfo*)(state->cookie); 412 FILE* cmd_pipe = ui->cmd_pipe; 413 414 ZipArchive* za = ((UpdaterInfo*)(state->cookie))->package_zip; 415 416 const ZipEntry* patch_entry = mzFindZipEntry(za, patch_data_fn->data); 417 if (patch_entry == NULL) { 418 ErrorAbort(state, "%s(): no file \"%s\" in package", name, patch_data_fn->data); 419 goto done; 420 } 421 422 uint8_t* patch_start = ((UpdaterInfo*)(state->cookie))->package_zip_addr + 423 mzGetZipEntryOffset(patch_entry); 424 425 const ZipEntry* new_entry = mzFindZipEntry(za, new_data_fn->data); 426 if (new_entry == NULL) { 427 ErrorAbort(state, "%s(): no file \"%s\" in package", name, new_data_fn->data); 428 goto done; 429 } 430 431 // The transfer list is a text file containing commands to 432 // transfer data from one place to another on the target 433 // partition. We parse it and execute the commands in order: 434 // 435 // zero [rangeset] 436 // - fill the indicated blocks with zeros 437 // 438 // new [rangeset] 439 // - fill the blocks with data read from the new_data file 440 // 441 // erase [rangeset] 442 // - mark the given blocks as empty 443 // 444 // move <...> 445 // bsdiff <patchstart> <patchlen> <...> 446 // imgdiff <patchstart> <patchlen> <...> 447 // - read the source blocks, apply a patch (or not in the 448 // case of move), write result to target blocks. bsdiff or 449 // imgdiff specifies the type of patch; move means no patch 450 // at all. 451 // 452 // The format of <...> differs between versions 1 and 2; 453 // see the LoadSrcTgtVersion{1,2}() functions for a 454 // description of what's expected. 455 // 456 // stash <stash_id> <src_range> 457 // - (version 2 only) load the given source range and stash 458 // the data in the given slot of the stash table. 459 // 460 // The creator of the transfer list will guarantee that no block 461 // is read (ie, used as the source for a patch or move) after it 462 // has been written. 463 // 464 // In version 2, the creator will guarantee that a given stash is 465 // loaded (with a stash command) before it's used in a 466 // move/bsdiff/imgdiff command. 467 // 468 // Within one command the source and target ranges may overlap so 469 // in general we need to read the entire source into memory before 470 // writing anything to the target blocks. 471 // 472 // All the patch data is concatenated into one patch_data file in 473 // the update package. It must be stored uncompressed because we 474 // memory-map it in directly from the archive. (Since patches are 475 // already compressed, we lose very little by not compressing 476 // their concatenation.) 477 478 pthread_t new_data_thread; 479 NewThreadInfo nti; 480 nti.za = za; 481 nti.entry = new_entry; 482 nti.rss = NULL; 483 pthread_mutex_init(&nti.mu, NULL); 484 pthread_cond_init(&nti.cv, NULL); 485 486 pthread_attr_t attr; 487 pthread_attr_init(&attr); 488 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); 489 pthread_create(&new_data_thread, &attr, unzip_new_data, &nti); 490 491 int i, j; 492 493 char* linesave; 494 char* wordsave; 495 496 int fd = open(blockdev_filename->data, O_RDWR); 497 if (fd < 0) { 498 ErrorAbort(state, "failed to open %s: %s", blockdev_filename->data, strerror(errno)); 499 goto done; 500 } 501 502 char* line; 503 char* word; 504 505 // The data in transfer_list_value is not necessarily 506 // null-terminated, so we need to copy it to a new buffer and add 507 // the null that strtok_r will need. 508 transfer_list = malloc(transfer_list_value->size+1); 509 if (transfer_list == NULL) { 510 fprintf(stderr, "failed to allocate %zd bytes for transfer list\n", 511 transfer_list_value->size+1); 512 exit(1); 513 } 514 memcpy(transfer_list, transfer_list_value->data, transfer_list_value->size); 515 transfer_list[transfer_list_value->size] = '\0'; 516 517 line = strtok_r(transfer_list, "\n", &linesave); 518 519 int version; 520 // first line in transfer list is the version number; currently 521 // there's only version 1. 522 if (strcmp(line, "1") == 0) { 523 version = 1; 524 } else if (strcmp(line, "2") == 0) { 525 version = 2; 526 } else { 527 ErrorAbort(state, "unexpected transfer list version [%s]\n", line); 528 goto done; 529 } 530 printf("blockimg version is %d\n", version); 531 532 // second line in transfer list is the total number of blocks we 533 // expect to write. 534 line = strtok_r(NULL, "\n", &linesave); 535 int total_blocks = strtol(line, NULL, 0); 536 // shouldn't happen, but avoid divide by zero. 537 if (total_blocks == 0) ++total_blocks; 538 int blocks_so_far = 0; 539 540 uint8_t** stash_table = NULL; 541 if (version >= 2) { 542 // Next line is how many stash entries are needed simultaneously. 543 line = strtok_r(NULL, "\n", &linesave); 544 int stash_entries = strtol(line, NULL, 0); 545 546 stash_table = (uint8_t**) calloc(stash_entries, sizeof(uint8_t*)); 547 if (stash_table == NULL) { 548 fprintf(stderr, "failed to allocate %d-entry stash table\n", stash_entries); 549 exit(1); 550 } 551 552 // Next line is the maximum number of blocks that will be 553 // stashed simultaneously. This could be used to verify that 554 // enough memory or scratch disk space is available. 555 line = strtok_r(NULL, "\n", &linesave); 556 int stash_max_blocks = strtol(line, NULL, 0); 557 } 558 559 uint8_t* buffer = NULL; 560 size_t buffer_alloc = 0; 561 562 // third and subsequent lines are all individual transfer commands. 563 for (line = strtok_r(NULL, "\n", &linesave); line; 564 line = strtok_r(NULL, "\n", &linesave)) { 565 566 char* style; 567 style = strtok_r(line, " ", &wordsave); 568 569 if (strcmp("move", style) == 0) { 570 RangeSet* tgt; 571 int src_blocks; 572 if (version == 1) { 573 LoadSrcTgtVersion1(wordsave, &tgt, &src_blocks, 574 &buffer, &buffer_alloc, fd); 575 } else if (version == 2) { 576 LoadSrcTgtVersion2(wordsave, &tgt, &src_blocks, 577 &buffer, &buffer_alloc, fd, stash_table); 578 } 579 580 printf(" moving %d blocks\n", src_blocks); 581 582 size_t p = 0; 583 for (i = 0; i < tgt->count; ++i) { 584 check_lseek(fd, (off64_t)tgt->pos[i*2] * BLOCKSIZE, SEEK_SET); 585 size_t sz = (tgt->pos[i*2+1] - tgt->pos[i*2]) * BLOCKSIZE; 586 writeblock(fd, buffer+p, sz); 587 p += sz; 588 } 589 590 blocks_so_far += tgt->size; 591 fprintf(cmd_pipe, "set_progress %.4f\n", (double)blocks_so_far / total_blocks); 592 fflush(cmd_pipe); 593 594 free(tgt); 595 596 } else if (strcmp("stash", style) == 0) { 597 word = strtok_r(NULL, " ", &wordsave); 598 int stash_id = strtol(word, NULL, 0); 599 int src_blocks; 600 size_t stash_alloc = 0; 601 602 // Even though the "stash" style only appears in version 603 // 2, the version 1 source loader happens to do exactly 604 // what we want to read data into the stash_table. 605 LoadSrcTgtVersion1(wordsave, NULL, &src_blocks, 606 stash_table + stash_id, &stash_alloc, fd); 607 608 } else if (strcmp("zero", style) == 0 || 609 (DEBUG_ERASE && strcmp("erase", style) == 0)) { 610 word = strtok_r(NULL, " ", &wordsave); 611 RangeSet* tgt = parse_range(word); 612 613 printf(" zeroing %d blocks\n", tgt->size); 614 615 allocate(BLOCKSIZE, &buffer, &buffer_alloc); 616 memset(buffer, 0, BLOCKSIZE); 617 for (i = 0; i < tgt->count; ++i) { 618 check_lseek(fd, (off64_t)tgt->pos[i*2] * BLOCKSIZE, SEEK_SET); 619 for (j = tgt->pos[i*2]; j < tgt->pos[i*2+1]; ++j) { 620 writeblock(fd, buffer, BLOCKSIZE); 621 } 622 } 623 624 if (style[0] == 'z') { // "zero" but not "erase" 625 blocks_so_far += tgt->size; 626 fprintf(cmd_pipe, "set_progress %.4f\n", (double)blocks_so_far / total_blocks); 627 fflush(cmd_pipe); 628 } 629 630 free(tgt); 631 } else if (strcmp("new", style) == 0) { 632 633 word = strtok_r(NULL, " ", &wordsave); 634 RangeSet* tgt = parse_range(word); 635 636 printf(" writing %d blocks of new data\n", tgt->size); 637 638 RangeSinkState rss; 639 rss.fd = fd; 640 rss.tgt = tgt; 641 rss.p_block = 0; 642 rss.p_remain = (tgt->pos[1] - tgt->pos[0]) * BLOCKSIZE; 643 check_lseek(fd, (off64_t)tgt->pos[0] * BLOCKSIZE, SEEK_SET); 644 645 pthread_mutex_lock(&nti.mu); 646 nti.rss = &rss; 647 pthread_cond_broadcast(&nti.cv); 648 while (nti.rss) { 649 pthread_cond_wait(&nti.cv, &nti.mu); 650 } 651 pthread_mutex_unlock(&nti.mu); 652 653 blocks_so_far += tgt->size; 654 fprintf(cmd_pipe, "set_progress %.4f\n", (double)blocks_so_far / total_blocks); 655 fflush(cmd_pipe); 656 657 free(tgt); 658 659 } else if (strcmp("bsdiff", style) == 0 || 660 strcmp("imgdiff", style) == 0) { 661 word = strtok_r(NULL, " ", &wordsave); 662 size_t patch_offset = strtoul(word, NULL, 0); 663 word = strtok_r(NULL, " ", &wordsave); 664 size_t patch_len = strtoul(word, NULL, 0); 665 666 RangeSet* tgt; 667 int src_blocks; 668 if (version == 1) { 669 LoadSrcTgtVersion1(wordsave, &tgt, &src_blocks, 670 &buffer, &buffer_alloc, fd); 671 } else if (version == 2) { 672 LoadSrcTgtVersion2(wordsave, &tgt, &src_blocks, 673 &buffer, &buffer_alloc, fd, stash_table); 674 } 675 676 printf(" patching %d blocks to %d\n", src_blocks, tgt->size); 677 678 Value patch_value; 679 patch_value.type = VAL_BLOB; 680 patch_value.size = patch_len; 681 patch_value.data = (char*)(patch_start + patch_offset); 682 683 RangeSinkState rss; 684 rss.fd = fd; 685 rss.tgt = tgt; 686 rss.p_block = 0; 687 rss.p_remain = (tgt->pos[1] - tgt->pos[0]) * BLOCKSIZE; 688 check_lseek(fd, (off64_t)tgt->pos[0] * BLOCKSIZE, SEEK_SET); 689 690 int ret; 691 if (style[0] == 'i') { // imgdiff 692 ret = ApplyImagePatch(buffer, src_blocks * BLOCKSIZE, 693 &patch_value, 694 &RangeSinkWrite, &rss, NULL, NULL); 695 } else { 696 ret = ApplyBSDiffPatch(buffer, src_blocks * BLOCKSIZE, 697 &patch_value, 0, 698 &RangeSinkWrite, &rss, NULL); 699 } 700 701 if (ret != 0) { 702 ErrorAbort(state, "patch failed\n"); 703 goto done; 704 } 705 706 // We expect the output of the patcher to fill the tgt ranges exactly. 707 if (rss.p_block != tgt->count || rss.p_remain != 0) { 708 ErrorAbort(state, "range sink underrun?\n"); 709 goto done; 710 } 711 712 blocks_so_far += tgt->size; 713 fprintf(cmd_pipe, "set_progress %.4f\n", (double)blocks_so_far / total_blocks); 714 fflush(cmd_pipe); 715 716 free(tgt); 717 } else if (!DEBUG_ERASE && strcmp("erase", style) == 0) { 718 struct stat st; 719 if (fstat(fd, &st) == 0 && S_ISBLK(st.st_mode)) { 720 word = strtok_r(NULL, " ", &wordsave); 721 RangeSet* tgt = parse_range(word); 722 723 printf(" erasing %d blocks\n", tgt->size); 724 725 for (i = 0; i < tgt->count; ++i) { 726 uint64_t range[2]; 727 // offset in bytes 728 range[0] = tgt->pos[i*2] * (uint64_t)BLOCKSIZE; 729 // len in bytes 730 range[1] = (tgt->pos[i*2+1] - tgt->pos[i*2]) * (uint64_t)BLOCKSIZE; 731 732 if (ioctl(fd, BLKDISCARD, &range) < 0) { 733 ErrorAbort(state, " blkdiscard failed: %s\n", strerror(errno)); 734 goto done; 735 } 736 } 737 738 free(tgt); 739 } else { 740 printf(" ignoring erase (not block device)\n"); 741 } 742 } else { 743 ErrorAbort(state, "unknown transfer style \"%s\"\n", style); 744 goto done; 745 } 746 } 747 748 pthread_join(new_data_thread, NULL); 749 success = true; 750 751 free(buffer); 752 printf("wrote %d blocks; expected %d\n", blocks_so_far, total_blocks); 753 printf("max alloc needed was %zu\n", buffer_alloc); 754 755 done: 756 free(transfer_list); 757 FreeValue(blockdev_filename); 758 FreeValue(transfer_list_value); 759 FreeValue(new_data_fn); 760 FreeValue(patch_data_fn); 761 if (success) { 762 return StringValue(strdup("t")); 763 } else { 764 // NULL will be passed to its caller at Evaluate() and abort the OTA 765 // process. 766 return NULL; 767 } 768 } 769 770 Value* RangeSha1Fn(const char* name, State* state, int argc, Expr* argv[]) { 771 Value* blockdev_filename; 772 Value* ranges; 773 const uint8_t* digest = NULL; 774 if (ReadValueArgs(state, argv, 2, &blockdev_filename, &ranges) < 0) { 775 return NULL; 776 } 777 778 if (blockdev_filename->type != VAL_STRING) { 779 ErrorAbort(state, "blockdev_filename argument to %s must be string", name); 780 goto done; 781 } 782 if (ranges->type != VAL_STRING) { 783 ErrorAbort(state, "ranges argument to %s must be string", name); 784 goto done; 785 } 786 787 int fd = open(blockdev_filename->data, O_RDWR); 788 if (fd < 0) { 789 ErrorAbort(state, "failed to open %s: %s", blockdev_filename->data, strerror(errno)); 790 goto done; 791 } 792 793 RangeSet* rs = parse_range(ranges->data); 794 uint8_t buffer[BLOCKSIZE]; 795 796 SHA_CTX ctx; 797 SHA_init(&ctx); 798 799 int i, j; 800 for (i = 0; i < rs->count; ++i) { 801 check_lseek(fd, (off64_t)rs->pos[i*2] * BLOCKSIZE, SEEK_SET); 802 for (j = rs->pos[i*2]; j < rs->pos[i*2+1]; ++j) { 803 readblock(fd, buffer, BLOCKSIZE); 804 SHA_update(&ctx, buffer, BLOCKSIZE); 805 } 806 } 807 digest = SHA_final(&ctx); 808 close(fd); 809 810 done: 811 FreeValue(blockdev_filename); 812 FreeValue(ranges); 813 if (digest == NULL) { 814 return NULL; 815 } else { 816 return StringValue(PrintSha1(digest)); 817 } 818 } 819 820 void RegisterBlockImageFunctions() { 821 RegisterFunction("block_image_update", BlockImageUpdateFn); 822 RegisterFunction("range_sha1", RangeSha1Fn); 823 } 824