blob: c442ab22abf15f9319eb918c3ecda16e0a42e6a8 [file] [log] [blame]
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001/*
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 1 to interpret 'erase' transfers to mean do a
41// BLKDISCARD ioctl. Set to 0 to interpret erase to mean fill the
42// region with zeroes.
43#define DEBUG_ERASE 0
44
45#ifndef BLKDISCARD
46#define BLKDISCARD _IO(0x12,119)
47#endif
48
49char* PrintSha1(const uint8_t* digest);
50
51typedef struct {
52 int count;
53 int size;
54 int pos[0];
55} RangeSet;
56
57static 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 %d 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
83static 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
96static 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
109static void check_lseek(int fd, off_t offset, int whence) {
110 while (true) {
111 int ret = lseek(fd, offset, whence);
112 if (ret < 0) {
113 if (errno != EINTR) {
114 fprintf(stderr, "lseek failed: %s\n", strerror(errno));
115 exit(1);
116 }
117 } else {
118 break;
119 }
120 }
121}
122
123static 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 %d bytes\n", size);
132 exit(1);
133 }
134 *buffer_alloc = size;
135}
136
137typedef struct {
138 int fd;
139 RangeSet* tgt;
140 int p_block;
141 size_t p_remain;
142} RangeSinkState;
143
144static 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, 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
201typedef 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
211static 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
242static 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// args:
249// - block device (or file) to modify in-place
250// - transfer list (blob)
251// - new data stream (filename within package.zip)
252// - patch stream (filename within package.zip, must be uncompressed)
253
254Value* BlockImageUpdateFn(const char* name, State* state, int argc, Expr* argv[]) {
255 Value* blockdev_filename;
256 Value* transfer_list;
257 Value* new_data_fn;
258 Value* patch_data_fn;
259 bool success = false;
260
261 if (ReadValueArgs(state, argv, 4, &blockdev_filename, &transfer_list,
262 &new_data_fn, &patch_data_fn) < 0) {
263 return NULL;
264 }
265
266 if (blockdev_filename->type != VAL_STRING) {
267 ErrorAbort(state, "blockdev_filename argument to %s must be string", name);
268 goto done;
269 }
270 if (transfer_list->type != VAL_BLOB) {
271 ErrorAbort(state, "transfer_list argument to %s must be blob", name);
272 goto done;
273 }
274 if (new_data_fn->type != VAL_STRING) {
275 ErrorAbort(state, "new_data_fn argument to %s must be string", name);
276 goto done;
277 }
278 if (patch_data_fn->type != VAL_STRING) {
279 ErrorAbort(state, "patch_data_fn argument to %s must be string", name);
280 goto done;
281 }
282
283 UpdaterInfo* ui = (UpdaterInfo*)(state->cookie);
284 FILE* cmd_pipe = ui->cmd_pipe;
285
286 ZipArchive* za = ((UpdaterInfo*)(state->cookie))->package_zip;
287
288 const ZipEntry* patch_entry = mzFindZipEntry(za, patch_data_fn->data);
289 if (patch_entry == NULL) {
290 ErrorAbort(state, "%s(): no file \"%s\" in package", name, patch_data_fn->data);
291 goto done;
292 }
293
294 uint8_t* patch_start = ((UpdaterInfo*)(state->cookie))->package_zip_addr +
295 mzGetZipEntryOffset(patch_entry);
296
297 const ZipEntry* new_entry = mzFindZipEntry(za, new_data_fn->data);
298 if (new_entry == NULL) {
299 ErrorAbort(state, "%s(): no file \"%s\" in package", name, new_data_fn->data);
300 goto done;
301 }
302
303 // The transfer list is a text file containing commands to
304 // transfer data from one place to another on the target
305 // partition. We parse it and execute the commands in order:
306 //
307 // zero [rangeset]
308 // - fill the indicated blocks with zeros
309 //
310 // new [rangeset]
311 // - fill the blocks with data read from the new_data file
312 //
313 // bsdiff patchstart patchlen [src rangeset] [tgt rangeset]
314 // imgdiff patchstart patchlen [src rangeset] [tgt rangeset]
315 // - read the source blocks, apply a patch, write result to
316 // target blocks. bsdiff or imgdiff specifies the type of
317 // patch.
318 //
319 // move [src rangeset] [tgt rangeset]
320 // - copy data from source blocks to target blocks (no patch
321 // needed; rangesets are the same size)
322 //
323 // erase [rangeset]
324 // - mark the given blocks as empty
325 //
326 // The creator of the transfer list will guarantee that no block
327 // is read (ie, used as the source for a patch or move) after it
328 // has been written.
329 //
330 // Within one command the source and target ranges may overlap so
331 // in general we need to read the entire source into memory before
332 // writing anything to the target blocks.
333 //
334 // All the patch data is concatenated into one patch_data file in
335 // the update package. It must be stored uncompressed because we
336 // memory-map it in directly from the archive. (Since patches are
337 // already compressed, we lose very little by not compressing
338 // their concatenation.)
339
340 pthread_t new_data_thread;
341 NewThreadInfo nti;
342 nti.za = za;
343 nti.entry = new_entry;
344 nti.rss = NULL;
345 pthread_mutex_init(&nti.mu, NULL);
346 pthread_cond_init(&nti.cv, NULL);
347
348 pthread_attr_t attr;
349 pthread_attr_init(&attr);
350 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
351 pthread_create(&new_data_thread, &attr, unzip_new_data, &nti);
352
353 int i, j;
354
355 char* linesave;
356 char* wordsave;
357
358 int fd = open(blockdev_filename->data, O_RDWR);
359 if (fd < 0) {
360 ErrorAbort(state, "failed to open %s: %s", blockdev_filename->data, strerror(errno));
361 goto done;
362 }
363
364 char* line;
365 char* word;
366
367 line = strtok_r(transfer_list->data, "\n", &linesave);
368
369 // first line in transfer list is the version number; currently
370 // there's only version 1.
371 if (strcmp(line, "1") != 0) {
372 ErrorAbort(state, "unexpected transfer list version [%s]\n", line);
373 goto done;
374 }
375
376 // second line in transfer list is the total number of blocks we
377 // expect to write.
378 line = strtok_r(NULL, "\n", &linesave);
379 int total_blocks = strtol(line, NULL, 0);
380 // shouldn't happen, but avoid divide by zero.
381 if (total_blocks == 0) ++total_blocks;
382 int blocks_so_far = 0;
383
384 uint8_t* buffer = NULL;
385 size_t buffer_alloc = 0;
386
387 // third and subsequent lines are all individual transfer commands.
388 for (line = strtok_r(NULL, "\n", &linesave); line;
389 line = strtok_r(NULL, "\n", &linesave)) {
390 char* style;
391 style = strtok_r(line, " ", &wordsave);
392
393 if (strcmp("move", style) == 0) {
394 word = strtok_r(NULL, " ", &wordsave);
395 RangeSet* src = parse_range(word);
396 word = strtok_r(NULL, " ", &wordsave);
397 RangeSet* tgt = parse_range(word);
398
399 printf(" moving %d blocks\n", src->size);
400
401 allocate(src->size * BLOCKSIZE, &buffer, &buffer_alloc);
402 size_t p = 0;
403 for (i = 0; i < src->count; ++i) {
404 check_lseek(fd, src->pos[i*2] * BLOCKSIZE, SEEK_SET);
405 size_t sz = (src->pos[i*2+1] - src->pos[i*2]) * BLOCKSIZE;
406 readblock(fd, buffer+p, sz);
407 p += sz;
408 }
409
410 p = 0;
411 for (i = 0; i < tgt->count; ++i) {
412 check_lseek(fd, tgt->pos[i*2] * BLOCKSIZE, SEEK_SET);
413 size_t sz = (tgt->pos[i*2+1] - tgt->pos[i*2]) * BLOCKSIZE;
414 writeblock(fd, buffer+p, sz);
415 p += sz;
416 }
417
418 blocks_so_far += tgt->size;
419 fprintf(cmd_pipe, "set_progress %.4f\n", (double)blocks_so_far / total_blocks);
420 fflush(cmd_pipe);
421
422 free(src);
423 free(tgt);
424
425 } else if (strcmp("zero", style) == 0 ||
426 (DEBUG_ERASE && strcmp("erase", style) == 0)) {
427 word = strtok_r(NULL, " ", &wordsave);
428 RangeSet* tgt = parse_range(word);
429
430 printf(" zeroing %d blocks\n", tgt->size);
431
432 allocate(BLOCKSIZE, &buffer, &buffer_alloc);
433 memset(buffer, 0, BLOCKSIZE);
434 for (i = 0; i < tgt->count; ++i) {
435 check_lseek(fd, tgt->pos[i*2] * BLOCKSIZE, SEEK_SET);
436 for (j = tgt->pos[i*2]; j < tgt->pos[i*2+1]; ++j) {
437 writeblock(fd, buffer, BLOCKSIZE);
438 }
439 }
440
441 if (style[0] == 'z') { // "zero" but not "erase"
442 blocks_so_far += tgt->size;
443 fprintf(cmd_pipe, "set_progress %.4f\n", (double)blocks_so_far / total_blocks);
444 fflush(cmd_pipe);
445 }
446
447 free(tgt);
448 } else if (strcmp("new", style) == 0) {
449
450 word = strtok_r(NULL, " ", &wordsave);
451 RangeSet* tgt = parse_range(word);
452
453 printf(" writing %d blocks of new data\n", tgt->size);
454
455 RangeSinkState rss;
456 rss.fd = fd;
457 rss.tgt = tgt;
458 rss.p_block = 0;
459 rss.p_remain = (tgt->pos[1] - tgt->pos[0]) * BLOCKSIZE;
460 check_lseek(fd, tgt->pos[0] * BLOCKSIZE, SEEK_SET);
461
462 pthread_mutex_lock(&nti.mu);
463 nti.rss = &rss;
464 pthread_cond_broadcast(&nti.cv);
465 while (nti.rss) {
466 pthread_cond_wait(&nti.cv, &nti.mu);
467 }
468 pthread_mutex_unlock(&nti.mu);
469
470 blocks_so_far += tgt->size;
471 fprintf(cmd_pipe, "set_progress %.4f\n", (double)blocks_so_far / total_blocks);
472 fflush(cmd_pipe);
473
474 free(tgt);
475
476 } else if (strcmp("bsdiff", style) == 0 ||
477 strcmp("imgdiff", style) == 0) {
478 word = strtok_r(NULL, " ", &wordsave);
479 size_t patch_offset = strtoul(word, NULL, 0);
480 word = strtok_r(NULL, " ", &wordsave);
481 size_t patch_len = strtoul(word, NULL, 0);
482
483 word = strtok_r(NULL, " ", &wordsave);
484 RangeSet* src = parse_range(word);
485 word = strtok_r(NULL, " ", &wordsave);
486 RangeSet* tgt = parse_range(word);
487
488 printf(" patching %d blocks to %d\n", src->size, tgt->size);
489
490 // Read the source into memory.
491 allocate(src->size * BLOCKSIZE, &buffer, &buffer_alloc);
492 size_t p = 0;
493 for (i = 0; i < src->count; ++i) {
494 check_lseek(fd, src->pos[i*2] * BLOCKSIZE, SEEK_SET);
495 size_t sz = (src->pos[i*2+1] - src->pos[i*2]) * BLOCKSIZE;
496 readblock(fd, buffer+p, sz);
497 p += sz;
498 }
499
500 Value patch_value;
501 patch_value.type = VAL_BLOB;
502 patch_value.size = patch_len;
503 patch_value.data = (char*)(patch_start + patch_offset);
504
505 RangeSinkState rss;
506 rss.fd = fd;
507 rss.tgt = tgt;
508 rss.p_block = 0;
509 rss.p_remain = (tgt->pos[1] - tgt->pos[0]) * BLOCKSIZE;
510 check_lseek(fd, tgt->pos[0] * BLOCKSIZE, SEEK_SET);
511
512 if (style[0] == 'i') { // imgdiff
513 ApplyImagePatch(buffer, src->size * BLOCKSIZE,
514 &patch_value,
515 &RangeSinkWrite, &rss, NULL, NULL);
516 } else {
517 ApplyBSDiffPatch(buffer, src->size * BLOCKSIZE,
518 &patch_value, 0,
519 &RangeSinkWrite, &rss, NULL);
520 }
521
522 // We expect the output of the patcher to fill the tgt ranges exactly.
523 if (rss.p_block != tgt->count || rss.p_remain != 0) {
524 fprintf(stderr, "range sink underrun?\n");
525 }
526
527 blocks_so_far += tgt->size;
528 fprintf(cmd_pipe, "set_progress %.4f\n", (double)blocks_so_far / total_blocks);
529 fflush(cmd_pipe);
530
531 free(src);
532 free(tgt);
533 } else if (!DEBUG_ERASE && strcmp("erase", style) == 0) {
534 struct stat st;
535 if (fstat(fd, &st) == 0 && S_ISBLK(st.st_mode)) {
536 word = strtok_r(NULL, " ", &wordsave);
537 RangeSet* tgt = parse_range(word);
538
539 printf(" erasing %d blocks\n", tgt->size);
540
541 for (i = 0; i < tgt->count; ++i) {
542 uint64_t range[2];
543 // offset in bytes
544 range[0] = tgt->pos[i*2] * BLOCKSIZE;
545 // len in bytes
546 range[1] = (tgt->pos[i*2+1] - tgt->pos[i*2]) * BLOCKSIZE;
547
548 if (ioctl(fd, BLKDISCARD, &range) < 0) {
549 printf(" blkdiscard failed: %s\n", strerror(errno));
550 }
551 }
552
553 free(tgt);
554 } else {
555 printf(" ignoring erase (not block device)\n");
556 }
557 } else {
558 fprintf(stderr, "unknown transfer style \"%s\"\n", style);
559 exit(1);
560 }
561 }
562
563 pthread_join(new_data_thread, NULL);
564 success = true;
565
566 free(buffer);
567 printf("wrote %d blocks; expected %d\n", blocks_so_far, total_blocks);
568 printf("max alloc needed was %zu\n", buffer_alloc);
569
570 done:
571 FreeValue(blockdev_filename);
572 FreeValue(transfer_list);
573 FreeValue(new_data_fn);
574 FreeValue(patch_data_fn);
575 return StringValue(success ? strdup("t") : strdup(""));
576}
577
578Value* RangeSha1Fn(const char* name, State* state, int argc, Expr* argv[]) {
579 Value* blockdev_filename;
580 Value* ranges;
581 const uint8_t* digest = NULL;
582 if (ReadValueArgs(state, argv, 2, &blockdev_filename, &ranges) < 0) {
583 return NULL;
584 }
585
586 if (blockdev_filename->type != VAL_STRING) {
587 ErrorAbort(state, "blockdev_filename argument to %s must be string", name);
588 goto done;
589 }
590 if (ranges->type != VAL_STRING) {
591 ErrorAbort(state, "ranges argument to %s must be string", name);
592 goto done;
593 }
594
595 int fd = open(blockdev_filename->data, O_RDWR);
596 if (fd < 0) {
597 ErrorAbort(state, "failed to open %s: %s", blockdev_filename->data, strerror(errno));
598 goto done;
599 }
600
601 RangeSet* rs = parse_range(ranges->data);
602 uint8_t buffer[BLOCKSIZE];
603
604 SHA_CTX ctx;
605 SHA_init(&ctx);
606
607 int i, j;
608 for (i = 0; i < rs->count; ++i) {
609 check_lseek(fd, rs->pos[i*2] * BLOCKSIZE, SEEK_SET);
610 for (j = rs->pos[i*2]; j < rs->pos[i*2+1]; ++j) {
611 readblock(fd, buffer, BLOCKSIZE);
612 SHA_update(&ctx, buffer, BLOCKSIZE);
613 }
614 }
615 digest = SHA_final(&ctx);
616 close(fd);
617
618 done:
619 FreeValue(blockdev_filename);
620 FreeValue(ranges);
621 if (digest == NULL) {
622 return StringValue(strdup(""));
623 } else {
624 return StringValue(PrintSha1(digest));
625 }
626}
627
628void RegisterBlockImageFunctions() {
629 RegisterFunction("block_image_update", BlockImageUpdateFn);
630 RegisterFunction("range_sha1", RangeSha1Fn);
631}