blob: 90b55b2d224b68d606534310e085d525d10d3982 [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,
638 int checkspace) {
639 char *fn = NULL;
640 char *cn = NULL;
641 int fd = -1;
642 int rc = -1;
643 int res;
644
645 if (base == NULL || buffer == NULL) {
646 goto wsout;
647 }
648
649 if (checkspace && CacheSizeCheck(blocks * BLOCKSIZE) != 0) {
650 fprintf(stderr, "not enough space to write stash\n");
651 goto wsout;
652 }
653
654 fn = GetStashFileName(base, id, ".partial");
655 cn = GetStashFileName(base, id, NULL);
656
657 if (fn == NULL || cn == NULL) {
658 goto wsout;
659 }
660
661 fprintf(stderr, " writing %d blocks to %s\n", blocks, cn);
662
663 fd = TEMP_FAILURE_RETRY(open(fn, O_WRONLY | O_CREAT | O_TRUNC | O_SYNC, STASH_FILE_MODE));
664
665 if (fd == -1) {
Elliott Hughes1fdd4522015-03-23 13:33:57 -0700666 fprintf(stderr, "failed to create \"%s\": %s\n", fn, strerror(errno));
Sami Tolvanen90221202014-12-09 16:39:47 +0000667 goto wsout;
668 }
669
670 if (write_all(fd, buffer, blocks * BLOCKSIZE) == -1) {
671 goto wsout;
672 }
673
674 if (fsync(fd) == -1) {
Elliott Hughes1fdd4522015-03-23 13:33:57 -0700675 fprintf(stderr, "fsync \"%s\" failed: %s\n", fn, strerror(errno));
Sami Tolvanen90221202014-12-09 16:39:47 +0000676 goto wsout;
677 }
678
679 if (rename(fn, cn) == -1) {
Elliott Hughes1fdd4522015-03-23 13:33:57 -0700680 fprintf(stderr, "rename(\"%s\", \"%s\") failed: %s\n", fn, cn, strerror(errno));
Sami Tolvanen90221202014-12-09 16:39:47 +0000681 goto wsout;
682 }
683
684 rc = 0;
685
686wsout:
687 if (fd != -1) {
688 TEMP_FAILURE_RETRY(close(fd));
689 }
690
691 if (fn) {
692 free(fn);
693 }
694
695 if (cn) {
696 free(cn);
697 }
698
699 return rc;
700}
701
702// Creates a directory for storing stash files and checks if the /cache partition
703// hash enough space for the expected amount of blocks we need to store. Returns
704// >0 if we created the directory, zero if it existed already, and <0 of failure.
705
706static int CreateStash(State* state, int maxblocks, const char* blockdev, char** base) {
707 char* dirname = NULL;
708 const uint8_t* digest;
709 int rc = -1;
710 int res;
711 int size = 0;
712 SHA_CTX ctx;
713 struct stat st;
714
715 if (blockdev == NULL || base == NULL) {
716 goto csout;
717 }
718
719 // Stash directory should be different for each partition to avoid conflicts
720 // when updating multiple partitions at the same time, so we use the hash of
721 // the block device name as the base directory
722 SHA_init(&ctx);
723 SHA_update(&ctx, blockdev, strlen(blockdev));
724 digest = SHA_final(&ctx);
725 *base = PrintSha1(digest);
726
727 if (*base == NULL) {
728 goto csout;
729 }
730
731 dirname = GetStashFileName(*base, NULL, NULL);
732
733 if (dirname == NULL) {
734 goto csout;
735 }
736
737 res = stat(dirname, &st);
738
739 if (res == -1 && errno != ENOENT) {
Elliott Hughes1fdd4522015-03-23 13:33:57 -0700740 ErrorAbort(state, "stat \"%s\" failed: %s\n", dirname, strerror(errno));
Sami Tolvanen90221202014-12-09 16:39:47 +0000741 goto csout;
742 } else if (res != 0) {
743 fprintf(stderr, "creating stash %s\n", dirname);
744 res = mkdir(dirname, STASH_DIRECTORY_MODE);
745
746 if (res != 0) {
Elliott Hughes1fdd4522015-03-23 13:33:57 -0700747 ErrorAbort(state, "mkdir \"%s\" failed: %s\n", dirname, strerror(errno));
Sami Tolvanen90221202014-12-09 16:39:47 +0000748 goto csout;
749 }
750
751 if (CacheSizeCheck(maxblocks * BLOCKSIZE) != 0) {
752 ErrorAbort(state, "not enough space for stash\n");
753 goto csout;
754 }
755
756 rc = 1; // Created directory
757 goto csout;
758 }
759
760 fprintf(stderr, "using existing stash %s\n", dirname);
761
762 // If the directory already exists, calculate the space already allocated to
763 // stash files and check if there's enough for all required blocks. Delete any
764 // partially completed stash files first.
765
766 EnumerateStash(dirname, DeletePartial, NULL);
767 EnumerateStash(dirname, UpdateFileSize, &size);
768
769 size = (maxblocks * BLOCKSIZE) - size;
770
771 if (size > 0 && CacheSizeCheck(size) != 0) {
772 ErrorAbort(state, "not enough space for stash (%d more needed)\n", size);
773 goto csout;
774 }
775
776 rc = 0; // Using existing directory
777
778csout:
779 if (dirname) {
780 free(dirname);
781 }
782
783 return rc;
784}
785
786static int SaveStash(const char* base, char** wordsave, uint8_t** buffer, size_t* buffer_alloc,
787 int fd, int usehash, int* isunresumable) {
788 char *id = NULL;
789 int res = -1;
790 int blocks = 0;
791
792 if (!wordsave || !buffer || !buffer_alloc || !isunresumable) {
793 return -1;
794 }
795
796 id = strtok_r(NULL, " ", wordsave);
797
798 if (id == NULL) {
799 fprintf(stderr, "missing id field in stash command\n");
800 return -1;
801 }
802
803 if (usehash && LoadStash(base, id, 1, &blocks, buffer, buffer_alloc, 0) == 0) {
804 // Stash file already exists and has expected contents. Do not
805 // read from source again, as the source may have been already
806 // overwritten during a previous attempt.
807 return 0;
808 }
809
810 if (LoadSrcTgtVersion1(wordsave, NULL, &blocks, buffer, buffer_alloc, fd) == -1) {
811 return -1;
812 }
813
814 if (usehash && VerifyBlocks(id, *buffer, blocks, 1) != 0) {
815 // Source blocks have unexpected contents. If we actually need this
816 // data later, this is an unrecoverable error. However, the command
817 // that uses the data may have already completed previously, so the
818 // possible failure will occur during source block verification.
819 fprintf(stderr, "failed to load source blocks for stash %s\n", id);
820 return 0;
821 }
822
823 fprintf(stderr, "stashing %d blocks to %s\n", blocks, id);
824 return WriteStash(base, id, blocks, *buffer, 0);
825}
826
827static int FreeStash(const char* base, const char* id) {
828 char *fn = NULL;
829
830 if (base == NULL || id == NULL) {
831 return -1;
832 }
833
834 fn = GetStashFileName(base, id, NULL);
835
836 if (fn == NULL) {
837 return -1;
838 }
839
840 DeleteFile(fn, NULL);
841 free(fn);
842
843 return 0;
Doug Zongker52ae67d2014-09-08 12:22:09 -0700844}
845
846static void MoveRange(uint8_t* dest, RangeSet* locs, const uint8_t* source) {
847 // source contains packed data, which we want to move to the
848 // locations given in *locs in the dest buffer. source and dest
849 // may be the same buffer.
850
851 int start = locs->size;
852 int i;
853 for (i = locs->count-1; i >= 0; --i) {
854 int blocks = locs->pos[i*2+1] - locs->pos[i*2];
855 start -= blocks;
856 memmove(dest + (locs->pos[i*2] * BLOCKSIZE), source + (start * BLOCKSIZE),
857 blocks * BLOCKSIZE);
858 }
859}
860
861// Do a source/target load for move/bsdiff/imgdiff in version 2.
862// 'wordsave' is the save_ptr of a strtok_r()-in-progress. We expect
863// to parse the remainder of the string as one of:
864//
865// <tgt_range> <src_block_count> <src_range>
866// (loads data from source image only)
867//
868// <tgt_range> <src_block_count> - <[stash_id:stash_range] ...>
869// (loads data from stashes only)
870//
871// <tgt_range> <src_block_count> <src_range> <src_loc> <[stash_id:stash_range] ...>
872// (loads data from both source image and stashes)
873//
874// On return, buffer is filled with the loaded source data (rearranged
875// and combined with stashed data as necessary). buffer may be
876// reallocated if needed to accommodate the source data. *tgt is the
Sami Tolvanen90221202014-12-09 16:39:47 +0000877// target RangeSet. Any stashes required are loaded using LoadStash.
Doug Zongker52ae67d2014-09-08 12:22:09 -0700878
Sami Tolvanen90221202014-12-09 16:39:47 +0000879static int LoadSrcTgtVersion2(char** wordsave, RangeSet** tgt, int* src_blocks,
Doug Zongker52ae67d2014-09-08 12:22:09 -0700880 uint8_t** buffer, size_t* buffer_alloc, int fd,
Sami Tolvanen90221202014-12-09 16:39:47 +0000881 const char* stashbase, int* overlap) {
Doug Zongker52ae67d2014-09-08 12:22:09 -0700882 char* word;
Sami Tolvanen90221202014-12-09 16:39:47 +0000883 char* colonsave;
884 char* colon;
885 int id;
886 int res;
887 RangeSet* locs;
888 size_t stashalloc = 0;
889 uint8_t* stash = NULL;
Doug Zongker52ae67d2014-09-08 12:22:09 -0700890
891 if (tgt != NULL) {
Sami Tolvanen90221202014-12-09 16:39:47 +0000892 word = strtok_r(NULL, " ", wordsave);
Doug Zongker52ae67d2014-09-08 12:22:09 -0700893 *tgt = parse_range(word);
894 }
895
Sami Tolvanen90221202014-12-09 16:39:47 +0000896 word = strtok_r(NULL, " ", wordsave);
Doug Zongker52ae67d2014-09-08 12:22:09 -0700897 *src_blocks = strtol(word, NULL, 0);
898
899 allocate(*src_blocks * BLOCKSIZE, buffer, buffer_alloc);
900
Sami Tolvanen90221202014-12-09 16:39:47 +0000901 word = strtok_r(NULL, " ", wordsave);
Doug Zongker52ae67d2014-09-08 12:22:09 -0700902 if (word[0] == '-' && word[1] == '\0') {
903 // no source ranges, only stashes
904 } else {
905 RangeSet* src = parse_range(word);
Sami Tolvanen90221202014-12-09 16:39:47 +0000906 res = ReadBlocks(src, *buffer, fd);
Doug Zongker52ae67d2014-09-08 12:22:09 -0700907
Sami Tolvanen90221202014-12-09 16:39:47 +0000908 if (overlap && tgt) {
909 *overlap = range_overlaps(src, *tgt);
Doug Zongker52ae67d2014-09-08 12:22:09 -0700910 }
Sami Tolvanen90221202014-12-09 16:39:47 +0000911
Doug Zongker52ae67d2014-09-08 12:22:09 -0700912 free(src);
913
Sami Tolvanen90221202014-12-09 16:39:47 +0000914 if (res == -1) {
915 return -1;
Doug Zongker52ae67d2014-09-08 12:22:09 -0700916 }
917
Sami Tolvanen90221202014-12-09 16:39:47 +0000918 word = strtok_r(NULL, " ", wordsave);
919 if (word == NULL) {
920 // no stashes, only source range
921 return 0;
922 }
923
924 locs = parse_range(word);
Doug Zongker52ae67d2014-09-08 12:22:09 -0700925 MoveRange(*buffer, locs, *buffer);
Sami Tolvanen90221202014-12-09 16:39:47 +0000926 free(locs);
Doug Zongker52ae67d2014-09-08 12:22:09 -0700927 }
928
Sami Tolvanen90221202014-12-09 16:39:47 +0000929 while ((word = strtok_r(NULL, " ", wordsave)) != NULL) {
Doug Zongker52ae67d2014-09-08 12:22:09 -0700930 // Each word is a an index into the stash table, a colon, and
931 // then a rangeset describing where in the source block that
932 // stashed data should go.
Sami Tolvanen90221202014-12-09 16:39:47 +0000933 colonsave = NULL;
934 colon = strtok_r(word, ":", &colonsave);
935
936 res = LoadStash(stashbase, colon, 0, NULL, &stash, &stashalloc, 1);
937
938 if (res == -1) {
939 // These source blocks will fail verification if used later, but we
940 // will let the caller decide if this is a fatal failure
941 fprintf(stderr, "failed to load stash %s\n", colon);
942 continue;
943 }
944
Doug Zongker52ae67d2014-09-08 12:22:09 -0700945 colon = strtok_r(NULL, ":", &colonsave);
Sami Tolvanen90221202014-12-09 16:39:47 +0000946 locs = parse_range(colon);
947
948 MoveRange(*buffer, locs, stash);
Doug Zongker52ae67d2014-09-08 12:22:09 -0700949 free(locs);
950 }
Sami Tolvanen90221202014-12-09 16:39:47 +0000951
952 if (stash) {
953 free(stash);
954 }
955
956 return 0;
957}
958
959// Parameters for transfer list command functions
960typedef struct {
961 char* cmdname;
962 char* cpos;
963 char* freestash;
964 char* stashbase;
965 int canwrite;
966 int createdstash;
967 int fd;
968 int foundwrites;
969 int isunresumable;
970 int version;
971 int written;
972 NewThreadInfo nti;
973 pthread_t thread;
974 size_t bufsize;
975 uint8_t* buffer;
976 uint8_t* patch_start;
977} CommandParameters;
978
979// Do a source/target load for move/bsdiff/imgdiff in version 3.
980//
981// Parameters are the same as for LoadSrcTgtVersion2, except for 'onehash', which
982// tells the function whether to expect separate source and targe block hashes, or
983// if they are both the same and only one hash should be expected, and
984// 'isunresumable', which receives a non-zero value if block verification fails in
985// a way that the update cannot be resumed anymore.
986//
987// If the function is unable to load the necessary blocks or their contents don't
988// match the hashes, the return value is -1 and the command should be aborted.
989//
990// If the return value is 1, the command has already been completed according to
991// the contents of the target blocks, and should not be performed again.
992//
993// If the return value is 0, source blocks have expected content and the command
994// can be performed.
995
996static int LoadSrcTgtVersion3(CommandParameters* params, RangeSet** tgt, int* src_blocks,
997 int onehash, int* overlap) {
998 char* srchash = NULL;
999 char* tgthash = NULL;
1000 int overlap_blocks = 0;
1001 int rc = -1;
1002 uint8_t* tgtbuffer = NULL;
1003
1004 if (!params|| !tgt || !src_blocks || !overlap) {
1005 goto v3out;
1006 }
1007
1008 srchash = strtok_r(NULL, " ", &params->cpos);
1009
1010 if (srchash == NULL) {
1011 fprintf(stderr, "missing source hash\n");
1012 goto v3out;
1013 }
1014
1015 if (onehash) {
1016 tgthash = srchash;
1017 } else {
1018 tgthash = strtok_r(NULL, " ", &params->cpos);
1019
1020 if (tgthash == NULL) {
1021 fprintf(stderr, "missing target hash\n");
1022 goto v3out;
1023 }
1024 }
1025
1026 if (LoadSrcTgtVersion2(&params->cpos, tgt, src_blocks, &params->buffer, &params->bufsize,
1027 params->fd, params->stashbase, overlap) == -1) {
1028 goto v3out;
1029 }
1030
1031 tgtbuffer = (uint8_t*) malloc((*tgt)->size * BLOCKSIZE);
1032
1033 if (tgtbuffer == NULL) {
1034 fprintf(stderr, "failed to allocate %d bytes\n", (*tgt)->size * BLOCKSIZE);
1035 goto v3out;
1036 }
1037
1038 if (ReadBlocks(*tgt, tgtbuffer, params->fd) == -1) {
1039 goto v3out;
1040 }
1041
1042 if (VerifyBlocks(tgthash, tgtbuffer, (*tgt)->size, 0) == 0) {
1043 // Target blocks already have expected content, command should be skipped
1044 rc = 1;
1045 goto v3out;
1046 }
1047
1048 if (VerifyBlocks(srchash, params->buffer, *src_blocks, 1) == 0) {
1049 // If source and target blocks overlap, stash the source blocks so we can
1050 // resume from possible write errors
1051 if (*overlap) {
1052 fprintf(stderr, "stashing %d overlapping blocks to %s\n", *src_blocks,
1053 srchash);
1054
1055 if (WriteStash(params->stashbase, srchash, *src_blocks, params->buffer, 1) != 0) {
1056 fprintf(stderr, "failed to stash overlapping source blocks\n");
1057 goto v3out;
1058 }
1059
1060 // Can be deleted when the write has completed
1061 params->freestash = srchash;
1062 }
1063
1064 // Source blocks have expected content, command can proceed
1065 rc = 0;
1066 goto v3out;
1067 }
1068
1069 if (*overlap && LoadStash(params->stashbase, srchash, 1, NULL, &params->buffer,
1070 &params->bufsize, 1) == 0) {
1071 // Overlapping source blocks were previously stashed, command can proceed
1072 if (params->canwrite) {
1073 // We didn't create the stash, so delete after write only if we will
1074 // actually perform the write
1075 params->freestash = srchash;
1076 }
1077 rc = 0;
1078 goto v3out;
1079 }
1080
1081 // Valid source data not available, update cannot be resumed
1082 fprintf(stderr, "partition has unexpected contents\n");
1083 params->isunresumable = 1;
1084
1085v3out:
1086 if (tgtbuffer) {
1087 free(tgtbuffer);
1088 }
1089
1090 return rc;
1091}
1092
1093static int PerformCommandMove(CommandParameters* params) {
1094 int blocks = 0;
1095 int overlap = 0;
1096 int rc = -1;
1097 int status = 0;
1098 RangeSet* tgt = NULL;
1099
1100 if (!params) {
1101 goto pcmout;
1102 }
1103
1104 if (params->version == 1) {
1105 status = LoadSrcTgtVersion1(&params->cpos, &tgt, &blocks, &params->buffer,
1106 &params->bufsize, params->fd);
1107 } else if (params->version == 2) {
1108 status = LoadSrcTgtVersion2(&params->cpos, &tgt, &blocks, &params->buffer,
1109 &params->bufsize, params->fd, params->stashbase, NULL);
1110 } else if (params->version >= 3) {
1111 status = LoadSrcTgtVersion3(params, &tgt, &blocks, 1, &overlap);
1112 }
1113
1114 if (status == -1) {
1115 fprintf(stderr, "failed to read blocks for move\n");
1116 goto pcmout;
1117 }
1118
1119 if (status == 0) {
1120 params->foundwrites = 1;
1121 } else if (params->foundwrites) {
1122 fprintf(stderr, "warning: commands executed out of order [%s]\n", params->cmdname);
1123 }
1124
1125 if (params->canwrite) {
1126 if (status == 0) {
1127 fprintf(stderr, " moving %d blocks\n", blocks);
1128
1129 if (WriteBlocks(tgt, params->buffer, params->fd) == -1) {
1130 goto pcmout;
1131 }
1132 } else {
1133 fprintf(stderr, "skipping %d already moved blocks\n", blocks);
1134 }
1135
1136 }
1137
1138 if (params->freestash) {
1139 FreeStash(params->stashbase, params->freestash);
1140 params->freestash = NULL;
1141 }
1142
1143 params->written += tgt->size;
1144 rc = 0;
1145
1146pcmout:
1147 if (tgt) {
1148 free(tgt);
1149 }
1150
1151 return rc;
1152}
1153
1154static int PerformCommandStash(CommandParameters* params) {
1155 if (!params) {
1156 return -1;
1157 }
1158
1159 return SaveStash(params->stashbase, &params->cpos, &params->buffer, &params->bufsize,
1160 params->fd, (params->version >= 3), &params->isunresumable);
1161}
1162
1163static int PerformCommandFree(CommandParameters* params) {
1164 if (!params) {
1165 return -1;
1166 }
1167
1168 if (params->createdstash || params->canwrite) {
1169 return FreeStash(params->stashbase, params->cpos);
1170 }
1171
1172 return 0;
1173}
1174
1175static int PerformCommandZero(CommandParameters* params) {
1176 char* range = NULL;
1177 int i;
1178 int j;
1179 int rc = -1;
1180 RangeSet* tgt = NULL;
1181
1182 if (!params) {
1183 goto pczout;
1184 }
1185
1186 range = strtok_r(NULL, " ", &params->cpos);
1187
1188 if (range == NULL) {
1189 fprintf(stderr, "missing target blocks for zero\n");
1190 goto pczout;
1191 }
1192
1193 tgt = parse_range(range);
1194
1195 fprintf(stderr, " zeroing %d blocks\n", tgt->size);
1196
1197 allocate(BLOCKSIZE, &params->buffer, &params->bufsize);
1198 memset(params->buffer, 0, BLOCKSIZE);
1199
1200 if (params->canwrite) {
1201 for (i = 0; i < tgt->count; ++i) {
1202 if (check_lseek(params->fd, (off64_t) tgt->pos[i * 2] * BLOCKSIZE, SEEK_SET) == -1) {
1203 goto pczout;
1204 }
1205
1206 for (j = tgt->pos[i * 2]; j < tgt->pos[i * 2 + 1]; ++j) {
1207 if (write_all(params->fd, params->buffer, BLOCKSIZE) == -1) {
1208 goto pczout;
1209 }
1210 }
1211 }
1212 }
1213
1214 if (params->cmdname[0] == 'z') {
1215 // Update only for the zero command, as the erase command will call
1216 // this if DEBUG_ERASE is defined.
1217 params->written += tgt->size;
1218 }
1219
1220 rc = 0;
1221
1222pczout:
1223 if (tgt) {
1224 free(tgt);
1225 }
1226
1227 return rc;
1228}
1229
1230static int PerformCommandNew(CommandParameters* params) {
1231 char* range = NULL;
1232 int rc = -1;
1233 RangeSet* tgt = NULL;
1234 RangeSinkState rss;
1235
1236 if (!params) {
1237 goto pcnout;
1238 }
1239
1240 range = strtok_r(NULL, " ", &params->cpos);
1241
1242 if (range == NULL) {
1243 goto pcnout;
1244 }
1245
1246 tgt = parse_range(range);
1247
1248 if (params->canwrite) {
1249 fprintf(stderr, " writing %d blocks of new data\n", tgt->size);
1250
1251 rss.fd = params->fd;
1252 rss.tgt = tgt;
1253 rss.p_block = 0;
1254 rss.p_remain = (tgt->pos[1] - tgt->pos[0]) * BLOCKSIZE;
1255
1256 if (check_lseek(params->fd, (off64_t) tgt->pos[0] * BLOCKSIZE, SEEK_SET) == -1) {
1257 goto pcnout;
1258 }
1259
1260 pthread_mutex_lock(&params->nti.mu);
1261 params->nti.rss = &rss;
1262 pthread_cond_broadcast(&params->nti.cv);
1263
1264 while (params->nti.rss) {
1265 pthread_cond_wait(&params->nti.cv, &params->nti.mu);
1266 }
1267
1268 pthread_mutex_unlock(&params->nti.mu);
1269 }
1270
1271 params->written += tgt->size;
1272 rc = 0;
1273
1274pcnout:
1275 if (tgt) {
1276 free(tgt);
1277 }
1278
1279 return rc;
1280}
1281
1282static int PerformCommandDiff(CommandParameters* params) {
1283 char* logparams = NULL;
1284 char* value = NULL;
1285 int blocks = 0;
1286 int overlap = 0;
1287 int rc = -1;
1288 int status = 0;
1289 RangeSet* tgt = NULL;
1290 RangeSinkState rss;
1291 size_t len = 0;
1292 size_t offset = 0;
1293 Value patch_value;
1294
1295 if (!params) {
1296 goto pcdout;
1297 }
1298
1299 logparams = strdup(params->cpos);
1300 value = strtok_r(NULL, " ", &params->cpos);
1301
1302 if (value == NULL) {
1303 fprintf(stderr, "missing patch offset for %s\n", params->cmdname);
1304 goto pcdout;
1305 }
1306
1307 offset = strtoul(value, NULL, 0);
1308
1309 value = strtok_r(NULL, " ", &params->cpos);
1310
1311 if (value == NULL) {
1312 fprintf(stderr, "missing patch length for %s\n", params->cmdname);
1313 goto pcdout;
1314 }
1315
1316 len = strtoul(value, NULL, 0);
1317
1318 if (params->version == 1) {
1319 status = LoadSrcTgtVersion1(&params->cpos, &tgt, &blocks, &params->buffer,
1320 &params->bufsize, params->fd);
1321 } else if (params->version == 2) {
1322 status = LoadSrcTgtVersion2(&params->cpos, &tgt, &blocks, &params->buffer,
1323 &params->bufsize, params->fd, params->stashbase, NULL);
1324 } else if (params->version >= 3) {
1325 status = LoadSrcTgtVersion3(params, &tgt, &blocks, 0, &overlap);
1326 }
1327
1328 if (status == -1) {
1329 fprintf(stderr, "failed to read blocks for diff\n");
1330 goto pcdout;
1331 }
1332
1333 if (status == 0) {
1334 params->foundwrites = 1;
1335 } else if (params->foundwrites) {
1336 fprintf(stderr, "warning: commands executed out of order [%s]\n", params->cmdname);
1337 }
1338
1339 if (params->canwrite) {
1340 if (status == 0) {
1341 fprintf(stderr, "patching %d blocks to %d\n", blocks, tgt->size);
1342
1343 patch_value.type = VAL_BLOB;
1344 patch_value.size = len;
1345 patch_value.data = (char*) (params->patch_start + offset);
1346
1347 rss.fd = params->fd;
1348 rss.tgt = tgt;
1349 rss.p_block = 0;
1350 rss.p_remain = (tgt->pos[1] - tgt->pos[0]) * BLOCKSIZE;
1351
1352 if (check_lseek(params->fd, (off64_t) tgt->pos[0] * BLOCKSIZE, SEEK_SET) == -1) {
1353 goto pcdout;
1354 }
1355
1356 if (params->cmdname[0] == 'i') { // imgdiff
1357 ApplyImagePatch(params->buffer, blocks * BLOCKSIZE, &patch_value,
1358 &RangeSinkWrite, &rss, NULL, NULL);
1359 } else {
1360 ApplyBSDiffPatch(params->buffer, blocks * BLOCKSIZE, &patch_value,
1361 0, &RangeSinkWrite, &rss, NULL);
1362 }
1363
1364 // We expect the output of the patcher to fill the tgt ranges exactly.
1365 if (rss.p_block != tgt->count || rss.p_remain != 0) {
1366 fprintf(stderr, "range sink underrun?\n");
1367 }
1368 } else {
1369 fprintf(stderr, "skipping %d blocks already patched to %d [%s]\n",
1370 blocks, tgt->size, logparams);
1371 }
1372 }
1373
1374 if (params->freestash) {
1375 FreeStash(params->stashbase, params->freestash);
1376 params->freestash = NULL;
1377 }
1378
1379 params->written += tgt->size;
1380 rc = 0;
1381
1382pcdout:
1383 if (logparams) {
1384 free(logparams);
1385 }
1386
1387 if (tgt) {
1388 free(tgt);
1389 }
1390
1391 return rc;
1392}
1393
1394static int PerformCommandErase(CommandParameters* params) {
1395 char* range = NULL;
1396 int i;
1397 int rc = -1;
1398 RangeSet* tgt = NULL;
1399 struct stat st;
1400 uint64_t blocks[2];
1401
1402 if (DEBUG_ERASE) {
1403 return PerformCommandZero(params);
1404 }
1405
1406 if (!params) {
1407 goto pceout;
1408 }
1409
1410 if (fstat(params->fd, &st) == -1) {
Elliott Hughes1fdd4522015-03-23 13:33:57 -07001411 fprintf(stderr, "failed to fstat device to erase: %s\n", strerror(errno));
Sami Tolvanen90221202014-12-09 16:39:47 +00001412 goto pceout;
1413 }
1414
1415 if (!S_ISBLK(st.st_mode)) {
1416 fprintf(stderr, "not a block device; skipping erase\n");
1417 rc = 0;
1418 goto pceout;
1419 }
1420
1421 range = strtok_r(NULL, " ", &params->cpos);
1422
1423 if (range == NULL) {
1424 fprintf(stderr, "missing target blocks for zero\n");
1425 goto pceout;
1426 }
1427
1428 tgt = parse_range(range);
1429
1430 if (params->canwrite) {
1431 fprintf(stderr, " erasing %d blocks\n", tgt->size);
1432
1433 for (i = 0; i < tgt->count; ++i) {
1434 // offset in bytes
1435 blocks[0] = tgt->pos[i * 2] * (uint64_t) BLOCKSIZE;
1436 // length in bytes
1437 blocks[1] = (tgt->pos[i * 2 + 1] - tgt->pos[i * 2]) * (uint64_t) BLOCKSIZE;
1438
1439 if (ioctl(params->fd, BLKDISCARD, &blocks) == -1) {
Elliott Hughes1fdd4522015-03-23 13:33:57 -07001440 fprintf(stderr, "BLKDISCARD ioctl failed: %s\n", strerror(errno));
Sami Tolvanen90221202014-12-09 16:39:47 +00001441 // Continue anyway, nothing we can do
1442 }
1443 }
1444 }
1445
1446 rc = 0;
1447
1448pceout:
1449 if (tgt) {
1450 free(tgt);
1451 }
1452
1453 return rc;
1454}
1455
1456// Definitions for transfer list command functions
1457typedef int (*CommandFunction)(CommandParameters*);
1458
1459typedef struct {
1460 const char* name;
1461 CommandFunction f;
1462} Command;
1463
1464// CompareCommands and CompareCommandNames are for the hash table
1465
1466static int CompareCommands(const void* c1, const void* c2) {
1467 return strcmp(((const Command*) c1)->name, ((const Command*) c2)->name);
1468}
1469
1470static int CompareCommandNames(const void* c1, const void* c2) {
1471 return strcmp(((const Command*) c1)->name, (const char*) c2);
1472}
1473
1474// HashString is used to hash command names for the hash table
1475
1476static unsigned int HashString(const char *s) {
1477 unsigned int hash = 0;
1478 if (s) {
1479 while (*s) {
1480 hash = hash * 33 + *s++;
1481 }
1482 }
1483 return hash;
Doug Zongker52ae67d2014-09-08 12:22:09 -07001484}
1485
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001486// args:
1487// - block device (or file) to modify in-place
1488// - transfer list (blob)
1489// - new data stream (filename within package.zip)
1490// - patch stream (filename within package.zip, must be uncompressed)
1491
Sami Tolvanen90221202014-12-09 16:39:47 +00001492static Value* PerformBlockImageUpdate(const char* name, State* state, int argc, Expr* argv[],
1493 const Command* commands, int cmdcount, int dryrun) {
1494
1495 char* line = NULL;
1496 char* linesave = NULL;
1497 char* logcmd = NULL;
Doug Zongker1d5d6092014-08-21 10:47:24 -07001498 char* transfer_list = NULL;
Sami Tolvanen90221202014-12-09 16:39:47 +00001499 CommandParameters params;
1500 const Command* cmd = NULL;
1501 const ZipEntry* new_entry = NULL;
1502 const ZipEntry* patch_entry = NULL;
1503 FILE* cmd_pipe = NULL;
1504 HashTable* cmdht = NULL;
1505 int i;
1506 int res;
1507 int rc = -1;
1508 int stash_max_blocks = 0;
1509 int total_blocks = 0;
1510 pthread_attr_t attr;
1511 unsigned int cmdhash;
1512 UpdaterInfo* ui = NULL;
1513 Value* blockdev_filename = NULL;
1514 Value* new_data_fn = NULL;
1515 Value* patch_data_fn = NULL;
1516 Value* transfer_list_value = NULL;
1517 ZipArchive* za = NULL;
1518
1519 memset(&params, 0, sizeof(params));
1520 params.canwrite = !dryrun;
1521
1522 fprintf(stderr, "performing %s\n", dryrun ? "verification" : "update");
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001523
Doug Zongker1d5d6092014-08-21 10:47:24 -07001524 if (ReadValueArgs(state, argv, 4, &blockdev_filename, &transfer_list_value,
Sami Tolvanen90221202014-12-09 16:39:47 +00001525 &new_data_fn, &patch_data_fn) < 0) {
1526 goto pbiudone;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001527 }
1528
1529 if (blockdev_filename->type != VAL_STRING) {
1530 ErrorAbort(state, "blockdev_filename argument to %s must be string", name);
Sami Tolvanen90221202014-12-09 16:39:47 +00001531 goto pbiudone;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001532 }
Doug Zongker1d5d6092014-08-21 10:47:24 -07001533 if (transfer_list_value->type != VAL_BLOB) {
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001534 ErrorAbort(state, "transfer_list argument to %s must be blob", name);
Sami Tolvanen90221202014-12-09 16:39:47 +00001535 goto pbiudone;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001536 }
1537 if (new_data_fn->type != VAL_STRING) {
1538 ErrorAbort(state, "new_data_fn argument to %s must be string", name);
Sami Tolvanen90221202014-12-09 16:39:47 +00001539 goto pbiudone;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001540 }
1541 if (patch_data_fn->type != VAL_STRING) {
1542 ErrorAbort(state, "patch_data_fn argument to %s must be string", name);
Sami Tolvanen90221202014-12-09 16:39:47 +00001543 goto pbiudone;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001544 }
1545
Sami Tolvanen90221202014-12-09 16:39:47 +00001546 ui = (UpdaterInfo*) state->cookie;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001547
Sami Tolvanen90221202014-12-09 16:39:47 +00001548 if (ui == NULL) {
1549 goto pbiudone;
1550 }
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001551
Sami Tolvanen90221202014-12-09 16:39:47 +00001552 cmd_pipe = ui->cmd_pipe;
1553 za = ui->package_zip;
1554
1555 if (cmd_pipe == NULL || za == NULL) {
1556 goto pbiudone;
1557 }
1558
1559 patch_entry = mzFindZipEntry(za, patch_data_fn->data);
1560
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001561 if (patch_entry == NULL) {
Sami Tolvanen90221202014-12-09 16:39:47 +00001562 fprintf(stderr, "%s(): no file \"%s\" in package", name, patch_data_fn->data);
1563 goto pbiudone;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001564 }
1565
Sami Tolvanen90221202014-12-09 16:39:47 +00001566 params.patch_start = ui->package_zip_addr + mzGetZipEntryOffset(patch_entry);
1567 new_entry = mzFindZipEntry(za, new_data_fn->data);
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001568
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001569 if (new_entry == NULL) {
Sami Tolvanen90221202014-12-09 16:39:47 +00001570 fprintf(stderr, "%s(): no file \"%s\" in package", name, new_data_fn->data);
1571 goto pbiudone;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001572 }
1573
Sami Tolvanen90221202014-12-09 16:39:47 +00001574 params.fd = TEMP_FAILURE_RETRY(open(blockdev_filename->data, O_RDWR));
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001575
Sami Tolvanen90221202014-12-09 16:39:47 +00001576 if (params.fd == -1) {
Elliott Hughes1fdd4522015-03-23 13:33:57 -07001577 fprintf(stderr, "open \"%s\" failed: %s\n", blockdev_filename->data, strerror(errno));
Sami Tolvanen90221202014-12-09 16:39:47 +00001578 goto pbiudone;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001579 }
1580
Sami Tolvanen90221202014-12-09 16:39:47 +00001581 if (params.canwrite) {
1582 params.nti.za = za;
1583 params.nti.entry = new_entry;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001584
Sami Tolvanen90221202014-12-09 16:39:47 +00001585 pthread_mutex_init(&params.nti.mu, NULL);
1586 pthread_cond_init(&params.nti.cv, NULL);
1587 pthread_attr_init(&attr);
1588 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
1589
Elliott Hughes1fdd4522015-03-23 13:33:57 -07001590 int error = pthread_create(&params.thread, &attr, unzip_new_data, &params.nti);
1591 if (error != 0) {
1592 fprintf(stderr, "pthread_create failed: %s\n", strerror(error));
Sami Tolvanen90221202014-12-09 16:39:47 +00001593 goto pbiudone;
1594 }
1595 }
1596
1597 // The data in transfer_list_value is not necessarily null-terminated, so we need
1598 // to copy it to a new buffer and add the null that strtok_r will need.
1599 transfer_list = malloc(transfer_list_value->size + 1);
1600
Doug Zongker1d5d6092014-08-21 10:47:24 -07001601 if (transfer_list == NULL) {
1602 fprintf(stderr, "failed to allocate %zd bytes for transfer list\n",
Sami Tolvanen90221202014-12-09 16:39:47 +00001603 transfer_list_value->size + 1);
1604 goto pbiudone;
Doug Zongker1d5d6092014-08-21 10:47:24 -07001605 }
Sami Tolvanen90221202014-12-09 16:39:47 +00001606
Doug Zongker1d5d6092014-08-21 10:47:24 -07001607 memcpy(transfer_list, transfer_list_value->data, transfer_list_value->size);
1608 transfer_list[transfer_list_value->size] = '\0';
1609
Sami Tolvanen90221202014-12-09 16:39:47 +00001610 // First line in transfer list is the version number
Doug Zongker1d5d6092014-08-21 10:47:24 -07001611 line = strtok_r(transfer_list, "\n", &linesave);
Sami Tolvanen90221202014-12-09 16:39:47 +00001612 params.version = strtol(line, NULL, 0);
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001613
Sami Tolvanen90221202014-12-09 16:39:47 +00001614 if (params.version < 1 || params.version > 3) {
1615 fprintf(stderr, "unexpected transfer list version [%s]\n", line);
1616 goto pbiudone;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001617 }
1618
Sami Tolvanen90221202014-12-09 16:39:47 +00001619 fprintf(stderr, "blockimg version is %d\n", params.version);
1620
1621 // Second line in transfer list is the total number of blocks we expect to write
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001622 line = strtok_r(NULL, "\n", &linesave);
Sami Tolvanen90221202014-12-09 16:39:47 +00001623 total_blocks = strtol(line, NULL, 0);
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001624
Sami Tolvanen90221202014-12-09 16:39:47 +00001625 if (total_blocks < 0) {
1626 ErrorAbort(state, "unexpected block count [%s]\n", line);
1627 goto pbiudone;
1628 } else if (total_blocks == 0) {
1629 rc = 0;
1630 goto pbiudone;
1631 }
1632
1633 if (params.version >= 2) {
1634 // Third line is how many stash entries are needed simultaneously
Doug Zongker52ae67d2014-09-08 12:22:09 -07001635 line = strtok_r(NULL, "\n", &linesave);
Sami Tolvanen90221202014-12-09 16:39:47 +00001636 fprintf(stderr, "maximum stash entries %s\n", line);
Doug Zongker52ae67d2014-09-08 12:22:09 -07001637
Sami Tolvanen90221202014-12-09 16:39:47 +00001638 // Fourth line is the maximum number of blocks that will be stashed simultaneously
1639 line = strtok_r(NULL, "\n", &linesave);
1640 stash_max_blocks = strtol(line, NULL, 0);
1641
1642 if (stash_max_blocks < 0) {
1643 ErrorAbort(state, "unexpected maximum stash blocks [%s]\n", line);
1644 goto pbiudone;
Doug Zongker52ae67d2014-09-08 12:22:09 -07001645 }
1646
Jesse Zhao1df64d32015-02-17 17:09:23 -08001647 if (stash_max_blocks >= 0) {
Sami Tolvanen90221202014-12-09 16:39:47 +00001648 res = CreateStash(state, stash_max_blocks, blockdev_filename->data,
1649 &params.stashbase);
1650
1651 if (res == -1) {
1652 goto pbiudone;
1653 }
1654
1655 params.createdstash = res;
1656 }
Doug Zongker52ae67d2014-09-08 12:22:09 -07001657 }
1658
Sami Tolvanen90221202014-12-09 16:39:47 +00001659 // Build a hash table of the available commands
1660 cmdht = mzHashTableCreate(cmdcount, NULL);
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001661
Sami Tolvanen90221202014-12-09 16:39:47 +00001662 for (i = 0; i < cmdcount; ++i) {
1663 cmdhash = HashString(commands[i].name);
1664 mzHashTableLookup(cmdht, cmdhash, (void*) &commands[i], CompareCommands, true);
1665 }
1666
1667 // Subsequent lines are all individual transfer commands
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001668 for (line = strtok_r(NULL, "\n", &linesave); line;
1669 line = strtok_r(NULL, "\n", &linesave)) {
Doug Zongker52ae67d2014-09-08 12:22:09 -07001670
Sami Tolvanen90221202014-12-09 16:39:47 +00001671 logcmd = strdup(line);
1672 params.cmdname = strtok_r(line, " ", &params.cpos);
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001673
Sami Tolvanen90221202014-12-09 16:39:47 +00001674 if (params.cmdname == NULL) {
1675 fprintf(stderr, "missing command [%s]\n", line);
1676 goto pbiudone;
1677 }
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001678
Sami Tolvanen90221202014-12-09 16:39:47 +00001679 cmdhash = HashString(params.cmdname);
1680 cmd = (const Command*) mzHashTableLookup(cmdht, cmdhash, params.cmdname,
1681 CompareCommandNames, false);
Doug Zongker52ae67d2014-09-08 12:22:09 -07001682
Sami Tolvanen90221202014-12-09 16:39:47 +00001683 if (cmd == NULL) {
1684 fprintf(stderr, "unexpected command [%s]\n", params.cmdname);
1685 goto pbiudone;
1686 }
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001687
Sami Tolvanen90221202014-12-09 16:39:47 +00001688 if (cmd->f != NULL && cmd->f(&params) == -1) {
1689 fprintf(stderr, "failed to execute command [%s]\n",
1690 logcmd ? logcmd : params.cmdname);
1691 goto pbiudone;
1692 }
1693
1694 if (logcmd) {
1695 free(logcmd);
1696 logcmd = NULL;
1697 }
1698
1699 if (params.canwrite) {
1700 fprintf(cmd_pipe, "set_progress %.4f\n", (double) params.written / total_blocks);
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001701 fflush(cmd_pipe);
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001702 }
1703 }
1704
Sami Tolvanen90221202014-12-09 16:39:47 +00001705 if (params.canwrite) {
1706 pthread_join(params.thread, NULL);
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001707
Sami Tolvanen90221202014-12-09 16:39:47 +00001708 fprintf(stderr, "wrote %d blocks; expected %d\n", params.written, total_blocks);
1709 fprintf(stderr, "max alloc needed was %zu\n", params.bufsize);
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001710
Sami Tolvanen90221202014-12-09 16:39:47 +00001711 // Delete stash only after successfully completing the update, as it
1712 // may contain blocks needed to complete the update later.
1713 DeleteStash(params.stashbase);
1714 } else {
1715 fprintf(stderr, "verified partition contents; update may be resumed\n");
1716 }
1717
1718 rc = 0;
1719
1720pbiudone:
1721 if (params.fd != -1) {
1722 if (fsync(params.fd) == -1) {
Elliott Hughes1fdd4522015-03-23 13:33:57 -07001723 fprintf(stderr, "fsync failed: %s\n", strerror(errno));
Sami Tolvanen90221202014-12-09 16:39:47 +00001724 }
1725 TEMP_FAILURE_RETRY(close(params.fd));
1726 }
1727
1728 if (logcmd) {
1729 free(logcmd);
1730 }
1731
1732 if (cmdht) {
1733 mzHashTableFree(cmdht);
1734 }
1735
1736 if (params.buffer) {
1737 free(params.buffer);
1738 }
1739
1740 if (transfer_list) {
1741 free(transfer_list);
1742 }
1743
1744 if (blockdev_filename) {
1745 FreeValue(blockdev_filename);
1746 }
1747
1748 if (transfer_list_value) {
1749 FreeValue(transfer_list_value);
1750 }
1751
1752 if (new_data_fn) {
1753 FreeValue(new_data_fn);
1754 }
1755
1756 if (patch_data_fn) {
1757 FreeValue(patch_data_fn);
1758 }
1759
1760 // Only delete the stash if the update cannot be resumed, or it's
1761 // a verification run and we created the stash.
1762 if (params.isunresumable || (!params.canwrite && params.createdstash)) {
1763 DeleteStash(params.stashbase);
1764 }
1765
1766 if (params.stashbase) {
1767 free(params.stashbase);
1768 }
1769
1770 return StringValue(rc == 0 ? strdup("t") : strdup(""));
1771}
1772
1773// The transfer list is a text file containing commands to
1774// transfer data from one place to another on the target
1775// partition. We parse it and execute the commands in order:
1776//
1777// zero [rangeset]
1778// - fill the indicated blocks with zeros
1779//
1780// new [rangeset]
1781// - fill the blocks with data read from the new_data file
1782//
1783// erase [rangeset]
1784// - mark the given blocks as empty
1785//
1786// move <...>
1787// bsdiff <patchstart> <patchlen> <...>
1788// imgdiff <patchstart> <patchlen> <...>
1789// - read the source blocks, apply a patch (or not in the
1790// case of move), write result to target blocks. bsdiff or
1791// imgdiff specifies the type of patch; move means no patch
1792// at all.
1793//
1794// The format of <...> differs between versions 1 and 2;
1795// see the LoadSrcTgtVersion{1,2}() functions for a
1796// description of what's expected.
1797//
1798// stash <stash_id> <src_range>
1799// - (version 2+ only) load the given source range and stash
1800// the data in the given slot of the stash table.
1801//
1802// The creator of the transfer list will guarantee that no block
1803// is read (ie, used as the source for a patch or move) after it
1804// has been written.
1805//
1806// In version 2, the creator will guarantee that a given stash is
1807// loaded (with a stash command) before it's used in a
1808// move/bsdiff/imgdiff command.
1809//
1810// Within one command the source and target ranges may overlap so
1811// in general we need to read the entire source into memory before
1812// writing anything to the target blocks.
1813//
1814// All the patch data is concatenated into one patch_data file in
1815// the update package. It must be stored uncompressed because we
1816// memory-map it in directly from the archive. (Since patches are
1817// already compressed, we lose very little by not compressing
1818// their concatenation.)
1819//
1820// In version 3, commands that read data from the partition (i.e.
1821// move/bsdiff/imgdiff/stash) have one or more additional hashes
1822// before the range parameters, which are used to check if the
1823// command has already been completed and verify the integrity of
1824// the source data.
1825
1826Value* BlockImageVerifyFn(const char* name, State* state, int argc, Expr* argv[]) {
1827 // Commands which are not tested are set to NULL to skip them completely
1828 const Command commands[] = {
1829 { "bsdiff", PerformCommandDiff },
1830 { "erase", NULL },
1831 { "free", PerformCommandFree },
1832 { "imgdiff", PerformCommandDiff },
1833 { "move", PerformCommandMove },
1834 { "new", NULL },
1835 { "stash", PerformCommandStash },
1836 { "zero", NULL }
1837 };
1838
1839 // Perform a dry run without writing to test if an update can proceed
1840 return PerformBlockImageUpdate(name, state, argc, argv, commands,
1841 sizeof(commands) / sizeof(commands[0]), 1);
1842}
1843
1844Value* BlockImageUpdateFn(const char* name, State* state, int argc, Expr* argv[]) {
1845 const Command commands[] = {
1846 { "bsdiff", PerformCommandDiff },
1847 { "erase", PerformCommandErase },
1848 { "free", PerformCommandFree },
1849 { "imgdiff", PerformCommandDiff },
1850 { "move", PerformCommandMove },
1851 { "new", PerformCommandNew },
1852 { "stash", PerformCommandStash },
1853 { "zero", PerformCommandZero }
1854 };
1855
1856 return PerformBlockImageUpdate(name, state, argc, argv, commands,
1857 sizeof(commands) / sizeof(commands[0]), 0);
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001858}
1859
1860Value* RangeSha1Fn(const char* name, State* state, int argc, Expr* argv[]) {
1861 Value* blockdev_filename;
1862 Value* ranges;
1863 const uint8_t* digest = NULL;
1864 if (ReadValueArgs(state, argv, 2, &blockdev_filename, &ranges) < 0) {
1865 return NULL;
1866 }
1867
1868 if (blockdev_filename->type != VAL_STRING) {
1869 ErrorAbort(state, "blockdev_filename argument to %s must be string", name);
1870 goto done;
1871 }
1872 if (ranges->type != VAL_STRING) {
1873 ErrorAbort(state, "ranges argument to %s must be string", name);
1874 goto done;
1875 }
1876
1877 int fd = open(blockdev_filename->data, O_RDWR);
1878 if (fd < 0) {
Elliott Hughes1fdd4522015-03-23 13:33:57 -07001879 ErrorAbort(state, "open \"%s\" failed: %s", blockdev_filename->data, strerror(errno));
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001880 goto done;
1881 }
1882
1883 RangeSet* rs = parse_range(ranges->data);
1884 uint8_t buffer[BLOCKSIZE];
1885
1886 SHA_CTX ctx;
1887 SHA_init(&ctx);
1888
1889 int i, j;
1890 for (i = 0; i < rs->count; ++i) {
Sami Tolvanen90221202014-12-09 16:39:47 +00001891 if (check_lseek(fd, (off64_t)rs->pos[i*2] * BLOCKSIZE, SEEK_SET) == -1) {
1892 ErrorAbort(state, "failed to seek %s: %s", blockdev_filename->data,
1893 strerror(errno));
1894 goto done;
1895 }
1896
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001897 for (j = rs->pos[i*2]; j < rs->pos[i*2+1]; ++j) {
Sami Tolvanen90221202014-12-09 16:39:47 +00001898 if (read_all(fd, buffer, BLOCKSIZE) == -1) {
1899 ErrorAbort(state, "failed to read %s: %s", blockdev_filename->data,
1900 strerror(errno));
1901 goto done;
1902 }
1903
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001904 SHA_update(&ctx, buffer, BLOCKSIZE);
1905 }
1906 }
1907 digest = SHA_final(&ctx);
1908 close(fd);
1909
1910 done:
1911 FreeValue(blockdev_filename);
1912 FreeValue(ranges);
1913 if (digest == NULL) {
1914 return StringValue(strdup(""));
1915 } else {
1916 return StringValue(PrintSha1(digest));
1917 }
1918}
1919
1920void RegisterBlockImageFunctions() {
Sami Tolvanen90221202014-12-09 16:39:47 +00001921 RegisterFunction("block_image_verify", BlockImageVerifyFn);
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001922 RegisterFunction("block_image_update", BlockImageUpdateFn);
1923 RegisterFunction("range_sha1", RangeSha1Fn);
1924}