blob: 5d0f1560c1429e628574ef4b29d0533323356188 [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>
Sami Tolvanen90221202014-12-09 16:39:47 +000019#include <dirent.h>
Doug Zongkerbc7ffed2014-08-15 14:31:52 -070020#include <fcntl.h>
21#include <inttypes.h>
22#include <pthread.h>
23#include <stdarg.h>
24#include <stdio.h>
25#include <stdlib.h>
26#include <string.h>
Sami Tolvanen90221202014-12-09 16:39:47 +000027#include <sys/stat.h>
Doug Zongkerbc7ffed2014-08-15 14:31:52 -070028#include <sys/types.h>
29#include <sys/wait.h>
30#include <sys/ioctl.h>
31#include <time.h>
32#include <unistd.h>
33
34#include "applypatch/applypatch.h"
35#include "edify/expr.h"
36#include "mincrypt/sha.h"
Sami Tolvanen90221202014-12-09 16:39:47 +000037#include "minzip/Hash.h"
Doug Zongkerbc7ffed2014-08-15 14:31:52 -070038#include "updater.h"
39
40#define BLOCKSIZE 4096
41
Doug Zongkerf7bb09d2014-09-04 08:10:32 -070042// Set this to 0 to interpret 'erase' transfers to mean do a
43// BLKDISCARD ioctl (the normal behavior). Set to 1 to interpret
44// erase to mean fill the region with zeroes.
Doug Zongkerbc7ffed2014-08-15 14:31:52 -070045#define DEBUG_ERASE 0
46
47#ifndef BLKDISCARD
48#define BLKDISCARD _IO(0x12,119)
49#endif
50
Sami Tolvanen90221202014-12-09 16:39:47 +000051#define STASH_DIRECTORY_BASE "/cache/recovery"
52#define STASH_DIRECTORY_MODE 0700
53#define STASH_FILE_MODE 0600
54
Doug Zongkerbc7ffed2014-08-15 14:31:52 -070055char* PrintSha1(const uint8_t* digest);
56
57typedef struct {
58 int count;
59 int size;
60 int pos[0];
61} RangeSet;
62
Sami Tolvanenf2bac042015-05-12 12:48:46 +010063#define RANGESET_MAX_POINTS \
64 ((int)((INT_MAX / sizeof(int)) - sizeof(RangeSet)))
65
Doug Zongkerbc7ffed2014-08-15 14:31:52 -070066static RangeSet* parse_range(char* text) {
67 char* save;
Sami Tolvanenf2bac042015-05-12 12:48:46 +010068 char* token;
69 int i, num;
70 long int val;
71 RangeSet* out = NULL;
72 size_t bufsize;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -070073
Sami Tolvanenf2bac042015-05-12 12:48:46 +010074 if (!text) {
75 goto err;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -070076 }
Sami Tolvanenf2bac042015-05-12 12:48:46 +010077
78 token = strtok_r(text, ",", &save);
79
80 if (!token) {
81 goto err;
82 }
83
84 val = strtol(token, NULL, 0);
85
86 if (val < 2 || val > RANGESET_MAX_POINTS) {
87 goto err;
88 } else if (val % 2) {
89 goto err; // must be even
90 }
91
92 num = (int) val;
93 bufsize = sizeof(RangeSet) + num * sizeof(int);
94
95 out = malloc(bufsize);
96
97 if (!out) {
98 fprintf(stderr, "failed to allocate range of %zu bytes\n", bufsize);
99 goto err;
100 }
101
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700102 out->count = num / 2;
103 out->size = 0;
Sami Tolvanenf2bac042015-05-12 12:48:46 +0100104
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700105 for (i = 0; i < num; ++i) {
Sami Tolvanenf2bac042015-05-12 12:48:46 +0100106 token = strtok_r(NULL, ",", &save);
107
108 if (!token) {
109 goto err;
110 }
111
112 val = strtol(token, NULL, 0);
113
114 if (val < 0 || val > INT_MAX) {
115 goto err;
116 }
117
118 out->pos[i] = (int) val;
119
120 if (i % 2) {
121 if (out->pos[i - 1] >= out->pos[i]) {
122 goto err; // empty or negative range
123 }
124
125 if (out->size > INT_MAX - out->pos[i]) {
126 goto err; // overflow
127 }
128
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700129 out->size += out->pos[i];
130 } else {
Sami Tolvanenf2bac042015-05-12 12:48:46 +0100131 if (out->size < 0) {
132 goto err;
133 }
134
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700135 out->size -= out->pos[i];
136 }
137 }
138
Sami Tolvanenf2bac042015-05-12 12:48:46 +0100139 if (out->size <= 0) {
140 goto err;
141 }
142
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700143 return out;
Sami Tolvanenf2bac042015-05-12 12:48:46 +0100144
145err:
146 fprintf(stderr, "failed to parse range '%s'\n", text ? text : "NULL");
147 exit(1);
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700148}
149
Sami Tolvanen90221202014-12-09 16:39:47 +0000150static int range_overlaps(RangeSet* r1, RangeSet* r2) {
151 int i, j, r1_0, r1_1, r2_0, r2_1;
152
153 if (!r1 || !r2) {
154 return 0;
155 }
156
157 for (i = 0; i < r1->count; ++i) {
158 r1_0 = r1->pos[i * 2];
159 r1_1 = r1->pos[i * 2 + 1];
160
161 for (j = 0; j < r2->count; ++j) {
162 r2_0 = r2->pos[j * 2];
163 r2_1 = r2->pos[j * 2 + 1];
164
165 if (!(r2_0 > r1_1 || r1_0 > r2_1)) {
166 return 1;
167 }
168 }
169 }
170
171 return 0;
172}
173
174static int read_all(int fd, uint8_t* data, size_t size) {
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700175 size_t so_far = 0;
176 while (so_far < size) {
Elliott Hughes7bad7c42015-04-28 17:24:24 -0700177 ssize_t r = TEMP_FAILURE_RETRY(read(fd, data+so_far, size-so_far));
178 if (r == -1) {
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700179 fprintf(stderr, "read failed: %s\n", strerror(errno));
Sami Tolvanen90221202014-12-09 16:39:47 +0000180 return -1;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700181 }
Elliott Hughes7bad7c42015-04-28 17:24:24 -0700182 so_far += r;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700183 }
Sami Tolvanen90221202014-12-09 16:39:47 +0000184 return 0;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700185}
186
Sami Tolvanen90221202014-12-09 16:39:47 +0000187static int write_all(int fd, const uint8_t* data, size_t size) {
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700188 size_t written = 0;
189 while (written < size) {
Elliott Hughes7bad7c42015-04-28 17:24:24 -0700190 ssize_t w = TEMP_FAILURE_RETRY(write(fd, data+written, size-written));
191 if (w == -1) {
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700192 fprintf(stderr, "write failed: %s\n", strerror(errno));
Sami Tolvanen90221202014-12-09 16:39:47 +0000193 return -1;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700194 }
Elliott Hughes7bad7c42015-04-28 17:24:24 -0700195 written += w;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700196 }
Sami Tolvanen90221202014-12-09 16:39:47 +0000197
198 if (fsync(fd) == -1) {
199 fprintf(stderr, "fsync failed: %s\n", strerror(errno));
200 return -1;
201 }
202
203 return 0;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700204}
205
Elliott Hughes7bad7c42015-04-28 17:24:24 -0700206static bool check_lseek(int fd, off64_t offset, int whence) {
207 off64_t rc = TEMP_FAILURE_RETRY(lseek64(fd, offset, whence));
208 if (rc == -1) {
209 fprintf(stderr, "lseek64 failed: %s\n", strerror(errno));
210 return false;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700211 }
Elliott Hughes7bad7c42015-04-28 17:24:24 -0700212 return true;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700213}
214
215static void allocate(size_t size, uint8_t** buffer, size_t* buffer_alloc) {
216 // if the buffer's big enough, reuse it.
217 if (size <= *buffer_alloc) return;
218
219 free(*buffer);
220
221 *buffer = (uint8_t*) malloc(size);
222 if (*buffer == NULL) {
Doug Zongker1d5d6092014-08-21 10:47:24 -0700223 fprintf(stderr, "failed to allocate %zu bytes\n", size);
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700224 exit(1);
225 }
226 *buffer_alloc = size;
227}
228
229typedef struct {
230 int fd;
231 RangeSet* tgt;
232 int p_block;
233 size_t p_remain;
234} RangeSinkState;
235
236static ssize_t RangeSinkWrite(const uint8_t* data, ssize_t size, void* token) {
237 RangeSinkState* rss = (RangeSinkState*) token;
238
239 if (rss->p_remain <= 0) {
240 fprintf(stderr, "range sink write overrun");
Sami Tolvanen90221202014-12-09 16:39:47 +0000241 return 0;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700242 }
243
244 ssize_t written = 0;
245 while (size > 0) {
246 size_t write_now = size;
Sami Tolvanen90221202014-12-09 16:39:47 +0000247
248 if (rss->p_remain < write_now) {
249 write_now = rss->p_remain;
250 }
251
252 if (write_all(rss->fd, data, write_now) == -1) {
253 break;
254 }
255
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700256 data += write_now;
257 size -= write_now;
258
259 rss->p_remain -= write_now;
260 written += write_now;
261
262 if (rss->p_remain == 0) {
263 // move to the next block
264 ++rss->p_block;
265 if (rss->p_block < rss->tgt->count) {
Sami Tolvanen90221202014-12-09 16:39:47 +0000266 rss->p_remain = (rss->tgt->pos[rss->p_block * 2 + 1] -
267 rss->tgt->pos[rss->p_block * 2]) * BLOCKSIZE;
268
Elliott Hughes7bad7c42015-04-28 17:24:24 -0700269 if (!check_lseek(rss->fd, (off64_t)rss->tgt->pos[rss->p_block*2] * BLOCKSIZE,
270 SEEK_SET)) {
Sami Tolvanen90221202014-12-09 16:39:47 +0000271 break;
272 }
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700273 } else {
274 // we can't write any more; return how many bytes have
275 // been written so far.
Sami Tolvanen90221202014-12-09 16:39:47 +0000276 break;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700277 }
278 }
279 }
280
281 return written;
282}
283
284// All of the data for all the 'new' transfers is contained in one
285// file in the update package, concatenated together in the order in
286// which transfers.list will need it. We want to stream it out of the
287// archive (it's compressed) without writing it to a temp file, but we
288// can't write each section until it's that transfer's turn to go.
289//
290// To achieve this, we expand the new data from the archive in a
291// background thread, and block that threads 'receive uncompressed
292// data' function until the main thread has reached a point where we
293// want some new data to be written. We signal the background thread
294// with the destination for the data and block the main thread,
295// waiting for the background thread to complete writing that section.
296// Then it signals the main thread to wake up and goes back to
297// blocking waiting for a transfer.
298//
299// NewThreadInfo is the struct used to pass information back and forth
300// between the two threads. When the main thread wants some data
301// written, it sets rss to the destination location and signals the
302// condition. When the background thread is done writing, it clears
303// rss and signals the condition again.
304
305typedef struct {
306 ZipArchive* za;
307 const ZipEntry* entry;
308
309 RangeSinkState* rss;
310
311 pthread_mutex_t mu;
312 pthread_cond_t cv;
313} NewThreadInfo;
314
315static bool receive_new_data(const unsigned char* data, int size, void* cookie) {
316 NewThreadInfo* nti = (NewThreadInfo*) cookie;
317
318 while (size > 0) {
319 // Wait for nti->rss to be non-NULL, indicating some of this
320 // data is wanted.
321 pthread_mutex_lock(&nti->mu);
322 while (nti->rss == NULL) {
323 pthread_cond_wait(&nti->cv, &nti->mu);
324 }
325 pthread_mutex_unlock(&nti->mu);
326
327 // At this point nti->rss is set, and we own it. The main
328 // thread is waiting for it to disappear from nti.
329 ssize_t written = RangeSinkWrite(data, size, nti->rss);
330 data += written;
331 size -= written;
332
333 if (nti->rss->p_block == nti->rss->tgt->count) {
334 // we have written all the bytes desired by this rss.
335
336 pthread_mutex_lock(&nti->mu);
337 nti->rss = NULL;
338 pthread_cond_broadcast(&nti->cv);
339 pthread_mutex_unlock(&nti->mu);
340 }
341 }
342
343 return true;
344}
345
346static void* unzip_new_data(void* cookie) {
347 NewThreadInfo* nti = (NewThreadInfo*) cookie;
348 mzProcessZipEntryContents(nti->za, nti->entry, receive_new_data, nti);
349 return NULL;
350}
351
Sami Tolvanen90221202014-12-09 16:39:47 +0000352static int ReadBlocks(RangeSet* src, uint8_t* buffer, int fd) {
353 int i;
354 size_t p = 0;
355 size_t size;
356
357 if (!src || !buffer) {
358 return -1;
359 }
360
361 for (i = 0; i < src->count; ++i) {
Elliott Hughes7bad7c42015-04-28 17:24:24 -0700362 if (!check_lseek(fd, (off64_t) src->pos[i * 2] * BLOCKSIZE, SEEK_SET)) {
Sami Tolvanen90221202014-12-09 16:39:47 +0000363 return -1;
364 }
365
366 size = (src->pos[i * 2 + 1] - src->pos[i * 2]) * BLOCKSIZE;
367
368 if (read_all(fd, buffer + p, size) == -1) {
369 return -1;
370 }
371
372 p += size;
373 }
374
375 return 0;
376}
377
378static int WriteBlocks(RangeSet* tgt, uint8_t* buffer, int fd) {
379 int i;
380 size_t p = 0;
381 size_t size;
382
383 if (!tgt || !buffer) {
384 return -1;
385 }
386
387 for (i = 0; i < tgt->count; ++i) {
Elliott Hughes7bad7c42015-04-28 17:24:24 -0700388 if (!check_lseek(fd, (off64_t) tgt->pos[i * 2] * BLOCKSIZE, SEEK_SET)) {
Sami Tolvanen90221202014-12-09 16:39:47 +0000389 return -1;
390 }
391
392 size = (tgt->pos[i * 2 + 1] - tgt->pos[i * 2]) * BLOCKSIZE;
393
394 if (write_all(fd, buffer + p, size) == -1) {
395 return -1;
396 }
397
398 p += size;
399 }
400
401 return 0;
402}
403
Doug Zongker52ae67d2014-09-08 12:22:09 -0700404// Do a source/target load for move/bsdiff/imgdiff in version 1.
405// 'wordsave' is the save_ptr of a strtok_r()-in-progress. We expect
406// to parse the remainder of the string as:
407//
408// <src_range> <tgt_range>
409//
410// The source range is loaded into the provided buffer, reallocating
411// it to make it larger if necessary. The target ranges are returned
412// in *tgt, if tgt is non-NULL.
413
Sami Tolvanen90221202014-12-09 16:39:47 +0000414static int LoadSrcTgtVersion1(char** wordsave, RangeSet** tgt, int* src_blocks,
Doug Zongker52ae67d2014-09-08 12:22:09 -0700415 uint8_t** buffer, size_t* buffer_alloc, int fd) {
416 char* word;
Sami Tolvanen90221202014-12-09 16:39:47 +0000417 int rc;
Doug Zongker52ae67d2014-09-08 12:22:09 -0700418
Sami Tolvanen90221202014-12-09 16:39:47 +0000419 word = strtok_r(NULL, " ", wordsave);
Doug Zongker52ae67d2014-09-08 12:22:09 -0700420 RangeSet* src = parse_range(word);
421
422 if (tgt != NULL) {
Sami Tolvanen90221202014-12-09 16:39:47 +0000423 word = strtok_r(NULL, " ", wordsave);
Doug Zongker52ae67d2014-09-08 12:22:09 -0700424 *tgt = parse_range(word);
425 }
426
427 allocate(src->size * BLOCKSIZE, buffer, buffer_alloc);
Sami Tolvanen90221202014-12-09 16:39:47 +0000428 rc = ReadBlocks(src, *buffer, fd);
429 *src_blocks = src->size;
430
431 free(src);
432 return rc;
433}
434
435static int VerifyBlocks(const char *expected, const uint8_t *buffer,
436 size_t blocks, int printerror) {
437 char* hexdigest = NULL;
438 int rc = -1;
439 uint8_t digest[SHA_DIGEST_SIZE];
440
441 if (!expected || !buffer) {
442 return rc;
Doug Zongker52ae67d2014-09-08 12:22:09 -0700443 }
444
Sami Tolvanen90221202014-12-09 16:39:47 +0000445 SHA_hash(buffer, blocks * BLOCKSIZE, digest);
446 hexdigest = PrintSha1(digest);
447
448 if (hexdigest != NULL) {
449 rc = strcmp(expected, hexdigest);
450
451 if (rc != 0 && printerror) {
452 fprintf(stderr, "failed to verify blocks (expected %s, read %s)\n",
453 expected, hexdigest);
454 }
455
456 free(hexdigest);
457 }
458
459 return rc;
460}
461
462static char* GetStashFileName(const char* base, const char* id, const char* postfix) {
463 char* fn;
464 int len;
465 int res;
466
467 if (base == NULL) {
468 return NULL;
469 }
470
471 if (id == NULL) {
472 id = "";
473 }
474
475 if (postfix == NULL) {
476 postfix = "";
477 }
478
479 len = strlen(STASH_DIRECTORY_BASE) + 1 + strlen(base) + 1 + strlen(id) + strlen(postfix) + 1;
480 fn = malloc(len);
481
482 if (fn == NULL) {
483 fprintf(stderr, "failed to malloc %d bytes for fn\n", len);
484 return NULL;
485 }
486
487 res = snprintf(fn, len, STASH_DIRECTORY_BASE "/%s/%s%s", base, id, postfix);
488
489 if (res < 0 || res >= len) {
490 fprintf(stderr, "failed to format file name (return value %d)\n", res);
491 free(fn);
492 return NULL;
493 }
494
495 return fn;
496}
497
498typedef void (*StashCallback)(const char*, void*);
499
500// Does a best effort enumeration of stash files. Ignores possible non-file
501// items in the stash directory and continues despite of errors. Calls the
502// 'callback' function for each file and passes 'data' to the function as a
503// parameter.
504
505static void EnumerateStash(const char* dirname, StashCallback callback, void* data) {
506 char* fn;
507 DIR* directory;
508 int len;
509 int res;
510 struct dirent* item;
511
512 if (dirname == NULL || callback == NULL) {
513 return;
514 }
515
516 directory = opendir(dirname);
517
518 if (directory == NULL) {
519 if (errno != ENOENT) {
Elliott Hughes1fdd4522015-03-23 13:33:57 -0700520 fprintf(stderr, "opendir \"%s\" failed: %s\n", dirname, strerror(errno));
Sami Tolvanen90221202014-12-09 16:39:47 +0000521 }
522 return;
523 }
524
525 while ((item = readdir(directory)) != NULL) {
526 if (item->d_type != DT_REG) {
527 continue;
528 }
529
530 len = strlen(dirname) + 1 + strlen(item->d_name) + 1;
531 fn = malloc(len);
532
533 if (fn == NULL) {
534 fprintf(stderr, "failed to malloc %d bytes for fn\n", len);
535 continue;
536 }
537
538 res = snprintf(fn, len, "%s/%s", dirname, item->d_name);
539
540 if (res < 0 || res >= len) {
541 fprintf(stderr, "failed to format file name (return value %d)\n", res);
542 free(fn);
543 continue;
544 }
545
546 callback(fn, data);
547 free(fn);
548 }
549
550 if (closedir(directory) == -1) {
Elliott Hughes1fdd4522015-03-23 13:33:57 -0700551 fprintf(stderr, "closedir \"%s\" failed: %s\n", dirname, strerror(errno));
Sami Tolvanen90221202014-12-09 16:39:47 +0000552 }
553}
554
555static void UpdateFileSize(const char* fn, void* data) {
556 int* size = (int*) data;
557 struct stat st;
558
559 if (!fn || !data) {
560 return;
561 }
562
563 if (stat(fn, &st) == -1) {
Elliott Hughes1fdd4522015-03-23 13:33:57 -0700564 fprintf(stderr, "stat \"%s\" failed: %s\n", fn, strerror(errno));
Sami Tolvanen90221202014-12-09 16:39:47 +0000565 return;
566 }
567
568 *size += st.st_size;
569}
570
571// Deletes the stash directory and all files in it. Assumes that it only
572// contains files. There is nothing we can do about unlikely, but possible
573// errors, so they are merely logged.
574
575static void DeleteFile(const char* fn, void* data) {
576 if (fn) {
577 fprintf(stderr, "deleting %s\n", fn);
578
579 if (unlink(fn) == -1 && errno != ENOENT) {
Elliott Hughes1fdd4522015-03-23 13:33:57 -0700580 fprintf(stderr, "unlink \"%s\" failed: %s\n", fn, strerror(errno));
Sami Tolvanen90221202014-12-09 16:39:47 +0000581 }
582 }
583}
584
585static void DeletePartial(const char* fn, void* data) {
586 if (fn && strstr(fn, ".partial") != NULL) {
587 DeleteFile(fn, data);
588 }
589}
590
591static void DeleteStash(const char* base) {
592 char* dirname;
593
594 if (base == NULL) {
595 return;
596 }
597
598 dirname = GetStashFileName(base, NULL, NULL);
599
600 if (dirname == NULL) {
601 return;
602 }
603
604 fprintf(stderr, "deleting stash %s\n", base);
605 EnumerateStash(dirname, DeleteFile, NULL);
606
607 if (rmdir(dirname) == -1) {
608 if (errno != ENOENT && errno != ENOTDIR) {
Elliott Hughes1fdd4522015-03-23 13:33:57 -0700609 fprintf(stderr, "rmdir \"%s\" failed: %s\n", dirname, strerror(errno));
Sami Tolvanen90221202014-12-09 16:39:47 +0000610 }
611 }
612
613 free(dirname);
614}
615
616static int LoadStash(const char* base, const char* id, int verify, int* blocks, uint8_t** buffer,
617 size_t* buffer_alloc, int printnoent) {
618 char *fn = NULL;
619 int blockcount = 0;
620 int fd = -1;
621 int rc = -1;
622 int res;
623 struct stat st;
624
625 if (!base || !id || !buffer || !buffer_alloc) {
626 goto lsout;
627 }
628
629 if (!blocks) {
630 blocks = &blockcount;
631 }
632
633 fn = GetStashFileName(base, id, NULL);
634
635 if (fn == NULL) {
636 goto lsout;
637 }
638
639 res = stat(fn, &st);
640
641 if (res == -1) {
642 if (errno != ENOENT || printnoent) {
Elliott Hughes1fdd4522015-03-23 13:33:57 -0700643 fprintf(stderr, "stat \"%s\" failed: %s\n", fn, strerror(errno));
Sami Tolvanen90221202014-12-09 16:39:47 +0000644 }
645 goto lsout;
646 }
647
648 fprintf(stderr, " loading %s\n", fn);
649
650 if ((st.st_size % BLOCKSIZE) != 0) {
651 fprintf(stderr, "%s size %zd not multiple of block size %d", fn, st.st_size, BLOCKSIZE);
652 goto lsout;
653 }
654
655 fd = TEMP_FAILURE_RETRY(open(fn, O_RDONLY));
656
657 if (fd == -1) {
Elliott Hughes1fdd4522015-03-23 13:33:57 -0700658 fprintf(stderr, "open \"%s\" failed: %s\n", fn, strerror(errno));
Sami Tolvanen90221202014-12-09 16:39:47 +0000659 goto lsout;
660 }
661
662 allocate(st.st_size, buffer, buffer_alloc);
663
664 if (read_all(fd, *buffer, st.st_size) == -1) {
665 goto lsout;
666 }
667
668 *blocks = st.st_size / BLOCKSIZE;
669
670 if (verify && VerifyBlocks(id, *buffer, *blocks, 1) != 0) {
671 fprintf(stderr, "unexpected contents in %s\n", fn);
672 DeleteFile(fn, NULL);
673 goto lsout;
674 }
675
676 rc = 0;
677
678lsout:
679 if (fd != -1) {
Elliott Hughesb3ac6762015-05-28 23:06:17 -0700680 close(fd);
Sami Tolvanen90221202014-12-09 16:39:47 +0000681 }
682
683 if (fn) {
684 free(fn);
685 }
686
687 return rc;
688}
689
690static int WriteStash(const char* base, const char* id, int blocks, uint8_t* buffer,
Sami Tolvanen43b748f2015-04-17 12:50:31 +0100691 int checkspace, int *exists) {
Sami Tolvanen90221202014-12-09 16:39:47 +0000692 char *fn = NULL;
693 char *cn = NULL;
694 int fd = -1;
695 int rc = -1;
696 int res;
Sami Tolvanen43b748f2015-04-17 12:50:31 +0100697 struct stat st;
Sami Tolvanen90221202014-12-09 16:39:47 +0000698
699 if (base == NULL || buffer == NULL) {
700 goto wsout;
701 }
702
703 if (checkspace && CacheSizeCheck(blocks * BLOCKSIZE) != 0) {
704 fprintf(stderr, "not enough space to write stash\n");
705 goto wsout;
706 }
707
708 fn = GetStashFileName(base, id, ".partial");
709 cn = GetStashFileName(base, id, NULL);
710
711 if (fn == NULL || cn == NULL) {
712 goto wsout;
713 }
714
Sami Tolvanen43b748f2015-04-17 12:50:31 +0100715 if (exists) {
716 res = stat(cn, &st);
717
718 if (res == 0) {
719 // The file already exists and since the name is the hash of the contents,
720 // it's safe to assume the contents are identical (accidental hash collisions
721 // are unlikely)
722 fprintf(stderr, " skipping %d existing blocks in %s\n", blocks, cn);
723 *exists = 1;
724 rc = 0;
725 goto wsout;
726 }
727
728 *exists = 0;
729 }
730
Sami Tolvanen90221202014-12-09 16:39:47 +0000731 fprintf(stderr, " writing %d blocks to %s\n", blocks, cn);
732
733 fd = TEMP_FAILURE_RETRY(open(fn, O_WRONLY | O_CREAT | O_TRUNC | O_SYNC, STASH_FILE_MODE));
734
735 if (fd == -1) {
Elliott Hughes1fdd4522015-03-23 13:33:57 -0700736 fprintf(stderr, "failed to create \"%s\": %s\n", fn, strerror(errno));
Sami Tolvanen90221202014-12-09 16:39:47 +0000737 goto wsout;
738 }
739
740 if (write_all(fd, buffer, blocks * BLOCKSIZE) == -1) {
741 goto wsout;
742 }
743
744 if (fsync(fd) == -1) {
Elliott Hughes1fdd4522015-03-23 13:33:57 -0700745 fprintf(stderr, "fsync \"%s\" failed: %s\n", fn, strerror(errno));
Sami Tolvanen90221202014-12-09 16:39:47 +0000746 goto wsout;
747 }
748
749 if (rename(fn, cn) == -1) {
Elliott Hughes1fdd4522015-03-23 13:33:57 -0700750 fprintf(stderr, "rename(\"%s\", \"%s\") failed: %s\n", fn, cn, strerror(errno));
Sami Tolvanen90221202014-12-09 16:39:47 +0000751 goto wsout;
752 }
753
754 rc = 0;
755
756wsout:
757 if (fd != -1) {
Elliott Hughesb47afed2015-05-15 16:19:20 -0700758 close(fd);
Sami Tolvanen90221202014-12-09 16:39:47 +0000759 }
760
761 if (fn) {
762 free(fn);
763 }
764
765 if (cn) {
766 free(cn);
767 }
768
769 return rc;
770}
771
772// Creates a directory for storing stash files and checks if the /cache partition
773// hash enough space for the expected amount of blocks we need to store. Returns
774// >0 if we created the directory, zero if it existed already, and <0 of failure.
775
776static int CreateStash(State* state, int maxblocks, const char* blockdev, char** base) {
777 char* dirname = NULL;
778 const uint8_t* digest;
779 int rc = -1;
780 int res;
781 int size = 0;
782 SHA_CTX ctx;
783 struct stat st;
784
785 if (blockdev == NULL || base == NULL) {
786 goto csout;
787 }
788
789 // Stash directory should be different for each partition to avoid conflicts
790 // when updating multiple partitions at the same time, so we use the hash of
791 // the block device name as the base directory
792 SHA_init(&ctx);
793 SHA_update(&ctx, blockdev, strlen(blockdev));
794 digest = SHA_final(&ctx);
795 *base = PrintSha1(digest);
796
797 if (*base == NULL) {
798 goto csout;
799 }
800
801 dirname = GetStashFileName(*base, NULL, NULL);
802
803 if (dirname == NULL) {
804 goto csout;
805 }
806
807 res = stat(dirname, &st);
808
809 if (res == -1 && errno != ENOENT) {
Elliott Hughes1fdd4522015-03-23 13:33:57 -0700810 ErrorAbort(state, "stat \"%s\" failed: %s\n", dirname, strerror(errno));
Sami Tolvanen90221202014-12-09 16:39:47 +0000811 goto csout;
812 } else if (res != 0) {
813 fprintf(stderr, "creating stash %s\n", dirname);
814 res = mkdir(dirname, STASH_DIRECTORY_MODE);
815
816 if (res != 0) {
Elliott Hughes1fdd4522015-03-23 13:33:57 -0700817 ErrorAbort(state, "mkdir \"%s\" failed: %s\n", dirname, strerror(errno));
Sami Tolvanen90221202014-12-09 16:39:47 +0000818 goto csout;
819 }
820
821 if (CacheSizeCheck(maxblocks * BLOCKSIZE) != 0) {
822 ErrorAbort(state, "not enough space for stash\n");
823 goto csout;
824 }
825
826 rc = 1; // Created directory
827 goto csout;
828 }
829
830 fprintf(stderr, "using existing stash %s\n", dirname);
831
832 // If the directory already exists, calculate the space already allocated to
833 // stash files and check if there's enough for all required blocks. Delete any
834 // partially completed stash files first.
835
836 EnumerateStash(dirname, DeletePartial, NULL);
837 EnumerateStash(dirname, UpdateFileSize, &size);
838
839 size = (maxblocks * BLOCKSIZE) - size;
840
841 if (size > 0 && CacheSizeCheck(size) != 0) {
842 ErrorAbort(state, "not enough space for stash (%d more needed)\n", size);
843 goto csout;
844 }
845
846 rc = 0; // Using existing directory
847
848csout:
849 if (dirname) {
850 free(dirname);
851 }
852
853 return rc;
854}
855
856static int SaveStash(const char* base, char** wordsave, uint8_t** buffer, size_t* buffer_alloc,
857 int fd, int usehash, int* isunresumable) {
858 char *id = NULL;
859 int res = -1;
860 int blocks = 0;
861
862 if (!wordsave || !buffer || !buffer_alloc || !isunresumable) {
863 return -1;
864 }
865
866 id = strtok_r(NULL, " ", wordsave);
867
868 if (id == NULL) {
869 fprintf(stderr, "missing id field in stash command\n");
870 return -1;
871 }
872
873 if (usehash && LoadStash(base, id, 1, &blocks, buffer, buffer_alloc, 0) == 0) {
874 // Stash file already exists and has expected contents. Do not
875 // read from source again, as the source may have been already
876 // overwritten during a previous attempt.
877 return 0;
878 }
879
880 if (LoadSrcTgtVersion1(wordsave, NULL, &blocks, buffer, buffer_alloc, fd) == -1) {
881 return -1;
882 }
883
884 if (usehash && VerifyBlocks(id, *buffer, blocks, 1) != 0) {
885 // Source blocks have unexpected contents. If we actually need this
886 // data later, this is an unrecoverable error. However, the command
887 // that uses the data may have already completed previously, so the
888 // possible failure will occur during source block verification.
889 fprintf(stderr, "failed to load source blocks for stash %s\n", id);
890 return 0;
891 }
892
893 fprintf(stderr, "stashing %d blocks to %s\n", blocks, id);
Sami Tolvanen43b748f2015-04-17 12:50:31 +0100894 return WriteStash(base, id, blocks, *buffer, 0, NULL);
Sami Tolvanen90221202014-12-09 16:39:47 +0000895}
896
897static int FreeStash(const char* base, const char* id) {
898 char *fn = NULL;
899
900 if (base == NULL || id == NULL) {
901 return -1;
902 }
903
904 fn = GetStashFileName(base, id, NULL);
905
906 if (fn == NULL) {
907 return -1;
908 }
909
910 DeleteFile(fn, NULL);
911 free(fn);
912
913 return 0;
Doug Zongker52ae67d2014-09-08 12:22:09 -0700914}
915
916static void MoveRange(uint8_t* dest, RangeSet* locs, const uint8_t* source) {
917 // source contains packed data, which we want to move to the
918 // locations given in *locs in the dest buffer. source and dest
919 // may be the same buffer.
920
921 int start = locs->size;
922 int i;
923 for (i = locs->count-1; i >= 0; --i) {
924 int blocks = locs->pos[i*2+1] - locs->pos[i*2];
925 start -= blocks;
926 memmove(dest + (locs->pos[i*2] * BLOCKSIZE), source + (start * BLOCKSIZE),
927 blocks * BLOCKSIZE);
928 }
929}
930
931// Do a source/target load for move/bsdiff/imgdiff in version 2.
932// 'wordsave' is the save_ptr of a strtok_r()-in-progress. We expect
933// to parse the remainder of the string as one of:
934//
935// <tgt_range> <src_block_count> <src_range>
936// (loads data from source image only)
937//
938// <tgt_range> <src_block_count> - <[stash_id:stash_range] ...>
939// (loads data from stashes only)
940//
941// <tgt_range> <src_block_count> <src_range> <src_loc> <[stash_id:stash_range] ...>
942// (loads data from both source image and stashes)
943//
944// On return, buffer is filled with the loaded source data (rearranged
945// and combined with stashed data as necessary). buffer may be
946// reallocated if needed to accommodate the source data. *tgt is the
Sami Tolvanen90221202014-12-09 16:39:47 +0000947// target RangeSet. Any stashes required are loaded using LoadStash.
Doug Zongker52ae67d2014-09-08 12:22:09 -0700948
Sami Tolvanen90221202014-12-09 16:39:47 +0000949static int LoadSrcTgtVersion2(char** wordsave, RangeSet** tgt, int* src_blocks,
Doug Zongker52ae67d2014-09-08 12:22:09 -0700950 uint8_t** buffer, size_t* buffer_alloc, int fd,
Sami Tolvanen90221202014-12-09 16:39:47 +0000951 const char* stashbase, int* overlap) {
Doug Zongker52ae67d2014-09-08 12:22:09 -0700952 char* word;
Sami Tolvanen90221202014-12-09 16:39:47 +0000953 char* colonsave;
954 char* colon;
955 int id;
956 int res;
957 RangeSet* locs;
958 size_t stashalloc = 0;
959 uint8_t* stash = NULL;
Doug Zongker52ae67d2014-09-08 12:22:09 -0700960
961 if (tgt != NULL) {
Sami Tolvanen90221202014-12-09 16:39:47 +0000962 word = strtok_r(NULL, " ", wordsave);
Doug Zongker52ae67d2014-09-08 12:22:09 -0700963 *tgt = parse_range(word);
964 }
965
Sami Tolvanen90221202014-12-09 16:39:47 +0000966 word = strtok_r(NULL, " ", wordsave);
Doug Zongker52ae67d2014-09-08 12:22:09 -0700967 *src_blocks = strtol(word, NULL, 0);
968
969 allocate(*src_blocks * BLOCKSIZE, buffer, buffer_alloc);
970
Sami Tolvanen90221202014-12-09 16:39:47 +0000971 word = strtok_r(NULL, " ", wordsave);
Doug Zongker52ae67d2014-09-08 12:22:09 -0700972 if (word[0] == '-' && word[1] == '\0') {
973 // no source ranges, only stashes
974 } else {
975 RangeSet* src = parse_range(word);
Sami Tolvanen90221202014-12-09 16:39:47 +0000976 res = ReadBlocks(src, *buffer, fd);
Doug Zongker52ae67d2014-09-08 12:22:09 -0700977
Sami Tolvanen90221202014-12-09 16:39:47 +0000978 if (overlap && tgt) {
979 *overlap = range_overlaps(src, *tgt);
Doug Zongker52ae67d2014-09-08 12:22:09 -0700980 }
Sami Tolvanen90221202014-12-09 16:39:47 +0000981
Doug Zongker52ae67d2014-09-08 12:22:09 -0700982 free(src);
983
Sami Tolvanen90221202014-12-09 16:39:47 +0000984 if (res == -1) {
985 return -1;
Doug Zongker52ae67d2014-09-08 12:22:09 -0700986 }
987
Sami Tolvanen90221202014-12-09 16:39:47 +0000988 word = strtok_r(NULL, " ", wordsave);
989 if (word == NULL) {
990 // no stashes, only source range
991 return 0;
992 }
993
994 locs = parse_range(word);
Doug Zongker52ae67d2014-09-08 12:22:09 -0700995 MoveRange(*buffer, locs, *buffer);
Sami Tolvanen90221202014-12-09 16:39:47 +0000996 free(locs);
Doug Zongker52ae67d2014-09-08 12:22:09 -0700997 }
998
Sami Tolvanen90221202014-12-09 16:39:47 +0000999 while ((word = strtok_r(NULL, " ", wordsave)) != NULL) {
Doug Zongker52ae67d2014-09-08 12:22:09 -07001000 // Each word is a an index into the stash table, a colon, and
1001 // then a rangeset describing where in the source block that
1002 // stashed data should go.
Sami Tolvanen90221202014-12-09 16:39:47 +00001003 colonsave = NULL;
1004 colon = strtok_r(word, ":", &colonsave);
1005
1006 res = LoadStash(stashbase, colon, 0, NULL, &stash, &stashalloc, 1);
1007
1008 if (res == -1) {
1009 // These source blocks will fail verification if used later, but we
1010 // will let the caller decide if this is a fatal failure
1011 fprintf(stderr, "failed to load stash %s\n", colon);
1012 continue;
1013 }
1014
Doug Zongker52ae67d2014-09-08 12:22:09 -07001015 colon = strtok_r(NULL, ":", &colonsave);
Sami Tolvanen90221202014-12-09 16:39:47 +00001016 locs = parse_range(colon);
1017
1018 MoveRange(*buffer, locs, stash);
Doug Zongker52ae67d2014-09-08 12:22:09 -07001019 free(locs);
1020 }
Sami Tolvanen90221202014-12-09 16:39:47 +00001021
1022 if (stash) {
1023 free(stash);
1024 }
1025
1026 return 0;
1027}
1028
1029// Parameters for transfer list command functions
1030typedef struct {
1031 char* cmdname;
1032 char* cpos;
1033 char* freestash;
1034 char* stashbase;
1035 int canwrite;
1036 int createdstash;
1037 int fd;
1038 int foundwrites;
1039 int isunresumable;
1040 int version;
1041 int written;
1042 NewThreadInfo nti;
1043 pthread_t thread;
1044 size_t bufsize;
1045 uint8_t* buffer;
1046 uint8_t* patch_start;
1047} CommandParameters;
1048
1049// Do a source/target load for move/bsdiff/imgdiff in version 3.
1050//
1051// Parameters are the same as for LoadSrcTgtVersion2, except for 'onehash', which
1052// tells the function whether to expect separate source and targe block hashes, or
1053// if they are both the same and only one hash should be expected, and
1054// 'isunresumable', which receives a non-zero value if block verification fails in
1055// a way that the update cannot be resumed anymore.
1056//
1057// If the function is unable to load the necessary blocks or their contents don't
1058// match the hashes, the return value is -1 and the command should be aborted.
1059//
1060// If the return value is 1, the command has already been completed according to
1061// the contents of the target blocks, and should not be performed again.
1062//
1063// If the return value is 0, source blocks have expected content and the command
1064// can be performed.
1065
1066static int LoadSrcTgtVersion3(CommandParameters* params, RangeSet** tgt, int* src_blocks,
1067 int onehash, int* overlap) {
1068 char* srchash = NULL;
1069 char* tgthash = NULL;
Sami Tolvanen43b748f2015-04-17 12:50:31 +01001070 int stash_exists = 0;
Sami Tolvanen90221202014-12-09 16:39:47 +00001071 int overlap_blocks = 0;
1072 int rc = -1;
1073 uint8_t* tgtbuffer = NULL;
1074
1075 if (!params|| !tgt || !src_blocks || !overlap) {
1076 goto v3out;
1077 }
1078
1079 srchash = strtok_r(NULL, " ", &params->cpos);
1080
1081 if (srchash == NULL) {
1082 fprintf(stderr, "missing source hash\n");
1083 goto v3out;
1084 }
1085
1086 if (onehash) {
1087 tgthash = srchash;
1088 } else {
1089 tgthash = strtok_r(NULL, " ", &params->cpos);
1090
1091 if (tgthash == NULL) {
1092 fprintf(stderr, "missing target hash\n");
1093 goto v3out;
1094 }
1095 }
1096
1097 if (LoadSrcTgtVersion2(&params->cpos, tgt, src_blocks, &params->buffer, &params->bufsize,
1098 params->fd, params->stashbase, overlap) == -1) {
1099 goto v3out;
1100 }
1101
1102 tgtbuffer = (uint8_t*) malloc((*tgt)->size * BLOCKSIZE);
1103
1104 if (tgtbuffer == NULL) {
1105 fprintf(stderr, "failed to allocate %d bytes\n", (*tgt)->size * BLOCKSIZE);
1106 goto v3out;
1107 }
1108
1109 if (ReadBlocks(*tgt, tgtbuffer, params->fd) == -1) {
1110 goto v3out;
1111 }
1112
1113 if (VerifyBlocks(tgthash, tgtbuffer, (*tgt)->size, 0) == 0) {
1114 // Target blocks already have expected content, command should be skipped
1115 rc = 1;
1116 goto v3out;
1117 }
1118
1119 if (VerifyBlocks(srchash, params->buffer, *src_blocks, 1) == 0) {
1120 // If source and target blocks overlap, stash the source blocks so we can
1121 // resume from possible write errors
1122 if (*overlap) {
1123 fprintf(stderr, "stashing %d overlapping blocks to %s\n", *src_blocks,
1124 srchash);
1125
Sami Tolvanen43b748f2015-04-17 12:50:31 +01001126 if (WriteStash(params->stashbase, srchash, *src_blocks, params->buffer, 1,
1127 &stash_exists) != 0) {
Sami Tolvanen90221202014-12-09 16:39:47 +00001128 fprintf(stderr, "failed to stash overlapping source blocks\n");
1129 goto v3out;
1130 }
1131
1132 // Can be deleted when the write has completed
Sami Tolvanen43b748f2015-04-17 12:50:31 +01001133 if (!stash_exists) {
1134 params->freestash = srchash;
1135 }
Sami Tolvanen90221202014-12-09 16:39:47 +00001136 }
1137
1138 // Source blocks have expected content, command can proceed
1139 rc = 0;
1140 goto v3out;
1141 }
1142
1143 if (*overlap && LoadStash(params->stashbase, srchash, 1, NULL, &params->buffer,
1144 &params->bufsize, 1) == 0) {
Sami Tolvanen43b748f2015-04-17 12:50:31 +01001145 // Overlapping source blocks were previously stashed, command can proceed.
1146 // We are recovering from an interrupted command, so we don't know if the
1147 // stash can safely be deleted after this command.
Sami Tolvanen90221202014-12-09 16:39:47 +00001148 rc = 0;
1149 goto v3out;
1150 }
1151
1152 // Valid source data not available, update cannot be resumed
1153 fprintf(stderr, "partition has unexpected contents\n");
1154 params->isunresumable = 1;
1155
1156v3out:
1157 if (tgtbuffer) {
1158 free(tgtbuffer);
1159 }
1160
1161 return rc;
1162}
1163
1164static int PerformCommandMove(CommandParameters* params) {
1165 int blocks = 0;
1166 int overlap = 0;
1167 int rc = -1;
1168 int status = 0;
1169 RangeSet* tgt = NULL;
1170
1171 if (!params) {
1172 goto pcmout;
1173 }
1174
1175 if (params->version == 1) {
1176 status = LoadSrcTgtVersion1(&params->cpos, &tgt, &blocks, &params->buffer,
1177 &params->bufsize, params->fd);
1178 } else if (params->version == 2) {
1179 status = LoadSrcTgtVersion2(&params->cpos, &tgt, &blocks, &params->buffer,
1180 &params->bufsize, params->fd, params->stashbase, NULL);
1181 } else if (params->version >= 3) {
1182 status = LoadSrcTgtVersion3(params, &tgt, &blocks, 1, &overlap);
1183 }
1184
1185 if (status == -1) {
1186 fprintf(stderr, "failed to read blocks for move\n");
1187 goto pcmout;
1188 }
1189
1190 if (status == 0) {
1191 params->foundwrites = 1;
1192 } else if (params->foundwrites) {
1193 fprintf(stderr, "warning: commands executed out of order [%s]\n", params->cmdname);
1194 }
1195
1196 if (params->canwrite) {
1197 if (status == 0) {
1198 fprintf(stderr, " moving %d blocks\n", blocks);
1199
1200 if (WriteBlocks(tgt, params->buffer, params->fd) == -1) {
1201 goto pcmout;
1202 }
1203 } else {
1204 fprintf(stderr, "skipping %d already moved blocks\n", blocks);
1205 }
1206
1207 }
1208
1209 if (params->freestash) {
1210 FreeStash(params->stashbase, params->freestash);
1211 params->freestash = NULL;
1212 }
1213
1214 params->written += tgt->size;
1215 rc = 0;
1216
1217pcmout:
1218 if (tgt) {
1219 free(tgt);
1220 }
1221
1222 return rc;
1223}
1224
1225static int PerformCommandStash(CommandParameters* params) {
1226 if (!params) {
1227 return -1;
1228 }
1229
1230 return SaveStash(params->stashbase, &params->cpos, &params->buffer, &params->bufsize,
1231 params->fd, (params->version >= 3), &params->isunresumable);
1232}
1233
1234static int PerformCommandFree(CommandParameters* params) {
1235 if (!params) {
1236 return -1;
1237 }
1238
1239 if (params->createdstash || params->canwrite) {
1240 return FreeStash(params->stashbase, params->cpos);
1241 }
1242
1243 return 0;
1244}
1245
1246static int PerformCommandZero(CommandParameters* params) {
1247 char* range = NULL;
1248 int i;
1249 int j;
1250 int rc = -1;
1251 RangeSet* tgt = NULL;
1252
1253 if (!params) {
1254 goto pczout;
1255 }
1256
1257 range = strtok_r(NULL, " ", &params->cpos);
1258
1259 if (range == NULL) {
1260 fprintf(stderr, "missing target blocks for zero\n");
1261 goto pczout;
1262 }
1263
1264 tgt = parse_range(range);
1265
1266 fprintf(stderr, " zeroing %d blocks\n", tgt->size);
1267
1268 allocate(BLOCKSIZE, &params->buffer, &params->bufsize);
1269 memset(params->buffer, 0, BLOCKSIZE);
1270
1271 if (params->canwrite) {
1272 for (i = 0; i < tgt->count; ++i) {
Elliott Hughes7bad7c42015-04-28 17:24:24 -07001273 if (!check_lseek(params->fd, (off64_t) tgt->pos[i * 2] * BLOCKSIZE, SEEK_SET)) {
Sami Tolvanen90221202014-12-09 16:39:47 +00001274 goto pczout;
1275 }
1276
1277 for (j = tgt->pos[i * 2]; j < tgt->pos[i * 2 + 1]; ++j) {
1278 if (write_all(params->fd, params->buffer, BLOCKSIZE) == -1) {
1279 goto pczout;
1280 }
1281 }
1282 }
1283 }
1284
1285 if (params->cmdname[0] == 'z') {
1286 // Update only for the zero command, as the erase command will call
1287 // this if DEBUG_ERASE is defined.
1288 params->written += tgt->size;
1289 }
1290
1291 rc = 0;
1292
1293pczout:
1294 if (tgt) {
1295 free(tgt);
1296 }
1297
1298 return rc;
1299}
1300
1301static int PerformCommandNew(CommandParameters* params) {
1302 char* range = NULL;
1303 int rc = -1;
1304 RangeSet* tgt = NULL;
1305 RangeSinkState rss;
1306
1307 if (!params) {
1308 goto pcnout;
1309 }
1310
1311 range = strtok_r(NULL, " ", &params->cpos);
1312
1313 if (range == NULL) {
1314 goto pcnout;
1315 }
1316
1317 tgt = parse_range(range);
1318
1319 if (params->canwrite) {
1320 fprintf(stderr, " writing %d blocks of new data\n", tgt->size);
1321
1322 rss.fd = params->fd;
1323 rss.tgt = tgt;
1324 rss.p_block = 0;
1325 rss.p_remain = (tgt->pos[1] - tgt->pos[0]) * BLOCKSIZE;
1326
Elliott Hughes7bad7c42015-04-28 17:24:24 -07001327 if (!check_lseek(params->fd, (off64_t) tgt->pos[0] * BLOCKSIZE, SEEK_SET)) {
Sami Tolvanen90221202014-12-09 16:39:47 +00001328 goto pcnout;
1329 }
1330
1331 pthread_mutex_lock(&params->nti.mu);
1332 params->nti.rss = &rss;
1333 pthread_cond_broadcast(&params->nti.cv);
1334
1335 while (params->nti.rss) {
1336 pthread_cond_wait(&params->nti.cv, &params->nti.mu);
1337 }
1338
1339 pthread_mutex_unlock(&params->nti.mu);
1340 }
1341
1342 params->written += tgt->size;
1343 rc = 0;
1344
1345pcnout:
1346 if (tgt) {
1347 free(tgt);
1348 }
1349
1350 return rc;
1351}
1352
1353static int PerformCommandDiff(CommandParameters* params) {
1354 char* logparams = NULL;
1355 char* value = NULL;
1356 int blocks = 0;
1357 int overlap = 0;
1358 int rc = -1;
1359 int status = 0;
1360 RangeSet* tgt = NULL;
1361 RangeSinkState rss;
1362 size_t len = 0;
1363 size_t offset = 0;
1364 Value patch_value;
1365
1366 if (!params) {
1367 goto pcdout;
1368 }
1369
1370 logparams = strdup(params->cpos);
1371 value = strtok_r(NULL, " ", &params->cpos);
1372
1373 if (value == NULL) {
1374 fprintf(stderr, "missing patch offset for %s\n", params->cmdname);
1375 goto pcdout;
1376 }
1377
1378 offset = strtoul(value, NULL, 0);
1379
1380 value = strtok_r(NULL, " ", &params->cpos);
1381
1382 if (value == NULL) {
1383 fprintf(stderr, "missing patch length for %s\n", params->cmdname);
1384 goto pcdout;
1385 }
1386
1387 len = strtoul(value, NULL, 0);
1388
1389 if (params->version == 1) {
1390 status = LoadSrcTgtVersion1(&params->cpos, &tgt, &blocks, &params->buffer,
1391 &params->bufsize, params->fd);
1392 } else if (params->version == 2) {
1393 status = LoadSrcTgtVersion2(&params->cpos, &tgt, &blocks, &params->buffer,
1394 &params->bufsize, params->fd, params->stashbase, NULL);
1395 } else if (params->version >= 3) {
1396 status = LoadSrcTgtVersion3(params, &tgt, &blocks, 0, &overlap);
1397 }
1398
1399 if (status == -1) {
1400 fprintf(stderr, "failed to read blocks for diff\n");
1401 goto pcdout;
1402 }
1403
1404 if (status == 0) {
1405 params->foundwrites = 1;
1406 } else if (params->foundwrites) {
1407 fprintf(stderr, "warning: commands executed out of order [%s]\n", params->cmdname);
1408 }
1409
1410 if (params->canwrite) {
1411 if (status == 0) {
1412 fprintf(stderr, "patching %d blocks to %d\n", blocks, tgt->size);
1413
1414 patch_value.type = VAL_BLOB;
1415 patch_value.size = len;
1416 patch_value.data = (char*) (params->patch_start + offset);
1417
1418 rss.fd = params->fd;
1419 rss.tgt = tgt;
1420 rss.p_block = 0;
1421 rss.p_remain = (tgt->pos[1] - tgt->pos[0]) * BLOCKSIZE;
1422
Elliott Hughes7bad7c42015-04-28 17:24:24 -07001423 if (!check_lseek(params->fd, (off64_t) tgt->pos[0] * BLOCKSIZE, SEEK_SET)) {
Sami Tolvanen90221202014-12-09 16:39:47 +00001424 goto pcdout;
1425 }
1426
1427 if (params->cmdname[0] == 'i') { // imgdiff
1428 ApplyImagePatch(params->buffer, blocks * BLOCKSIZE, &patch_value,
1429 &RangeSinkWrite, &rss, NULL, NULL);
1430 } else {
1431 ApplyBSDiffPatch(params->buffer, blocks * BLOCKSIZE, &patch_value,
1432 0, &RangeSinkWrite, &rss, NULL);
1433 }
1434
1435 // We expect the output of the patcher to fill the tgt ranges exactly.
1436 if (rss.p_block != tgt->count || rss.p_remain != 0) {
1437 fprintf(stderr, "range sink underrun?\n");
1438 }
1439 } else {
1440 fprintf(stderr, "skipping %d blocks already patched to %d [%s]\n",
1441 blocks, tgt->size, logparams);
1442 }
1443 }
1444
1445 if (params->freestash) {
1446 FreeStash(params->stashbase, params->freestash);
1447 params->freestash = NULL;
1448 }
1449
1450 params->written += tgt->size;
1451 rc = 0;
1452
1453pcdout:
1454 if (logparams) {
1455 free(logparams);
1456 }
1457
1458 if (tgt) {
1459 free(tgt);
1460 }
1461
1462 return rc;
1463}
1464
1465static int PerformCommandErase(CommandParameters* params) {
1466 char* range = NULL;
1467 int i;
1468 int rc = -1;
1469 RangeSet* tgt = NULL;
1470 struct stat st;
1471 uint64_t blocks[2];
1472
1473 if (DEBUG_ERASE) {
1474 return PerformCommandZero(params);
1475 }
1476
1477 if (!params) {
1478 goto pceout;
1479 }
1480
1481 if (fstat(params->fd, &st) == -1) {
Elliott Hughes1fdd4522015-03-23 13:33:57 -07001482 fprintf(stderr, "failed to fstat device to erase: %s\n", strerror(errno));
Sami Tolvanen90221202014-12-09 16:39:47 +00001483 goto pceout;
1484 }
1485
1486 if (!S_ISBLK(st.st_mode)) {
1487 fprintf(stderr, "not a block device; skipping erase\n");
Sami Tolvanen90221202014-12-09 16:39:47 +00001488 goto pceout;
1489 }
1490
1491 range = strtok_r(NULL, " ", &params->cpos);
1492
1493 if (range == NULL) {
1494 fprintf(stderr, "missing target blocks for zero\n");
1495 goto pceout;
1496 }
1497
1498 tgt = parse_range(range);
1499
1500 if (params->canwrite) {
1501 fprintf(stderr, " erasing %d blocks\n", tgt->size);
1502
1503 for (i = 0; i < tgt->count; ++i) {
1504 // offset in bytes
1505 blocks[0] = tgt->pos[i * 2] * (uint64_t) BLOCKSIZE;
1506 // length in bytes
1507 blocks[1] = (tgt->pos[i * 2 + 1] - tgt->pos[i * 2]) * (uint64_t) BLOCKSIZE;
1508
1509 if (ioctl(params->fd, BLKDISCARD, &blocks) == -1) {
Elliott Hughes1fdd4522015-03-23 13:33:57 -07001510 fprintf(stderr, "BLKDISCARD ioctl failed: %s\n", strerror(errno));
Sami Tolvanencc2428c2015-04-27 11:24:29 +01001511 goto pceout;
Sami Tolvanen90221202014-12-09 16:39:47 +00001512 }
1513 }
1514 }
1515
1516 rc = 0;
1517
1518pceout:
1519 if (tgt) {
1520 free(tgt);
1521 }
1522
1523 return rc;
1524}
1525
1526// Definitions for transfer list command functions
1527typedef int (*CommandFunction)(CommandParameters*);
1528
1529typedef struct {
1530 const char* name;
1531 CommandFunction f;
1532} Command;
1533
1534// CompareCommands and CompareCommandNames are for the hash table
1535
1536static int CompareCommands(const void* c1, const void* c2) {
1537 return strcmp(((const Command*) c1)->name, ((const Command*) c2)->name);
1538}
1539
1540static int CompareCommandNames(const void* c1, const void* c2) {
1541 return strcmp(((const Command*) c1)->name, (const char*) c2);
1542}
1543
1544// HashString is used to hash command names for the hash table
1545
1546static unsigned int HashString(const char *s) {
1547 unsigned int hash = 0;
1548 if (s) {
1549 while (*s) {
1550 hash = hash * 33 + *s++;
1551 }
1552 }
1553 return hash;
Doug Zongker52ae67d2014-09-08 12:22:09 -07001554}
1555
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001556// args:
1557// - block device (or file) to modify in-place
1558// - transfer list (blob)
1559// - new data stream (filename within package.zip)
1560// - patch stream (filename within package.zip, must be uncompressed)
1561
Sami Tolvanen90221202014-12-09 16:39:47 +00001562static Value* PerformBlockImageUpdate(const char* name, State* state, int argc, Expr* argv[],
1563 const Command* commands, int cmdcount, int dryrun) {
1564
1565 char* line = NULL;
1566 char* linesave = NULL;
1567 char* logcmd = NULL;
Doug Zongker1d5d6092014-08-21 10:47:24 -07001568 char* transfer_list = NULL;
Sami Tolvanen90221202014-12-09 16:39:47 +00001569 CommandParameters params;
1570 const Command* cmd = NULL;
1571 const ZipEntry* new_entry = NULL;
1572 const ZipEntry* patch_entry = NULL;
1573 FILE* cmd_pipe = NULL;
1574 HashTable* cmdht = NULL;
1575 int i;
1576 int res;
1577 int rc = -1;
1578 int stash_max_blocks = 0;
1579 int total_blocks = 0;
1580 pthread_attr_t attr;
1581 unsigned int cmdhash;
1582 UpdaterInfo* ui = NULL;
1583 Value* blockdev_filename = NULL;
1584 Value* new_data_fn = NULL;
1585 Value* patch_data_fn = NULL;
1586 Value* transfer_list_value = NULL;
1587 ZipArchive* za = NULL;
1588
1589 memset(&params, 0, sizeof(params));
1590 params.canwrite = !dryrun;
1591
1592 fprintf(stderr, "performing %s\n", dryrun ? "verification" : "update");
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001593
Doug Zongker1d5d6092014-08-21 10:47:24 -07001594 if (ReadValueArgs(state, argv, 4, &blockdev_filename, &transfer_list_value,
Sami Tolvanen90221202014-12-09 16:39:47 +00001595 &new_data_fn, &patch_data_fn) < 0) {
1596 goto pbiudone;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001597 }
1598
1599 if (blockdev_filename->type != VAL_STRING) {
1600 ErrorAbort(state, "blockdev_filename argument to %s must be string", name);
Sami Tolvanen90221202014-12-09 16:39:47 +00001601 goto pbiudone;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001602 }
Doug Zongker1d5d6092014-08-21 10:47:24 -07001603 if (transfer_list_value->type != VAL_BLOB) {
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001604 ErrorAbort(state, "transfer_list argument to %s must be blob", name);
Sami Tolvanen90221202014-12-09 16:39:47 +00001605 goto pbiudone;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001606 }
1607 if (new_data_fn->type != VAL_STRING) {
1608 ErrorAbort(state, "new_data_fn argument to %s must be string", name);
Sami Tolvanen90221202014-12-09 16:39:47 +00001609 goto pbiudone;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001610 }
1611 if (patch_data_fn->type != VAL_STRING) {
1612 ErrorAbort(state, "patch_data_fn argument to %s must be string", name);
Sami Tolvanen90221202014-12-09 16:39:47 +00001613 goto pbiudone;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001614 }
1615
Sami Tolvanen90221202014-12-09 16:39:47 +00001616 ui = (UpdaterInfo*) state->cookie;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001617
Sami Tolvanen90221202014-12-09 16:39:47 +00001618 if (ui == NULL) {
1619 goto pbiudone;
1620 }
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001621
Sami Tolvanen90221202014-12-09 16:39:47 +00001622 cmd_pipe = ui->cmd_pipe;
1623 za = ui->package_zip;
1624
1625 if (cmd_pipe == NULL || za == NULL) {
1626 goto pbiudone;
1627 }
1628
1629 patch_entry = mzFindZipEntry(za, patch_data_fn->data);
1630
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001631 if (patch_entry == NULL) {
Sami Tolvanen90221202014-12-09 16:39:47 +00001632 fprintf(stderr, "%s(): no file \"%s\" in package", name, patch_data_fn->data);
1633 goto pbiudone;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001634 }
1635
Sami Tolvanen90221202014-12-09 16:39:47 +00001636 params.patch_start = ui->package_zip_addr + mzGetZipEntryOffset(patch_entry);
1637 new_entry = mzFindZipEntry(za, new_data_fn->data);
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001638
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001639 if (new_entry == NULL) {
Sami Tolvanen90221202014-12-09 16:39:47 +00001640 fprintf(stderr, "%s(): no file \"%s\" in package", name, new_data_fn->data);
1641 goto pbiudone;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001642 }
1643
Sami Tolvanen90221202014-12-09 16:39:47 +00001644 params.fd = TEMP_FAILURE_RETRY(open(blockdev_filename->data, O_RDWR));
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001645
Sami Tolvanen90221202014-12-09 16:39:47 +00001646 if (params.fd == -1) {
Elliott Hughes1fdd4522015-03-23 13:33:57 -07001647 fprintf(stderr, "open \"%s\" failed: %s\n", blockdev_filename->data, strerror(errno));
Sami Tolvanen90221202014-12-09 16:39:47 +00001648 goto pbiudone;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001649 }
1650
Sami Tolvanen90221202014-12-09 16:39:47 +00001651 if (params.canwrite) {
1652 params.nti.za = za;
1653 params.nti.entry = new_entry;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001654
Sami Tolvanen90221202014-12-09 16:39:47 +00001655 pthread_mutex_init(&params.nti.mu, NULL);
1656 pthread_cond_init(&params.nti.cv, NULL);
1657 pthread_attr_init(&attr);
1658 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
1659
Elliott Hughes1fdd4522015-03-23 13:33:57 -07001660 int error = pthread_create(&params.thread, &attr, unzip_new_data, &params.nti);
1661 if (error != 0) {
1662 fprintf(stderr, "pthread_create failed: %s\n", strerror(error));
Sami Tolvanen90221202014-12-09 16:39:47 +00001663 goto pbiudone;
1664 }
1665 }
1666
1667 // The data in transfer_list_value is not necessarily null-terminated, so we need
1668 // to copy it to a new buffer and add the null that strtok_r will need.
1669 transfer_list = malloc(transfer_list_value->size + 1);
1670
Doug Zongker1d5d6092014-08-21 10:47:24 -07001671 if (transfer_list == NULL) {
1672 fprintf(stderr, "failed to allocate %zd bytes for transfer list\n",
Sami Tolvanen90221202014-12-09 16:39:47 +00001673 transfer_list_value->size + 1);
1674 goto pbiudone;
Doug Zongker1d5d6092014-08-21 10:47:24 -07001675 }
Sami Tolvanen90221202014-12-09 16:39:47 +00001676
Doug Zongker1d5d6092014-08-21 10:47:24 -07001677 memcpy(transfer_list, transfer_list_value->data, transfer_list_value->size);
1678 transfer_list[transfer_list_value->size] = '\0';
1679
Sami Tolvanen90221202014-12-09 16:39:47 +00001680 // First line in transfer list is the version number
Doug Zongker1d5d6092014-08-21 10:47:24 -07001681 line = strtok_r(transfer_list, "\n", &linesave);
Sami Tolvanen90221202014-12-09 16:39:47 +00001682 params.version = strtol(line, NULL, 0);
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001683
Sami Tolvanen90221202014-12-09 16:39:47 +00001684 if (params.version < 1 || params.version > 3) {
1685 fprintf(stderr, "unexpected transfer list version [%s]\n", line);
1686 goto pbiudone;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001687 }
1688
Sami Tolvanen90221202014-12-09 16:39:47 +00001689 fprintf(stderr, "blockimg version is %d\n", params.version);
1690
1691 // Second line in transfer list is the total number of blocks we expect to write
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001692 line = strtok_r(NULL, "\n", &linesave);
Sami Tolvanen90221202014-12-09 16:39:47 +00001693 total_blocks = strtol(line, NULL, 0);
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001694
Sami Tolvanen90221202014-12-09 16:39:47 +00001695 if (total_blocks < 0) {
1696 ErrorAbort(state, "unexpected block count [%s]\n", line);
1697 goto pbiudone;
1698 } else if (total_blocks == 0) {
1699 rc = 0;
1700 goto pbiudone;
1701 }
1702
1703 if (params.version >= 2) {
1704 // Third line is how many stash entries are needed simultaneously
Doug Zongker52ae67d2014-09-08 12:22:09 -07001705 line = strtok_r(NULL, "\n", &linesave);
Sami Tolvanen90221202014-12-09 16:39:47 +00001706 fprintf(stderr, "maximum stash entries %s\n", line);
Doug Zongker52ae67d2014-09-08 12:22:09 -07001707
Sami Tolvanen90221202014-12-09 16:39:47 +00001708 // Fourth line is the maximum number of blocks that will be stashed simultaneously
1709 line = strtok_r(NULL, "\n", &linesave);
1710 stash_max_blocks = strtol(line, NULL, 0);
1711
1712 if (stash_max_blocks < 0) {
1713 ErrorAbort(state, "unexpected maximum stash blocks [%s]\n", line);
1714 goto pbiudone;
Doug Zongker52ae67d2014-09-08 12:22:09 -07001715 }
1716
Jesse Zhao1df64d32015-02-17 17:09:23 -08001717 if (stash_max_blocks >= 0) {
Sami Tolvanen90221202014-12-09 16:39:47 +00001718 res = CreateStash(state, stash_max_blocks, blockdev_filename->data,
1719 &params.stashbase);
1720
1721 if (res == -1) {
1722 goto pbiudone;
1723 }
1724
1725 params.createdstash = res;
1726 }
Doug Zongker52ae67d2014-09-08 12:22:09 -07001727 }
1728
Sami Tolvanen90221202014-12-09 16:39:47 +00001729 // Build a hash table of the available commands
1730 cmdht = mzHashTableCreate(cmdcount, NULL);
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001731
Sami Tolvanen90221202014-12-09 16:39:47 +00001732 for (i = 0; i < cmdcount; ++i) {
1733 cmdhash = HashString(commands[i].name);
1734 mzHashTableLookup(cmdht, cmdhash, (void*) &commands[i], CompareCommands, true);
1735 }
1736
1737 // Subsequent lines are all individual transfer commands
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001738 for (line = strtok_r(NULL, "\n", &linesave); line;
1739 line = strtok_r(NULL, "\n", &linesave)) {
Doug Zongker52ae67d2014-09-08 12:22:09 -07001740
Sami Tolvanen90221202014-12-09 16:39:47 +00001741 logcmd = strdup(line);
1742 params.cmdname = strtok_r(line, " ", &params.cpos);
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001743
Sami Tolvanen90221202014-12-09 16:39:47 +00001744 if (params.cmdname == NULL) {
1745 fprintf(stderr, "missing command [%s]\n", line);
1746 goto pbiudone;
1747 }
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001748
Sami Tolvanen90221202014-12-09 16:39:47 +00001749 cmdhash = HashString(params.cmdname);
1750 cmd = (const Command*) mzHashTableLookup(cmdht, cmdhash, params.cmdname,
1751 CompareCommandNames, false);
Doug Zongker52ae67d2014-09-08 12:22:09 -07001752
Sami Tolvanen90221202014-12-09 16:39:47 +00001753 if (cmd == NULL) {
1754 fprintf(stderr, "unexpected command [%s]\n", params.cmdname);
1755 goto pbiudone;
1756 }
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001757
Sami Tolvanen90221202014-12-09 16:39:47 +00001758 if (cmd->f != NULL && cmd->f(&params) == -1) {
1759 fprintf(stderr, "failed to execute command [%s]\n",
1760 logcmd ? logcmd : params.cmdname);
1761 goto pbiudone;
1762 }
1763
1764 if (logcmd) {
1765 free(logcmd);
1766 logcmd = NULL;
1767 }
1768
1769 if (params.canwrite) {
1770 fprintf(cmd_pipe, "set_progress %.4f\n", (double) params.written / total_blocks);
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001771 fflush(cmd_pipe);
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001772 }
1773 }
1774
Sami Tolvanen90221202014-12-09 16:39:47 +00001775 if (params.canwrite) {
1776 pthread_join(params.thread, NULL);
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001777
Sami Tolvanen90221202014-12-09 16:39:47 +00001778 fprintf(stderr, "wrote %d blocks; expected %d\n", params.written, total_blocks);
1779 fprintf(stderr, "max alloc needed was %zu\n", params.bufsize);
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001780
Sami Tolvanen90221202014-12-09 16:39:47 +00001781 // Delete stash only after successfully completing the update, as it
1782 // may contain blocks needed to complete the update later.
1783 DeleteStash(params.stashbase);
1784 } else {
1785 fprintf(stderr, "verified partition contents; update may be resumed\n");
1786 }
1787
1788 rc = 0;
1789
1790pbiudone:
1791 if (params.fd != -1) {
1792 if (fsync(params.fd) == -1) {
Elliott Hughes1fdd4522015-03-23 13:33:57 -07001793 fprintf(stderr, "fsync failed: %s\n", strerror(errno));
Sami Tolvanen90221202014-12-09 16:39:47 +00001794 }
Elliott Hughesb47afed2015-05-15 16:19:20 -07001795 close(params.fd);
Sami Tolvanen90221202014-12-09 16:39:47 +00001796 }
1797
1798 if (logcmd) {
1799 free(logcmd);
1800 }
1801
1802 if (cmdht) {
1803 mzHashTableFree(cmdht);
1804 }
1805
1806 if (params.buffer) {
1807 free(params.buffer);
1808 }
1809
1810 if (transfer_list) {
1811 free(transfer_list);
1812 }
1813
1814 if (blockdev_filename) {
1815 FreeValue(blockdev_filename);
1816 }
1817
1818 if (transfer_list_value) {
1819 FreeValue(transfer_list_value);
1820 }
1821
1822 if (new_data_fn) {
1823 FreeValue(new_data_fn);
1824 }
1825
1826 if (patch_data_fn) {
1827 FreeValue(patch_data_fn);
1828 }
1829
1830 // Only delete the stash if the update cannot be resumed, or it's
1831 // a verification run and we created the stash.
1832 if (params.isunresumable || (!params.canwrite && params.createdstash)) {
1833 DeleteStash(params.stashbase);
1834 }
1835
1836 if (params.stashbase) {
1837 free(params.stashbase);
1838 }
1839
1840 return StringValue(rc == 0 ? strdup("t") : strdup(""));
1841}
1842
1843// The transfer list is a text file containing commands to
1844// transfer data from one place to another on the target
1845// partition. We parse it and execute the commands in order:
1846//
1847// zero [rangeset]
1848// - fill the indicated blocks with zeros
1849//
1850// new [rangeset]
1851// - fill the blocks with data read from the new_data file
1852//
1853// erase [rangeset]
1854// - mark the given blocks as empty
1855//
1856// move <...>
1857// bsdiff <patchstart> <patchlen> <...>
1858// imgdiff <patchstart> <patchlen> <...>
1859// - read the source blocks, apply a patch (or not in the
1860// case of move), write result to target blocks. bsdiff or
1861// imgdiff specifies the type of patch; move means no patch
1862// at all.
1863//
1864// The format of <...> differs between versions 1 and 2;
1865// see the LoadSrcTgtVersion{1,2}() functions for a
1866// description of what's expected.
1867//
1868// stash <stash_id> <src_range>
1869// - (version 2+ only) load the given source range and stash
1870// the data in the given slot of the stash table.
1871//
1872// The creator of the transfer list will guarantee that no block
1873// is read (ie, used as the source for a patch or move) after it
1874// has been written.
1875//
1876// In version 2, the creator will guarantee that a given stash is
1877// loaded (with a stash command) before it's used in a
1878// move/bsdiff/imgdiff command.
1879//
1880// Within one command the source and target ranges may overlap so
1881// in general we need to read the entire source into memory before
1882// writing anything to the target blocks.
1883//
1884// All the patch data is concatenated into one patch_data file in
1885// the update package. It must be stored uncompressed because we
1886// memory-map it in directly from the archive. (Since patches are
1887// already compressed, we lose very little by not compressing
1888// their concatenation.)
1889//
1890// In version 3, commands that read data from the partition (i.e.
1891// move/bsdiff/imgdiff/stash) have one or more additional hashes
1892// before the range parameters, which are used to check if the
1893// command has already been completed and verify the integrity of
1894// the source data.
1895
1896Value* BlockImageVerifyFn(const char* name, State* state, int argc, Expr* argv[]) {
1897 // Commands which are not tested are set to NULL to skip them completely
1898 const Command commands[] = {
1899 { "bsdiff", PerformCommandDiff },
1900 { "erase", NULL },
1901 { "free", PerformCommandFree },
1902 { "imgdiff", PerformCommandDiff },
1903 { "move", PerformCommandMove },
1904 { "new", NULL },
1905 { "stash", PerformCommandStash },
1906 { "zero", NULL }
1907 };
1908
1909 // Perform a dry run without writing to test if an update can proceed
1910 return PerformBlockImageUpdate(name, state, argc, argv, commands,
1911 sizeof(commands) / sizeof(commands[0]), 1);
1912}
1913
1914Value* BlockImageUpdateFn(const char* name, State* state, int argc, Expr* argv[]) {
1915 const Command commands[] = {
1916 { "bsdiff", PerformCommandDiff },
1917 { "erase", PerformCommandErase },
1918 { "free", PerformCommandFree },
1919 { "imgdiff", PerformCommandDiff },
1920 { "move", PerformCommandMove },
1921 { "new", PerformCommandNew },
1922 { "stash", PerformCommandStash },
1923 { "zero", PerformCommandZero }
1924 };
1925
1926 return PerformBlockImageUpdate(name, state, argc, argv, commands,
1927 sizeof(commands) / sizeof(commands[0]), 0);
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001928}
1929
1930Value* RangeSha1Fn(const char* name, State* state, int argc, Expr* argv[]) {
1931 Value* blockdev_filename;
1932 Value* ranges;
1933 const uint8_t* digest = NULL;
1934 if (ReadValueArgs(state, argv, 2, &blockdev_filename, &ranges) < 0) {
1935 return NULL;
1936 }
1937
1938 if (blockdev_filename->type != VAL_STRING) {
1939 ErrorAbort(state, "blockdev_filename argument to %s must be string", name);
1940 goto done;
1941 }
1942 if (ranges->type != VAL_STRING) {
1943 ErrorAbort(state, "ranges argument to %s must be string", name);
1944 goto done;
1945 }
1946
1947 int fd = open(blockdev_filename->data, O_RDWR);
1948 if (fd < 0) {
Elliott Hughes1fdd4522015-03-23 13:33:57 -07001949 ErrorAbort(state, "open \"%s\" failed: %s", blockdev_filename->data, strerror(errno));
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001950 goto done;
1951 }
1952
1953 RangeSet* rs = parse_range(ranges->data);
1954 uint8_t buffer[BLOCKSIZE];
1955
1956 SHA_CTX ctx;
1957 SHA_init(&ctx);
1958
1959 int i, j;
1960 for (i = 0; i < rs->count; ++i) {
Elliott Hughes7bad7c42015-04-28 17:24:24 -07001961 if (!check_lseek(fd, (off64_t)rs->pos[i*2] * BLOCKSIZE, SEEK_SET)) {
Sami Tolvanen90221202014-12-09 16:39:47 +00001962 ErrorAbort(state, "failed to seek %s: %s", blockdev_filename->data,
1963 strerror(errno));
1964 goto done;
1965 }
1966
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001967 for (j = rs->pos[i*2]; j < rs->pos[i*2+1]; ++j) {
Sami Tolvanen90221202014-12-09 16:39:47 +00001968 if (read_all(fd, buffer, BLOCKSIZE) == -1) {
1969 ErrorAbort(state, "failed to read %s: %s", blockdev_filename->data,
1970 strerror(errno));
1971 goto done;
1972 }
1973
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001974 SHA_update(&ctx, buffer, BLOCKSIZE);
1975 }
1976 }
1977 digest = SHA_final(&ctx);
1978 close(fd);
1979
1980 done:
1981 FreeValue(blockdev_filename);
1982 FreeValue(ranges);
1983 if (digest == NULL) {
1984 return StringValue(strdup(""));
1985 } else {
1986 return StringValue(PrintSha1(digest));
1987 }
1988}
1989
1990void RegisterBlockImageFunctions() {
Sami Tolvanen90221202014-12-09 16:39:47 +00001991 RegisterFunction("block_image_verify", BlockImageVerifyFn);
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001992 RegisterFunction("block_image_update", BlockImageUpdateFn);
1993 RegisterFunction("range_sha1", RangeSha1Fn);
1994}