blob: 310bbf94c846fe7b6cb7e70f9f8b56d46be2e950 [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>
Tao Baobe19dce2015-07-31 15:56:44 -070022#include <libgen.h>
Tao Bao818fa782015-06-23 23:23:33 -070023#include <linux/fs.h>
Doug Zongkerbc7ffed2014-08-15 14:31:52 -070024#include <pthread.h>
25#include <stdarg.h>
26#include <stdio.h>
27#include <stdlib.h>
28#include <string.h>
Sami Tolvanen90221202014-12-09 16:39:47 +000029#include <sys/stat.h>
Doug Zongkerbc7ffed2014-08-15 14:31:52 -070030#include <sys/types.h>
31#include <sys/wait.h>
32#include <sys/ioctl.h>
33#include <time.h>
34#include <unistd.h>
35
36#include "applypatch/applypatch.h"
37#include "edify/expr.h"
38#include "mincrypt/sha.h"
Sami Tolvanen90221202014-12-09 16:39:47 +000039#include "minzip/Hash.h"
Doug Zongkerbc7ffed2014-08-15 14:31:52 -070040#include "updater.h"
41
42#define BLOCKSIZE 4096
43
Sami Tolvanen6abd52f2015-06-10 15:52:04 +000044// Set this to 0 to interpret 'erase' transfers to mean do a
45// BLKDISCARD ioctl (the normal behavior). Set to 1 to interpret
46// erase to mean fill the region with zeroes.
47#define DEBUG_ERASE 0
48
Sami Tolvanen90221202014-12-09 16:39:47 +000049#define STASH_DIRECTORY_BASE "/cache/recovery"
50#define STASH_DIRECTORY_MODE 0700
51#define STASH_FILE_MODE 0600
52
Doug Zongkerbc7ffed2014-08-15 14:31:52 -070053char* PrintSha1(const uint8_t* digest);
54
55typedef struct {
56 int count;
57 int size;
58 int pos[0];
59} RangeSet;
60
Sami Tolvanen806f72f2015-05-12 12:48:46 +010061#define RANGESET_MAX_POINTS \
62 ((int)((INT_MAX / sizeof(int)) - sizeof(RangeSet)))
63
Doug Zongkerbc7ffed2014-08-15 14:31:52 -070064static RangeSet* parse_range(char* text) {
65 char* save;
Sami Tolvanen806f72f2015-05-12 12:48:46 +010066 char* token;
Tao Bao818fa782015-06-23 23:23:33 -070067 int num;
Sami Tolvanen806f72f2015-05-12 12:48:46 +010068 long int val;
69 RangeSet* out = NULL;
70 size_t bufsize;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -070071
Sami Tolvanen806f72f2015-05-12 12:48:46 +010072 if (!text) {
73 goto err;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -070074 }
Sami Tolvanen806f72f2015-05-12 12:48:46 +010075
76 token = strtok_r(text, ",", &save);
77
78 if (!token) {
79 goto err;
80 }
81
82 val = strtol(token, NULL, 0);
83
84 if (val < 2 || val > RANGESET_MAX_POINTS) {
85 goto err;
86 } else if (val % 2) {
87 goto err; // must be even
88 }
89
90 num = (int) val;
91 bufsize = sizeof(RangeSet) + num * sizeof(int);
92
Tao Bao818fa782015-06-23 23:23:33 -070093 out = reinterpret_cast<RangeSet*>(malloc(bufsize));
Sami Tolvanen806f72f2015-05-12 12:48:46 +010094
95 if (!out) {
96 fprintf(stderr, "failed to allocate range of %zu bytes\n", bufsize);
97 goto err;
98 }
99
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700100 out->count = num / 2;
101 out->size = 0;
Sami Tolvanen806f72f2015-05-12 12:48:46 +0100102
Tao Bao818fa782015-06-23 23:23:33 -0700103 for (int i = 0; i < num; ++i) {
Sami Tolvanen806f72f2015-05-12 12:48:46 +0100104 token = strtok_r(NULL, ",", &save);
105
106 if (!token) {
107 goto err;
108 }
109
110 val = strtol(token, NULL, 0);
111
112 if (val < 0 || val > INT_MAX) {
113 goto err;
114 }
115
116 out->pos[i] = (int) val;
117
118 if (i % 2) {
119 if (out->pos[i - 1] >= out->pos[i]) {
120 goto err; // empty or negative range
121 }
122
123 if (out->size > INT_MAX - out->pos[i]) {
124 goto err; // overflow
125 }
126
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700127 out->size += out->pos[i];
128 } else {
Sami Tolvanen806f72f2015-05-12 12:48:46 +0100129 if (out->size < 0) {
130 goto err;
131 }
132
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700133 out->size -= out->pos[i];
134 }
135 }
136
Sami Tolvanen806f72f2015-05-12 12:48:46 +0100137 if (out->size <= 0) {
138 goto err;
139 }
140
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700141 return out;
Sami Tolvanen806f72f2015-05-12 12:48:46 +0100142
143err:
144 fprintf(stderr, "failed to parse range '%s'\n", text ? text : "NULL");
145 exit(1);
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700146}
147
Sami Tolvanen90221202014-12-09 16:39:47 +0000148static int range_overlaps(RangeSet* r1, RangeSet* r2) {
149 int i, j, r1_0, r1_1, r2_0, r2_1;
150
151 if (!r1 || !r2) {
152 return 0;
153 }
154
155 for (i = 0; i < r1->count; ++i) {
156 r1_0 = r1->pos[i * 2];
157 r1_1 = r1->pos[i * 2 + 1];
158
159 for (j = 0; j < r2->count; ++j) {
160 r2_0 = r2->pos[j * 2];
161 r2_1 = r2->pos[j * 2 + 1];
162
Tao Baoc3dddce2015-06-25 14:00:31 -0700163 if (!(r2_0 >= r1_1 || r1_0 >= r2_1)) {
Sami Tolvanen90221202014-12-09 16:39:47 +0000164 return 1;
165 }
166 }
167 }
168
169 return 0;
170}
171
172static int read_all(int fd, uint8_t* data, size_t size) {
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700173 size_t so_far = 0;
174 while (so_far < size) {
Elliott Hughes2f5feed2015-04-28 17:24:24 -0700175 ssize_t r = TEMP_FAILURE_RETRY(read(fd, data+so_far, size-so_far));
176 if (r == -1) {
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700177 fprintf(stderr, "read failed: %s\n", strerror(errno));
Sami Tolvanen90221202014-12-09 16:39:47 +0000178 return -1;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700179 }
Elliott Hughes2f5feed2015-04-28 17:24:24 -0700180 so_far += r;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700181 }
Sami Tolvanen90221202014-12-09 16:39:47 +0000182 return 0;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700183}
184
Sami Tolvanen90221202014-12-09 16:39:47 +0000185static int write_all(int fd, const uint8_t* data, size_t size) {
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700186 size_t written = 0;
187 while (written < size) {
Elliott Hughes2f5feed2015-04-28 17:24:24 -0700188 ssize_t w = TEMP_FAILURE_RETRY(write(fd, data+written, size-written));
189 if (w == -1) {
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700190 fprintf(stderr, "write failed: %s\n", strerror(errno));
Sami Tolvanen90221202014-12-09 16:39:47 +0000191 return -1;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700192 }
Elliott Hughes2f5feed2015-04-28 17:24:24 -0700193 written += w;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700194 }
Sami Tolvanen90221202014-12-09 16:39:47 +0000195
196 if (fsync(fd) == -1) {
197 fprintf(stderr, "fsync failed: %s\n", strerror(errno));
198 return -1;
199 }
200
201 return 0;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700202}
203
Elliott Hughes2f5feed2015-04-28 17:24:24 -0700204static bool check_lseek(int fd, off64_t offset, int whence) {
205 off64_t rc = TEMP_FAILURE_RETRY(lseek64(fd, offset, whence));
206 if (rc == -1) {
207 fprintf(stderr, "lseek64 failed: %s\n", strerror(errno));
208 return false;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700209 }
Elliott Hughes2f5feed2015-04-28 17:24:24 -0700210 return true;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700211}
212
213static void allocate(size_t size, uint8_t** buffer, size_t* buffer_alloc) {
214 // if the buffer's big enough, reuse it.
215 if (size <= *buffer_alloc) return;
216
217 free(*buffer);
218
219 *buffer = (uint8_t*) malloc(size);
220 if (*buffer == NULL) {
Doug Zongker1d5d6092014-08-21 10:47:24 -0700221 fprintf(stderr, "failed to allocate %zu bytes\n", size);
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700222 exit(1);
223 }
224 *buffer_alloc = size;
225}
226
227typedef struct {
228 int fd;
229 RangeSet* tgt;
230 int p_block;
231 size_t p_remain;
232} RangeSinkState;
233
234static ssize_t RangeSinkWrite(const uint8_t* data, ssize_t size, void* token) {
235 RangeSinkState* rss = (RangeSinkState*) token;
236
237 if (rss->p_remain <= 0) {
238 fprintf(stderr, "range sink write overrun");
Sami Tolvanen90221202014-12-09 16:39:47 +0000239 return 0;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700240 }
241
242 ssize_t written = 0;
243 while (size > 0) {
244 size_t write_now = size;
Sami Tolvanen90221202014-12-09 16:39:47 +0000245
246 if (rss->p_remain < write_now) {
247 write_now = rss->p_remain;
248 }
249
250 if (write_all(rss->fd, data, write_now) == -1) {
251 break;
252 }
253
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700254 data += write_now;
255 size -= write_now;
256
257 rss->p_remain -= write_now;
258 written += write_now;
259
260 if (rss->p_remain == 0) {
261 // move to the next block
262 ++rss->p_block;
263 if (rss->p_block < rss->tgt->count) {
Sami Tolvanen90221202014-12-09 16:39:47 +0000264 rss->p_remain = (rss->tgt->pos[rss->p_block * 2 + 1] -
265 rss->tgt->pos[rss->p_block * 2]) * BLOCKSIZE;
266
Elliott Hughes2f5feed2015-04-28 17:24:24 -0700267 if (!check_lseek(rss->fd, (off64_t)rss->tgt->pos[rss->p_block*2] * BLOCKSIZE,
268 SEEK_SET)) {
Sami Tolvanen90221202014-12-09 16:39:47 +0000269 break;
270 }
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700271 } else {
272 // we can't write any more; return how many bytes have
273 // been written so far.
Sami Tolvanen90221202014-12-09 16:39:47 +0000274 break;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700275 }
276 }
277 }
278
279 return written;
280}
281
282// All of the data for all the 'new' transfers is contained in one
283// file in the update package, concatenated together in the order in
284// which transfers.list will need it. We want to stream it out of the
285// archive (it's compressed) without writing it to a temp file, but we
286// can't write each section until it's that transfer's turn to go.
287//
288// To achieve this, we expand the new data from the archive in a
289// background thread, and block that threads 'receive uncompressed
290// data' function until the main thread has reached a point where we
291// want some new data to be written. We signal the background thread
292// with the destination for the data and block the main thread,
293// waiting for the background thread to complete writing that section.
294// Then it signals the main thread to wake up and goes back to
295// blocking waiting for a transfer.
296//
297// NewThreadInfo is the struct used to pass information back and forth
298// between the two threads. When the main thread wants some data
299// written, it sets rss to the destination location and signals the
300// condition. When the background thread is done writing, it clears
301// rss and signals the condition again.
302
303typedef struct {
304 ZipArchive* za;
305 const ZipEntry* entry;
306
307 RangeSinkState* rss;
308
309 pthread_mutex_t mu;
310 pthread_cond_t cv;
311} NewThreadInfo;
312
313static bool receive_new_data(const unsigned char* data, int size, void* cookie) {
314 NewThreadInfo* nti = (NewThreadInfo*) cookie;
315
316 while (size > 0) {
317 // Wait for nti->rss to be non-NULL, indicating some of this
318 // data is wanted.
319 pthread_mutex_lock(&nti->mu);
320 while (nti->rss == NULL) {
321 pthread_cond_wait(&nti->cv, &nti->mu);
322 }
323 pthread_mutex_unlock(&nti->mu);
324
325 // At this point nti->rss is set, and we own it. The main
326 // thread is waiting for it to disappear from nti.
327 ssize_t written = RangeSinkWrite(data, size, nti->rss);
328 data += written;
329 size -= written;
330
331 if (nti->rss->p_block == nti->rss->tgt->count) {
332 // we have written all the bytes desired by this rss.
333
334 pthread_mutex_lock(&nti->mu);
335 nti->rss = NULL;
336 pthread_cond_broadcast(&nti->cv);
337 pthread_mutex_unlock(&nti->mu);
338 }
339 }
340
341 return true;
342}
343
344static void* unzip_new_data(void* cookie) {
345 NewThreadInfo* nti = (NewThreadInfo*) cookie;
346 mzProcessZipEntryContents(nti->za, nti->entry, receive_new_data, nti);
347 return NULL;
348}
349
Sami Tolvanen90221202014-12-09 16:39:47 +0000350static int ReadBlocks(RangeSet* src, uint8_t* buffer, int fd) {
351 int i;
352 size_t p = 0;
353 size_t size;
354
355 if (!src || !buffer) {
356 return -1;
357 }
358
359 for (i = 0; i < src->count; ++i) {
Elliott Hughes2f5feed2015-04-28 17:24:24 -0700360 if (!check_lseek(fd, (off64_t) src->pos[i * 2] * BLOCKSIZE, SEEK_SET)) {
Sami Tolvanen90221202014-12-09 16:39:47 +0000361 return -1;
362 }
363
364 size = (src->pos[i * 2 + 1] - src->pos[i * 2]) * BLOCKSIZE;
365
366 if (read_all(fd, buffer + p, size) == -1) {
367 return -1;
368 }
369
370 p += size;
371 }
372
373 return 0;
374}
375
376static int WriteBlocks(RangeSet* tgt, uint8_t* buffer, int fd) {
377 int i;
378 size_t p = 0;
379 size_t size;
380
381 if (!tgt || !buffer) {
382 return -1;
383 }
384
385 for (i = 0; i < tgt->count; ++i) {
Elliott Hughes2f5feed2015-04-28 17:24:24 -0700386 if (!check_lseek(fd, (off64_t) tgt->pos[i * 2] * BLOCKSIZE, SEEK_SET)) {
Sami Tolvanen90221202014-12-09 16:39:47 +0000387 return -1;
388 }
389
390 size = (tgt->pos[i * 2 + 1] - tgt->pos[i * 2]) * BLOCKSIZE;
391
392 if (write_all(fd, buffer + p, size) == -1) {
393 return -1;
394 }
395
396 p += size;
397 }
398
399 return 0;
400}
401
Doug Zongker52ae67d2014-09-08 12:22:09 -0700402// Do a source/target load for move/bsdiff/imgdiff in version 1.
403// 'wordsave' is the save_ptr of a strtok_r()-in-progress. We expect
404// to parse the remainder of the string as:
405//
406// <src_range> <tgt_range>
407//
408// The source range is loaded into the provided buffer, reallocating
409// it to make it larger if necessary. The target ranges are returned
410// in *tgt, if tgt is non-NULL.
411
Sami Tolvanen90221202014-12-09 16:39:47 +0000412static int LoadSrcTgtVersion1(char** wordsave, RangeSet** tgt, int* src_blocks,
Doug Zongker52ae67d2014-09-08 12:22:09 -0700413 uint8_t** buffer, size_t* buffer_alloc, int fd) {
414 char* word;
Sami Tolvanen90221202014-12-09 16:39:47 +0000415 int rc;
Doug Zongker52ae67d2014-09-08 12:22:09 -0700416
Sami Tolvanen90221202014-12-09 16:39:47 +0000417 word = strtok_r(NULL, " ", wordsave);
Doug Zongker52ae67d2014-09-08 12:22:09 -0700418 RangeSet* src = parse_range(word);
419
420 if (tgt != NULL) {
Sami Tolvanen90221202014-12-09 16:39:47 +0000421 word = strtok_r(NULL, " ", wordsave);
Doug Zongker52ae67d2014-09-08 12:22:09 -0700422 *tgt = parse_range(word);
423 }
424
425 allocate(src->size * BLOCKSIZE, buffer, buffer_alloc);
Sami Tolvanen90221202014-12-09 16:39:47 +0000426 rc = ReadBlocks(src, *buffer, fd);
427 *src_blocks = src->size;
428
429 free(src);
430 return rc;
431}
432
433static int VerifyBlocks(const char *expected, const uint8_t *buffer,
434 size_t blocks, int printerror) {
435 char* hexdigest = NULL;
436 int rc = -1;
437 uint8_t digest[SHA_DIGEST_SIZE];
438
439 if (!expected || !buffer) {
440 return rc;
Doug Zongker52ae67d2014-09-08 12:22:09 -0700441 }
442
Sami Tolvanen90221202014-12-09 16:39:47 +0000443 SHA_hash(buffer, blocks * BLOCKSIZE, digest);
444 hexdigest = PrintSha1(digest);
445
446 if (hexdigest != NULL) {
447 rc = strcmp(expected, hexdigest);
448
449 if (rc != 0 && printerror) {
450 fprintf(stderr, "failed to verify blocks (expected %s, read %s)\n",
451 expected, hexdigest);
452 }
453
454 free(hexdigest);
455 }
456
457 return rc;
458}
459
460static char* GetStashFileName(const char* base, const char* id, const char* postfix) {
461 char* fn;
462 int len;
463 int res;
464
465 if (base == NULL) {
466 return NULL;
467 }
468
469 if (id == NULL) {
470 id = "";
471 }
472
473 if (postfix == NULL) {
474 postfix = "";
475 }
476
477 len = strlen(STASH_DIRECTORY_BASE) + 1 + strlen(base) + 1 + strlen(id) + strlen(postfix) + 1;
Tao Bao818fa782015-06-23 23:23:33 -0700478 fn = reinterpret_cast<char*>(malloc(len));
Sami Tolvanen90221202014-12-09 16:39:47 +0000479
480 if (fn == NULL) {
481 fprintf(stderr, "failed to malloc %d bytes for fn\n", len);
482 return NULL;
483 }
484
485 res = snprintf(fn, len, STASH_DIRECTORY_BASE "/%s/%s%s", base, id, postfix);
486
487 if (res < 0 || res >= len) {
488 fprintf(stderr, "failed to format file name (return value %d)\n", res);
489 free(fn);
490 return NULL;
491 }
492
493 return fn;
494}
495
496typedef void (*StashCallback)(const char*, void*);
497
498// Does a best effort enumeration of stash files. Ignores possible non-file
499// items in the stash directory and continues despite of errors. Calls the
500// 'callback' function for each file and passes 'data' to the function as a
501// parameter.
502
503static void EnumerateStash(const char* dirname, StashCallback callback, void* data) {
504 char* fn;
505 DIR* directory;
506 int len;
507 int res;
508 struct dirent* item;
509
510 if (dirname == NULL || callback == NULL) {
511 return;
512 }
513
514 directory = opendir(dirname);
515
516 if (directory == NULL) {
517 if (errno != ENOENT) {
Elliott Hughes1fdd4522015-03-23 13:33:57 -0700518 fprintf(stderr, "opendir \"%s\" failed: %s\n", dirname, strerror(errno));
Sami Tolvanen90221202014-12-09 16:39:47 +0000519 }
520 return;
521 }
522
523 while ((item = readdir(directory)) != NULL) {
524 if (item->d_type != DT_REG) {
525 continue;
526 }
527
528 len = strlen(dirname) + 1 + strlen(item->d_name) + 1;
Tao Bao818fa782015-06-23 23:23:33 -0700529 fn = reinterpret_cast<char*>(malloc(len));
Sami Tolvanen90221202014-12-09 16:39:47 +0000530
531 if (fn == NULL) {
532 fprintf(stderr, "failed to malloc %d bytes for fn\n", len);
533 continue;
534 }
535
536 res = snprintf(fn, len, "%s/%s", dirname, item->d_name);
537
538 if (res < 0 || res >= len) {
539 fprintf(stderr, "failed to format file name (return value %d)\n", res);
540 free(fn);
541 continue;
542 }
543
544 callback(fn, data);
545 free(fn);
546 }
547
548 if (closedir(directory) == -1) {
Elliott Hughes1fdd4522015-03-23 13:33:57 -0700549 fprintf(stderr, "closedir \"%s\" failed: %s\n", dirname, strerror(errno));
Sami Tolvanen90221202014-12-09 16:39:47 +0000550 }
551}
552
553static void UpdateFileSize(const char* fn, void* data) {
554 int* size = (int*) data;
555 struct stat st;
556
557 if (!fn || !data) {
558 return;
559 }
560
561 if (stat(fn, &st) == -1) {
Elliott Hughes1fdd4522015-03-23 13:33:57 -0700562 fprintf(stderr, "stat \"%s\" failed: %s\n", fn, strerror(errno));
Sami Tolvanen90221202014-12-09 16:39:47 +0000563 return;
564 }
565
566 *size += st.st_size;
567}
568
569// Deletes the stash directory and all files in it. Assumes that it only
570// contains files. There is nothing we can do about unlikely, but possible
571// errors, so they are merely logged.
572
573static void DeleteFile(const char* fn, void* data) {
574 if (fn) {
575 fprintf(stderr, "deleting %s\n", fn);
576
577 if (unlink(fn) == -1 && errno != ENOENT) {
Elliott Hughes1fdd4522015-03-23 13:33:57 -0700578 fprintf(stderr, "unlink \"%s\" failed: %s\n", fn, strerror(errno));
Sami Tolvanen90221202014-12-09 16:39:47 +0000579 }
580 }
581}
582
583static void DeletePartial(const char* fn, void* data) {
584 if (fn && strstr(fn, ".partial") != NULL) {
585 DeleteFile(fn, data);
586 }
587}
588
589static void DeleteStash(const char* base) {
590 char* dirname;
591
592 if (base == NULL) {
593 return;
594 }
595
596 dirname = GetStashFileName(base, NULL, NULL);
597
598 if (dirname == NULL) {
599 return;
600 }
601
602 fprintf(stderr, "deleting stash %s\n", base);
603 EnumerateStash(dirname, DeleteFile, NULL);
604
605 if (rmdir(dirname) == -1) {
606 if (errno != ENOENT && errno != ENOTDIR) {
Elliott Hughes1fdd4522015-03-23 13:33:57 -0700607 fprintf(stderr, "rmdir \"%s\" failed: %s\n", dirname, strerror(errno));
Sami Tolvanen90221202014-12-09 16:39:47 +0000608 }
609 }
610
611 free(dirname);
612}
613
614static int LoadStash(const char* base, const char* id, int verify, int* blocks, uint8_t** buffer,
615 size_t* buffer_alloc, int printnoent) {
616 char *fn = NULL;
617 int blockcount = 0;
618 int fd = -1;
619 int rc = -1;
620 int res;
621 struct stat st;
622
623 if (!base || !id || !buffer || !buffer_alloc) {
624 goto lsout;
625 }
626
627 if (!blocks) {
628 blocks = &blockcount;
629 }
630
631 fn = GetStashFileName(base, id, NULL);
632
633 if (fn == NULL) {
634 goto lsout;
635 }
636
637 res = stat(fn, &st);
638
639 if (res == -1) {
640 if (errno != ENOENT || printnoent) {
Elliott Hughes1fdd4522015-03-23 13:33:57 -0700641 fprintf(stderr, "stat \"%s\" failed: %s\n", fn, strerror(errno));
Sami Tolvanen90221202014-12-09 16:39:47 +0000642 }
643 goto lsout;
644 }
645
646 fprintf(stderr, " loading %s\n", fn);
647
648 if ((st.st_size % BLOCKSIZE) != 0) {
Tao Bao818fa782015-06-23 23:23:33 -0700649 fprintf(stderr, "%s size %" PRId64 " not multiple of block size %d",
650 fn, static_cast<int64_t>(st.st_size), BLOCKSIZE);
Sami Tolvanen90221202014-12-09 16:39:47 +0000651 goto lsout;
652 }
653
654 fd = TEMP_FAILURE_RETRY(open(fn, O_RDONLY));
655
656 if (fd == -1) {
Elliott Hughes1fdd4522015-03-23 13:33:57 -0700657 fprintf(stderr, "open \"%s\" failed: %s\n", fn, strerror(errno));
Sami Tolvanen90221202014-12-09 16:39:47 +0000658 goto lsout;
659 }
660
661 allocate(st.st_size, buffer, buffer_alloc);
662
663 if (read_all(fd, *buffer, st.st_size) == -1) {
664 goto lsout;
665 }
666
667 *blocks = st.st_size / BLOCKSIZE;
668
669 if (verify && VerifyBlocks(id, *buffer, *blocks, 1) != 0) {
670 fprintf(stderr, "unexpected contents in %s\n", fn);
671 DeleteFile(fn, NULL);
672 goto lsout;
673 }
674
675 rc = 0;
676
677lsout:
678 if (fd != -1) {
Elliott Hughesb5dabd22015-05-28 23:06:17 -0700679 close(fd);
Sami Tolvanen90221202014-12-09 16:39:47 +0000680 }
681
682 if (fn) {
683 free(fn);
684 }
685
686 return rc;
687}
688
689static int WriteStash(const char* base, const char* id, int blocks, uint8_t* buffer,
Sami Tolvanen43b748f2015-04-17 12:50:31 +0100690 int checkspace, int *exists) {
Sami Tolvanen90221202014-12-09 16:39:47 +0000691 char *fn = NULL;
692 char *cn = NULL;
693 int fd = -1;
694 int rc = -1;
Tao Baobe19dce2015-07-31 15:56:44 -0700695 int dfd = -1;
Sami Tolvanen90221202014-12-09 16:39:47 +0000696 int res;
Sami Tolvanen43b748f2015-04-17 12:50:31 +0100697 struct stat st;
Sami Tolvanen90221202014-12-09 16:39:47 +0000698
699 if (base == NULL || buffer == NULL) {
700 goto wsout;
701 }
702
703 if (checkspace && CacheSizeCheck(blocks * BLOCKSIZE) != 0) {
704 fprintf(stderr, "not enough space to write stash\n");
705 goto wsout;
706 }
707
708 fn = GetStashFileName(base, id, ".partial");
709 cn = GetStashFileName(base, id, NULL);
710
711 if (fn == NULL || cn == NULL) {
712 goto wsout;
713 }
714
Sami Tolvanen43b748f2015-04-17 12:50:31 +0100715 if (exists) {
716 res = stat(cn, &st);
717
718 if (res == 0) {
719 // The file already exists and since the name is the hash of the contents,
720 // it's safe to assume the contents are identical (accidental hash collisions
721 // are unlikely)
722 fprintf(stderr, " skipping %d existing blocks in %s\n", blocks, cn);
723 *exists = 1;
724 rc = 0;
725 goto wsout;
726 }
727
728 *exists = 0;
729 }
730
Sami Tolvanen90221202014-12-09 16:39:47 +0000731 fprintf(stderr, " writing %d blocks to %s\n", blocks, cn);
732
733 fd = TEMP_FAILURE_RETRY(open(fn, O_WRONLY | O_CREAT | O_TRUNC | O_SYNC, STASH_FILE_MODE));
734
735 if (fd == -1) {
Elliott Hughes1fdd4522015-03-23 13:33:57 -0700736 fprintf(stderr, "failed to create \"%s\": %s\n", fn, strerror(errno));
Sami Tolvanen90221202014-12-09 16:39:47 +0000737 goto wsout;
738 }
739
740 if (write_all(fd, buffer, blocks * BLOCKSIZE) == -1) {
741 goto wsout;
742 }
743
744 if (fsync(fd) == -1) {
Elliott Hughes1fdd4522015-03-23 13:33:57 -0700745 fprintf(stderr, "fsync \"%s\" failed: %s\n", fn, strerror(errno));
Sami Tolvanen90221202014-12-09 16:39:47 +0000746 goto wsout;
747 }
748
749 if (rename(fn, cn) == -1) {
Elliott Hughes1fdd4522015-03-23 13:33:57 -0700750 fprintf(stderr, "rename(\"%s\", \"%s\") failed: %s\n", fn, cn, strerror(errno));
Sami Tolvanen90221202014-12-09 16:39:47 +0000751 goto wsout;
752 }
753
Tao Baobe19dce2015-07-31 15:56:44 -0700754 const char* dname;
755 dname = dirname(cn);
756 dfd = TEMP_FAILURE_RETRY(open(dname, O_RDONLY | O_DIRECTORY));
757
758 if (dfd == -1) {
759 fprintf(stderr, "failed to open \"%s\" failed: %s\n", dname, strerror(errno));
760 goto wsout;
761 }
762
763 if (fsync(dfd) == -1) {
764 fprintf(stderr, "fsync \"%s\" failed: %s\n", dname, strerror(errno));
765 goto wsout;
766 }
767
Sami Tolvanen90221202014-12-09 16:39:47 +0000768 rc = 0;
769
770wsout:
771 if (fd != -1) {
Elliott Hughes1857a7f2015-05-15 16:19:20 -0700772 close(fd);
Sami Tolvanen90221202014-12-09 16:39:47 +0000773 }
774
Tao Baobe19dce2015-07-31 15:56:44 -0700775 if (dfd != -1) {
776 close(dfd);
777 }
778
Sami Tolvanen90221202014-12-09 16:39:47 +0000779 if (fn) {
780 free(fn);
781 }
782
783 if (cn) {
784 free(cn);
785 }
786
787 return rc;
788}
789
790// Creates a directory for storing stash files and checks if the /cache partition
791// hash enough space for the expected amount of blocks we need to store. Returns
792// >0 if we created the directory, zero if it existed already, and <0 of failure.
793
794static int CreateStash(State* state, int maxblocks, const char* blockdev, char** base) {
795 char* dirname = NULL;
796 const uint8_t* digest;
797 int rc = -1;
798 int res;
799 int size = 0;
800 SHA_CTX ctx;
801 struct stat st;
802
803 if (blockdev == NULL || base == NULL) {
804 goto csout;
805 }
806
807 // Stash directory should be different for each partition to avoid conflicts
808 // when updating multiple partitions at the same time, so we use the hash of
809 // the block device name as the base directory
810 SHA_init(&ctx);
811 SHA_update(&ctx, blockdev, strlen(blockdev));
812 digest = SHA_final(&ctx);
813 *base = PrintSha1(digest);
814
815 if (*base == NULL) {
816 goto csout;
817 }
818
819 dirname = GetStashFileName(*base, NULL, NULL);
820
821 if (dirname == NULL) {
822 goto csout;
823 }
824
825 res = stat(dirname, &st);
826
827 if (res == -1 && errno != ENOENT) {
Elliott Hughes1fdd4522015-03-23 13:33:57 -0700828 ErrorAbort(state, "stat \"%s\" failed: %s\n", dirname, strerror(errno));
Sami Tolvanen90221202014-12-09 16:39:47 +0000829 goto csout;
830 } else if (res != 0) {
831 fprintf(stderr, "creating stash %s\n", dirname);
832 res = mkdir(dirname, STASH_DIRECTORY_MODE);
833
834 if (res != 0) {
Elliott Hughes1fdd4522015-03-23 13:33:57 -0700835 ErrorAbort(state, "mkdir \"%s\" failed: %s\n", dirname, strerror(errno));
Sami Tolvanen90221202014-12-09 16:39:47 +0000836 goto csout;
837 }
838
839 if (CacheSizeCheck(maxblocks * BLOCKSIZE) != 0) {
840 ErrorAbort(state, "not enough space for stash\n");
841 goto csout;
842 }
843
844 rc = 1; // Created directory
845 goto csout;
846 }
847
848 fprintf(stderr, "using existing stash %s\n", dirname);
849
850 // If the directory already exists, calculate the space already allocated to
851 // stash files and check if there's enough for all required blocks. Delete any
852 // partially completed stash files first.
853
854 EnumerateStash(dirname, DeletePartial, NULL);
855 EnumerateStash(dirname, UpdateFileSize, &size);
856
857 size = (maxblocks * BLOCKSIZE) - size;
858
859 if (size > 0 && CacheSizeCheck(size) != 0) {
860 ErrorAbort(state, "not enough space for stash (%d more needed)\n", size);
861 goto csout;
862 }
863
864 rc = 0; // Using existing directory
865
866csout:
867 if (dirname) {
868 free(dirname);
869 }
870
871 return rc;
872}
873
874static int SaveStash(const char* base, char** wordsave, uint8_t** buffer, size_t* buffer_alloc,
875 int fd, int usehash, int* isunresumable) {
876 char *id = NULL;
Sami Tolvanen90221202014-12-09 16:39:47 +0000877 int blocks = 0;
878
879 if (!wordsave || !buffer || !buffer_alloc || !isunresumable) {
880 return -1;
881 }
882
883 id = strtok_r(NULL, " ", wordsave);
884
885 if (id == NULL) {
886 fprintf(stderr, "missing id field in stash command\n");
887 return -1;
888 }
889
890 if (usehash && LoadStash(base, id, 1, &blocks, buffer, buffer_alloc, 0) == 0) {
891 // Stash file already exists and has expected contents. Do not
892 // read from source again, as the source may have been already
893 // overwritten during a previous attempt.
894 return 0;
895 }
896
897 if (LoadSrcTgtVersion1(wordsave, NULL, &blocks, buffer, buffer_alloc, fd) == -1) {
898 return -1;
899 }
900
901 if (usehash && VerifyBlocks(id, *buffer, blocks, 1) != 0) {
902 // Source blocks have unexpected contents. If we actually need this
903 // data later, this is an unrecoverable error. However, the command
904 // that uses the data may have already completed previously, so the
905 // possible failure will occur during source block verification.
906 fprintf(stderr, "failed to load source blocks for stash %s\n", id);
907 return 0;
908 }
909
910 fprintf(stderr, "stashing %d blocks to %s\n", blocks, id);
Sami Tolvanen43b748f2015-04-17 12:50:31 +0100911 return WriteStash(base, id, blocks, *buffer, 0, NULL);
Sami Tolvanen90221202014-12-09 16:39:47 +0000912}
913
914static int FreeStash(const char* base, const char* id) {
915 char *fn = NULL;
916
917 if (base == NULL || id == NULL) {
918 return -1;
919 }
920
921 fn = GetStashFileName(base, id, NULL);
922
923 if (fn == NULL) {
924 return -1;
925 }
926
927 DeleteFile(fn, NULL);
928 free(fn);
929
930 return 0;
Doug Zongker52ae67d2014-09-08 12:22:09 -0700931}
932
933static void MoveRange(uint8_t* dest, RangeSet* locs, const uint8_t* source) {
934 // source contains packed data, which we want to move to the
935 // locations given in *locs in the dest buffer. source and dest
936 // may be the same buffer.
937
938 int start = locs->size;
939 int i;
940 for (i = locs->count-1; i >= 0; --i) {
941 int blocks = locs->pos[i*2+1] - locs->pos[i*2];
942 start -= blocks;
943 memmove(dest + (locs->pos[i*2] * BLOCKSIZE), source + (start * BLOCKSIZE),
944 blocks * BLOCKSIZE);
945 }
946}
947
948// Do a source/target load for move/bsdiff/imgdiff in version 2.
949// 'wordsave' is the save_ptr of a strtok_r()-in-progress. We expect
950// to parse the remainder of the string as one of:
951//
952// <tgt_range> <src_block_count> <src_range>
953// (loads data from source image only)
954//
955// <tgt_range> <src_block_count> - <[stash_id:stash_range] ...>
956// (loads data from stashes only)
957//
958// <tgt_range> <src_block_count> <src_range> <src_loc> <[stash_id:stash_range] ...>
959// (loads data from both source image and stashes)
960//
961// On return, buffer is filled with the loaded source data (rearranged
962// and combined with stashed data as necessary). buffer may be
963// reallocated if needed to accommodate the source data. *tgt is the
Sami Tolvanen90221202014-12-09 16:39:47 +0000964// target RangeSet. Any stashes required are loaded using LoadStash.
Doug Zongker52ae67d2014-09-08 12:22:09 -0700965
Sami Tolvanen90221202014-12-09 16:39:47 +0000966static int LoadSrcTgtVersion2(char** wordsave, RangeSet** tgt, int* src_blocks,
Doug Zongker52ae67d2014-09-08 12:22:09 -0700967 uint8_t** buffer, size_t* buffer_alloc, int fd,
Sami Tolvanen90221202014-12-09 16:39:47 +0000968 const char* stashbase, int* overlap) {
Doug Zongker52ae67d2014-09-08 12:22:09 -0700969 char* word;
Sami Tolvanen90221202014-12-09 16:39:47 +0000970 char* colonsave;
971 char* colon;
Sami Tolvanen90221202014-12-09 16:39:47 +0000972 int res;
973 RangeSet* locs;
974 size_t stashalloc = 0;
975 uint8_t* stash = NULL;
Doug Zongker52ae67d2014-09-08 12:22:09 -0700976
977 if (tgt != NULL) {
Sami Tolvanen90221202014-12-09 16:39:47 +0000978 word = strtok_r(NULL, " ", wordsave);
Doug Zongker52ae67d2014-09-08 12:22:09 -0700979 *tgt = parse_range(word);
980 }
981
Sami Tolvanen90221202014-12-09 16:39:47 +0000982 word = strtok_r(NULL, " ", wordsave);
Doug Zongker52ae67d2014-09-08 12:22:09 -0700983 *src_blocks = strtol(word, NULL, 0);
984
985 allocate(*src_blocks * BLOCKSIZE, buffer, buffer_alloc);
986
Sami Tolvanen90221202014-12-09 16:39:47 +0000987 word = strtok_r(NULL, " ", wordsave);
Doug Zongker52ae67d2014-09-08 12:22:09 -0700988 if (word[0] == '-' && word[1] == '\0') {
989 // no source ranges, only stashes
990 } else {
991 RangeSet* src = parse_range(word);
Sami Tolvanen90221202014-12-09 16:39:47 +0000992 res = ReadBlocks(src, *buffer, fd);
Doug Zongker52ae67d2014-09-08 12:22:09 -0700993
Sami Tolvanen90221202014-12-09 16:39:47 +0000994 if (overlap && tgt) {
995 *overlap = range_overlaps(src, *tgt);
Doug Zongker52ae67d2014-09-08 12:22:09 -0700996 }
Sami Tolvanen90221202014-12-09 16:39:47 +0000997
Doug Zongker52ae67d2014-09-08 12:22:09 -0700998 free(src);
999
Sami Tolvanen90221202014-12-09 16:39:47 +00001000 if (res == -1) {
1001 return -1;
Doug Zongker52ae67d2014-09-08 12:22:09 -07001002 }
1003
Sami Tolvanen90221202014-12-09 16:39:47 +00001004 word = strtok_r(NULL, " ", wordsave);
1005 if (word == NULL) {
1006 // no stashes, only source range
1007 return 0;
1008 }
1009
1010 locs = parse_range(word);
Doug Zongker52ae67d2014-09-08 12:22:09 -07001011 MoveRange(*buffer, locs, *buffer);
Sami Tolvanen90221202014-12-09 16:39:47 +00001012 free(locs);
Doug Zongker52ae67d2014-09-08 12:22:09 -07001013 }
1014
Sami Tolvanen90221202014-12-09 16:39:47 +00001015 while ((word = strtok_r(NULL, " ", wordsave)) != NULL) {
Doug Zongker52ae67d2014-09-08 12:22:09 -07001016 // Each word is a an index into the stash table, a colon, and
1017 // then a rangeset describing where in the source block that
1018 // stashed data should go.
Sami Tolvanen90221202014-12-09 16:39:47 +00001019 colonsave = NULL;
1020 colon = strtok_r(word, ":", &colonsave);
1021
1022 res = LoadStash(stashbase, colon, 0, NULL, &stash, &stashalloc, 1);
1023
1024 if (res == -1) {
1025 // These source blocks will fail verification if used later, but we
1026 // will let the caller decide if this is a fatal failure
1027 fprintf(stderr, "failed to load stash %s\n", colon);
1028 continue;
1029 }
1030
Doug Zongker52ae67d2014-09-08 12:22:09 -07001031 colon = strtok_r(NULL, ":", &colonsave);
Sami Tolvanen90221202014-12-09 16:39:47 +00001032 locs = parse_range(colon);
1033
1034 MoveRange(*buffer, locs, stash);
Doug Zongker52ae67d2014-09-08 12:22:09 -07001035 free(locs);
1036 }
Sami Tolvanen90221202014-12-09 16:39:47 +00001037
1038 if (stash) {
1039 free(stash);
1040 }
1041
1042 return 0;
1043}
1044
1045// Parameters for transfer list command functions
1046typedef struct {
1047 char* cmdname;
1048 char* cpos;
1049 char* freestash;
1050 char* stashbase;
1051 int canwrite;
1052 int createdstash;
1053 int fd;
1054 int foundwrites;
1055 int isunresumable;
1056 int version;
1057 int written;
1058 NewThreadInfo nti;
1059 pthread_t thread;
1060 size_t bufsize;
1061 uint8_t* buffer;
1062 uint8_t* patch_start;
1063} CommandParameters;
1064
1065// Do a source/target load for move/bsdiff/imgdiff in version 3.
1066//
1067// Parameters are the same as for LoadSrcTgtVersion2, except for 'onehash', which
1068// tells the function whether to expect separate source and targe block hashes, or
1069// if they are both the same and only one hash should be expected, and
1070// 'isunresumable', which receives a non-zero value if block verification fails in
1071// a way that the update cannot be resumed anymore.
1072//
1073// If the function is unable to load the necessary blocks or their contents don't
1074// match the hashes, the return value is -1 and the command should be aborted.
1075//
1076// If the return value is 1, the command has already been completed according to
1077// the contents of the target blocks, and should not be performed again.
1078//
1079// If the return value is 0, source blocks have expected content and the command
1080// can be performed.
1081
1082static int LoadSrcTgtVersion3(CommandParameters* params, RangeSet** tgt, int* src_blocks,
1083 int onehash, int* overlap) {
1084 char* srchash = NULL;
1085 char* tgthash = NULL;
Sami Tolvanen43b748f2015-04-17 12:50:31 +01001086 int stash_exists = 0;
Sami Tolvanen90221202014-12-09 16:39:47 +00001087 int rc = -1;
1088 uint8_t* tgtbuffer = NULL;
1089
1090 if (!params|| !tgt || !src_blocks || !overlap) {
1091 goto v3out;
1092 }
1093
1094 srchash = strtok_r(NULL, " ", &params->cpos);
1095
1096 if (srchash == NULL) {
1097 fprintf(stderr, "missing source hash\n");
1098 goto v3out;
1099 }
1100
1101 if (onehash) {
1102 tgthash = srchash;
1103 } else {
1104 tgthash = strtok_r(NULL, " ", &params->cpos);
1105
1106 if (tgthash == NULL) {
1107 fprintf(stderr, "missing target hash\n");
1108 goto v3out;
1109 }
1110 }
1111
1112 if (LoadSrcTgtVersion2(&params->cpos, tgt, src_blocks, &params->buffer, &params->bufsize,
1113 params->fd, params->stashbase, overlap) == -1) {
1114 goto v3out;
1115 }
1116
1117 tgtbuffer = (uint8_t*) malloc((*tgt)->size * BLOCKSIZE);
1118
1119 if (tgtbuffer == NULL) {
1120 fprintf(stderr, "failed to allocate %d bytes\n", (*tgt)->size * BLOCKSIZE);
1121 goto v3out;
1122 }
1123
1124 if (ReadBlocks(*tgt, tgtbuffer, params->fd) == -1) {
1125 goto v3out;
1126 }
1127
1128 if (VerifyBlocks(tgthash, tgtbuffer, (*tgt)->size, 0) == 0) {
1129 // Target blocks already have expected content, command should be skipped
1130 rc = 1;
1131 goto v3out;
1132 }
1133
1134 if (VerifyBlocks(srchash, params->buffer, *src_blocks, 1) == 0) {
1135 // If source and target blocks overlap, stash the source blocks so we can
1136 // resume from possible write errors
1137 if (*overlap) {
1138 fprintf(stderr, "stashing %d overlapping blocks to %s\n", *src_blocks,
1139 srchash);
1140
Sami Tolvanen43b748f2015-04-17 12:50:31 +01001141 if (WriteStash(params->stashbase, srchash, *src_blocks, params->buffer, 1,
1142 &stash_exists) != 0) {
Sami Tolvanen90221202014-12-09 16:39:47 +00001143 fprintf(stderr, "failed to stash overlapping source blocks\n");
1144 goto v3out;
1145 }
1146
1147 // Can be deleted when the write has completed
Sami Tolvanen43b748f2015-04-17 12:50:31 +01001148 if (!stash_exists) {
1149 params->freestash = srchash;
1150 }
Sami Tolvanen90221202014-12-09 16:39:47 +00001151 }
1152
1153 // Source blocks have expected content, command can proceed
1154 rc = 0;
1155 goto v3out;
1156 }
1157
1158 if (*overlap && LoadStash(params->stashbase, srchash, 1, NULL, &params->buffer,
1159 &params->bufsize, 1) == 0) {
Sami Tolvanen43b748f2015-04-17 12:50:31 +01001160 // Overlapping source blocks were previously stashed, command can proceed.
1161 // We are recovering from an interrupted command, so we don't know if the
1162 // stash can safely be deleted after this command.
Sami Tolvanen90221202014-12-09 16:39:47 +00001163 rc = 0;
1164 goto v3out;
1165 }
1166
1167 // Valid source data not available, update cannot be resumed
1168 fprintf(stderr, "partition has unexpected contents\n");
1169 params->isunresumable = 1;
1170
1171v3out:
1172 if (tgtbuffer) {
1173 free(tgtbuffer);
1174 }
1175
1176 return rc;
1177}
1178
1179static int PerformCommandMove(CommandParameters* params) {
1180 int blocks = 0;
1181 int overlap = 0;
1182 int rc = -1;
1183 int status = 0;
1184 RangeSet* tgt = NULL;
1185
1186 if (!params) {
1187 goto pcmout;
1188 }
1189
1190 if (params->version == 1) {
1191 status = LoadSrcTgtVersion1(&params->cpos, &tgt, &blocks, &params->buffer,
1192 &params->bufsize, params->fd);
1193 } else if (params->version == 2) {
1194 status = LoadSrcTgtVersion2(&params->cpos, &tgt, &blocks, &params->buffer,
1195 &params->bufsize, params->fd, params->stashbase, NULL);
1196 } else if (params->version >= 3) {
1197 status = LoadSrcTgtVersion3(params, &tgt, &blocks, 1, &overlap);
1198 }
1199
1200 if (status == -1) {
1201 fprintf(stderr, "failed to read blocks for move\n");
1202 goto pcmout;
1203 }
1204
1205 if (status == 0) {
1206 params->foundwrites = 1;
1207 } else if (params->foundwrites) {
1208 fprintf(stderr, "warning: commands executed out of order [%s]\n", params->cmdname);
1209 }
1210
1211 if (params->canwrite) {
1212 if (status == 0) {
1213 fprintf(stderr, " moving %d blocks\n", blocks);
1214
1215 if (WriteBlocks(tgt, params->buffer, params->fd) == -1) {
1216 goto pcmout;
1217 }
1218 } else {
1219 fprintf(stderr, "skipping %d already moved blocks\n", blocks);
1220 }
1221
1222 }
1223
1224 if (params->freestash) {
1225 FreeStash(params->stashbase, params->freestash);
1226 params->freestash = NULL;
1227 }
1228
1229 params->written += tgt->size;
1230 rc = 0;
1231
1232pcmout:
1233 if (tgt) {
1234 free(tgt);
1235 }
1236
1237 return rc;
1238}
1239
1240static int PerformCommandStash(CommandParameters* params) {
1241 if (!params) {
1242 return -1;
1243 }
1244
1245 return SaveStash(params->stashbase, &params->cpos, &params->buffer, &params->bufsize,
1246 params->fd, (params->version >= 3), &params->isunresumable);
1247}
1248
1249static int PerformCommandFree(CommandParameters* params) {
1250 if (!params) {
1251 return -1;
1252 }
1253
1254 if (params->createdstash || params->canwrite) {
1255 return FreeStash(params->stashbase, params->cpos);
1256 }
1257
1258 return 0;
1259}
1260
1261static int PerformCommandZero(CommandParameters* params) {
1262 char* range = NULL;
1263 int i;
1264 int j;
1265 int rc = -1;
1266 RangeSet* tgt = NULL;
1267
1268 if (!params) {
1269 goto pczout;
1270 }
1271
1272 range = strtok_r(NULL, " ", &params->cpos);
1273
1274 if (range == NULL) {
1275 fprintf(stderr, "missing target blocks for zero\n");
1276 goto pczout;
1277 }
1278
1279 tgt = parse_range(range);
1280
1281 fprintf(stderr, " zeroing %d blocks\n", tgt->size);
1282
1283 allocate(BLOCKSIZE, &params->buffer, &params->bufsize);
1284 memset(params->buffer, 0, BLOCKSIZE);
1285
1286 if (params->canwrite) {
1287 for (i = 0; i < tgt->count; ++i) {
Elliott Hughes2f5feed2015-04-28 17:24:24 -07001288 if (!check_lseek(params->fd, (off64_t) tgt->pos[i * 2] * BLOCKSIZE, SEEK_SET)) {
Sami Tolvanen90221202014-12-09 16:39:47 +00001289 goto pczout;
1290 }
1291
1292 for (j = tgt->pos[i * 2]; j < tgt->pos[i * 2 + 1]; ++j) {
1293 if (write_all(params->fd, params->buffer, BLOCKSIZE) == -1) {
1294 goto pczout;
1295 }
1296 }
1297 }
1298 }
1299
1300 if (params->cmdname[0] == 'z') {
Sami Tolvanen6abd52f2015-06-10 15:52:04 +00001301 // Update only for the zero command, as the erase command will call
1302 // this if DEBUG_ERASE is defined.
Sami Tolvanen90221202014-12-09 16:39:47 +00001303 params->written += tgt->size;
1304 }
1305
1306 rc = 0;
1307
1308pczout:
1309 if (tgt) {
1310 free(tgt);
1311 }
1312
1313 return rc;
1314}
1315
1316static int PerformCommandNew(CommandParameters* params) {
1317 char* range = NULL;
1318 int rc = -1;
1319 RangeSet* tgt = NULL;
1320 RangeSinkState rss;
1321
1322 if (!params) {
1323 goto pcnout;
1324 }
1325
1326 range = strtok_r(NULL, " ", &params->cpos);
1327
1328 if (range == NULL) {
1329 goto pcnout;
1330 }
1331
1332 tgt = parse_range(range);
1333
1334 if (params->canwrite) {
1335 fprintf(stderr, " writing %d blocks of new data\n", tgt->size);
1336
1337 rss.fd = params->fd;
1338 rss.tgt = tgt;
1339 rss.p_block = 0;
1340 rss.p_remain = (tgt->pos[1] - tgt->pos[0]) * BLOCKSIZE;
1341
Elliott Hughes2f5feed2015-04-28 17:24:24 -07001342 if (!check_lseek(params->fd, (off64_t) tgt->pos[0] * BLOCKSIZE, SEEK_SET)) {
Sami Tolvanen90221202014-12-09 16:39:47 +00001343 goto pcnout;
1344 }
1345
1346 pthread_mutex_lock(&params->nti.mu);
1347 params->nti.rss = &rss;
1348 pthread_cond_broadcast(&params->nti.cv);
1349
1350 while (params->nti.rss) {
1351 pthread_cond_wait(&params->nti.cv, &params->nti.mu);
1352 }
1353
1354 pthread_mutex_unlock(&params->nti.mu);
1355 }
1356
1357 params->written += tgt->size;
1358 rc = 0;
1359
1360pcnout:
1361 if (tgt) {
1362 free(tgt);
1363 }
1364
1365 return rc;
1366}
1367
1368static int PerformCommandDiff(CommandParameters* params) {
1369 char* logparams = NULL;
1370 char* value = NULL;
1371 int blocks = 0;
1372 int overlap = 0;
1373 int rc = -1;
1374 int status = 0;
1375 RangeSet* tgt = NULL;
1376 RangeSinkState rss;
1377 size_t len = 0;
1378 size_t offset = 0;
1379 Value patch_value;
1380
1381 if (!params) {
1382 goto pcdout;
1383 }
1384
1385 logparams = strdup(params->cpos);
1386 value = strtok_r(NULL, " ", &params->cpos);
1387
1388 if (value == NULL) {
1389 fprintf(stderr, "missing patch offset for %s\n", params->cmdname);
1390 goto pcdout;
1391 }
1392
1393 offset = strtoul(value, NULL, 0);
1394
1395 value = strtok_r(NULL, " ", &params->cpos);
1396
1397 if (value == NULL) {
1398 fprintf(stderr, "missing patch length for %s\n", params->cmdname);
1399 goto pcdout;
1400 }
1401
1402 len = strtoul(value, NULL, 0);
1403
1404 if (params->version == 1) {
1405 status = LoadSrcTgtVersion1(&params->cpos, &tgt, &blocks, &params->buffer,
1406 &params->bufsize, params->fd);
1407 } else if (params->version == 2) {
1408 status = LoadSrcTgtVersion2(&params->cpos, &tgt, &blocks, &params->buffer,
1409 &params->bufsize, params->fd, params->stashbase, NULL);
1410 } else if (params->version >= 3) {
1411 status = LoadSrcTgtVersion3(params, &tgt, &blocks, 0, &overlap);
1412 }
1413
1414 if (status == -1) {
1415 fprintf(stderr, "failed to read blocks for diff\n");
1416 goto pcdout;
1417 }
1418
1419 if (status == 0) {
1420 params->foundwrites = 1;
1421 } else if (params->foundwrites) {
1422 fprintf(stderr, "warning: commands executed out of order [%s]\n", params->cmdname);
1423 }
1424
1425 if (params->canwrite) {
1426 if (status == 0) {
1427 fprintf(stderr, "patching %d blocks to %d\n", blocks, tgt->size);
1428
1429 patch_value.type = VAL_BLOB;
1430 patch_value.size = len;
1431 patch_value.data = (char*) (params->patch_start + offset);
1432
1433 rss.fd = params->fd;
1434 rss.tgt = tgt;
1435 rss.p_block = 0;
1436 rss.p_remain = (tgt->pos[1] - tgt->pos[0]) * BLOCKSIZE;
1437
Elliott Hughes2f5feed2015-04-28 17:24:24 -07001438 if (!check_lseek(params->fd, (off64_t) tgt->pos[0] * BLOCKSIZE, SEEK_SET)) {
Sami Tolvanen90221202014-12-09 16:39:47 +00001439 goto pcdout;
1440 }
1441
1442 if (params->cmdname[0] == 'i') { // imgdiff
1443 ApplyImagePatch(params->buffer, blocks * BLOCKSIZE, &patch_value,
1444 &RangeSinkWrite, &rss, NULL, NULL);
1445 } else {
1446 ApplyBSDiffPatch(params->buffer, blocks * BLOCKSIZE, &patch_value,
1447 0, &RangeSinkWrite, &rss, NULL);
1448 }
1449
1450 // We expect the output of the patcher to fill the tgt ranges exactly.
1451 if (rss.p_block != tgt->count || rss.p_remain != 0) {
1452 fprintf(stderr, "range sink underrun?\n");
1453 }
1454 } else {
1455 fprintf(stderr, "skipping %d blocks already patched to %d [%s]\n",
1456 blocks, tgt->size, logparams);
1457 }
1458 }
1459
1460 if (params->freestash) {
1461 FreeStash(params->stashbase, params->freestash);
1462 params->freestash = NULL;
1463 }
1464
1465 params->written += tgt->size;
1466 rc = 0;
1467
1468pcdout:
1469 if (logparams) {
1470 free(logparams);
1471 }
1472
1473 if (tgt) {
1474 free(tgt);
1475 }
1476
1477 return rc;
1478}
1479
1480static int PerformCommandErase(CommandParameters* params) {
1481 char* range = NULL;
1482 int i;
1483 int rc = -1;
1484 RangeSet* tgt = NULL;
1485 struct stat st;
1486 uint64_t blocks[2];
1487
Sami Tolvanen6abd52f2015-06-10 15:52:04 +00001488 if (DEBUG_ERASE) {
1489 return PerformCommandZero(params);
Sami Tolvanen90221202014-12-09 16:39:47 +00001490 }
1491
1492 if (!params) {
1493 goto pceout;
1494 }
1495
1496 if (fstat(params->fd, &st) == -1) {
Elliott Hughes1fdd4522015-03-23 13:33:57 -07001497 fprintf(stderr, "failed to fstat device to erase: %s\n", strerror(errno));
Sami Tolvanen90221202014-12-09 16:39:47 +00001498 goto pceout;
1499 }
1500
1501 if (!S_ISBLK(st.st_mode)) {
1502 fprintf(stderr, "not a block device; skipping erase\n");
Sami Tolvanen90221202014-12-09 16:39:47 +00001503 goto pceout;
1504 }
1505
1506 range = strtok_r(NULL, " ", &params->cpos);
1507
1508 if (range == NULL) {
Tao Bao818fa782015-06-23 23:23:33 -07001509 fprintf(stderr, "missing target blocks for erase\n");
Sami Tolvanen90221202014-12-09 16:39:47 +00001510 goto pceout;
1511 }
1512
1513 tgt = parse_range(range);
1514
1515 if (params->canwrite) {
1516 fprintf(stderr, " erasing %d blocks\n", tgt->size);
1517
1518 for (i = 0; i < tgt->count; ++i) {
1519 // offset in bytes
1520 blocks[0] = tgt->pos[i * 2] * (uint64_t) BLOCKSIZE;
1521 // length in bytes
1522 blocks[1] = (tgt->pos[i * 2 + 1] - tgt->pos[i * 2]) * (uint64_t) BLOCKSIZE;
1523
1524 if (ioctl(params->fd, BLKDISCARD, &blocks) == -1) {
Elliott Hughes1fdd4522015-03-23 13:33:57 -07001525 fprintf(stderr, "BLKDISCARD ioctl failed: %s\n", strerror(errno));
Sami Tolvanen92eea1b2015-04-27 11:24:29 +01001526 goto pceout;
Sami Tolvanen90221202014-12-09 16:39:47 +00001527 }
1528 }
1529 }
1530
1531 rc = 0;
1532
1533pceout:
1534 if (tgt) {
1535 free(tgt);
1536 }
1537
1538 return rc;
1539}
1540
1541// Definitions for transfer list command functions
1542typedef int (*CommandFunction)(CommandParameters*);
1543
1544typedef struct {
1545 const char* name;
1546 CommandFunction f;
1547} Command;
1548
1549// CompareCommands and CompareCommandNames are for the hash table
1550
1551static int CompareCommands(const void* c1, const void* c2) {
1552 return strcmp(((const Command*) c1)->name, ((const Command*) c2)->name);
1553}
1554
1555static int CompareCommandNames(const void* c1, const void* c2) {
1556 return strcmp(((const Command*) c1)->name, (const char*) c2);
1557}
1558
1559// HashString is used to hash command names for the hash table
1560
1561static unsigned int HashString(const char *s) {
1562 unsigned int hash = 0;
1563 if (s) {
1564 while (*s) {
1565 hash = hash * 33 + *s++;
1566 }
1567 }
1568 return hash;
Doug Zongker52ae67d2014-09-08 12:22:09 -07001569}
1570
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001571// args:
1572// - block device (or file) to modify in-place
1573// - transfer list (blob)
1574// - new data stream (filename within package.zip)
1575// - patch stream (filename within package.zip, must be uncompressed)
1576
Sami Tolvanen90221202014-12-09 16:39:47 +00001577static Value* PerformBlockImageUpdate(const char* name, State* state, int argc, Expr* argv[],
1578 const Command* commands, int cmdcount, int dryrun) {
1579
1580 char* line = NULL;
1581 char* linesave = NULL;
1582 char* logcmd = NULL;
Doug Zongker1d5d6092014-08-21 10:47:24 -07001583 char* transfer_list = NULL;
Sami Tolvanen90221202014-12-09 16:39:47 +00001584 CommandParameters params;
1585 const Command* cmd = NULL;
1586 const ZipEntry* new_entry = NULL;
1587 const ZipEntry* patch_entry = NULL;
1588 FILE* cmd_pipe = NULL;
1589 HashTable* cmdht = NULL;
1590 int i;
1591 int res;
1592 int rc = -1;
1593 int stash_max_blocks = 0;
1594 int total_blocks = 0;
1595 pthread_attr_t attr;
1596 unsigned int cmdhash;
1597 UpdaterInfo* ui = NULL;
1598 Value* blockdev_filename = NULL;
1599 Value* new_data_fn = NULL;
1600 Value* patch_data_fn = NULL;
1601 Value* transfer_list_value = NULL;
1602 ZipArchive* za = NULL;
1603
1604 memset(&params, 0, sizeof(params));
1605 params.canwrite = !dryrun;
1606
1607 fprintf(stderr, "performing %s\n", dryrun ? "verification" : "update");
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001608
Doug Zongker1d5d6092014-08-21 10:47:24 -07001609 if (ReadValueArgs(state, argv, 4, &blockdev_filename, &transfer_list_value,
Sami Tolvanen90221202014-12-09 16:39:47 +00001610 &new_data_fn, &patch_data_fn) < 0) {
1611 goto pbiudone;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001612 }
1613
1614 if (blockdev_filename->type != VAL_STRING) {
1615 ErrorAbort(state, "blockdev_filename argument to %s must be string", name);
Sami Tolvanen90221202014-12-09 16:39:47 +00001616 goto pbiudone;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001617 }
Doug Zongker1d5d6092014-08-21 10:47:24 -07001618 if (transfer_list_value->type != VAL_BLOB) {
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001619 ErrorAbort(state, "transfer_list argument to %s must be blob", name);
Sami Tolvanen90221202014-12-09 16:39:47 +00001620 goto pbiudone;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001621 }
1622 if (new_data_fn->type != VAL_STRING) {
1623 ErrorAbort(state, "new_data_fn argument to %s must be string", name);
Sami Tolvanen90221202014-12-09 16:39:47 +00001624 goto pbiudone;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001625 }
1626 if (patch_data_fn->type != VAL_STRING) {
1627 ErrorAbort(state, "patch_data_fn argument to %s must be string", name);
Sami Tolvanen90221202014-12-09 16:39:47 +00001628 goto pbiudone;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001629 }
1630
Sami Tolvanen90221202014-12-09 16:39:47 +00001631 ui = (UpdaterInfo*) state->cookie;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001632
Sami Tolvanen90221202014-12-09 16:39:47 +00001633 if (ui == NULL) {
1634 goto pbiudone;
1635 }
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001636
Sami Tolvanen90221202014-12-09 16:39:47 +00001637 cmd_pipe = ui->cmd_pipe;
1638 za = ui->package_zip;
1639
1640 if (cmd_pipe == NULL || za == NULL) {
1641 goto pbiudone;
1642 }
1643
1644 patch_entry = mzFindZipEntry(za, patch_data_fn->data);
1645
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001646 if (patch_entry == NULL) {
Sami Tolvanen90221202014-12-09 16:39:47 +00001647 fprintf(stderr, "%s(): no file \"%s\" in package", name, patch_data_fn->data);
1648 goto pbiudone;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001649 }
1650
Sami Tolvanen90221202014-12-09 16:39:47 +00001651 params.patch_start = ui->package_zip_addr + mzGetZipEntryOffset(patch_entry);
1652 new_entry = mzFindZipEntry(za, new_data_fn->data);
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001653
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001654 if (new_entry == NULL) {
Sami Tolvanen90221202014-12-09 16:39:47 +00001655 fprintf(stderr, "%s(): no file \"%s\" in package", name, new_data_fn->data);
1656 goto pbiudone;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001657 }
1658
Sami Tolvanen90221202014-12-09 16:39:47 +00001659 params.fd = TEMP_FAILURE_RETRY(open(blockdev_filename->data, O_RDWR));
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001660
Sami Tolvanen90221202014-12-09 16:39:47 +00001661 if (params.fd == -1) {
Elliott Hughes1fdd4522015-03-23 13:33:57 -07001662 fprintf(stderr, "open \"%s\" failed: %s\n", blockdev_filename->data, strerror(errno));
Sami Tolvanen90221202014-12-09 16:39:47 +00001663 goto pbiudone;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001664 }
1665
Sami Tolvanen90221202014-12-09 16:39:47 +00001666 if (params.canwrite) {
1667 params.nti.za = za;
1668 params.nti.entry = new_entry;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001669
Sami Tolvanen90221202014-12-09 16:39:47 +00001670 pthread_mutex_init(&params.nti.mu, NULL);
1671 pthread_cond_init(&params.nti.cv, NULL);
1672 pthread_attr_init(&attr);
1673 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
1674
Elliott Hughes1fdd4522015-03-23 13:33:57 -07001675 int error = pthread_create(&params.thread, &attr, unzip_new_data, &params.nti);
1676 if (error != 0) {
1677 fprintf(stderr, "pthread_create failed: %s\n", strerror(error));
Sami Tolvanen90221202014-12-09 16:39:47 +00001678 goto pbiudone;
1679 }
1680 }
1681
1682 // The data in transfer_list_value is not necessarily null-terminated, so we need
1683 // to copy it to a new buffer and add the null that strtok_r will need.
Tao Bao818fa782015-06-23 23:23:33 -07001684 transfer_list = reinterpret_cast<char*>(malloc(transfer_list_value->size + 1));
Sami Tolvanen90221202014-12-09 16:39:47 +00001685
Doug Zongker1d5d6092014-08-21 10:47:24 -07001686 if (transfer_list == NULL) {
1687 fprintf(stderr, "failed to allocate %zd bytes for transfer list\n",
Sami Tolvanen90221202014-12-09 16:39:47 +00001688 transfer_list_value->size + 1);
1689 goto pbiudone;
Doug Zongker1d5d6092014-08-21 10:47:24 -07001690 }
Sami Tolvanen90221202014-12-09 16:39:47 +00001691
Doug Zongker1d5d6092014-08-21 10:47:24 -07001692 memcpy(transfer_list, transfer_list_value->data, transfer_list_value->size);
1693 transfer_list[transfer_list_value->size] = '\0';
1694
Sami Tolvanen90221202014-12-09 16:39:47 +00001695 // First line in transfer list is the version number
Doug Zongker1d5d6092014-08-21 10:47:24 -07001696 line = strtok_r(transfer_list, "\n", &linesave);
Sami Tolvanen90221202014-12-09 16:39:47 +00001697 params.version = strtol(line, NULL, 0);
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001698
Sami Tolvanen90221202014-12-09 16:39:47 +00001699 if (params.version < 1 || params.version > 3) {
1700 fprintf(stderr, "unexpected transfer list version [%s]\n", line);
1701 goto pbiudone;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001702 }
1703
Sami Tolvanen90221202014-12-09 16:39:47 +00001704 fprintf(stderr, "blockimg version is %d\n", params.version);
1705
1706 // Second line in transfer list is the total number of blocks we expect to write
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001707 line = strtok_r(NULL, "\n", &linesave);
Sami Tolvanen90221202014-12-09 16:39:47 +00001708 total_blocks = strtol(line, NULL, 0);
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001709
Sami Tolvanen90221202014-12-09 16:39:47 +00001710 if (total_blocks < 0) {
1711 ErrorAbort(state, "unexpected block count [%s]\n", line);
1712 goto pbiudone;
1713 } else if (total_blocks == 0) {
1714 rc = 0;
1715 goto pbiudone;
1716 }
1717
1718 if (params.version >= 2) {
1719 // Third line is how many stash entries are needed simultaneously
Doug Zongker52ae67d2014-09-08 12:22:09 -07001720 line = strtok_r(NULL, "\n", &linesave);
Sami Tolvanen90221202014-12-09 16:39:47 +00001721 fprintf(stderr, "maximum stash entries %s\n", line);
Doug Zongker52ae67d2014-09-08 12:22:09 -07001722
Sami Tolvanen90221202014-12-09 16:39:47 +00001723 // Fourth line is the maximum number of blocks that will be stashed simultaneously
1724 line = strtok_r(NULL, "\n", &linesave);
1725 stash_max_blocks = strtol(line, NULL, 0);
1726
1727 if (stash_max_blocks < 0) {
1728 ErrorAbort(state, "unexpected maximum stash blocks [%s]\n", line);
1729 goto pbiudone;
Doug Zongker52ae67d2014-09-08 12:22:09 -07001730 }
1731
Jesse Zhao1df64d32015-02-17 17:09:23 -08001732 if (stash_max_blocks >= 0) {
Sami Tolvanen90221202014-12-09 16:39:47 +00001733 res = CreateStash(state, stash_max_blocks, blockdev_filename->data,
1734 &params.stashbase);
1735
1736 if (res == -1) {
1737 goto pbiudone;
1738 }
1739
1740 params.createdstash = res;
1741 }
Doug Zongker52ae67d2014-09-08 12:22:09 -07001742 }
1743
Sami Tolvanen90221202014-12-09 16:39:47 +00001744 // Build a hash table of the available commands
1745 cmdht = mzHashTableCreate(cmdcount, NULL);
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001746
Sami Tolvanen90221202014-12-09 16:39:47 +00001747 for (i = 0; i < cmdcount; ++i) {
1748 cmdhash = HashString(commands[i].name);
1749 mzHashTableLookup(cmdht, cmdhash, (void*) &commands[i], CompareCommands, true);
1750 }
1751
1752 // Subsequent lines are all individual transfer commands
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001753 for (line = strtok_r(NULL, "\n", &linesave); line;
1754 line = strtok_r(NULL, "\n", &linesave)) {
Doug Zongker52ae67d2014-09-08 12:22:09 -07001755
Sami Tolvanen90221202014-12-09 16:39:47 +00001756 logcmd = strdup(line);
1757 params.cmdname = strtok_r(line, " ", &params.cpos);
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001758
Sami Tolvanen90221202014-12-09 16:39:47 +00001759 if (params.cmdname == NULL) {
1760 fprintf(stderr, "missing command [%s]\n", line);
1761 goto pbiudone;
1762 }
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001763
Sami Tolvanen90221202014-12-09 16:39:47 +00001764 cmdhash = HashString(params.cmdname);
1765 cmd = (const Command*) mzHashTableLookup(cmdht, cmdhash, params.cmdname,
1766 CompareCommandNames, false);
Doug Zongker52ae67d2014-09-08 12:22:09 -07001767
Sami Tolvanen90221202014-12-09 16:39:47 +00001768 if (cmd == NULL) {
1769 fprintf(stderr, "unexpected command [%s]\n", params.cmdname);
1770 goto pbiudone;
1771 }
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001772
Sami Tolvanen90221202014-12-09 16:39:47 +00001773 if (cmd->f != NULL && cmd->f(&params) == -1) {
1774 fprintf(stderr, "failed to execute command [%s]\n",
1775 logcmd ? logcmd : params.cmdname);
1776 goto pbiudone;
1777 }
1778
1779 if (logcmd) {
1780 free(logcmd);
1781 logcmd = NULL;
1782 }
1783
1784 if (params.canwrite) {
1785 fprintf(cmd_pipe, "set_progress %.4f\n", (double) params.written / total_blocks);
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001786 fflush(cmd_pipe);
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001787 }
1788 }
1789
Sami Tolvanen90221202014-12-09 16:39:47 +00001790 if (params.canwrite) {
1791 pthread_join(params.thread, NULL);
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001792
Sami Tolvanen90221202014-12-09 16:39:47 +00001793 fprintf(stderr, "wrote %d blocks; expected %d\n", params.written, total_blocks);
1794 fprintf(stderr, "max alloc needed was %zu\n", params.bufsize);
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001795
Sami Tolvanen90221202014-12-09 16:39:47 +00001796 // Delete stash only after successfully completing the update, as it
1797 // may contain blocks needed to complete the update later.
1798 DeleteStash(params.stashbase);
1799 } else {
1800 fprintf(stderr, "verified partition contents; update may be resumed\n");
1801 }
1802
1803 rc = 0;
1804
1805pbiudone:
1806 if (params.fd != -1) {
1807 if (fsync(params.fd) == -1) {
Elliott Hughes1fdd4522015-03-23 13:33:57 -07001808 fprintf(stderr, "fsync failed: %s\n", strerror(errno));
Sami Tolvanen90221202014-12-09 16:39:47 +00001809 }
Elliott Hughes1857a7f2015-05-15 16:19:20 -07001810 close(params.fd);
Sami Tolvanen90221202014-12-09 16:39:47 +00001811 }
1812
1813 if (logcmd) {
1814 free(logcmd);
1815 }
1816
1817 if (cmdht) {
1818 mzHashTableFree(cmdht);
1819 }
1820
1821 if (params.buffer) {
1822 free(params.buffer);
1823 }
1824
1825 if (transfer_list) {
1826 free(transfer_list);
1827 }
1828
1829 if (blockdev_filename) {
1830 FreeValue(blockdev_filename);
1831 }
1832
1833 if (transfer_list_value) {
1834 FreeValue(transfer_list_value);
1835 }
1836
1837 if (new_data_fn) {
1838 FreeValue(new_data_fn);
1839 }
1840
1841 if (patch_data_fn) {
1842 FreeValue(patch_data_fn);
1843 }
1844
1845 // Only delete the stash if the update cannot be resumed, or it's
1846 // a verification run and we created the stash.
1847 if (params.isunresumable || (!params.canwrite && params.createdstash)) {
1848 DeleteStash(params.stashbase);
1849 }
1850
1851 if (params.stashbase) {
1852 free(params.stashbase);
1853 }
1854
1855 return StringValue(rc == 0 ? strdup("t") : strdup(""));
1856}
1857
1858// The transfer list is a text file containing commands to
1859// transfer data from one place to another on the target
1860// partition. We parse it and execute the commands in order:
1861//
1862// zero [rangeset]
1863// - fill the indicated blocks with zeros
1864//
1865// new [rangeset]
1866// - fill the blocks with data read from the new_data file
1867//
1868// erase [rangeset]
1869// - mark the given blocks as empty
1870//
1871// move <...>
1872// bsdiff <patchstart> <patchlen> <...>
1873// imgdiff <patchstart> <patchlen> <...>
1874// - read the source blocks, apply a patch (or not in the
1875// case of move), write result to target blocks. bsdiff or
1876// imgdiff specifies the type of patch; move means no patch
1877// at all.
1878//
1879// The format of <...> differs between versions 1 and 2;
1880// see the LoadSrcTgtVersion{1,2}() functions for a
1881// description of what's expected.
1882//
1883// stash <stash_id> <src_range>
1884// - (version 2+ only) load the given source range and stash
1885// the data in the given slot of the stash table.
1886//
1887// The creator of the transfer list will guarantee that no block
1888// is read (ie, used as the source for a patch or move) after it
1889// has been written.
1890//
1891// In version 2, the creator will guarantee that a given stash is
1892// loaded (with a stash command) before it's used in a
1893// move/bsdiff/imgdiff command.
1894//
1895// Within one command the source and target ranges may overlap so
1896// in general we need to read the entire source into memory before
1897// writing anything to the target blocks.
1898//
1899// All the patch data is concatenated into one patch_data file in
1900// the update package. It must be stored uncompressed because we
1901// memory-map it in directly from the archive. (Since patches are
1902// already compressed, we lose very little by not compressing
1903// their concatenation.)
1904//
1905// In version 3, commands that read data from the partition (i.e.
1906// move/bsdiff/imgdiff/stash) have one or more additional hashes
1907// before the range parameters, which are used to check if the
1908// command has already been completed and verify the integrity of
1909// the source data.
1910
1911Value* BlockImageVerifyFn(const char* name, State* state, int argc, Expr* argv[]) {
1912 // Commands which are not tested are set to NULL to skip them completely
1913 const Command commands[] = {
1914 { "bsdiff", PerformCommandDiff },
1915 { "erase", NULL },
1916 { "free", PerformCommandFree },
1917 { "imgdiff", PerformCommandDiff },
1918 { "move", PerformCommandMove },
1919 { "new", NULL },
1920 { "stash", PerformCommandStash },
1921 { "zero", NULL }
1922 };
1923
1924 // Perform a dry run without writing to test if an update can proceed
1925 return PerformBlockImageUpdate(name, state, argc, argv, commands,
1926 sizeof(commands) / sizeof(commands[0]), 1);
1927}
1928
1929Value* BlockImageUpdateFn(const char* name, State* state, int argc, Expr* argv[]) {
1930 const Command commands[] = {
1931 { "bsdiff", PerformCommandDiff },
1932 { "erase", PerformCommandErase },
1933 { "free", PerformCommandFree },
1934 { "imgdiff", PerformCommandDiff },
1935 { "move", PerformCommandMove },
1936 { "new", PerformCommandNew },
1937 { "stash", PerformCommandStash },
1938 { "zero", PerformCommandZero }
1939 };
1940
1941 return PerformBlockImageUpdate(name, state, argc, argv, commands,
1942 sizeof(commands) / sizeof(commands[0]), 0);
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001943}
1944
1945Value* RangeSha1Fn(const char* name, State* state, int argc, Expr* argv[]) {
1946 Value* blockdev_filename;
1947 Value* ranges;
1948 const uint8_t* digest = NULL;
1949 if (ReadValueArgs(state, argv, 2, &blockdev_filename, &ranges) < 0) {
1950 return NULL;
1951 }
1952
1953 if (blockdev_filename->type != VAL_STRING) {
1954 ErrorAbort(state, "blockdev_filename argument to %s must be string", name);
1955 goto done;
1956 }
1957 if (ranges->type != VAL_STRING) {
1958 ErrorAbort(state, "ranges argument to %s must be string", name);
1959 goto done;
1960 }
1961
Tao Bao818fa782015-06-23 23:23:33 -07001962 int fd;
1963 fd = open(blockdev_filename->data, O_RDWR);
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001964 if (fd < 0) {
Elliott Hughes1fdd4522015-03-23 13:33:57 -07001965 ErrorAbort(state, "open \"%s\" failed: %s", blockdev_filename->data, strerror(errno));
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001966 goto done;
1967 }
1968
Tao Bao818fa782015-06-23 23:23:33 -07001969 RangeSet* rs;
1970 rs = parse_range(ranges->data);
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001971 uint8_t buffer[BLOCKSIZE];
1972
1973 SHA_CTX ctx;
1974 SHA_init(&ctx);
1975
1976 int i, j;
1977 for (i = 0; i < rs->count; ++i) {
Elliott Hughes2f5feed2015-04-28 17:24:24 -07001978 if (!check_lseek(fd, (off64_t)rs->pos[i*2] * BLOCKSIZE, SEEK_SET)) {
Sami Tolvanen90221202014-12-09 16:39:47 +00001979 ErrorAbort(state, "failed to seek %s: %s", blockdev_filename->data,
1980 strerror(errno));
1981 goto done;
1982 }
1983
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001984 for (j = rs->pos[i*2]; j < rs->pos[i*2+1]; ++j) {
Sami Tolvanen90221202014-12-09 16:39:47 +00001985 if (read_all(fd, buffer, BLOCKSIZE) == -1) {
1986 ErrorAbort(state, "failed to read %s: %s", blockdev_filename->data,
1987 strerror(errno));
1988 goto done;
1989 }
1990
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001991 SHA_update(&ctx, buffer, BLOCKSIZE);
1992 }
1993 }
1994 digest = SHA_final(&ctx);
1995 close(fd);
1996
1997 done:
1998 FreeValue(blockdev_filename);
1999 FreeValue(ranges);
2000 if (digest == NULL) {
2001 return StringValue(strdup(""));
2002 } else {
2003 return StringValue(PrintSha1(digest));
2004 }
2005}
2006
2007void RegisterBlockImageFunctions() {
Sami Tolvanen90221202014-12-09 16:39:47 +00002008 RegisterFunction("block_image_verify", BlockImageVerifyFn);
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07002009 RegisterFunction("block_image_update", BlockImageUpdateFn);
2010 RegisterFunction("range_sha1", RangeSha1Fn);
2011}