blob: d5344f9916f4cba176e0ce74a2d8a68c017e295b [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
63static RangeSet* parse_range(char* text) {
64 char* save;
65 int num;
66 num = strtol(strtok_r(text, ",", &save), NULL, 0);
67
68 RangeSet* out = malloc(sizeof(RangeSet) + num * sizeof(int));
69 if (out == NULL) {
Doug Zongker52ae67d2014-09-08 12:22:09 -070070 fprintf(stderr, "failed to allocate range of %zu bytes\n",
Doug Zongkerbc7ffed2014-08-15 14:31:52 -070071 sizeof(RangeSet) + num * sizeof(int));
72 exit(1);
73 }
74 out->count = num / 2;
75 out->size = 0;
76 int i;
77 for (i = 0; i < num; ++i) {
78 out->pos[i] = strtol(strtok_r(NULL, ",", &save), NULL, 0);
79 if (i%2) {
80 out->size += out->pos[i];
81 } else {
82 out->size -= out->pos[i];
83 }
84 }
85
86 return out;
87}
88
Sami Tolvanen90221202014-12-09 16:39:47 +000089static int range_overlaps(RangeSet* r1, RangeSet* r2) {
90 int i, j, r1_0, r1_1, r2_0, r2_1;
91
92 if (!r1 || !r2) {
93 return 0;
94 }
95
96 for (i = 0; i < r1->count; ++i) {
97 r1_0 = r1->pos[i * 2];
98 r1_1 = r1->pos[i * 2 + 1];
99
100 for (j = 0; j < r2->count; ++j) {
101 r2_0 = r2->pos[j * 2];
102 r2_1 = r2->pos[j * 2 + 1];
103
104 if (!(r2_0 > r1_1 || r1_0 > r2_1)) {
105 return 1;
106 }
107 }
108 }
109
110 return 0;
111}
112
113static int read_all(int fd, uint8_t* data, size_t size) {
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700114 size_t so_far = 0;
115 while (so_far < size) {
116 ssize_t r = read(fd, data+so_far, size-so_far);
117 if (r < 0 && errno != EINTR) {
118 fprintf(stderr, "read failed: %s\n", strerror(errno));
Sami Tolvanen90221202014-12-09 16:39:47 +0000119 return -1;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700120 } else {
121 so_far += r;
122 }
123 }
Sami Tolvanen90221202014-12-09 16:39:47 +0000124 return 0;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700125}
126
Sami Tolvanen90221202014-12-09 16:39:47 +0000127static int write_all(int fd, const uint8_t* data, size_t size) {
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700128 size_t written = 0;
129 while (written < size) {
130 ssize_t w = write(fd, data+written, size-written);
131 if (w < 0 && errno != EINTR) {
132 fprintf(stderr, "write failed: %s\n", strerror(errno));
Sami Tolvanen90221202014-12-09 16:39:47 +0000133 return -1;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700134 } else {
135 written += w;
136 }
137 }
Sami Tolvanen90221202014-12-09 16:39:47 +0000138
139 if (fsync(fd) == -1) {
140 fprintf(stderr, "fsync failed: %s\n", strerror(errno));
141 return -1;
142 }
143
144 return 0;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700145}
146
Sami Tolvanen90221202014-12-09 16:39:47 +0000147static int check_lseek(int fd, off64_t offset, int whence) {
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700148 while (true) {
Andrew Boie83289222014-09-03 12:41:06 -0700149 off64_t ret = lseek64(fd, offset, whence);
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700150 if (ret < 0) {
151 if (errno != EINTR) {
Andrew Boie83289222014-09-03 12:41:06 -0700152 fprintf(stderr, "lseek64 failed: %s\n", strerror(errno));
Sami Tolvanen90221202014-12-09 16:39:47 +0000153 return -1;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700154 }
155 } else {
156 break;
157 }
158 }
Sami Tolvanen90221202014-12-09 16:39:47 +0000159 return 0;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700160}
161
162static void allocate(size_t size, uint8_t** buffer, size_t* buffer_alloc) {
163 // if the buffer's big enough, reuse it.
164 if (size <= *buffer_alloc) return;
165
166 free(*buffer);
167
168 *buffer = (uint8_t*) malloc(size);
169 if (*buffer == NULL) {
Doug Zongker1d5d6092014-08-21 10:47:24 -0700170 fprintf(stderr, "failed to allocate %zu bytes\n", size);
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700171 exit(1);
172 }
173 *buffer_alloc = size;
174}
175
176typedef struct {
177 int fd;
178 RangeSet* tgt;
179 int p_block;
180 size_t p_remain;
181} RangeSinkState;
182
183static ssize_t RangeSinkWrite(const uint8_t* data, ssize_t size, void* token) {
184 RangeSinkState* rss = (RangeSinkState*) token;
185
186 if (rss->p_remain <= 0) {
187 fprintf(stderr, "range sink write overrun");
Sami Tolvanen90221202014-12-09 16:39:47 +0000188 return 0;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700189 }
190
191 ssize_t written = 0;
192 while (size > 0) {
193 size_t write_now = size;
Sami Tolvanen90221202014-12-09 16:39:47 +0000194
195 if (rss->p_remain < write_now) {
196 write_now = rss->p_remain;
197 }
198
199 if (write_all(rss->fd, data, write_now) == -1) {
200 break;
201 }
202
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700203 data += write_now;
204 size -= write_now;
205
206 rss->p_remain -= write_now;
207 written += write_now;
208
209 if (rss->p_remain == 0) {
210 // move to the next block
211 ++rss->p_block;
212 if (rss->p_block < rss->tgt->count) {
Sami Tolvanen90221202014-12-09 16:39:47 +0000213 rss->p_remain = (rss->tgt->pos[rss->p_block * 2 + 1] -
214 rss->tgt->pos[rss->p_block * 2]) * BLOCKSIZE;
215
216 if (check_lseek(rss->fd, (off64_t)rss->tgt->pos[rss->p_block*2] * BLOCKSIZE,
217 SEEK_SET) == -1) {
218 break;
219 }
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700220 } else {
221 // we can't write any more; return how many bytes have
222 // been written so far.
Sami Tolvanen90221202014-12-09 16:39:47 +0000223 break;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700224 }
225 }
226 }
227
228 return written;
229}
230
231// All of the data for all the 'new' transfers is contained in one
232// file in the update package, concatenated together in the order in
233// which transfers.list will need it. We want to stream it out of the
234// archive (it's compressed) without writing it to a temp file, but we
235// can't write each section until it's that transfer's turn to go.
236//
237// To achieve this, we expand the new data from the archive in a
238// background thread, and block that threads 'receive uncompressed
239// data' function until the main thread has reached a point where we
240// want some new data to be written. We signal the background thread
241// with the destination for the data and block the main thread,
242// waiting for the background thread to complete writing that section.
243// Then it signals the main thread to wake up and goes back to
244// blocking waiting for a transfer.
245//
246// NewThreadInfo is the struct used to pass information back and forth
247// between the two threads. When the main thread wants some data
248// written, it sets rss to the destination location and signals the
249// condition. When the background thread is done writing, it clears
250// rss and signals the condition again.
251
252typedef struct {
253 ZipArchive* za;
254 const ZipEntry* entry;
255
256 RangeSinkState* rss;
257
258 pthread_mutex_t mu;
259 pthread_cond_t cv;
260} NewThreadInfo;
261
262static bool receive_new_data(const unsigned char* data, int size, void* cookie) {
263 NewThreadInfo* nti = (NewThreadInfo*) cookie;
264
265 while (size > 0) {
266 // Wait for nti->rss to be non-NULL, indicating some of this
267 // data is wanted.
268 pthread_mutex_lock(&nti->mu);
269 while (nti->rss == NULL) {
270 pthread_cond_wait(&nti->cv, &nti->mu);
271 }
272 pthread_mutex_unlock(&nti->mu);
273
274 // At this point nti->rss is set, and we own it. The main
275 // thread is waiting for it to disappear from nti.
276 ssize_t written = RangeSinkWrite(data, size, nti->rss);
277 data += written;
278 size -= written;
279
280 if (nti->rss->p_block == nti->rss->tgt->count) {
281 // we have written all the bytes desired by this rss.
282
283 pthread_mutex_lock(&nti->mu);
284 nti->rss = NULL;
285 pthread_cond_broadcast(&nti->cv);
286 pthread_mutex_unlock(&nti->mu);
287 }
288 }
289
290 return true;
291}
292
293static void* unzip_new_data(void* cookie) {
294 NewThreadInfo* nti = (NewThreadInfo*) cookie;
295 mzProcessZipEntryContents(nti->za, nti->entry, receive_new_data, nti);
296 return NULL;
297}
298
Sami Tolvanen90221202014-12-09 16:39:47 +0000299static int ReadBlocks(RangeSet* src, uint8_t* buffer, int fd) {
300 int i;
301 size_t p = 0;
302 size_t size;
303
304 if (!src || !buffer) {
305 return -1;
306 }
307
308 for (i = 0; i < src->count; ++i) {
309 if (check_lseek(fd, (off64_t) src->pos[i * 2] * BLOCKSIZE, SEEK_SET) == -1) {
310 return -1;
311 }
312
313 size = (src->pos[i * 2 + 1] - src->pos[i * 2]) * BLOCKSIZE;
314
315 if (read_all(fd, buffer + p, size) == -1) {
316 return -1;
317 }
318
319 p += size;
320 }
321
322 return 0;
323}
324
325static int WriteBlocks(RangeSet* tgt, uint8_t* buffer, int fd) {
326 int i;
327 size_t p = 0;
328 size_t size;
329
330 if (!tgt || !buffer) {
331 return -1;
332 }
333
334 for (i = 0; i < tgt->count; ++i) {
335 if (check_lseek(fd, (off64_t) tgt->pos[i * 2] * BLOCKSIZE, SEEK_SET) == -1) {
336 return -1;
337 }
338
339 size = (tgt->pos[i * 2 + 1] - tgt->pos[i * 2]) * BLOCKSIZE;
340
341 if (write_all(fd, buffer + p, size) == -1) {
342 return -1;
343 }
344
345 p += size;
346 }
347
348 return 0;
349}
350
Doug Zongker52ae67d2014-09-08 12:22:09 -0700351// Do a source/target load for move/bsdiff/imgdiff in version 1.
352// 'wordsave' is the save_ptr of a strtok_r()-in-progress. We expect
353// to parse the remainder of the string as:
354//
355// <src_range> <tgt_range>
356//
357// The source range is loaded into the provided buffer, reallocating
358// it to make it larger if necessary. The target ranges are returned
359// in *tgt, if tgt is non-NULL.
360
Sami Tolvanen90221202014-12-09 16:39:47 +0000361static int LoadSrcTgtVersion1(char** wordsave, RangeSet** tgt, int* src_blocks,
Doug Zongker52ae67d2014-09-08 12:22:09 -0700362 uint8_t** buffer, size_t* buffer_alloc, int fd) {
363 char* word;
Sami Tolvanen90221202014-12-09 16:39:47 +0000364 int rc;
Doug Zongker52ae67d2014-09-08 12:22:09 -0700365
Sami Tolvanen90221202014-12-09 16:39:47 +0000366 word = strtok_r(NULL, " ", wordsave);
Doug Zongker52ae67d2014-09-08 12:22:09 -0700367 RangeSet* src = parse_range(word);
368
369 if (tgt != NULL) {
Sami Tolvanen90221202014-12-09 16:39:47 +0000370 word = strtok_r(NULL, " ", wordsave);
Doug Zongker52ae67d2014-09-08 12:22:09 -0700371 *tgt = parse_range(word);
372 }
373
374 allocate(src->size * BLOCKSIZE, buffer, buffer_alloc);
Sami Tolvanen90221202014-12-09 16:39:47 +0000375 rc = ReadBlocks(src, *buffer, fd);
376 *src_blocks = src->size;
377
378 free(src);
379 return rc;
380}
381
382static int VerifyBlocks(const char *expected, const uint8_t *buffer,
383 size_t blocks, int printerror) {
384 char* hexdigest = NULL;
385 int rc = -1;
386 uint8_t digest[SHA_DIGEST_SIZE];
387
388 if (!expected || !buffer) {
389 return rc;
Doug Zongker52ae67d2014-09-08 12:22:09 -0700390 }
391
Sami Tolvanen90221202014-12-09 16:39:47 +0000392 SHA_hash(buffer, blocks * BLOCKSIZE, digest);
393 hexdigest = PrintSha1(digest);
394
395 if (hexdigest != NULL) {
396 rc = strcmp(expected, hexdigest);
397
398 if (rc != 0 && printerror) {
399 fprintf(stderr, "failed to verify blocks (expected %s, read %s)\n",
400 expected, hexdigest);
401 }
402
403 free(hexdigest);
404 }
405
406 return rc;
407}
408
409static char* GetStashFileName(const char* base, const char* id, const char* postfix) {
410 char* fn;
411 int len;
412 int res;
413
414 if (base == NULL) {
415 return NULL;
416 }
417
418 if (id == NULL) {
419 id = "";
420 }
421
422 if (postfix == NULL) {
423 postfix = "";
424 }
425
426 len = strlen(STASH_DIRECTORY_BASE) + 1 + strlen(base) + 1 + strlen(id) + strlen(postfix) + 1;
427 fn = malloc(len);
428
429 if (fn == NULL) {
430 fprintf(stderr, "failed to malloc %d bytes for fn\n", len);
431 return NULL;
432 }
433
434 res = snprintf(fn, len, STASH_DIRECTORY_BASE "/%s/%s%s", base, id, postfix);
435
436 if (res < 0 || res >= len) {
437 fprintf(stderr, "failed to format file name (return value %d)\n", res);
438 free(fn);
439 return NULL;
440 }
441
442 return fn;
443}
444
445typedef void (*StashCallback)(const char*, void*);
446
447// Does a best effort enumeration of stash files. Ignores possible non-file
448// items in the stash directory and continues despite of errors. Calls the
449// 'callback' function for each file and passes 'data' to the function as a
450// parameter.
451
452static void EnumerateStash(const char* dirname, StashCallback callback, void* data) {
453 char* fn;
454 DIR* directory;
455 int len;
456 int res;
457 struct dirent* item;
458
459 if (dirname == NULL || callback == NULL) {
460 return;
461 }
462
463 directory = opendir(dirname);
464
465 if (directory == NULL) {
466 if (errno != ENOENT) {
Elliott Hughes1fdd4522015-03-23 13:33:57 -0700467 fprintf(stderr, "opendir \"%s\" failed: %s\n", dirname, strerror(errno));
Sami Tolvanen90221202014-12-09 16:39:47 +0000468 }
469 return;
470 }
471
472 while ((item = readdir(directory)) != NULL) {
473 if (item->d_type != DT_REG) {
474 continue;
475 }
476
477 len = strlen(dirname) + 1 + strlen(item->d_name) + 1;
478 fn = malloc(len);
479
480 if (fn == NULL) {
481 fprintf(stderr, "failed to malloc %d bytes for fn\n", len);
482 continue;
483 }
484
485 res = snprintf(fn, len, "%s/%s", dirname, item->d_name);
486
487 if (res < 0 || res >= len) {
488 fprintf(stderr, "failed to format file name (return value %d)\n", res);
489 free(fn);
490 continue;
491 }
492
493 callback(fn, data);
494 free(fn);
495 }
496
497 if (closedir(directory) == -1) {
Elliott Hughes1fdd4522015-03-23 13:33:57 -0700498 fprintf(stderr, "closedir \"%s\" failed: %s\n", dirname, strerror(errno));
Sami Tolvanen90221202014-12-09 16:39:47 +0000499 }
500}
501
502static void UpdateFileSize(const char* fn, void* data) {
503 int* size = (int*) data;
504 struct stat st;
505
506 if (!fn || !data) {
507 return;
508 }
509
510 if (stat(fn, &st) == -1) {
Elliott Hughes1fdd4522015-03-23 13:33:57 -0700511 fprintf(stderr, "stat \"%s\" failed: %s\n", fn, strerror(errno));
Sami Tolvanen90221202014-12-09 16:39:47 +0000512 return;
513 }
514
515 *size += st.st_size;
516}
517
518// Deletes the stash directory and all files in it. Assumes that it only
519// contains files. There is nothing we can do about unlikely, but possible
520// errors, so they are merely logged.
521
522static void DeleteFile(const char* fn, void* data) {
523 if (fn) {
524 fprintf(stderr, "deleting %s\n", fn);
525
526 if (unlink(fn) == -1 && errno != ENOENT) {
Elliott Hughes1fdd4522015-03-23 13:33:57 -0700527 fprintf(stderr, "unlink \"%s\" failed: %s\n", fn, strerror(errno));
Sami Tolvanen90221202014-12-09 16:39:47 +0000528 }
529 }
530}
531
532static void DeletePartial(const char* fn, void* data) {
533 if (fn && strstr(fn, ".partial") != NULL) {
534 DeleteFile(fn, data);
535 }
536}
537
538static void DeleteStash(const char* base) {
539 char* dirname;
540
541 if (base == NULL) {
542 return;
543 }
544
545 dirname = GetStashFileName(base, NULL, NULL);
546
547 if (dirname == NULL) {
548 return;
549 }
550
551 fprintf(stderr, "deleting stash %s\n", base);
552 EnumerateStash(dirname, DeleteFile, NULL);
553
554 if (rmdir(dirname) == -1) {
555 if (errno != ENOENT && errno != ENOTDIR) {
Elliott Hughes1fdd4522015-03-23 13:33:57 -0700556 fprintf(stderr, "rmdir \"%s\" failed: %s\n", dirname, strerror(errno));
Sami Tolvanen90221202014-12-09 16:39:47 +0000557 }
558 }
559
560 free(dirname);
561}
562
563static int LoadStash(const char* base, const char* id, int verify, int* blocks, uint8_t** buffer,
564 size_t* buffer_alloc, int printnoent) {
565 char *fn = NULL;
566 int blockcount = 0;
567 int fd = -1;
568 int rc = -1;
569 int res;
570 struct stat st;
571
572 if (!base || !id || !buffer || !buffer_alloc) {
573 goto lsout;
574 }
575
576 if (!blocks) {
577 blocks = &blockcount;
578 }
579
580 fn = GetStashFileName(base, id, NULL);
581
582 if (fn == NULL) {
583 goto lsout;
584 }
585
586 res = stat(fn, &st);
587
588 if (res == -1) {
589 if (errno != ENOENT || printnoent) {
Elliott Hughes1fdd4522015-03-23 13:33:57 -0700590 fprintf(stderr, "stat \"%s\" failed: %s\n", fn, strerror(errno));
Sami Tolvanen90221202014-12-09 16:39:47 +0000591 }
592 goto lsout;
593 }
594
595 fprintf(stderr, " loading %s\n", fn);
596
597 if ((st.st_size % BLOCKSIZE) != 0) {
598 fprintf(stderr, "%s size %zd not multiple of block size %d", fn, st.st_size, BLOCKSIZE);
599 goto lsout;
600 }
601
602 fd = TEMP_FAILURE_RETRY(open(fn, O_RDONLY));
603
604 if (fd == -1) {
Elliott Hughes1fdd4522015-03-23 13:33:57 -0700605 fprintf(stderr, "open \"%s\" failed: %s\n", fn, strerror(errno));
Sami Tolvanen90221202014-12-09 16:39:47 +0000606 goto lsout;
607 }
608
609 allocate(st.st_size, buffer, buffer_alloc);
610
611 if (read_all(fd, *buffer, st.st_size) == -1) {
612 goto lsout;
613 }
614
615 *blocks = st.st_size / BLOCKSIZE;
616
617 if (verify && VerifyBlocks(id, *buffer, *blocks, 1) != 0) {
618 fprintf(stderr, "unexpected contents in %s\n", fn);
619 DeleteFile(fn, NULL);
620 goto lsout;
621 }
622
623 rc = 0;
624
625lsout:
626 if (fd != -1) {
627 TEMP_FAILURE_RETRY(close(fd));
628 }
629
630 if (fn) {
631 free(fn);
632 }
633
634 return rc;
635}
636
637static int WriteStash(const char* base, const char* id, int blocks, uint8_t* buffer,
Sami Tolvanen43b748f2015-04-17 12:50:31 +0100638 int checkspace, int *exists) {
Sami Tolvanen90221202014-12-09 16:39:47 +0000639 char *fn = NULL;
640 char *cn = NULL;
641 int fd = -1;
642 int rc = -1;
643 int res;
Sami Tolvanen43b748f2015-04-17 12:50:31 +0100644 struct stat st;
Sami Tolvanen90221202014-12-09 16:39:47 +0000645
646 if (base == NULL || buffer == NULL) {
647 goto wsout;
648 }
649
650 if (checkspace && CacheSizeCheck(blocks * BLOCKSIZE) != 0) {
651 fprintf(stderr, "not enough space to write stash\n");
652 goto wsout;
653 }
654
655 fn = GetStashFileName(base, id, ".partial");
656 cn = GetStashFileName(base, id, NULL);
657
658 if (fn == NULL || cn == NULL) {
659 goto wsout;
660 }
661
Sami Tolvanen43b748f2015-04-17 12:50:31 +0100662 if (exists) {
663 res = stat(cn, &st);
664
665 if (res == 0) {
666 // The file already exists and since the name is the hash of the contents,
667 // it's safe to assume the contents are identical (accidental hash collisions
668 // are unlikely)
669 fprintf(stderr, " skipping %d existing blocks in %s\n", blocks, cn);
670 *exists = 1;
671 rc = 0;
672 goto wsout;
673 }
674
675 *exists = 0;
676 }
677
Sami Tolvanen90221202014-12-09 16:39:47 +0000678 fprintf(stderr, " writing %d blocks to %s\n", blocks, cn);
679
680 fd = TEMP_FAILURE_RETRY(open(fn, O_WRONLY | O_CREAT | O_TRUNC | O_SYNC, STASH_FILE_MODE));
681
682 if (fd == -1) {
Elliott Hughes1fdd4522015-03-23 13:33:57 -0700683 fprintf(stderr, "failed to create \"%s\": %s\n", fn, strerror(errno));
Sami Tolvanen90221202014-12-09 16:39:47 +0000684 goto wsout;
685 }
686
687 if (write_all(fd, buffer, blocks * BLOCKSIZE) == -1) {
688 goto wsout;
689 }
690
691 if (fsync(fd) == -1) {
Elliott Hughes1fdd4522015-03-23 13:33:57 -0700692 fprintf(stderr, "fsync \"%s\" failed: %s\n", fn, strerror(errno));
Sami Tolvanen90221202014-12-09 16:39:47 +0000693 goto wsout;
694 }
695
696 if (rename(fn, cn) == -1) {
Elliott Hughes1fdd4522015-03-23 13:33:57 -0700697 fprintf(stderr, "rename(\"%s\", \"%s\") failed: %s\n", fn, cn, strerror(errno));
Sami Tolvanen90221202014-12-09 16:39:47 +0000698 goto wsout;
699 }
700
701 rc = 0;
702
703wsout:
704 if (fd != -1) {
705 TEMP_FAILURE_RETRY(close(fd));
706 }
707
708 if (fn) {
709 free(fn);
710 }
711
712 if (cn) {
713 free(cn);
714 }
715
716 return rc;
717}
718
719// Creates a directory for storing stash files and checks if the /cache partition
720// hash enough space for the expected amount of blocks we need to store. Returns
721// >0 if we created the directory, zero if it existed already, and <0 of failure.
722
723static int CreateStash(State* state, int maxblocks, const char* blockdev, char** base) {
724 char* dirname = NULL;
725 const uint8_t* digest;
726 int rc = -1;
727 int res;
728 int size = 0;
729 SHA_CTX ctx;
730 struct stat st;
731
732 if (blockdev == NULL || base == NULL) {
733 goto csout;
734 }
735
736 // Stash directory should be different for each partition to avoid conflicts
737 // when updating multiple partitions at the same time, so we use the hash of
738 // the block device name as the base directory
739 SHA_init(&ctx);
740 SHA_update(&ctx, blockdev, strlen(blockdev));
741 digest = SHA_final(&ctx);
742 *base = PrintSha1(digest);
743
744 if (*base == NULL) {
745 goto csout;
746 }
747
748 dirname = GetStashFileName(*base, NULL, NULL);
749
750 if (dirname == NULL) {
751 goto csout;
752 }
753
754 res = stat(dirname, &st);
755
756 if (res == -1 && errno != ENOENT) {
Elliott Hughes1fdd4522015-03-23 13:33:57 -0700757 ErrorAbort(state, "stat \"%s\" failed: %s\n", dirname, strerror(errno));
Sami Tolvanen90221202014-12-09 16:39:47 +0000758 goto csout;
759 } else if (res != 0) {
760 fprintf(stderr, "creating stash %s\n", dirname);
761 res = mkdir(dirname, STASH_DIRECTORY_MODE);
762
763 if (res != 0) {
Elliott Hughes1fdd4522015-03-23 13:33:57 -0700764 ErrorAbort(state, "mkdir \"%s\" failed: %s\n", dirname, strerror(errno));
Sami Tolvanen90221202014-12-09 16:39:47 +0000765 goto csout;
766 }
767
768 if (CacheSizeCheck(maxblocks * BLOCKSIZE) != 0) {
769 ErrorAbort(state, "not enough space for stash\n");
770 goto csout;
771 }
772
773 rc = 1; // Created directory
774 goto csout;
775 }
776
777 fprintf(stderr, "using existing stash %s\n", dirname);
778
779 // If the directory already exists, calculate the space already allocated to
780 // stash files and check if there's enough for all required blocks. Delete any
781 // partially completed stash files first.
782
783 EnumerateStash(dirname, DeletePartial, NULL);
784 EnumerateStash(dirname, UpdateFileSize, &size);
785
786 size = (maxblocks * BLOCKSIZE) - size;
787
788 if (size > 0 && CacheSizeCheck(size) != 0) {
789 ErrorAbort(state, "not enough space for stash (%d more needed)\n", size);
790 goto csout;
791 }
792
793 rc = 0; // Using existing directory
794
795csout:
796 if (dirname) {
797 free(dirname);
798 }
799
800 return rc;
801}
802
803static int SaveStash(const char* base, char** wordsave, uint8_t** buffer, size_t* buffer_alloc,
804 int fd, int usehash, int* isunresumable) {
805 char *id = NULL;
806 int res = -1;
807 int blocks = 0;
808
809 if (!wordsave || !buffer || !buffer_alloc || !isunresumable) {
810 return -1;
811 }
812
813 id = strtok_r(NULL, " ", wordsave);
814
815 if (id == NULL) {
816 fprintf(stderr, "missing id field in stash command\n");
817 return -1;
818 }
819
820 if (usehash && LoadStash(base, id, 1, &blocks, buffer, buffer_alloc, 0) == 0) {
821 // Stash file already exists and has expected contents. Do not
822 // read from source again, as the source may have been already
823 // overwritten during a previous attempt.
824 return 0;
825 }
826
827 if (LoadSrcTgtVersion1(wordsave, NULL, &blocks, buffer, buffer_alloc, fd) == -1) {
828 return -1;
829 }
830
831 if (usehash && VerifyBlocks(id, *buffer, blocks, 1) != 0) {
832 // Source blocks have unexpected contents. If we actually need this
833 // data later, this is an unrecoverable error. However, the command
834 // that uses the data may have already completed previously, so the
835 // possible failure will occur during source block verification.
836 fprintf(stderr, "failed to load source blocks for stash %s\n", id);
837 return 0;
838 }
839
840 fprintf(stderr, "stashing %d blocks to %s\n", blocks, id);
Sami Tolvanen43b748f2015-04-17 12:50:31 +0100841 return WriteStash(base, id, blocks, *buffer, 0, NULL);
Sami Tolvanen90221202014-12-09 16:39:47 +0000842}
843
844static int FreeStash(const char* base, const char* id) {
845 char *fn = NULL;
846
847 if (base == NULL || id == NULL) {
848 return -1;
849 }
850
851 fn = GetStashFileName(base, id, NULL);
852
853 if (fn == NULL) {
854 return -1;
855 }
856
857 DeleteFile(fn, NULL);
858 free(fn);
859
860 return 0;
Doug Zongker52ae67d2014-09-08 12:22:09 -0700861}
862
863static void MoveRange(uint8_t* dest, RangeSet* locs, const uint8_t* source) {
864 // source contains packed data, which we want to move to the
865 // locations given in *locs in the dest buffer. source and dest
866 // may be the same buffer.
867
868 int start = locs->size;
869 int i;
870 for (i = locs->count-1; i >= 0; --i) {
871 int blocks = locs->pos[i*2+1] - locs->pos[i*2];
872 start -= blocks;
873 memmove(dest + (locs->pos[i*2] * BLOCKSIZE), source + (start * BLOCKSIZE),
874 blocks * BLOCKSIZE);
875 }
876}
877
878// Do a source/target load for move/bsdiff/imgdiff in version 2.
879// 'wordsave' is the save_ptr of a strtok_r()-in-progress. We expect
880// to parse the remainder of the string as one of:
881//
882// <tgt_range> <src_block_count> <src_range>
883// (loads data from source image only)
884//
885// <tgt_range> <src_block_count> - <[stash_id:stash_range] ...>
886// (loads data from stashes only)
887//
888// <tgt_range> <src_block_count> <src_range> <src_loc> <[stash_id:stash_range] ...>
889// (loads data from both source image and stashes)
890//
891// On return, buffer is filled with the loaded source data (rearranged
892// and combined with stashed data as necessary). buffer may be
893// reallocated if needed to accommodate the source data. *tgt is the
Sami Tolvanen90221202014-12-09 16:39:47 +0000894// target RangeSet. Any stashes required are loaded using LoadStash.
Doug Zongker52ae67d2014-09-08 12:22:09 -0700895
Sami Tolvanen90221202014-12-09 16:39:47 +0000896static int LoadSrcTgtVersion2(char** wordsave, RangeSet** tgt, int* src_blocks,
Doug Zongker52ae67d2014-09-08 12:22:09 -0700897 uint8_t** buffer, size_t* buffer_alloc, int fd,
Sami Tolvanen90221202014-12-09 16:39:47 +0000898 const char* stashbase, int* overlap) {
Doug Zongker52ae67d2014-09-08 12:22:09 -0700899 char* word;
Sami Tolvanen90221202014-12-09 16:39:47 +0000900 char* colonsave;
901 char* colon;
902 int id;
903 int res;
904 RangeSet* locs;
905 size_t stashalloc = 0;
906 uint8_t* stash = NULL;
Doug Zongker52ae67d2014-09-08 12:22:09 -0700907
908 if (tgt != NULL) {
Sami Tolvanen90221202014-12-09 16:39:47 +0000909 word = strtok_r(NULL, " ", wordsave);
Doug Zongker52ae67d2014-09-08 12:22:09 -0700910 *tgt = parse_range(word);
911 }
912
Sami Tolvanen90221202014-12-09 16:39:47 +0000913 word = strtok_r(NULL, " ", wordsave);
Doug Zongker52ae67d2014-09-08 12:22:09 -0700914 *src_blocks = strtol(word, NULL, 0);
915
916 allocate(*src_blocks * BLOCKSIZE, buffer, buffer_alloc);
917
Sami Tolvanen90221202014-12-09 16:39:47 +0000918 word = strtok_r(NULL, " ", wordsave);
Doug Zongker52ae67d2014-09-08 12:22:09 -0700919 if (word[0] == '-' && word[1] == '\0') {
920 // no source ranges, only stashes
921 } else {
922 RangeSet* src = parse_range(word);
Sami Tolvanen90221202014-12-09 16:39:47 +0000923 res = ReadBlocks(src, *buffer, fd);
Doug Zongker52ae67d2014-09-08 12:22:09 -0700924
Sami Tolvanen90221202014-12-09 16:39:47 +0000925 if (overlap && tgt) {
926 *overlap = range_overlaps(src, *tgt);
Doug Zongker52ae67d2014-09-08 12:22:09 -0700927 }
Sami Tolvanen90221202014-12-09 16:39:47 +0000928
Doug Zongker52ae67d2014-09-08 12:22:09 -0700929 free(src);
930
Sami Tolvanen90221202014-12-09 16:39:47 +0000931 if (res == -1) {
932 return -1;
Doug Zongker52ae67d2014-09-08 12:22:09 -0700933 }
934
Sami Tolvanen90221202014-12-09 16:39:47 +0000935 word = strtok_r(NULL, " ", wordsave);
936 if (word == NULL) {
937 // no stashes, only source range
938 return 0;
939 }
940
941 locs = parse_range(word);
Doug Zongker52ae67d2014-09-08 12:22:09 -0700942 MoveRange(*buffer, locs, *buffer);
Sami Tolvanen90221202014-12-09 16:39:47 +0000943 free(locs);
Doug Zongker52ae67d2014-09-08 12:22:09 -0700944 }
945
Sami Tolvanen90221202014-12-09 16:39:47 +0000946 while ((word = strtok_r(NULL, " ", wordsave)) != NULL) {
Doug Zongker52ae67d2014-09-08 12:22:09 -0700947 // Each word is a an index into the stash table, a colon, and
948 // then a rangeset describing where in the source block that
949 // stashed data should go.
Sami Tolvanen90221202014-12-09 16:39:47 +0000950 colonsave = NULL;
951 colon = strtok_r(word, ":", &colonsave);
952
953 res = LoadStash(stashbase, colon, 0, NULL, &stash, &stashalloc, 1);
954
955 if (res == -1) {
956 // These source blocks will fail verification if used later, but we
957 // will let the caller decide if this is a fatal failure
958 fprintf(stderr, "failed to load stash %s\n", colon);
959 continue;
960 }
961
Doug Zongker52ae67d2014-09-08 12:22:09 -0700962 colon = strtok_r(NULL, ":", &colonsave);
Sami Tolvanen90221202014-12-09 16:39:47 +0000963 locs = parse_range(colon);
964
965 MoveRange(*buffer, locs, stash);
Doug Zongker52ae67d2014-09-08 12:22:09 -0700966 free(locs);
967 }
Sami Tolvanen90221202014-12-09 16:39:47 +0000968
969 if (stash) {
970 free(stash);
971 }
972
973 return 0;
974}
975
976// Parameters for transfer list command functions
977typedef struct {
978 char* cmdname;
979 char* cpos;
980 char* freestash;
981 char* stashbase;
982 int canwrite;
983 int createdstash;
984 int fd;
985 int foundwrites;
986 int isunresumable;
987 int version;
988 int written;
989 NewThreadInfo nti;
990 pthread_t thread;
991 size_t bufsize;
992 uint8_t* buffer;
993 uint8_t* patch_start;
994} CommandParameters;
995
996// Do a source/target load for move/bsdiff/imgdiff in version 3.
997//
998// Parameters are the same as for LoadSrcTgtVersion2, except for 'onehash', which
999// tells the function whether to expect separate source and targe block hashes, or
1000// if they are both the same and only one hash should be expected, and
1001// 'isunresumable', which receives a non-zero value if block verification fails in
1002// a way that the update cannot be resumed anymore.
1003//
1004// If the function is unable to load the necessary blocks or their contents don't
1005// match the hashes, the return value is -1 and the command should be aborted.
1006//
1007// If the return value is 1, the command has already been completed according to
1008// the contents of the target blocks, and should not be performed again.
1009//
1010// If the return value is 0, source blocks have expected content and the command
1011// can be performed.
1012
1013static int LoadSrcTgtVersion3(CommandParameters* params, RangeSet** tgt, int* src_blocks,
1014 int onehash, int* overlap) {
1015 char* srchash = NULL;
1016 char* tgthash = NULL;
Sami Tolvanen43b748f2015-04-17 12:50:31 +01001017 int stash_exists = 0;
Sami Tolvanen90221202014-12-09 16:39:47 +00001018 int overlap_blocks = 0;
1019 int rc = -1;
1020 uint8_t* tgtbuffer = NULL;
1021
1022 if (!params|| !tgt || !src_blocks || !overlap) {
1023 goto v3out;
1024 }
1025
1026 srchash = strtok_r(NULL, " ", &params->cpos);
1027
1028 if (srchash == NULL) {
1029 fprintf(stderr, "missing source hash\n");
1030 goto v3out;
1031 }
1032
1033 if (onehash) {
1034 tgthash = srchash;
1035 } else {
1036 tgthash = strtok_r(NULL, " ", &params->cpos);
1037
1038 if (tgthash == NULL) {
1039 fprintf(stderr, "missing target hash\n");
1040 goto v3out;
1041 }
1042 }
1043
1044 if (LoadSrcTgtVersion2(&params->cpos, tgt, src_blocks, &params->buffer, &params->bufsize,
1045 params->fd, params->stashbase, overlap) == -1) {
1046 goto v3out;
1047 }
1048
1049 tgtbuffer = (uint8_t*) malloc((*tgt)->size * BLOCKSIZE);
1050
1051 if (tgtbuffer == NULL) {
1052 fprintf(stderr, "failed to allocate %d bytes\n", (*tgt)->size * BLOCKSIZE);
1053 goto v3out;
1054 }
1055
1056 if (ReadBlocks(*tgt, tgtbuffer, params->fd) == -1) {
1057 goto v3out;
1058 }
1059
1060 if (VerifyBlocks(tgthash, tgtbuffer, (*tgt)->size, 0) == 0) {
1061 // Target blocks already have expected content, command should be skipped
1062 rc = 1;
1063 goto v3out;
1064 }
1065
1066 if (VerifyBlocks(srchash, params->buffer, *src_blocks, 1) == 0) {
1067 // If source and target blocks overlap, stash the source blocks so we can
1068 // resume from possible write errors
1069 if (*overlap) {
1070 fprintf(stderr, "stashing %d overlapping blocks to %s\n", *src_blocks,
1071 srchash);
1072
Sami Tolvanen43b748f2015-04-17 12:50:31 +01001073 if (WriteStash(params->stashbase, srchash, *src_blocks, params->buffer, 1,
1074 &stash_exists) != 0) {
Sami Tolvanen90221202014-12-09 16:39:47 +00001075 fprintf(stderr, "failed to stash overlapping source blocks\n");
1076 goto v3out;
1077 }
1078
1079 // Can be deleted when the write has completed
Sami Tolvanen43b748f2015-04-17 12:50:31 +01001080 if (!stash_exists) {
1081 params->freestash = srchash;
1082 }
Sami Tolvanen90221202014-12-09 16:39:47 +00001083 }
1084
1085 // Source blocks have expected content, command can proceed
1086 rc = 0;
1087 goto v3out;
1088 }
1089
1090 if (*overlap && LoadStash(params->stashbase, srchash, 1, NULL, &params->buffer,
1091 &params->bufsize, 1) == 0) {
Sami Tolvanen43b748f2015-04-17 12:50:31 +01001092 // Overlapping source blocks were previously stashed, command can proceed.
1093 // We are recovering from an interrupted command, so we don't know if the
1094 // stash can safely be deleted after this command.
Sami Tolvanen90221202014-12-09 16:39:47 +00001095 rc = 0;
1096 goto v3out;
1097 }
1098
1099 // Valid source data not available, update cannot be resumed
1100 fprintf(stderr, "partition has unexpected contents\n");
1101 params->isunresumable = 1;
1102
1103v3out:
1104 if (tgtbuffer) {
1105 free(tgtbuffer);
1106 }
1107
1108 return rc;
1109}
1110
1111static int PerformCommandMove(CommandParameters* params) {
1112 int blocks = 0;
1113 int overlap = 0;
1114 int rc = -1;
1115 int status = 0;
1116 RangeSet* tgt = NULL;
1117
1118 if (!params) {
1119 goto pcmout;
1120 }
1121
1122 if (params->version == 1) {
1123 status = LoadSrcTgtVersion1(&params->cpos, &tgt, &blocks, &params->buffer,
1124 &params->bufsize, params->fd);
1125 } else if (params->version == 2) {
1126 status = LoadSrcTgtVersion2(&params->cpos, &tgt, &blocks, &params->buffer,
1127 &params->bufsize, params->fd, params->stashbase, NULL);
1128 } else if (params->version >= 3) {
1129 status = LoadSrcTgtVersion3(params, &tgt, &blocks, 1, &overlap);
1130 }
1131
1132 if (status == -1) {
1133 fprintf(stderr, "failed to read blocks for move\n");
1134 goto pcmout;
1135 }
1136
1137 if (status == 0) {
1138 params->foundwrites = 1;
1139 } else if (params->foundwrites) {
1140 fprintf(stderr, "warning: commands executed out of order [%s]\n", params->cmdname);
1141 }
1142
1143 if (params->canwrite) {
1144 if (status == 0) {
1145 fprintf(stderr, " moving %d blocks\n", blocks);
1146
1147 if (WriteBlocks(tgt, params->buffer, params->fd) == -1) {
1148 goto pcmout;
1149 }
1150 } else {
1151 fprintf(stderr, "skipping %d already moved blocks\n", blocks);
1152 }
1153
1154 }
1155
1156 if (params->freestash) {
1157 FreeStash(params->stashbase, params->freestash);
1158 params->freestash = NULL;
1159 }
1160
1161 params->written += tgt->size;
1162 rc = 0;
1163
1164pcmout:
1165 if (tgt) {
1166 free(tgt);
1167 }
1168
1169 return rc;
1170}
1171
1172static int PerformCommandStash(CommandParameters* params) {
1173 if (!params) {
1174 return -1;
1175 }
1176
1177 return SaveStash(params->stashbase, &params->cpos, &params->buffer, &params->bufsize,
1178 params->fd, (params->version >= 3), &params->isunresumable);
1179}
1180
1181static int PerformCommandFree(CommandParameters* params) {
1182 if (!params) {
1183 return -1;
1184 }
1185
1186 if (params->createdstash || params->canwrite) {
1187 return FreeStash(params->stashbase, params->cpos);
1188 }
1189
1190 return 0;
1191}
1192
1193static int PerformCommandZero(CommandParameters* params) {
1194 char* range = NULL;
1195 int i;
1196 int j;
1197 int rc = -1;
1198 RangeSet* tgt = NULL;
1199
1200 if (!params) {
1201 goto pczout;
1202 }
1203
1204 range = strtok_r(NULL, " ", &params->cpos);
1205
1206 if (range == NULL) {
1207 fprintf(stderr, "missing target blocks for zero\n");
1208 goto pczout;
1209 }
1210
1211 tgt = parse_range(range);
1212
1213 fprintf(stderr, " zeroing %d blocks\n", tgt->size);
1214
1215 allocate(BLOCKSIZE, &params->buffer, &params->bufsize);
1216 memset(params->buffer, 0, BLOCKSIZE);
1217
1218 if (params->canwrite) {
1219 for (i = 0; i < tgt->count; ++i) {
1220 if (check_lseek(params->fd, (off64_t) tgt->pos[i * 2] * BLOCKSIZE, SEEK_SET) == -1) {
1221 goto pczout;
1222 }
1223
1224 for (j = tgt->pos[i * 2]; j < tgt->pos[i * 2 + 1]; ++j) {
1225 if (write_all(params->fd, params->buffer, BLOCKSIZE) == -1) {
1226 goto pczout;
1227 }
1228 }
1229 }
1230 }
1231
1232 if (params->cmdname[0] == 'z') {
1233 // Update only for the zero command, as the erase command will call
1234 // this if DEBUG_ERASE is defined.
1235 params->written += tgt->size;
1236 }
1237
1238 rc = 0;
1239
1240pczout:
1241 if (tgt) {
1242 free(tgt);
1243 }
1244
1245 return rc;
1246}
1247
1248static int PerformCommandNew(CommandParameters* params) {
1249 char* range = NULL;
1250 int rc = -1;
1251 RangeSet* tgt = NULL;
1252 RangeSinkState rss;
1253
1254 if (!params) {
1255 goto pcnout;
1256 }
1257
1258 range = strtok_r(NULL, " ", &params->cpos);
1259
1260 if (range == NULL) {
1261 goto pcnout;
1262 }
1263
1264 tgt = parse_range(range);
1265
1266 if (params->canwrite) {
1267 fprintf(stderr, " writing %d blocks of new data\n", tgt->size);
1268
1269 rss.fd = params->fd;
1270 rss.tgt = tgt;
1271 rss.p_block = 0;
1272 rss.p_remain = (tgt->pos[1] - tgt->pos[0]) * BLOCKSIZE;
1273
1274 if (check_lseek(params->fd, (off64_t) tgt->pos[0] * BLOCKSIZE, SEEK_SET) == -1) {
1275 goto pcnout;
1276 }
1277
1278 pthread_mutex_lock(&params->nti.mu);
1279 params->nti.rss = &rss;
1280 pthread_cond_broadcast(&params->nti.cv);
1281
1282 while (params->nti.rss) {
1283 pthread_cond_wait(&params->nti.cv, &params->nti.mu);
1284 }
1285
1286 pthread_mutex_unlock(&params->nti.mu);
1287 }
1288
1289 params->written += tgt->size;
1290 rc = 0;
1291
1292pcnout:
1293 if (tgt) {
1294 free(tgt);
1295 }
1296
1297 return rc;
1298}
1299
1300static int PerformCommandDiff(CommandParameters* params) {
1301 char* logparams = NULL;
1302 char* value = NULL;
1303 int blocks = 0;
1304 int overlap = 0;
1305 int rc = -1;
1306 int status = 0;
1307 RangeSet* tgt = NULL;
1308 RangeSinkState rss;
1309 size_t len = 0;
1310 size_t offset = 0;
1311 Value patch_value;
1312
1313 if (!params) {
1314 goto pcdout;
1315 }
1316
1317 logparams = strdup(params->cpos);
1318 value = strtok_r(NULL, " ", &params->cpos);
1319
1320 if (value == NULL) {
1321 fprintf(stderr, "missing patch offset for %s\n", params->cmdname);
1322 goto pcdout;
1323 }
1324
1325 offset = strtoul(value, NULL, 0);
1326
1327 value = strtok_r(NULL, " ", &params->cpos);
1328
1329 if (value == NULL) {
1330 fprintf(stderr, "missing patch length for %s\n", params->cmdname);
1331 goto pcdout;
1332 }
1333
1334 len = strtoul(value, NULL, 0);
1335
1336 if (params->version == 1) {
1337 status = LoadSrcTgtVersion1(&params->cpos, &tgt, &blocks, &params->buffer,
1338 &params->bufsize, params->fd);
1339 } else if (params->version == 2) {
1340 status = LoadSrcTgtVersion2(&params->cpos, &tgt, &blocks, &params->buffer,
1341 &params->bufsize, params->fd, params->stashbase, NULL);
1342 } else if (params->version >= 3) {
1343 status = LoadSrcTgtVersion3(params, &tgt, &blocks, 0, &overlap);
1344 }
1345
1346 if (status == -1) {
1347 fprintf(stderr, "failed to read blocks for diff\n");
1348 goto pcdout;
1349 }
1350
1351 if (status == 0) {
1352 params->foundwrites = 1;
1353 } else if (params->foundwrites) {
1354 fprintf(stderr, "warning: commands executed out of order [%s]\n", params->cmdname);
1355 }
1356
1357 if (params->canwrite) {
1358 if (status == 0) {
1359 fprintf(stderr, "patching %d blocks to %d\n", blocks, tgt->size);
1360
1361 patch_value.type = VAL_BLOB;
1362 patch_value.size = len;
1363 patch_value.data = (char*) (params->patch_start + offset);
1364
1365 rss.fd = params->fd;
1366 rss.tgt = tgt;
1367 rss.p_block = 0;
1368 rss.p_remain = (tgt->pos[1] - tgt->pos[0]) * BLOCKSIZE;
1369
1370 if (check_lseek(params->fd, (off64_t) tgt->pos[0] * BLOCKSIZE, SEEK_SET) == -1) {
1371 goto pcdout;
1372 }
1373
1374 if (params->cmdname[0] == 'i') { // imgdiff
1375 ApplyImagePatch(params->buffer, blocks * BLOCKSIZE, &patch_value,
1376 &RangeSinkWrite, &rss, NULL, NULL);
1377 } else {
1378 ApplyBSDiffPatch(params->buffer, blocks * BLOCKSIZE, &patch_value,
1379 0, &RangeSinkWrite, &rss, NULL);
1380 }
1381
1382 // We expect the output of the patcher to fill the tgt ranges exactly.
1383 if (rss.p_block != tgt->count || rss.p_remain != 0) {
1384 fprintf(stderr, "range sink underrun?\n");
1385 }
1386 } else {
1387 fprintf(stderr, "skipping %d blocks already patched to %d [%s]\n",
1388 blocks, tgt->size, logparams);
1389 }
1390 }
1391
1392 if (params->freestash) {
1393 FreeStash(params->stashbase, params->freestash);
1394 params->freestash = NULL;
1395 }
1396
1397 params->written += tgt->size;
1398 rc = 0;
1399
1400pcdout:
1401 if (logparams) {
1402 free(logparams);
1403 }
1404
1405 if (tgt) {
1406 free(tgt);
1407 }
1408
1409 return rc;
1410}
1411
1412static int PerformCommandErase(CommandParameters* params) {
1413 char* range = NULL;
1414 int i;
1415 int rc = -1;
1416 RangeSet* tgt = NULL;
1417 struct stat st;
1418 uint64_t blocks[2];
1419
1420 if (DEBUG_ERASE) {
1421 return PerformCommandZero(params);
1422 }
1423
1424 if (!params) {
1425 goto pceout;
1426 }
1427
1428 if (fstat(params->fd, &st) == -1) {
Elliott Hughes1fdd4522015-03-23 13:33:57 -07001429 fprintf(stderr, "failed to fstat device to erase: %s\n", strerror(errno));
Sami Tolvanen90221202014-12-09 16:39:47 +00001430 goto pceout;
1431 }
1432
1433 if (!S_ISBLK(st.st_mode)) {
1434 fprintf(stderr, "not a block device; skipping erase\n");
1435 rc = 0;
1436 goto pceout;
1437 }
1438
1439 range = strtok_r(NULL, " ", &params->cpos);
1440
1441 if (range == NULL) {
1442 fprintf(stderr, "missing target blocks for zero\n");
1443 goto pceout;
1444 }
1445
1446 tgt = parse_range(range);
1447
1448 if (params->canwrite) {
1449 fprintf(stderr, " erasing %d blocks\n", tgt->size);
1450
1451 for (i = 0; i < tgt->count; ++i) {
1452 // offset in bytes
1453 blocks[0] = tgt->pos[i * 2] * (uint64_t) BLOCKSIZE;
1454 // length in bytes
1455 blocks[1] = (tgt->pos[i * 2 + 1] - tgt->pos[i * 2]) * (uint64_t) BLOCKSIZE;
1456
1457 if (ioctl(params->fd, BLKDISCARD, &blocks) == -1) {
Elliott Hughes1fdd4522015-03-23 13:33:57 -07001458 fprintf(stderr, "BLKDISCARD ioctl failed: %s\n", strerror(errno));
Sami Tolvanen90221202014-12-09 16:39:47 +00001459 // Continue anyway, nothing we can do
1460 }
1461 }
1462 }
1463
1464 rc = 0;
1465
1466pceout:
1467 if (tgt) {
1468 free(tgt);
1469 }
1470
1471 return rc;
1472}
1473
1474// Definitions for transfer list command functions
1475typedef int (*CommandFunction)(CommandParameters*);
1476
1477typedef struct {
1478 const char* name;
1479 CommandFunction f;
1480} Command;
1481
1482// CompareCommands and CompareCommandNames are for the hash table
1483
1484static int CompareCommands(const void* c1, const void* c2) {
1485 return strcmp(((const Command*) c1)->name, ((const Command*) c2)->name);
1486}
1487
1488static int CompareCommandNames(const void* c1, const void* c2) {
1489 return strcmp(((const Command*) c1)->name, (const char*) c2);
1490}
1491
1492// HashString is used to hash command names for the hash table
1493
1494static unsigned int HashString(const char *s) {
1495 unsigned int hash = 0;
1496 if (s) {
1497 while (*s) {
1498 hash = hash * 33 + *s++;
1499 }
1500 }
1501 return hash;
Doug Zongker52ae67d2014-09-08 12:22:09 -07001502}
1503
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001504// args:
1505// - block device (or file) to modify in-place
1506// - transfer list (blob)
1507// - new data stream (filename within package.zip)
1508// - patch stream (filename within package.zip, must be uncompressed)
1509
Sami Tolvanen90221202014-12-09 16:39:47 +00001510static Value* PerformBlockImageUpdate(const char* name, State* state, int argc, Expr* argv[],
1511 const Command* commands, int cmdcount, int dryrun) {
1512
1513 char* line = NULL;
1514 char* linesave = NULL;
1515 char* logcmd = NULL;
Doug Zongker1d5d6092014-08-21 10:47:24 -07001516 char* transfer_list = NULL;
Sami Tolvanen90221202014-12-09 16:39:47 +00001517 CommandParameters params;
1518 const Command* cmd = NULL;
1519 const ZipEntry* new_entry = NULL;
1520 const ZipEntry* patch_entry = NULL;
1521 FILE* cmd_pipe = NULL;
1522 HashTable* cmdht = NULL;
1523 int i;
1524 int res;
1525 int rc = -1;
1526 int stash_max_blocks = 0;
1527 int total_blocks = 0;
1528 pthread_attr_t attr;
1529 unsigned int cmdhash;
1530 UpdaterInfo* ui = NULL;
1531 Value* blockdev_filename = NULL;
1532 Value* new_data_fn = NULL;
1533 Value* patch_data_fn = NULL;
1534 Value* transfer_list_value = NULL;
1535 ZipArchive* za = NULL;
1536
1537 memset(&params, 0, sizeof(params));
1538 params.canwrite = !dryrun;
1539
1540 fprintf(stderr, "performing %s\n", dryrun ? "verification" : "update");
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001541
Doug Zongker1d5d6092014-08-21 10:47:24 -07001542 if (ReadValueArgs(state, argv, 4, &blockdev_filename, &transfer_list_value,
Sami Tolvanen90221202014-12-09 16:39:47 +00001543 &new_data_fn, &patch_data_fn) < 0) {
1544 goto pbiudone;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001545 }
1546
1547 if (blockdev_filename->type != VAL_STRING) {
1548 ErrorAbort(state, "blockdev_filename argument to %s must be string", name);
Sami Tolvanen90221202014-12-09 16:39:47 +00001549 goto pbiudone;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001550 }
Doug Zongker1d5d6092014-08-21 10:47:24 -07001551 if (transfer_list_value->type != VAL_BLOB) {
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001552 ErrorAbort(state, "transfer_list argument to %s must be blob", name);
Sami Tolvanen90221202014-12-09 16:39:47 +00001553 goto pbiudone;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001554 }
1555 if (new_data_fn->type != VAL_STRING) {
1556 ErrorAbort(state, "new_data_fn argument to %s must be string", name);
Sami Tolvanen90221202014-12-09 16:39:47 +00001557 goto pbiudone;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001558 }
1559 if (patch_data_fn->type != VAL_STRING) {
1560 ErrorAbort(state, "patch_data_fn argument to %s must be string", name);
Sami Tolvanen90221202014-12-09 16:39:47 +00001561 goto pbiudone;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001562 }
1563
Sami Tolvanen90221202014-12-09 16:39:47 +00001564 ui = (UpdaterInfo*) state->cookie;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001565
Sami Tolvanen90221202014-12-09 16:39:47 +00001566 if (ui == NULL) {
1567 goto pbiudone;
1568 }
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001569
Sami Tolvanen90221202014-12-09 16:39:47 +00001570 cmd_pipe = ui->cmd_pipe;
1571 za = ui->package_zip;
1572
1573 if (cmd_pipe == NULL || za == NULL) {
1574 goto pbiudone;
1575 }
1576
1577 patch_entry = mzFindZipEntry(za, patch_data_fn->data);
1578
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001579 if (patch_entry == NULL) {
Sami Tolvanen90221202014-12-09 16:39:47 +00001580 fprintf(stderr, "%s(): no file \"%s\" in package", name, patch_data_fn->data);
1581 goto pbiudone;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001582 }
1583
Sami Tolvanen90221202014-12-09 16:39:47 +00001584 params.patch_start = ui->package_zip_addr + mzGetZipEntryOffset(patch_entry);
1585 new_entry = mzFindZipEntry(za, new_data_fn->data);
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001586
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001587 if (new_entry == NULL) {
Sami Tolvanen90221202014-12-09 16:39:47 +00001588 fprintf(stderr, "%s(): no file \"%s\" in package", name, new_data_fn->data);
1589 goto pbiudone;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001590 }
1591
Sami Tolvanen90221202014-12-09 16:39:47 +00001592 params.fd = TEMP_FAILURE_RETRY(open(blockdev_filename->data, O_RDWR));
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001593
Sami Tolvanen90221202014-12-09 16:39:47 +00001594 if (params.fd == -1) {
Elliott Hughes1fdd4522015-03-23 13:33:57 -07001595 fprintf(stderr, "open \"%s\" failed: %s\n", blockdev_filename->data, strerror(errno));
Sami Tolvanen90221202014-12-09 16:39:47 +00001596 goto pbiudone;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001597 }
1598
Sami Tolvanen90221202014-12-09 16:39:47 +00001599 if (params.canwrite) {
1600 params.nti.za = za;
1601 params.nti.entry = new_entry;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001602
Sami Tolvanen90221202014-12-09 16:39:47 +00001603 pthread_mutex_init(&params.nti.mu, NULL);
1604 pthread_cond_init(&params.nti.cv, NULL);
1605 pthread_attr_init(&attr);
1606 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
1607
Elliott Hughes1fdd4522015-03-23 13:33:57 -07001608 int error = pthread_create(&params.thread, &attr, unzip_new_data, &params.nti);
1609 if (error != 0) {
1610 fprintf(stderr, "pthread_create failed: %s\n", strerror(error));
Sami Tolvanen90221202014-12-09 16:39:47 +00001611 goto pbiudone;
1612 }
1613 }
1614
1615 // The data in transfer_list_value is not necessarily null-terminated, so we need
1616 // to copy it to a new buffer and add the null that strtok_r will need.
1617 transfer_list = malloc(transfer_list_value->size + 1);
1618
Doug Zongker1d5d6092014-08-21 10:47:24 -07001619 if (transfer_list == NULL) {
1620 fprintf(stderr, "failed to allocate %zd bytes for transfer list\n",
Sami Tolvanen90221202014-12-09 16:39:47 +00001621 transfer_list_value->size + 1);
1622 goto pbiudone;
Doug Zongker1d5d6092014-08-21 10:47:24 -07001623 }
Sami Tolvanen90221202014-12-09 16:39:47 +00001624
Doug Zongker1d5d6092014-08-21 10:47:24 -07001625 memcpy(transfer_list, transfer_list_value->data, transfer_list_value->size);
1626 transfer_list[transfer_list_value->size] = '\0';
1627
Sami Tolvanen90221202014-12-09 16:39:47 +00001628 // First line in transfer list is the version number
Doug Zongker1d5d6092014-08-21 10:47:24 -07001629 line = strtok_r(transfer_list, "\n", &linesave);
Sami Tolvanen90221202014-12-09 16:39:47 +00001630 params.version = strtol(line, NULL, 0);
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001631
Sami Tolvanen90221202014-12-09 16:39:47 +00001632 if (params.version < 1 || params.version > 3) {
1633 fprintf(stderr, "unexpected transfer list version [%s]\n", line);
1634 goto pbiudone;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001635 }
1636
Sami Tolvanen90221202014-12-09 16:39:47 +00001637 fprintf(stderr, "blockimg version is %d\n", params.version);
1638
1639 // Second line in transfer list is the total number of blocks we expect to write
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001640 line = strtok_r(NULL, "\n", &linesave);
Sami Tolvanen90221202014-12-09 16:39:47 +00001641 total_blocks = strtol(line, NULL, 0);
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001642
Sami Tolvanen90221202014-12-09 16:39:47 +00001643 if (total_blocks < 0) {
1644 ErrorAbort(state, "unexpected block count [%s]\n", line);
1645 goto pbiudone;
1646 } else if (total_blocks == 0) {
1647 rc = 0;
1648 goto pbiudone;
1649 }
1650
1651 if (params.version >= 2) {
1652 // Third line is how many stash entries are needed simultaneously
Doug Zongker52ae67d2014-09-08 12:22:09 -07001653 line = strtok_r(NULL, "\n", &linesave);
Sami Tolvanen90221202014-12-09 16:39:47 +00001654 fprintf(stderr, "maximum stash entries %s\n", line);
Doug Zongker52ae67d2014-09-08 12:22:09 -07001655
Sami Tolvanen90221202014-12-09 16:39:47 +00001656 // Fourth line is the maximum number of blocks that will be stashed simultaneously
1657 line = strtok_r(NULL, "\n", &linesave);
1658 stash_max_blocks = strtol(line, NULL, 0);
1659
1660 if (stash_max_blocks < 0) {
1661 ErrorAbort(state, "unexpected maximum stash blocks [%s]\n", line);
1662 goto pbiudone;
Doug Zongker52ae67d2014-09-08 12:22:09 -07001663 }
1664
Jesse Zhao1df64d32015-02-17 17:09:23 -08001665 if (stash_max_blocks >= 0) {
Sami Tolvanen90221202014-12-09 16:39:47 +00001666 res = CreateStash(state, stash_max_blocks, blockdev_filename->data,
1667 &params.stashbase);
1668
1669 if (res == -1) {
1670 goto pbiudone;
1671 }
1672
1673 params.createdstash = res;
1674 }
Doug Zongker52ae67d2014-09-08 12:22:09 -07001675 }
1676
Sami Tolvanen90221202014-12-09 16:39:47 +00001677 // Build a hash table of the available commands
1678 cmdht = mzHashTableCreate(cmdcount, NULL);
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001679
Sami Tolvanen90221202014-12-09 16:39:47 +00001680 for (i = 0; i < cmdcount; ++i) {
1681 cmdhash = HashString(commands[i].name);
1682 mzHashTableLookup(cmdht, cmdhash, (void*) &commands[i], CompareCommands, true);
1683 }
1684
1685 // Subsequent lines are all individual transfer commands
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001686 for (line = strtok_r(NULL, "\n", &linesave); line;
1687 line = strtok_r(NULL, "\n", &linesave)) {
Doug Zongker52ae67d2014-09-08 12:22:09 -07001688
Sami Tolvanen90221202014-12-09 16:39:47 +00001689 logcmd = strdup(line);
1690 params.cmdname = strtok_r(line, " ", &params.cpos);
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001691
Sami Tolvanen90221202014-12-09 16:39:47 +00001692 if (params.cmdname == NULL) {
1693 fprintf(stderr, "missing command [%s]\n", line);
1694 goto pbiudone;
1695 }
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001696
Sami Tolvanen90221202014-12-09 16:39:47 +00001697 cmdhash = HashString(params.cmdname);
1698 cmd = (const Command*) mzHashTableLookup(cmdht, cmdhash, params.cmdname,
1699 CompareCommandNames, false);
Doug Zongker52ae67d2014-09-08 12:22:09 -07001700
Sami Tolvanen90221202014-12-09 16:39:47 +00001701 if (cmd == NULL) {
1702 fprintf(stderr, "unexpected command [%s]\n", params.cmdname);
1703 goto pbiudone;
1704 }
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001705
Sami Tolvanen90221202014-12-09 16:39:47 +00001706 if (cmd->f != NULL && cmd->f(&params) == -1) {
1707 fprintf(stderr, "failed to execute command [%s]\n",
1708 logcmd ? logcmd : params.cmdname);
1709 goto pbiudone;
1710 }
1711
1712 if (logcmd) {
1713 free(logcmd);
1714 logcmd = NULL;
1715 }
1716
1717 if (params.canwrite) {
1718 fprintf(cmd_pipe, "set_progress %.4f\n", (double) params.written / total_blocks);
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001719 fflush(cmd_pipe);
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001720 }
1721 }
1722
Sami Tolvanen90221202014-12-09 16:39:47 +00001723 if (params.canwrite) {
1724 pthread_join(params.thread, NULL);
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001725
Sami Tolvanen90221202014-12-09 16:39:47 +00001726 fprintf(stderr, "wrote %d blocks; expected %d\n", params.written, total_blocks);
1727 fprintf(stderr, "max alloc needed was %zu\n", params.bufsize);
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001728
Sami Tolvanen90221202014-12-09 16:39:47 +00001729 // Delete stash only after successfully completing the update, as it
1730 // may contain blocks needed to complete the update later.
1731 DeleteStash(params.stashbase);
1732 } else {
1733 fprintf(stderr, "verified partition contents; update may be resumed\n");
1734 }
1735
1736 rc = 0;
1737
1738pbiudone:
1739 if (params.fd != -1) {
1740 if (fsync(params.fd) == -1) {
Elliott Hughes1fdd4522015-03-23 13:33:57 -07001741 fprintf(stderr, "fsync failed: %s\n", strerror(errno));
Sami Tolvanen90221202014-12-09 16:39:47 +00001742 }
1743 TEMP_FAILURE_RETRY(close(params.fd));
1744 }
1745
1746 if (logcmd) {
1747 free(logcmd);
1748 }
1749
1750 if (cmdht) {
1751 mzHashTableFree(cmdht);
1752 }
1753
1754 if (params.buffer) {
1755 free(params.buffer);
1756 }
1757
1758 if (transfer_list) {
1759 free(transfer_list);
1760 }
1761
1762 if (blockdev_filename) {
1763 FreeValue(blockdev_filename);
1764 }
1765
1766 if (transfer_list_value) {
1767 FreeValue(transfer_list_value);
1768 }
1769
1770 if (new_data_fn) {
1771 FreeValue(new_data_fn);
1772 }
1773
1774 if (patch_data_fn) {
1775 FreeValue(patch_data_fn);
1776 }
1777
1778 // Only delete the stash if the update cannot be resumed, or it's
1779 // a verification run and we created the stash.
1780 if (params.isunresumable || (!params.canwrite && params.createdstash)) {
1781 DeleteStash(params.stashbase);
1782 }
1783
1784 if (params.stashbase) {
1785 free(params.stashbase);
1786 }
1787
1788 return StringValue(rc == 0 ? strdup("t") : strdup(""));
1789}
1790
1791// The transfer list is a text file containing commands to
1792// transfer data from one place to another on the target
1793// partition. We parse it and execute the commands in order:
1794//
1795// zero [rangeset]
1796// - fill the indicated blocks with zeros
1797//
1798// new [rangeset]
1799// - fill the blocks with data read from the new_data file
1800//
1801// erase [rangeset]
1802// - mark the given blocks as empty
1803//
1804// move <...>
1805// bsdiff <patchstart> <patchlen> <...>
1806// imgdiff <patchstart> <patchlen> <...>
1807// - read the source blocks, apply a patch (or not in the
1808// case of move), write result to target blocks. bsdiff or
1809// imgdiff specifies the type of patch; move means no patch
1810// at all.
1811//
1812// The format of <...> differs between versions 1 and 2;
1813// see the LoadSrcTgtVersion{1,2}() functions for a
1814// description of what's expected.
1815//
1816// stash <stash_id> <src_range>
1817// - (version 2+ only) load the given source range and stash
1818// the data in the given slot of the stash table.
1819//
1820// The creator of the transfer list will guarantee that no block
1821// is read (ie, used as the source for a patch or move) after it
1822// has been written.
1823//
1824// In version 2, the creator will guarantee that a given stash is
1825// loaded (with a stash command) before it's used in a
1826// move/bsdiff/imgdiff command.
1827//
1828// Within one command the source and target ranges may overlap so
1829// in general we need to read the entire source into memory before
1830// writing anything to the target blocks.
1831//
1832// All the patch data is concatenated into one patch_data file in
1833// the update package. It must be stored uncompressed because we
1834// memory-map it in directly from the archive. (Since patches are
1835// already compressed, we lose very little by not compressing
1836// their concatenation.)
1837//
1838// In version 3, commands that read data from the partition (i.e.
1839// move/bsdiff/imgdiff/stash) have one or more additional hashes
1840// before the range parameters, which are used to check if the
1841// command has already been completed and verify the integrity of
1842// the source data.
1843
1844Value* BlockImageVerifyFn(const char* name, State* state, int argc, Expr* argv[]) {
1845 // Commands which are not tested are set to NULL to skip them completely
1846 const Command commands[] = {
1847 { "bsdiff", PerformCommandDiff },
1848 { "erase", NULL },
1849 { "free", PerformCommandFree },
1850 { "imgdiff", PerformCommandDiff },
1851 { "move", PerformCommandMove },
1852 { "new", NULL },
1853 { "stash", PerformCommandStash },
1854 { "zero", NULL }
1855 };
1856
1857 // Perform a dry run without writing to test if an update can proceed
1858 return PerformBlockImageUpdate(name, state, argc, argv, commands,
1859 sizeof(commands) / sizeof(commands[0]), 1);
1860}
1861
1862Value* BlockImageUpdateFn(const char* name, State* state, int argc, Expr* argv[]) {
1863 const Command commands[] = {
1864 { "bsdiff", PerformCommandDiff },
1865 { "erase", PerformCommandErase },
1866 { "free", PerformCommandFree },
1867 { "imgdiff", PerformCommandDiff },
1868 { "move", PerformCommandMove },
1869 { "new", PerformCommandNew },
1870 { "stash", PerformCommandStash },
1871 { "zero", PerformCommandZero }
1872 };
1873
1874 return PerformBlockImageUpdate(name, state, argc, argv, commands,
1875 sizeof(commands) / sizeof(commands[0]), 0);
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001876}
1877
1878Value* RangeSha1Fn(const char* name, State* state, int argc, Expr* argv[]) {
1879 Value* blockdev_filename;
1880 Value* ranges;
1881 const uint8_t* digest = NULL;
1882 if (ReadValueArgs(state, argv, 2, &blockdev_filename, &ranges) < 0) {
1883 return NULL;
1884 }
1885
1886 if (blockdev_filename->type != VAL_STRING) {
1887 ErrorAbort(state, "blockdev_filename argument to %s must be string", name);
1888 goto done;
1889 }
1890 if (ranges->type != VAL_STRING) {
1891 ErrorAbort(state, "ranges argument to %s must be string", name);
1892 goto done;
1893 }
1894
1895 int fd = open(blockdev_filename->data, O_RDWR);
1896 if (fd < 0) {
Elliott Hughes1fdd4522015-03-23 13:33:57 -07001897 ErrorAbort(state, "open \"%s\" failed: %s", blockdev_filename->data, strerror(errno));
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001898 goto done;
1899 }
1900
1901 RangeSet* rs = parse_range(ranges->data);
1902 uint8_t buffer[BLOCKSIZE];
1903
1904 SHA_CTX ctx;
1905 SHA_init(&ctx);
1906
1907 int i, j;
1908 for (i = 0; i < rs->count; ++i) {
Sami Tolvanen90221202014-12-09 16:39:47 +00001909 if (check_lseek(fd, (off64_t)rs->pos[i*2] * BLOCKSIZE, SEEK_SET) == -1) {
1910 ErrorAbort(state, "failed to seek %s: %s", blockdev_filename->data,
1911 strerror(errno));
1912 goto done;
1913 }
1914
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001915 for (j = rs->pos[i*2]; j < rs->pos[i*2+1]; ++j) {
Sami Tolvanen90221202014-12-09 16:39:47 +00001916 if (read_all(fd, buffer, BLOCKSIZE) == -1) {
1917 ErrorAbort(state, "failed to read %s: %s", blockdev_filename->data,
1918 strerror(errno));
1919 goto done;
1920 }
1921
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001922 SHA_update(&ctx, buffer, BLOCKSIZE);
1923 }
1924 }
1925 digest = SHA_final(&ctx);
1926 close(fd);
1927
1928 done:
1929 FreeValue(blockdev_filename);
1930 FreeValue(ranges);
1931 if (digest == NULL) {
1932 return StringValue(strdup(""));
1933 } else {
1934 return StringValue(PrintSha1(digest));
1935 }
1936}
1937
1938void RegisterBlockImageFunctions() {
Sami Tolvanen90221202014-12-09 16:39:47 +00001939 RegisterFunction("block_image_verify", BlockImageVerifyFn);
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001940 RegisterFunction("block_image_update", BlockImageUpdateFn);
1941 RegisterFunction("range_sha1", RangeSha1Fn);
1942}