blob: 258d975522a8dbfa3e0e6b733a5fd19dc1bc7c35 [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 Baoba9a42a2015-06-23 23:23:33 -070022#include <linux/fs.h>
Doug Zongkerbc7ffed2014-08-15 14:31:52 -070023#include <pthread.h>
24#include <stdarg.h>
25#include <stdio.h>
26#include <stdlib.h>
27#include <string.h>
Sami Tolvanen90221202014-12-09 16:39:47 +000028#include <sys/stat.h>
Doug Zongkerbc7ffed2014-08-15 14:31:52 -070029#include <sys/types.h>
30#include <sys/wait.h>
31#include <sys/ioctl.h>
32#include <time.h>
33#include <unistd.h>
34
35#include "applypatch/applypatch.h"
36#include "edify/expr.h"
37#include "mincrypt/sha.h"
Sami Tolvanen90221202014-12-09 16:39:47 +000038#include "minzip/Hash.h"
Doug Zongkerbc7ffed2014-08-15 14:31:52 -070039#include "updater.h"
40
41#define BLOCKSIZE 4096
42
Sami Tolvanene82fa182015-06-10 15:58:12 +000043// Set this to 0 to interpret 'erase' transfers to mean do a
44// BLKDISCARD ioctl (the normal behavior). Set to 1 to interpret
45// erase to mean fill the region with zeroes.
46#define DEBUG_ERASE 0
47
Sami Tolvanen90221202014-12-09 16:39:47 +000048#define STASH_DIRECTORY_BASE "/cache/recovery"
49#define STASH_DIRECTORY_MODE 0700
50#define STASH_FILE_MODE 0600
51
Doug Zongkerbc7ffed2014-08-15 14:31:52 -070052char* PrintSha1(const uint8_t* digest);
53
54typedef struct {
55 int count;
56 int size;
57 int pos[0];
58} RangeSet;
59
Sami Tolvanenf2bac042015-05-12 12:48:46 +010060#define RANGESET_MAX_POINTS \
61 ((int)((INT_MAX / sizeof(int)) - sizeof(RangeSet)))
62
Doug Zongkerbc7ffed2014-08-15 14:31:52 -070063static RangeSet* parse_range(char* text) {
64 char* save;
Sami Tolvanenf2bac042015-05-12 12:48:46 +010065 char* token;
Tao Baoba9a42a2015-06-23 23:23:33 -070066 int num;
Sami Tolvanenf2bac042015-05-12 12:48:46 +010067 long int val;
68 RangeSet* out = NULL;
69 size_t bufsize;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -070070
Sami Tolvanenf2bac042015-05-12 12:48:46 +010071 if (!text) {
72 goto err;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -070073 }
Sami Tolvanenf2bac042015-05-12 12:48:46 +010074
75 token = strtok_r(text, ",", &save);
76
77 if (!token) {
78 goto err;
79 }
80
81 val = strtol(token, NULL, 0);
82
83 if (val < 2 || val > RANGESET_MAX_POINTS) {
84 goto err;
85 } else if (val % 2) {
86 goto err; // must be even
87 }
88
89 num = (int) val;
90 bufsize = sizeof(RangeSet) + num * sizeof(int);
91
Tao Baoba9a42a2015-06-23 23:23:33 -070092 out = reinterpret_cast<RangeSet*>(malloc(bufsize));
Sami Tolvanenf2bac042015-05-12 12:48:46 +010093
94 if (!out) {
95 fprintf(stderr, "failed to allocate range of %zu bytes\n", bufsize);
96 goto err;
97 }
98
Doug Zongkerbc7ffed2014-08-15 14:31:52 -070099 out->count = num / 2;
100 out->size = 0;
Sami Tolvanenf2bac042015-05-12 12:48:46 +0100101
Tao Baoba9a42a2015-06-23 23:23:33 -0700102 for (int i = 0; i < num; ++i) {
Sami Tolvanenf2bac042015-05-12 12:48:46 +0100103 token = strtok_r(NULL, ",", &save);
104
105 if (!token) {
106 goto err;
107 }
108
109 val = strtol(token, NULL, 0);
110
111 if (val < 0 || val > INT_MAX) {
112 goto err;
113 }
114
115 out->pos[i] = (int) val;
116
117 if (i % 2) {
118 if (out->pos[i - 1] >= out->pos[i]) {
119 goto err; // empty or negative range
120 }
121
122 if (out->size > INT_MAX - out->pos[i]) {
123 goto err; // overflow
124 }
125
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700126 out->size += out->pos[i];
127 } else {
Sami Tolvanenf2bac042015-05-12 12:48:46 +0100128 if (out->size < 0) {
129 goto err;
130 }
131
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700132 out->size -= out->pos[i];
133 }
134 }
135
Sami Tolvanenf2bac042015-05-12 12:48:46 +0100136 if (out->size <= 0) {
137 goto err;
138 }
139
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700140 return out;
Sami Tolvanenf2bac042015-05-12 12:48:46 +0100141
142err:
143 fprintf(stderr, "failed to parse range '%s'\n", text ? text : "NULL");
144 exit(1);
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700145}
146
Sami Tolvanen90221202014-12-09 16:39:47 +0000147static int range_overlaps(RangeSet* r1, RangeSet* r2) {
148 int i, j, r1_0, r1_1, r2_0, r2_1;
149
150 if (!r1 || !r2) {
151 return 0;
152 }
153
154 for (i = 0; i < r1->count; ++i) {
155 r1_0 = r1->pos[i * 2];
156 r1_1 = r1->pos[i * 2 + 1];
157
158 for (j = 0; j < r2->count; ++j) {
159 r2_0 = r2->pos[j * 2];
160 r2_1 = r2->pos[j * 2 + 1];
161
Tao Baoc0f56ad2015-06-25 14:00:31 -0700162 if (!(r2_0 >= r1_1 || r1_0 >= r2_1)) {
Sami Tolvanen90221202014-12-09 16:39:47 +0000163 return 1;
164 }
165 }
166 }
167
168 return 0;
169}
170
171static int read_all(int fd, uint8_t* data, size_t size) {
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700172 size_t so_far = 0;
173 while (so_far < size) {
Elliott Hughes7bad7c42015-04-28 17:24:24 -0700174 ssize_t r = TEMP_FAILURE_RETRY(read(fd, data+so_far, size-so_far));
175 if (r == -1) {
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700176 fprintf(stderr, "read failed: %s\n", strerror(errno));
Sami Tolvanen90221202014-12-09 16:39:47 +0000177 return -1;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700178 }
Elliott Hughes7bad7c42015-04-28 17:24:24 -0700179 so_far += r;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700180 }
Sami Tolvanen90221202014-12-09 16:39:47 +0000181 return 0;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700182}
183
Sami Tolvanen90221202014-12-09 16:39:47 +0000184static int write_all(int fd, const uint8_t* data, size_t size) {
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700185 size_t written = 0;
186 while (written < size) {
Elliott Hughes7bad7c42015-04-28 17:24:24 -0700187 ssize_t w = TEMP_FAILURE_RETRY(write(fd, data+written, size-written));
188 if (w == -1) {
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700189 fprintf(stderr, "write failed: %s\n", strerror(errno));
Sami Tolvanen90221202014-12-09 16:39:47 +0000190 return -1;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700191 }
Elliott Hughes7bad7c42015-04-28 17:24:24 -0700192 written += w;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700193 }
Sami Tolvanen90221202014-12-09 16:39:47 +0000194
195 if (fsync(fd) == -1) {
196 fprintf(stderr, "fsync failed: %s\n", strerror(errno));
197 return -1;
198 }
199
200 return 0;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700201}
202
Elliott Hughes7bad7c42015-04-28 17:24:24 -0700203static bool check_lseek(int fd, off64_t offset, int whence) {
204 off64_t rc = TEMP_FAILURE_RETRY(lseek64(fd, offset, whence));
205 if (rc == -1) {
206 fprintf(stderr, "lseek64 failed: %s\n", strerror(errno));
207 return false;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700208 }
Elliott Hughes7bad7c42015-04-28 17:24:24 -0700209 return true;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700210}
211
212static void allocate(size_t size, uint8_t** buffer, size_t* buffer_alloc) {
213 // if the buffer's big enough, reuse it.
214 if (size <= *buffer_alloc) return;
215
216 free(*buffer);
217
218 *buffer = (uint8_t*) malloc(size);
219 if (*buffer == NULL) {
Doug Zongker1d5d6092014-08-21 10:47:24 -0700220 fprintf(stderr, "failed to allocate %zu bytes\n", size);
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700221 exit(1);
222 }
223 *buffer_alloc = size;
224}
225
226typedef struct {
227 int fd;
228 RangeSet* tgt;
229 int p_block;
230 size_t p_remain;
231} RangeSinkState;
232
233static ssize_t RangeSinkWrite(const uint8_t* data, ssize_t size, void* token) {
234 RangeSinkState* rss = (RangeSinkState*) token;
235
236 if (rss->p_remain <= 0) {
237 fprintf(stderr, "range sink write overrun");
Sami Tolvanen90221202014-12-09 16:39:47 +0000238 return 0;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700239 }
240
241 ssize_t written = 0;
242 while (size > 0) {
243 size_t write_now = size;
Sami Tolvanen90221202014-12-09 16:39:47 +0000244
245 if (rss->p_remain < write_now) {
246 write_now = rss->p_remain;
247 }
248
249 if (write_all(rss->fd, data, write_now) == -1) {
250 break;
251 }
252
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700253 data += write_now;
254 size -= write_now;
255
256 rss->p_remain -= write_now;
257 written += write_now;
258
259 if (rss->p_remain == 0) {
260 // move to the next block
261 ++rss->p_block;
262 if (rss->p_block < rss->tgt->count) {
Sami Tolvanen90221202014-12-09 16:39:47 +0000263 rss->p_remain = (rss->tgt->pos[rss->p_block * 2 + 1] -
264 rss->tgt->pos[rss->p_block * 2]) * BLOCKSIZE;
265
Elliott Hughes7bad7c42015-04-28 17:24:24 -0700266 if (!check_lseek(rss->fd, (off64_t)rss->tgt->pos[rss->p_block*2] * BLOCKSIZE,
267 SEEK_SET)) {
Sami Tolvanen90221202014-12-09 16:39:47 +0000268 break;
269 }
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700270 } else {
271 // we can't write any more; return how many bytes have
272 // been written so far.
Sami Tolvanen90221202014-12-09 16:39:47 +0000273 break;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700274 }
275 }
276 }
277
278 return written;
279}
280
281// All of the data for all the 'new' transfers is contained in one
282// file in the update package, concatenated together in the order in
283// which transfers.list will need it. We want to stream it out of the
284// archive (it's compressed) without writing it to a temp file, but we
285// can't write each section until it's that transfer's turn to go.
286//
287// To achieve this, we expand the new data from the archive in a
288// background thread, and block that threads 'receive uncompressed
289// data' function until the main thread has reached a point where we
290// want some new data to be written. We signal the background thread
291// with the destination for the data and block the main thread,
292// waiting for the background thread to complete writing that section.
293// Then it signals the main thread to wake up and goes back to
294// blocking waiting for a transfer.
295//
296// NewThreadInfo is the struct used to pass information back and forth
297// between the two threads. When the main thread wants some data
298// written, it sets rss to the destination location and signals the
299// condition. When the background thread is done writing, it clears
300// rss and signals the condition again.
301
302typedef struct {
303 ZipArchive* za;
304 const ZipEntry* entry;
305
306 RangeSinkState* rss;
307
308 pthread_mutex_t mu;
309 pthread_cond_t cv;
310} NewThreadInfo;
311
312static bool receive_new_data(const unsigned char* data, int size, void* cookie) {
313 NewThreadInfo* nti = (NewThreadInfo*) cookie;
314
315 while (size > 0) {
316 // Wait for nti->rss to be non-NULL, indicating some of this
317 // data is wanted.
318 pthread_mutex_lock(&nti->mu);
319 while (nti->rss == NULL) {
320 pthread_cond_wait(&nti->cv, &nti->mu);
321 }
322 pthread_mutex_unlock(&nti->mu);
323
324 // At this point nti->rss is set, and we own it. The main
325 // thread is waiting for it to disappear from nti.
326 ssize_t written = RangeSinkWrite(data, size, nti->rss);
327 data += written;
328 size -= written;
329
330 if (nti->rss->p_block == nti->rss->tgt->count) {
331 // we have written all the bytes desired by this rss.
332
333 pthread_mutex_lock(&nti->mu);
334 nti->rss = NULL;
335 pthread_cond_broadcast(&nti->cv);
336 pthread_mutex_unlock(&nti->mu);
337 }
338 }
339
340 return true;
341}
342
343static void* unzip_new_data(void* cookie) {
344 NewThreadInfo* nti = (NewThreadInfo*) cookie;
345 mzProcessZipEntryContents(nti->za, nti->entry, receive_new_data, nti);
346 return NULL;
347}
348
Sami Tolvanen90221202014-12-09 16:39:47 +0000349static int ReadBlocks(RangeSet* src, uint8_t* buffer, int fd) {
350 int i;
351 size_t p = 0;
352 size_t size;
353
354 if (!src || !buffer) {
355 return -1;
356 }
357
358 for (i = 0; i < src->count; ++i) {
Elliott Hughes7bad7c42015-04-28 17:24:24 -0700359 if (!check_lseek(fd, (off64_t) src->pos[i * 2] * BLOCKSIZE, SEEK_SET)) {
Sami Tolvanen90221202014-12-09 16:39:47 +0000360 return -1;
361 }
362
363 size = (src->pos[i * 2 + 1] - src->pos[i * 2]) * BLOCKSIZE;
364
365 if (read_all(fd, buffer + p, size) == -1) {
366 return -1;
367 }
368
369 p += size;
370 }
371
372 return 0;
373}
374
375static int WriteBlocks(RangeSet* tgt, uint8_t* buffer, int fd) {
376 int i;
377 size_t p = 0;
378 size_t size;
379
380 if (!tgt || !buffer) {
381 return -1;
382 }
383
384 for (i = 0; i < tgt->count; ++i) {
Elliott Hughes7bad7c42015-04-28 17:24:24 -0700385 if (!check_lseek(fd, (off64_t) tgt->pos[i * 2] * BLOCKSIZE, SEEK_SET)) {
Sami Tolvanen90221202014-12-09 16:39:47 +0000386 return -1;
387 }
388
389 size = (tgt->pos[i * 2 + 1] - tgt->pos[i * 2]) * BLOCKSIZE;
390
391 if (write_all(fd, buffer + p, size) == -1) {
392 return -1;
393 }
394
395 p += size;
396 }
397
398 return 0;
399}
400
Doug Zongker52ae67d2014-09-08 12:22:09 -0700401// Do a source/target load for move/bsdiff/imgdiff in version 1.
402// 'wordsave' is the save_ptr of a strtok_r()-in-progress. We expect
403// to parse the remainder of the string as:
404//
405// <src_range> <tgt_range>
406//
407// The source range is loaded into the provided buffer, reallocating
408// it to make it larger if necessary. The target ranges are returned
409// in *tgt, if tgt is non-NULL.
410
Sami Tolvanen90221202014-12-09 16:39:47 +0000411static int LoadSrcTgtVersion1(char** wordsave, RangeSet** tgt, int* src_blocks,
Doug Zongker52ae67d2014-09-08 12:22:09 -0700412 uint8_t** buffer, size_t* buffer_alloc, int fd) {
413 char* word;
Sami Tolvanen90221202014-12-09 16:39:47 +0000414 int rc;
Doug Zongker52ae67d2014-09-08 12:22:09 -0700415
Sami Tolvanen90221202014-12-09 16:39:47 +0000416 word = strtok_r(NULL, " ", wordsave);
Doug Zongker52ae67d2014-09-08 12:22:09 -0700417 RangeSet* src = parse_range(word);
418
419 if (tgt != NULL) {
Sami Tolvanen90221202014-12-09 16:39:47 +0000420 word = strtok_r(NULL, " ", wordsave);
Doug Zongker52ae67d2014-09-08 12:22:09 -0700421 *tgt = parse_range(word);
422 }
423
424 allocate(src->size * BLOCKSIZE, buffer, buffer_alloc);
Sami Tolvanen90221202014-12-09 16:39:47 +0000425 rc = ReadBlocks(src, *buffer, fd);
426 *src_blocks = src->size;
427
428 free(src);
429 return rc;
430}
431
432static int VerifyBlocks(const char *expected, const uint8_t *buffer,
433 size_t blocks, int printerror) {
434 char* hexdigest = NULL;
435 int rc = -1;
436 uint8_t digest[SHA_DIGEST_SIZE];
437
438 if (!expected || !buffer) {
439 return rc;
Doug Zongker52ae67d2014-09-08 12:22:09 -0700440 }
441
Sami Tolvanen90221202014-12-09 16:39:47 +0000442 SHA_hash(buffer, blocks * BLOCKSIZE, digest);
443 hexdigest = PrintSha1(digest);
444
445 if (hexdigest != NULL) {
446 rc = strcmp(expected, hexdigest);
447
448 if (rc != 0 && printerror) {
449 fprintf(stderr, "failed to verify blocks (expected %s, read %s)\n",
450 expected, hexdigest);
451 }
452
453 free(hexdigest);
454 }
455
456 return rc;
457}
458
459static char* GetStashFileName(const char* base, const char* id, const char* postfix) {
460 char* fn;
461 int len;
462 int res;
463
464 if (base == NULL) {
465 return NULL;
466 }
467
468 if (id == NULL) {
469 id = "";
470 }
471
472 if (postfix == NULL) {
473 postfix = "";
474 }
475
476 len = strlen(STASH_DIRECTORY_BASE) + 1 + strlen(base) + 1 + strlen(id) + strlen(postfix) + 1;
Tao Baoba9a42a2015-06-23 23:23:33 -0700477 fn = reinterpret_cast<char*>(malloc(len));
Sami Tolvanen90221202014-12-09 16:39:47 +0000478
479 if (fn == NULL) {
480 fprintf(stderr, "failed to malloc %d bytes for fn\n", len);
481 return NULL;
482 }
483
484 res = snprintf(fn, len, STASH_DIRECTORY_BASE "/%s/%s%s", base, id, postfix);
485
486 if (res < 0 || res >= len) {
487 fprintf(stderr, "failed to format file name (return value %d)\n", res);
488 free(fn);
489 return NULL;
490 }
491
492 return fn;
493}
494
495typedef void (*StashCallback)(const char*, void*);
496
497// Does a best effort enumeration of stash files. Ignores possible non-file
498// items in the stash directory and continues despite of errors. Calls the
499// 'callback' function for each file and passes 'data' to the function as a
500// parameter.
501
502static void EnumerateStash(const char* dirname, StashCallback callback, void* data) {
503 char* fn;
504 DIR* directory;
505 int len;
506 int res;
507 struct dirent* item;
508
509 if (dirname == NULL || callback == NULL) {
510 return;
511 }
512
513 directory = opendir(dirname);
514
515 if (directory == NULL) {
516 if (errno != ENOENT) {
Elliott Hughes1fdd4522015-03-23 13:33:57 -0700517 fprintf(stderr, "opendir \"%s\" failed: %s\n", dirname, strerror(errno));
Sami Tolvanen90221202014-12-09 16:39:47 +0000518 }
519 return;
520 }
521
522 while ((item = readdir(directory)) != NULL) {
523 if (item->d_type != DT_REG) {
524 continue;
525 }
526
527 len = strlen(dirname) + 1 + strlen(item->d_name) + 1;
Tao Baoba9a42a2015-06-23 23:23:33 -0700528 fn = reinterpret_cast<char*>(malloc(len));
Sami Tolvanen90221202014-12-09 16:39:47 +0000529
530 if (fn == NULL) {
531 fprintf(stderr, "failed to malloc %d bytes for fn\n", len);
532 continue;
533 }
534
535 res = snprintf(fn, len, "%s/%s", dirname, item->d_name);
536
537 if (res < 0 || res >= len) {
538 fprintf(stderr, "failed to format file name (return value %d)\n", res);
539 free(fn);
540 continue;
541 }
542
543 callback(fn, data);
544 free(fn);
545 }
546
547 if (closedir(directory) == -1) {
Elliott Hughes1fdd4522015-03-23 13:33:57 -0700548 fprintf(stderr, "closedir \"%s\" failed: %s\n", dirname, strerror(errno));
Sami Tolvanen90221202014-12-09 16:39:47 +0000549 }
550}
551
552static void UpdateFileSize(const char* fn, void* data) {
553 int* size = (int*) data;
554 struct stat st;
555
556 if (!fn || !data) {
557 return;
558 }
559
560 if (stat(fn, &st) == -1) {
Elliott Hughes1fdd4522015-03-23 13:33:57 -0700561 fprintf(stderr, "stat \"%s\" failed: %s\n", fn, strerror(errno));
Sami Tolvanen90221202014-12-09 16:39:47 +0000562 return;
563 }
564
565 *size += st.st_size;
566}
567
568// Deletes the stash directory and all files in it. Assumes that it only
569// contains files. There is nothing we can do about unlikely, but possible
570// errors, so they are merely logged.
571
572static void DeleteFile(const char* fn, void* data) {
573 if (fn) {
574 fprintf(stderr, "deleting %s\n", fn);
575
576 if (unlink(fn) == -1 && errno != ENOENT) {
Elliott Hughes1fdd4522015-03-23 13:33:57 -0700577 fprintf(stderr, "unlink \"%s\" failed: %s\n", fn, strerror(errno));
Sami Tolvanen90221202014-12-09 16:39:47 +0000578 }
579 }
580}
581
582static void DeletePartial(const char* fn, void* data) {
583 if (fn && strstr(fn, ".partial") != NULL) {
584 DeleteFile(fn, data);
585 }
586}
587
588static void DeleteStash(const char* base) {
589 char* dirname;
590
591 if (base == NULL) {
592 return;
593 }
594
595 dirname = GetStashFileName(base, NULL, NULL);
596
597 if (dirname == NULL) {
598 return;
599 }
600
601 fprintf(stderr, "deleting stash %s\n", base);
602 EnumerateStash(dirname, DeleteFile, NULL);
603
604 if (rmdir(dirname) == -1) {
605 if (errno != ENOENT && errno != ENOTDIR) {
Elliott Hughes1fdd4522015-03-23 13:33:57 -0700606 fprintf(stderr, "rmdir \"%s\" failed: %s\n", dirname, strerror(errno));
Sami Tolvanen90221202014-12-09 16:39:47 +0000607 }
608 }
609
610 free(dirname);
611}
612
613static int LoadStash(const char* base, const char* id, int verify, int* blocks, uint8_t** buffer,
614 size_t* buffer_alloc, int printnoent) {
615 char *fn = NULL;
616 int blockcount = 0;
617 int fd = -1;
618 int rc = -1;
619 int res;
620 struct stat st;
621
622 if (!base || !id || !buffer || !buffer_alloc) {
623 goto lsout;
624 }
625
626 if (!blocks) {
627 blocks = &blockcount;
628 }
629
630 fn = GetStashFileName(base, id, NULL);
631
632 if (fn == NULL) {
633 goto lsout;
634 }
635
636 res = stat(fn, &st);
637
638 if (res == -1) {
639 if (errno != ENOENT || printnoent) {
Elliott Hughes1fdd4522015-03-23 13:33:57 -0700640 fprintf(stderr, "stat \"%s\" failed: %s\n", fn, strerror(errno));
Sami Tolvanen90221202014-12-09 16:39:47 +0000641 }
642 goto lsout;
643 }
644
645 fprintf(stderr, " loading %s\n", fn);
646
647 if ((st.st_size % BLOCKSIZE) != 0) {
Tao Baoba9a42a2015-06-23 23:23:33 -0700648 fprintf(stderr, "%s size %" PRId64 " not multiple of block size %d",
649 fn, static_cast<int64_t>(st.st_size), BLOCKSIZE);
Sami Tolvanen90221202014-12-09 16:39:47 +0000650 goto lsout;
651 }
652
653 fd = TEMP_FAILURE_RETRY(open(fn, O_RDONLY));
654
655 if (fd == -1) {
Elliott Hughes1fdd4522015-03-23 13:33:57 -0700656 fprintf(stderr, "open \"%s\" failed: %s\n", fn, strerror(errno));
Sami Tolvanen90221202014-12-09 16:39:47 +0000657 goto lsout;
658 }
659
660 allocate(st.st_size, buffer, buffer_alloc);
661
662 if (read_all(fd, *buffer, st.st_size) == -1) {
663 goto lsout;
664 }
665
666 *blocks = st.st_size / BLOCKSIZE;
667
668 if (verify && VerifyBlocks(id, *buffer, *blocks, 1) != 0) {
669 fprintf(stderr, "unexpected contents in %s\n", fn);
670 DeleteFile(fn, NULL);
671 goto lsout;
672 }
673
674 rc = 0;
675
676lsout:
677 if (fd != -1) {
Elliott Hughesb3ac6762015-05-28 23:06:17 -0700678 close(fd);
Sami Tolvanen90221202014-12-09 16:39:47 +0000679 }
680
681 if (fn) {
682 free(fn);
683 }
684
685 return rc;
686}
687
688static int WriteStash(const char* base, const char* id, int blocks, uint8_t* buffer,
Sami Tolvanen43b748f2015-04-17 12:50:31 +0100689 int checkspace, int *exists) {
Sami Tolvanen90221202014-12-09 16:39:47 +0000690 char *fn = NULL;
691 char *cn = NULL;
692 int fd = -1;
693 int rc = -1;
694 int res;
Sami Tolvanen43b748f2015-04-17 12:50:31 +0100695 struct stat st;
Sami Tolvanen90221202014-12-09 16:39:47 +0000696
697 if (base == NULL || buffer == NULL) {
698 goto wsout;
699 }
700
701 if (checkspace && CacheSizeCheck(blocks * BLOCKSIZE) != 0) {
702 fprintf(stderr, "not enough space to write stash\n");
703 goto wsout;
704 }
705
706 fn = GetStashFileName(base, id, ".partial");
707 cn = GetStashFileName(base, id, NULL);
708
709 if (fn == NULL || cn == NULL) {
710 goto wsout;
711 }
712
Sami Tolvanen43b748f2015-04-17 12:50:31 +0100713 if (exists) {
714 res = stat(cn, &st);
715
716 if (res == 0) {
717 // The file already exists and since the name is the hash of the contents,
718 // it's safe to assume the contents are identical (accidental hash collisions
719 // are unlikely)
720 fprintf(stderr, " skipping %d existing blocks in %s\n", blocks, cn);
721 *exists = 1;
722 rc = 0;
723 goto wsout;
724 }
725
726 *exists = 0;
727 }
728
Sami Tolvanen90221202014-12-09 16:39:47 +0000729 fprintf(stderr, " writing %d blocks to %s\n", blocks, cn);
730
731 fd = TEMP_FAILURE_RETRY(open(fn, O_WRONLY | O_CREAT | O_TRUNC | O_SYNC, STASH_FILE_MODE));
732
733 if (fd == -1) {
Elliott Hughes1fdd4522015-03-23 13:33:57 -0700734 fprintf(stderr, "failed to create \"%s\": %s\n", fn, strerror(errno));
Sami Tolvanen90221202014-12-09 16:39:47 +0000735 goto wsout;
736 }
737
738 if (write_all(fd, buffer, blocks * BLOCKSIZE) == -1) {
739 goto wsout;
740 }
741
742 if (fsync(fd) == -1) {
Elliott Hughes1fdd4522015-03-23 13:33:57 -0700743 fprintf(stderr, "fsync \"%s\" failed: %s\n", fn, strerror(errno));
Sami Tolvanen90221202014-12-09 16:39:47 +0000744 goto wsout;
745 }
746
747 if (rename(fn, cn) == -1) {
Elliott Hughes1fdd4522015-03-23 13:33:57 -0700748 fprintf(stderr, "rename(\"%s\", \"%s\") failed: %s\n", fn, cn, strerror(errno));
Sami Tolvanen90221202014-12-09 16:39:47 +0000749 goto wsout;
750 }
751
752 rc = 0;
753
754wsout:
755 if (fd != -1) {
Elliott Hughesb47afed2015-05-15 16:19:20 -0700756 close(fd);
Sami Tolvanen90221202014-12-09 16:39:47 +0000757 }
758
759 if (fn) {
760 free(fn);
761 }
762
763 if (cn) {
764 free(cn);
765 }
766
767 return rc;
768}
769
770// Creates a directory for storing stash files and checks if the /cache partition
771// hash enough space for the expected amount of blocks we need to store. Returns
772// >0 if we created the directory, zero if it existed already, and <0 of failure.
773
774static int CreateStash(State* state, int maxblocks, const char* blockdev, char** base) {
775 char* dirname = NULL;
776 const uint8_t* digest;
777 int rc = -1;
778 int res;
779 int size = 0;
780 SHA_CTX ctx;
781 struct stat st;
782
783 if (blockdev == NULL || base == NULL) {
784 goto csout;
785 }
786
787 // Stash directory should be different for each partition to avoid conflicts
788 // when updating multiple partitions at the same time, so we use the hash of
789 // the block device name as the base directory
790 SHA_init(&ctx);
791 SHA_update(&ctx, blockdev, strlen(blockdev));
792 digest = SHA_final(&ctx);
793 *base = PrintSha1(digest);
794
795 if (*base == NULL) {
796 goto csout;
797 }
798
799 dirname = GetStashFileName(*base, NULL, NULL);
800
801 if (dirname == NULL) {
802 goto csout;
803 }
804
805 res = stat(dirname, &st);
806
807 if (res == -1 && errno != ENOENT) {
Elliott Hughes1fdd4522015-03-23 13:33:57 -0700808 ErrorAbort(state, "stat \"%s\" failed: %s\n", dirname, strerror(errno));
Sami Tolvanen90221202014-12-09 16:39:47 +0000809 goto csout;
810 } else if (res != 0) {
811 fprintf(stderr, "creating stash %s\n", dirname);
812 res = mkdir(dirname, STASH_DIRECTORY_MODE);
813
814 if (res != 0) {
Elliott Hughes1fdd4522015-03-23 13:33:57 -0700815 ErrorAbort(state, "mkdir \"%s\" failed: %s\n", dirname, strerror(errno));
Sami Tolvanen90221202014-12-09 16:39:47 +0000816 goto csout;
817 }
818
819 if (CacheSizeCheck(maxblocks * BLOCKSIZE) != 0) {
820 ErrorAbort(state, "not enough space for stash\n");
821 goto csout;
822 }
823
824 rc = 1; // Created directory
825 goto csout;
826 }
827
828 fprintf(stderr, "using existing stash %s\n", dirname);
829
830 // If the directory already exists, calculate the space already allocated to
831 // stash files and check if there's enough for all required blocks. Delete any
832 // partially completed stash files first.
833
834 EnumerateStash(dirname, DeletePartial, NULL);
835 EnumerateStash(dirname, UpdateFileSize, &size);
836
837 size = (maxblocks * BLOCKSIZE) - size;
838
839 if (size > 0 && CacheSizeCheck(size) != 0) {
840 ErrorAbort(state, "not enough space for stash (%d more needed)\n", size);
841 goto csout;
842 }
843
844 rc = 0; // Using existing directory
845
846csout:
847 if (dirname) {
848 free(dirname);
849 }
850
851 return rc;
852}
853
854static int SaveStash(const char* base, char** wordsave, uint8_t** buffer, size_t* buffer_alloc,
855 int fd, int usehash, int* isunresumable) {
856 char *id = NULL;
Sami Tolvanen90221202014-12-09 16:39:47 +0000857 int blocks = 0;
858
859 if (!wordsave || !buffer || !buffer_alloc || !isunresumable) {
860 return -1;
861 }
862
863 id = strtok_r(NULL, " ", wordsave);
864
865 if (id == NULL) {
866 fprintf(stderr, "missing id field in stash command\n");
867 return -1;
868 }
869
870 if (usehash && LoadStash(base, id, 1, &blocks, buffer, buffer_alloc, 0) == 0) {
871 // Stash file already exists and has expected contents. Do not
872 // read from source again, as the source may have been already
873 // overwritten during a previous attempt.
874 return 0;
875 }
876
877 if (LoadSrcTgtVersion1(wordsave, NULL, &blocks, buffer, buffer_alloc, fd) == -1) {
878 return -1;
879 }
880
881 if (usehash && VerifyBlocks(id, *buffer, blocks, 1) != 0) {
882 // Source blocks have unexpected contents. If we actually need this
883 // data later, this is an unrecoverable error. However, the command
884 // that uses the data may have already completed previously, so the
885 // possible failure will occur during source block verification.
886 fprintf(stderr, "failed to load source blocks for stash %s\n", id);
887 return 0;
888 }
889
890 fprintf(stderr, "stashing %d blocks to %s\n", blocks, id);
Sami Tolvanen43b748f2015-04-17 12:50:31 +0100891 return WriteStash(base, id, blocks, *buffer, 0, NULL);
Sami Tolvanen90221202014-12-09 16:39:47 +0000892}
893
894static int FreeStash(const char* base, const char* id) {
895 char *fn = NULL;
896
897 if (base == NULL || id == NULL) {
898 return -1;
899 }
900
901 fn = GetStashFileName(base, id, NULL);
902
903 if (fn == NULL) {
904 return -1;
905 }
906
907 DeleteFile(fn, NULL);
908 free(fn);
909
910 return 0;
Doug Zongker52ae67d2014-09-08 12:22:09 -0700911}
912
913static void MoveRange(uint8_t* dest, RangeSet* locs, const uint8_t* source) {
914 // source contains packed data, which we want to move to the
915 // locations given in *locs in the dest buffer. source and dest
916 // may be the same buffer.
917
918 int start = locs->size;
919 int i;
920 for (i = locs->count-1; i >= 0; --i) {
921 int blocks = locs->pos[i*2+1] - locs->pos[i*2];
922 start -= blocks;
923 memmove(dest + (locs->pos[i*2] * BLOCKSIZE), source + (start * BLOCKSIZE),
924 blocks * BLOCKSIZE);
925 }
926}
927
928// Do a source/target load for move/bsdiff/imgdiff in version 2.
929// 'wordsave' is the save_ptr of a strtok_r()-in-progress. We expect
930// to parse the remainder of the string as one of:
931//
932// <tgt_range> <src_block_count> <src_range>
933// (loads data from source image only)
934//
935// <tgt_range> <src_block_count> - <[stash_id:stash_range] ...>
936// (loads data from stashes only)
937//
938// <tgt_range> <src_block_count> <src_range> <src_loc> <[stash_id:stash_range] ...>
939// (loads data from both source image and stashes)
940//
941// On return, buffer is filled with the loaded source data (rearranged
942// and combined with stashed data as necessary). buffer may be
943// reallocated if needed to accommodate the source data. *tgt is the
Sami Tolvanen90221202014-12-09 16:39:47 +0000944// target RangeSet. Any stashes required are loaded using LoadStash.
Doug Zongker52ae67d2014-09-08 12:22:09 -0700945
Sami Tolvanen90221202014-12-09 16:39:47 +0000946static int LoadSrcTgtVersion2(char** wordsave, RangeSet** tgt, int* src_blocks,
Doug Zongker52ae67d2014-09-08 12:22:09 -0700947 uint8_t** buffer, size_t* buffer_alloc, int fd,
Sami Tolvanen90221202014-12-09 16:39:47 +0000948 const char* stashbase, int* overlap) {
Doug Zongker52ae67d2014-09-08 12:22:09 -0700949 char* word;
Sami Tolvanen90221202014-12-09 16:39:47 +0000950 char* colonsave;
951 char* colon;
Sami Tolvanen90221202014-12-09 16:39:47 +0000952 int res;
953 RangeSet* locs;
954 size_t stashalloc = 0;
955 uint8_t* stash = NULL;
Doug Zongker52ae67d2014-09-08 12:22:09 -0700956
957 if (tgt != NULL) {
Sami Tolvanen90221202014-12-09 16:39:47 +0000958 word = strtok_r(NULL, " ", wordsave);
Doug Zongker52ae67d2014-09-08 12:22:09 -0700959 *tgt = parse_range(word);
960 }
961
Sami Tolvanen90221202014-12-09 16:39:47 +0000962 word = strtok_r(NULL, " ", wordsave);
Doug Zongker52ae67d2014-09-08 12:22:09 -0700963 *src_blocks = strtol(word, NULL, 0);
964
965 allocate(*src_blocks * BLOCKSIZE, buffer, buffer_alloc);
966
Sami Tolvanen90221202014-12-09 16:39:47 +0000967 word = strtok_r(NULL, " ", wordsave);
Doug Zongker52ae67d2014-09-08 12:22:09 -0700968 if (word[0] == '-' && word[1] == '\0') {
969 // no source ranges, only stashes
970 } else {
971 RangeSet* src = parse_range(word);
Sami Tolvanen90221202014-12-09 16:39:47 +0000972 res = ReadBlocks(src, *buffer, fd);
Doug Zongker52ae67d2014-09-08 12:22:09 -0700973
Sami Tolvanen90221202014-12-09 16:39:47 +0000974 if (overlap && tgt) {
975 *overlap = range_overlaps(src, *tgt);
Doug Zongker52ae67d2014-09-08 12:22:09 -0700976 }
Sami Tolvanen90221202014-12-09 16:39:47 +0000977
Doug Zongker52ae67d2014-09-08 12:22:09 -0700978 free(src);
979
Sami Tolvanen90221202014-12-09 16:39:47 +0000980 if (res == -1) {
981 return -1;
Doug Zongker52ae67d2014-09-08 12:22:09 -0700982 }
983
Sami Tolvanen90221202014-12-09 16:39:47 +0000984 word = strtok_r(NULL, " ", wordsave);
985 if (word == NULL) {
986 // no stashes, only source range
987 return 0;
988 }
989
990 locs = parse_range(word);
Doug Zongker52ae67d2014-09-08 12:22:09 -0700991 MoveRange(*buffer, locs, *buffer);
Sami Tolvanen90221202014-12-09 16:39:47 +0000992 free(locs);
Doug Zongker52ae67d2014-09-08 12:22:09 -0700993 }
994
Sami Tolvanen90221202014-12-09 16:39:47 +0000995 while ((word = strtok_r(NULL, " ", wordsave)) != NULL) {
Doug Zongker52ae67d2014-09-08 12:22:09 -0700996 // Each word is a an index into the stash table, a colon, and
997 // then a rangeset describing where in the source block that
998 // stashed data should go.
Sami Tolvanen90221202014-12-09 16:39:47 +0000999 colonsave = NULL;
1000 colon = strtok_r(word, ":", &colonsave);
1001
1002 res = LoadStash(stashbase, colon, 0, NULL, &stash, &stashalloc, 1);
1003
1004 if (res == -1) {
1005 // These source blocks will fail verification if used later, but we
1006 // will let the caller decide if this is a fatal failure
1007 fprintf(stderr, "failed to load stash %s\n", colon);
1008 continue;
1009 }
1010
Doug Zongker52ae67d2014-09-08 12:22:09 -07001011 colon = strtok_r(NULL, ":", &colonsave);
Sami Tolvanen90221202014-12-09 16:39:47 +00001012 locs = parse_range(colon);
1013
1014 MoveRange(*buffer, locs, stash);
Doug Zongker52ae67d2014-09-08 12:22:09 -07001015 free(locs);
1016 }
Sami Tolvanen90221202014-12-09 16:39:47 +00001017
1018 if (stash) {
1019 free(stash);
1020 }
1021
1022 return 0;
1023}
1024
1025// Parameters for transfer list command functions
1026typedef struct {
1027 char* cmdname;
1028 char* cpos;
1029 char* freestash;
1030 char* stashbase;
1031 int canwrite;
1032 int createdstash;
1033 int fd;
1034 int foundwrites;
1035 int isunresumable;
1036 int version;
1037 int written;
1038 NewThreadInfo nti;
1039 pthread_t thread;
1040 size_t bufsize;
1041 uint8_t* buffer;
1042 uint8_t* patch_start;
1043} CommandParameters;
1044
1045// Do a source/target load for move/bsdiff/imgdiff in version 3.
1046//
1047// Parameters are the same as for LoadSrcTgtVersion2, except for 'onehash', which
1048// tells the function whether to expect separate source and targe block hashes, or
1049// if they are both the same and only one hash should be expected, and
1050// 'isunresumable', which receives a non-zero value if block verification fails in
1051// a way that the update cannot be resumed anymore.
1052//
1053// If the function is unable to load the necessary blocks or their contents don't
1054// match the hashes, the return value is -1 and the command should be aborted.
1055//
1056// If the return value is 1, the command has already been completed according to
1057// the contents of the target blocks, and should not be performed again.
1058//
1059// If the return value is 0, source blocks have expected content and the command
1060// can be performed.
1061
1062static int LoadSrcTgtVersion3(CommandParameters* params, RangeSet** tgt, int* src_blocks,
1063 int onehash, int* overlap) {
1064 char* srchash = NULL;
1065 char* tgthash = NULL;
Sami Tolvanen43b748f2015-04-17 12:50:31 +01001066 int stash_exists = 0;
Sami Tolvanen90221202014-12-09 16:39:47 +00001067 int rc = -1;
1068 uint8_t* tgtbuffer = NULL;
1069
1070 if (!params|| !tgt || !src_blocks || !overlap) {
1071 goto v3out;
1072 }
1073
1074 srchash = strtok_r(NULL, " ", &params->cpos);
1075
1076 if (srchash == NULL) {
1077 fprintf(stderr, "missing source hash\n");
1078 goto v3out;
1079 }
1080
1081 if (onehash) {
1082 tgthash = srchash;
1083 } else {
1084 tgthash = strtok_r(NULL, " ", &params->cpos);
1085
1086 if (tgthash == NULL) {
1087 fprintf(stderr, "missing target hash\n");
1088 goto v3out;
1089 }
1090 }
1091
1092 if (LoadSrcTgtVersion2(&params->cpos, tgt, src_blocks, &params->buffer, &params->bufsize,
1093 params->fd, params->stashbase, overlap) == -1) {
1094 goto v3out;
1095 }
1096
1097 tgtbuffer = (uint8_t*) malloc((*tgt)->size * BLOCKSIZE);
1098
1099 if (tgtbuffer == NULL) {
1100 fprintf(stderr, "failed to allocate %d bytes\n", (*tgt)->size * BLOCKSIZE);
1101 goto v3out;
1102 }
1103
1104 if (ReadBlocks(*tgt, tgtbuffer, params->fd) == -1) {
1105 goto v3out;
1106 }
1107
1108 if (VerifyBlocks(tgthash, tgtbuffer, (*tgt)->size, 0) == 0) {
1109 // Target blocks already have expected content, command should be skipped
1110 rc = 1;
1111 goto v3out;
1112 }
1113
1114 if (VerifyBlocks(srchash, params->buffer, *src_blocks, 1) == 0) {
1115 // If source and target blocks overlap, stash the source blocks so we can
1116 // resume from possible write errors
1117 if (*overlap) {
1118 fprintf(stderr, "stashing %d overlapping blocks to %s\n", *src_blocks,
1119 srchash);
1120
Sami Tolvanen43b748f2015-04-17 12:50:31 +01001121 if (WriteStash(params->stashbase, srchash, *src_blocks, params->buffer, 1,
1122 &stash_exists) != 0) {
Sami Tolvanen90221202014-12-09 16:39:47 +00001123 fprintf(stderr, "failed to stash overlapping source blocks\n");
1124 goto v3out;
1125 }
1126
1127 // Can be deleted when the write has completed
Sami Tolvanen43b748f2015-04-17 12:50:31 +01001128 if (!stash_exists) {
1129 params->freestash = srchash;
1130 }
Sami Tolvanen90221202014-12-09 16:39:47 +00001131 }
1132
1133 // Source blocks have expected content, command can proceed
1134 rc = 0;
1135 goto v3out;
1136 }
1137
1138 if (*overlap && LoadStash(params->stashbase, srchash, 1, NULL, &params->buffer,
1139 &params->bufsize, 1) == 0) {
Sami Tolvanen43b748f2015-04-17 12:50:31 +01001140 // Overlapping source blocks were previously stashed, command can proceed.
1141 // We are recovering from an interrupted command, so we don't know if the
1142 // stash can safely be deleted after this command.
Sami Tolvanen90221202014-12-09 16:39:47 +00001143 rc = 0;
1144 goto v3out;
1145 }
1146
1147 // Valid source data not available, update cannot be resumed
1148 fprintf(stderr, "partition has unexpected contents\n");
1149 params->isunresumable = 1;
1150
1151v3out:
1152 if (tgtbuffer) {
1153 free(tgtbuffer);
1154 }
1155
1156 return rc;
1157}
1158
1159static int PerformCommandMove(CommandParameters* params) {
1160 int blocks = 0;
1161 int overlap = 0;
1162 int rc = -1;
1163 int status = 0;
1164 RangeSet* tgt = NULL;
1165
1166 if (!params) {
1167 goto pcmout;
1168 }
1169
1170 if (params->version == 1) {
1171 status = LoadSrcTgtVersion1(&params->cpos, &tgt, &blocks, &params->buffer,
1172 &params->bufsize, params->fd);
1173 } else if (params->version == 2) {
1174 status = LoadSrcTgtVersion2(&params->cpos, &tgt, &blocks, &params->buffer,
1175 &params->bufsize, params->fd, params->stashbase, NULL);
1176 } else if (params->version >= 3) {
1177 status = LoadSrcTgtVersion3(params, &tgt, &blocks, 1, &overlap);
1178 }
1179
1180 if (status == -1) {
1181 fprintf(stderr, "failed to read blocks for move\n");
1182 goto pcmout;
1183 }
1184
1185 if (status == 0) {
1186 params->foundwrites = 1;
1187 } else if (params->foundwrites) {
1188 fprintf(stderr, "warning: commands executed out of order [%s]\n", params->cmdname);
1189 }
1190
1191 if (params->canwrite) {
1192 if (status == 0) {
1193 fprintf(stderr, " moving %d blocks\n", blocks);
1194
1195 if (WriteBlocks(tgt, params->buffer, params->fd) == -1) {
1196 goto pcmout;
1197 }
1198 } else {
1199 fprintf(stderr, "skipping %d already moved blocks\n", blocks);
1200 }
1201
1202 }
1203
1204 if (params->freestash) {
1205 FreeStash(params->stashbase, params->freestash);
1206 params->freestash = NULL;
1207 }
1208
1209 params->written += tgt->size;
1210 rc = 0;
1211
1212pcmout:
1213 if (tgt) {
1214 free(tgt);
1215 }
1216
1217 return rc;
1218}
1219
1220static int PerformCommandStash(CommandParameters* params) {
1221 if (!params) {
1222 return -1;
1223 }
1224
1225 return SaveStash(params->stashbase, &params->cpos, &params->buffer, &params->bufsize,
1226 params->fd, (params->version >= 3), &params->isunresumable);
1227}
1228
1229static int PerformCommandFree(CommandParameters* params) {
1230 if (!params) {
1231 return -1;
1232 }
1233
1234 if (params->createdstash || params->canwrite) {
1235 return FreeStash(params->stashbase, params->cpos);
1236 }
1237
1238 return 0;
1239}
1240
1241static int PerformCommandZero(CommandParameters* params) {
1242 char* range = NULL;
1243 int i;
1244 int j;
1245 int rc = -1;
1246 RangeSet* tgt = NULL;
1247
1248 if (!params) {
1249 goto pczout;
1250 }
1251
1252 range = strtok_r(NULL, " ", &params->cpos);
1253
1254 if (range == NULL) {
1255 fprintf(stderr, "missing target blocks for zero\n");
1256 goto pczout;
1257 }
1258
1259 tgt = parse_range(range);
1260
1261 fprintf(stderr, " zeroing %d blocks\n", tgt->size);
1262
1263 allocate(BLOCKSIZE, &params->buffer, &params->bufsize);
1264 memset(params->buffer, 0, BLOCKSIZE);
1265
1266 if (params->canwrite) {
1267 for (i = 0; i < tgt->count; ++i) {
Elliott Hughes7bad7c42015-04-28 17:24:24 -07001268 if (!check_lseek(params->fd, (off64_t) tgt->pos[i * 2] * BLOCKSIZE, SEEK_SET)) {
Sami Tolvanen90221202014-12-09 16:39:47 +00001269 goto pczout;
1270 }
1271
1272 for (j = tgt->pos[i * 2]; j < tgt->pos[i * 2 + 1]; ++j) {
1273 if (write_all(params->fd, params->buffer, BLOCKSIZE) == -1) {
1274 goto pczout;
1275 }
1276 }
1277 }
1278 }
1279
1280 if (params->cmdname[0] == 'z') {
Sami Tolvanene82fa182015-06-10 15:58:12 +00001281 // Update only for the zero command, as the erase command will call
1282 // this if DEBUG_ERASE is defined.
Sami Tolvanen90221202014-12-09 16:39:47 +00001283 params->written += tgt->size;
1284 }
1285
1286 rc = 0;
1287
1288pczout:
1289 if (tgt) {
1290 free(tgt);
1291 }
1292
1293 return rc;
1294}
1295
1296static int PerformCommandNew(CommandParameters* params) {
1297 char* range = NULL;
1298 int rc = -1;
1299 RangeSet* tgt = NULL;
1300 RangeSinkState rss;
1301
1302 if (!params) {
1303 goto pcnout;
1304 }
1305
1306 range = strtok_r(NULL, " ", &params->cpos);
1307
1308 if (range == NULL) {
1309 goto pcnout;
1310 }
1311
1312 tgt = parse_range(range);
1313
1314 if (params->canwrite) {
1315 fprintf(stderr, " writing %d blocks of new data\n", tgt->size);
1316
1317 rss.fd = params->fd;
1318 rss.tgt = tgt;
1319 rss.p_block = 0;
1320 rss.p_remain = (tgt->pos[1] - tgt->pos[0]) * BLOCKSIZE;
1321
Elliott Hughes7bad7c42015-04-28 17:24:24 -07001322 if (!check_lseek(params->fd, (off64_t) tgt->pos[0] * BLOCKSIZE, SEEK_SET)) {
Sami Tolvanen90221202014-12-09 16:39:47 +00001323 goto pcnout;
1324 }
1325
1326 pthread_mutex_lock(&params->nti.mu);
1327 params->nti.rss = &rss;
1328 pthread_cond_broadcast(&params->nti.cv);
1329
1330 while (params->nti.rss) {
1331 pthread_cond_wait(&params->nti.cv, &params->nti.mu);
1332 }
1333
1334 pthread_mutex_unlock(&params->nti.mu);
1335 }
1336
1337 params->written += tgt->size;
1338 rc = 0;
1339
1340pcnout:
1341 if (tgt) {
1342 free(tgt);
1343 }
1344
1345 return rc;
1346}
1347
1348static int PerformCommandDiff(CommandParameters* params) {
1349 char* logparams = NULL;
1350 char* value = NULL;
1351 int blocks = 0;
1352 int overlap = 0;
1353 int rc = -1;
1354 int status = 0;
1355 RangeSet* tgt = NULL;
1356 RangeSinkState rss;
1357 size_t len = 0;
1358 size_t offset = 0;
1359 Value patch_value;
1360
1361 if (!params) {
1362 goto pcdout;
1363 }
1364
1365 logparams = strdup(params->cpos);
1366 value = strtok_r(NULL, " ", &params->cpos);
1367
1368 if (value == NULL) {
1369 fprintf(stderr, "missing patch offset for %s\n", params->cmdname);
1370 goto pcdout;
1371 }
1372
1373 offset = strtoul(value, NULL, 0);
1374
1375 value = strtok_r(NULL, " ", &params->cpos);
1376
1377 if (value == NULL) {
1378 fprintf(stderr, "missing patch length for %s\n", params->cmdname);
1379 goto pcdout;
1380 }
1381
1382 len = strtoul(value, NULL, 0);
1383
1384 if (params->version == 1) {
1385 status = LoadSrcTgtVersion1(&params->cpos, &tgt, &blocks, &params->buffer,
1386 &params->bufsize, params->fd);
1387 } else if (params->version == 2) {
1388 status = LoadSrcTgtVersion2(&params->cpos, &tgt, &blocks, &params->buffer,
1389 &params->bufsize, params->fd, params->stashbase, NULL);
1390 } else if (params->version >= 3) {
1391 status = LoadSrcTgtVersion3(params, &tgt, &blocks, 0, &overlap);
1392 }
1393
1394 if (status == -1) {
1395 fprintf(stderr, "failed to read blocks for diff\n");
1396 goto pcdout;
1397 }
1398
1399 if (status == 0) {
1400 params->foundwrites = 1;
1401 } else if (params->foundwrites) {
1402 fprintf(stderr, "warning: commands executed out of order [%s]\n", params->cmdname);
1403 }
1404
1405 if (params->canwrite) {
1406 if (status == 0) {
1407 fprintf(stderr, "patching %d blocks to %d\n", blocks, tgt->size);
1408
1409 patch_value.type = VAL_BLOB;
1410 patch_value.size = len;
1411 patch_value.data = (char*) (params->patch_start + offset);
1412
1413 rss.fd = params->fd;
1414 rss.tgt = tgt;
1415 rss.p_block = 0;
1416 rss.p_remain = (tgt->pos[1] - tgt->pos[0]) * BLOCKSIZE;
1417
Elliott Hughes7bad7c42015-04-28 17:24:24 -07001418 if (!check_lseek(params->fd, (off64_t) tgt->pos[0] * BLOCKSIZE, SEEK_SET)) {
Sami Tolvanen90221202014-12-09 16:39:47 +00001419 goto pcdout;
1420 }
1421
1422 if (params->cmdname[0] == 'i') { // imgdiff
1423 ApplyImagePatch(params->buffer, blocks * BLOCKSIZE, &patch_value,
1424 &RangeSinkWrite, &rss, NULL, NULL);
1425 } else {
1426 ApplyBSDiffPatch(params->buffer, blocks * BLOCKSIZE, &patch_value,
1427 0, &RangeSinkWrite, &rss, NULL);
1428 }
1429
1430 // We expect the output of the patcher to fill the tgt ranges exactly.
1431 if (rss.p_block != tgt->count || rss.p_remain != 0) {
1432 fprintf(stderr, "range sink underrun?\n");
1433 }
1434 } else {
1435 fprintf(stderr, "skipping %d blocks already patched to %d [%s]\n",
1436 blocks, tgt->size, logparams);
1437 }
1438 }
1439
1440 if (params->freestash) {
1441 FreeStash(params->stashbase, params->freestash);
1442 params->freestash = NULL;
1443 }
1444
1445 params->written += tgt->size;
1446 rc = 0;
1447
1448pcdout:
1449 if (logparams) {
1450 free(logparams);
1451 }
1452
1453 if (tgt) {
1454 free(tgt);
1455 }
1456
1457 return rc;
1458}
1459
1460static int PerformCommandErase(CommandParameters* params) {
1461 char* range = NULL;
1462 int i;
1463 int rc = -1;
1464 RangeSet* tgt = NULL;
1465 struct stat st;
1466 uint64_t blocks[2];
1467
Sami Tolvanene82fa182015-06-10 15:58:12 +00001468 if (DEBUG_ERASE) {
1469 return PerformCommandZero(params);
Sami Tolvanen90221202014-12-09 16:39:47 +00001470 }
1471
1472 if (!params) {
1473 goto pceout;
1474 }
1475
1476 if (fstat(params->fd, &st) == -1) {
Elliott Hughes1fdd4522015-03-23 13:33:57 -07001477 fprintf(stderr, "failed to fstat device to erase: %s\n", strerror(errno));
Sami Tolvanen90221202014-12-09 16:39:47 +00001478 goto pceout;
1479 }
1480
1481 if (!S_ISBLK(st.st_mode)) {
1482 fprintf(stderr, "not a block device; skipping erase\n");
Sami Tolvanen90221202014-12-09 16:39:47 +00001483 goto pceout;
1484 }
1485
1486 range = strtok_r(NULL, " ", &params->cpos);
1487
1488 if (range == NULL) {
Tao Baoba9a42a2015-06-23 23:23:33 -07001489 fprintf(stderr, "missing target blocks for erase\n");
Sami Tolvanen90221202014-12-09 16:39:47 +00001490 goto pceout;
1491 }
1492
1493 tgt = parse_range(range);
1494
1495 if (params->canwrite) {
1496 fprintf(stderr, " erasing %d blocks\n", tgt->size);
1497
1498 for (i = 0; i < tgt->count; ++i) {
1499 // offset in bytes
1500 blocks[0] = tgt->pos[i * 2] * (uint64_t) BLOCKSIZE;
1501 // length in bytes
1502 blocks[1] = (tgt->pos[i * 2 + 1] - tgt->pos[i * 2]) * (uint64_t) BLOCKSIZE;
1503
1504 if (ioctl(params->fd, BLKDISCARD, &blocks) == -1) {
Elliott Hughes1fdd4522015-03-23 13:33:57 -07001505 fprintf(stderr, "BLKDISCARD ioctl failed: %s\n", strerror(errno));
Sami Tolvanencc2428c2015-04-27 11:24:29 +01001506 goto pceout;
Sami Tolvanen90221202014-12-09 16:39:47 +00001507 }
1508 }
1509 }
1510
1511 rc = 0;
1512
1513pceout:
1514 if (tgt) {
1515 free(tgt);
1516 }
1517
1518 return rc;
1519}
1520
1521// Definitions for transfer list command functions
1522typedef int (*CommandFunction)(CommandParameters*);
1523
1524typedef struct {
1525 const char* name;
1526 CommandFunction f;
1527} Command;
1528
1529// CompareCommands and CompareCommandNames are for the hash table
1530
1531static int CompareCommands(const void* c1, const void* c2) {
1532 return strcmp(((const Command*) c1)->name, ((const Command*) c2)->name);
1533}
1534
1535static int CompareCommandNames(const void* c1, const void* c2) {
1536 return strcmp(((const Command*) c1)->name, (const char*) c2);
1537}
1538
1539// HashString is used to hash command names for the hash table
1540
1541static unsigned int HashString(const char *s) {
1542 unsigned int hash = 0;
1543 if (s) {
1544 while (*s) {
1545 hash = hash * 33 + *s++;
1546 }
1547 }
1548 return hash;
Doug Zongker52ae67d2014-09-08 12:22:09 -07001549}
1550
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001551// args:
1552// - block device (or file) to modify in-place
1553// - transfer list (blob)
1554// - new data stream (filename within package.zip)
1555// - patch stream (filename within package.zip, must be uncompressed)
1556
Sami Tolvanen90221202014-12-09 16:39:47 +00001557static Value* PerformBlockImageUpdate(const char* name, State* state, int argc, Expr* argv[],
1558 const Command* commands, int cmdcount, int dryrun) {
1559
1560 char* line = NULL;
1561 char* linesave = NULL;
1562 char* logcmd = NULL;
Doug Zongker1d5d6092014-08-21 10:47:24 -07001563 char* transfer_list = NULL;
Sami Tolvanen90221202014-12-09 16:39:47 +00001564 CommandParameters params;
1565 const Command* cmd = NULL;
1566 const ZipEntry* new_entry = NULL;
1567 const ZipEntry* patch_entry = NULL;
1568 FILE* cmd_pipe = NULL;
1569 HashTable* cmdht = NULL;
1570 int i;
1571 int res;
1572 int rc = -1;
1573 int stash_max_blocks = 0;
1574 int total_blocks = 0;
1575 pthread_attr_t attr;
1576 unsigned int cmdhash;
1577 UpdaterInfo* ui = NULL;
1578 Value* blockdev_filename = NULL;
1579 Value* new_data_fn = NULL;
1580 Value* patch_data_fn = NULL;
1581 Value* transfer_list_value = NULL;
1582 ZipArchive* za = NULL;
1583
1584 memset(&params, 0, sizeof(params));
1585 params.canwrite = !dryrun;
1586
1587 fprintf(stderr, "performing %s\n", dryrun ? "verification" : "update");
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001588
Doug Zongker1d5d6092014-08-21 10:47:24 -07001589 if (ReadValueArgs(state, argv, 4, &blockdev_filename, &transfer_list_value,
Sami Tolvanen90221202014-12-09 16:39:47 +00001590 &new_data_fn, &patch_data_fn) < 0) {
1591 goto pbiudone;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001592 }
1593
1594 if (blockdev_filename->type != VAL_STRING) {
1595 ErrorAbort(state, "blockdev_filename argument to %s must be string", name);
Sami Tolvanen90221202014-12-09 16:39:47 +00001596 goto pbiudone;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001597 }
Doug Zongker1d5d6092014-08-21 10:47:24 -07001598 if (transfer_list_value->type != VAL_BLOB) {
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001599 ErrorAbort(state, "transfer_list argument to %s must be blob", name);
Sami Tolvanen90221202014-12-09 16:39:47 +00001600 goto pbiudone;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001601 }
1602 if (new_data_fn->type != VAL_STRING) {
1603 ErrorAbort(state, "new_data_fn argument to %s must be string", name);
Sami Tolvanen90221202014-12-09 16:39:47 +00001604 goto pbiudone;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001605 }
1606 if (patch_data_fn->type != VAL_STRING) {
1607 ErrorAbort(state, "patch_data_fn argument to %s must be string", name);
Sami Tolvanen90221202014-12-09 16:39:47 +00001608 goto pbiudone;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001609 }
1610
Sami Tolvanen90221202014-12-09 16:39:47 +00001611 ui = (UpdaterInfo*) state->cookie;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001612
Sami Tolvanen90221202014-12-09 16:39:47 +00001613 if (ui == NULL) {
1614 goto pbiudone;
1615 }
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001616
Sami Tolvanen90221202014-12-09 16:39:47 +00001617 cmd_pipe = ui->cmd_pipe;
1618 za = ui->package_zip;
1619
1620 if (cmd_pipe == NULL || za == NULL) {
1621 goto pbiudone;
1622 }
1623
1624 patch_entry = mzFindZipEntry(za, patch_data_fn->data);
1625
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001626 if (patch_entry == NULL) {
Sami Tolvanen90221202014-12-09 16:39:47 +00001627 fprintf(stderr, "%s(): no file \"%s\" in package", name, patch_data_fn->data);
1628 goto pbiudone;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001629 }
1630
Sami Tolvanen90221202014-12-09 16:39:47 +00001631 params.patch_start = ui->package_zip_addr + mzGetZipEntryOffset(patch_entry);
1632 new_entry = mzFindZipEntry(za, new_data_fn->data);
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001633
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001634 if (new_entry == NULL) {
Sami Tolvanen90221202014-12-09 16:39:47 +00001635 fprintf(stderr, "%s(): no file \"%s\" in package", name, new_data_fn->data);
1636 goto pbiudone;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001637 }
1638
Sami Tolvanen90221202014-12-09 16:39:47 +00001639 params.fd = TEMP_FAILURE_RETRY(open(blockdev_filename->data, O_RDWR));
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001640
Sami Tolvanen90221202014-12-09 16:39:47 +00001641 if (params.fd == -1) {
Elliott Hughes1fdd4522015-03-23 13:33:57 -07001642 fprintf(stderr, "open \"%s\" failed: %s\n", blockdev_filename->data, strerror(errno));
Sami Tolvanen90221202014-12-09 16:39:47 +00001643 goto pbiudone;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001644 }
1645
Sami Tolvanen90221202014-12-09 16:39:47 +00001646 if (params.canwrite) {
1647 params.nti.za = za;
1648 params.nti.entry = new_entry;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001649
Sami Tolvanen90221202014-12-09 16:39:47 +00001650 pthread_mutex_init(&params.nti.mu, NULL);
1651 pthread_cond_init(&params.nti.cv, NULL);
1652 pthread_attr_init(&attr);
1653 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
1654
Elliott Hughes1fdd4522015-03-23 13:33:57 -07001655 int error = pthread_create(&params.thread, &attr, unzip_new_data, &params.nti);
1656 if (error != 0) {
1657 fprintf(stderr, "pthread_create failed: %s\n", strerror(error));
Sami Tolvanen90221202014-12-09 16:39:47 +00001658 goto pbiudone;
1659 }
1660 }
1661
1662 // The data in transfer_list_value is not necessarily null-terminated, so we need
1663 // to copy it to a new buffer and add the null that strtok_r will need.
Tao Baoba9a42a2015-06-23 23:23:33 -07001664 transfer_list = reinterpret_cast<char*>(malloc(transfer_list_value->size + 1));
Sami Tolvanen90221202014-12-09 16:39:47 +00001665
Doug Zongker1d5d6092014-08-21 10:47:24 -07001666 if (transfer_list == NULL) {
1667 fprintf(stderr, "failed to allocate %zd bytes for transfer list\n",
Sami Tolvanen90221202014-12-09 16:39:47 +00001668 transfer_list_value->size + 1);
1669 goto pbiudone;
Doug Zongker1d5d6092014-08-21 10:47:24 -07001670 }
Sami Tolvanen90221202014-12-09 16:39:47 +00001671
Doug Zongker1d5d6092014-08-21 10:47:24 -07001672 memcpy(transfer_list, transfer_list_value->data, transfer_list_value->size);
1673 transfer_list[transfer_list_value->size] = '\0';
1674
Sami Tolvanen90221202014-12-09 16:39:47 +00001675 // First line in transfer list is the version number
Doug Zongker1d5d6092014-08-21 10:47:24 -07001676 line = strtok_r(transfer_list, "\n", &linesave);
Sami Tolvanen90221202014-12-09 16:39:47 +00001677 params.version = strtol(line, NULL, 0);
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001678
Sami Tolvanen90221202014-12-09 16:39:47 +00001679 if (params.version < 1 || params.version > 3) {
1680 fprintf(stderr, "unexpected transfer list version [%s]\n", line);
1681 goto pbiudone;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001682 }
1683
Sami Tolvanen90221202014-12-09 16:39:47 +00001684 fprintf(stderr, "blockimg version is %d\n", params.version);
1685
1686 // Second line in transfer list is the total number of blocks we expect to write
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001687 line = strtok_r(NULL, "\n", &linesave);
Sami Tolvanen90221202014-12-09 16:39:47 +00001688 total_blocks = strtol(line, NULL, 0);
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001689
Sami Tolvanen90221202014-12-09 16:39:47 +00001690 if (total_blocks < 0) {
1691 ErrorAbort(state, "unexpected block count [%s]\n", line);
1692 goto pbiudone;
1693 } else if (total_blocks == 0) {
1694 rc = 0;
1695 goto pbiudone;
1696 }
1697
1698 if (params.version >= 2) {
1699 // Third line is how many stash entries are needed simultaneously
Doug Zongker52ae67d2014-09-08 12:22:09 -07001700 line = strtok_r(NULL, "\n", &linesave);
Sami Tolvanen90221202014-12-09 16:39:47 +00001701 fprintf(stderr, "maximum stash entries %s\n", line);
Doug Zongker52ae67d2014-09-08 12:22:09 -07001702
Sami Tolvanen90221202014-12-09 16:39:47 +00001703 // Fourth line is the maximum number of blocks that will be stashed simultaneously
1704 line = strtok_r(NULL, "\n", &linesave);
1705 stash_max_blocks = strtol(line, NULL, 0);
1706
1707 if (stash_max_blocks < 0) {
1708 ErrorAbort(state, "unexpected maximum stash blocks [%s]\n", line);
1709 goto pbiudone;
Doug Zongker52ae67d2014-09-08 12:22:09 -07001710 }
1711
Jesse Zhao1df64d32015-02-17 17:09:23 -08001712 if (stash_max_blocks >= 0) {
Sami Tolvanen90221202014-12-09 16:39:47 +00001713 res = CreateStash(state, stash_max_blocks, blockdev_filename->data,
1714 &params.stashbase);
1715
1716 if (res == -1) {
1717 goto pbiudone;
1718 }
1719
1720 params.createdstash = res;
1721 }
Doug Zongker52ae67d2014-09-08 12:22:09 -07001722 }
1723
Sami Tolvanen90221202014-12-09 16:39:47 +00001724 // Build a hash table of the available commands
1725 cmdht = mzHashTableCreate(cmdcount, NULL);
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001726
Sami Tolvanen90221202014-12-09 16:39:47 +00001727 for (i = 0; i < cmdcount; ++i) {
1728 cmdhash = HashString(commands[i].name);
1729 mzHashTableLookup(cmdht, cmdhash, (void*) &commands[i], CompareCommands, true);
1730 }
1731
1732 // Subsequent lines are all individual transfer commands
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001733 for (line = strtok_r(NULL, "\n", &linesave); line;
1734 line = strtok_r(NULL, "\n", &linesave)) {
Doug Zongker52ae67d2014-09-08 12:22:09 -07001735
Sami Tolvanen90221202014-12-09 16:39:47 +00001736 logcmd = strdup(line);
1737 params.cmdname = strtok_r(line, " ", &params.cpos);
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001738
Sami Tolvanen90221202014-12-09 16:39:47 +00001739 if (params.cmdname == NULL) {
1740 fprintf(stderr, "missing command [%s]\n", line);
1741 goto pbiudone;
1742 }
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001743
Sami Tolvanen90221202014-12-09 16:39:47 +00001744 cmdhash = HashString(params.cmdname);
1745 cmd = (const Command*) mzHashTableLookup(cmdht, cmdhash, params.cmdname,
1746 CompareCommandNames, false);
Doug Zongker52ae67d2014-09-08 12:22:09 -07001747
Sami Tolvanen90221202014-12-09 16:39:47 +00001748 if (cmd == NULL) {
1749 fprintf(stderr, "unexpected command [%s]\n", params.cmdname);
1750 goto pbiudone;
1751 }
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001752
Sami Tolvanen90221202014-12-09 16:39:47 +00001753 if (cmd->f != NULL && cmd->f(&params) == -1) {
1754 fprintf(stderr, "failed to execute command [%s]\n",
1755 logcmd ? logcmd : params.cmdname);
1756 goto pbiudone;
1757 }
1758
1759 if (logcmd) {
1760 free(logcmd);
1761 logcmd = NULL;
1762 }
1763
1764 if (params.canwrite) {
1765 fprintf(cmd_pipe, "set_progress %.4f\n", (double) params.written / total_blocks);
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001766 fflush(cmd_pipe);
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001767 }
1768 }
1769
Sami Tolvanen90221202014-12-09 16:39:47 +00001770 if (params.canwrite) {
1771 pthread_join(params.thread, NULL);
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001772
Sami Tolvanen90221202014-12-09 16:39:47 +00001773 fprintf(stderr, "wrote %d blocks; expected %d\n", params.written, total_blocks);
1774 fprintf(stderr, "max alloc needed was %zu\n", params.bufsize);
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001775
Sami Tolvanen90221202014-12-09 16:39:47 +00001776 // Delete stash only after successfully completing the update, as it
1777 // may contain blocks needed to complete the update later.
1778 DeleteStash(params.stashbase);
1779 } else {
1780 fprintf(stderr, "verified partition contents; update may be resumed\n");
1781 }
1782
1783 rc = 0;
1784
1785pbiudone:
1786 if (params.fd != -1) {
1787 if (fsync(params.fd) == -1) {
Elliott Hughes1fdd4522015-03-23 13:33:57 -07001788 fprintf(stderr, "fsync failed: %s\n", strerror(errno));
Sami Tolvanen90221202014-12-09 16:39:47 +00001789 }
Elliott Hughesb47afed2015-05-15 16:19:20 -07001790 close(params.fd);
Sami Tolvanen90221202014-12-09 16:39:47 +00001791 }
1792
1793 if (logcmd) {
1794 free(logcmd);
1795 }
1796
1797 if (cmdht) {
1798 mzHashTableFree(cmdht);
1799 }
1800
1801 if (params.buffer) {
1802 free(params.buffer);
1803 }
1804
1805 if (transfer_list) {
1806 free(transfer_list);
1807 }
1808
1809 if (blockdev_filename) {
1810 FreeValue(blockdev_filename);
1811 }
1812
1813 if (transfer_list_value) {
1814 FreeValue(transfer_list_value);
1815 }
1816
1817 if (new_data_fn) {
1818 FreeValue(new_data_fn);
1819 }
1820
1821 if (patch_data_fn) {
1822 FreeValue(patch_data_fn);
1823 }
1824
1825 // Only delete the stash if the update cannot be resumed, or it's
1826 // a verification run and we created the stash.
1827 if (params.isunresumable || (!params.canwrite && params.createdstash)) {
1828 DeleteStash(params.stashbase);
1829 }
1830
1831 if (params.stashbase) {
1832 free(params.stashbase);
1833 }
1834
1835 return StringValue(rc == 0 ? strdup("t") : strdup(""));
1836}
1837
1838// The transfer list is a text file containing commands to
1839// transfer data from one place to another on the target
1840// partition. We parse it and execute the commands in order:
1841//
1842// zero [rangeset]
1843// - fill the indicated blocks with zeros
1844//
1845// new [rangeset]
1846// - fill the blocks with data read from the new_data file
1847//
1848// erase [rangeset]
1849// - mark the given blocks as empty
1850//
1851// move <...>
1852// bsdiff <patchstart> <patchlen> <...>
1853// imgdiff <patchstart> <patchlen> <...>
1854// - read the source blocks, apply a patch (or not in the
1855// case of move), write result to target blocks. bsdiff or
1856// imgdiff specifies the type of patch; move means no patch
1857// at all.
1858//
1859// The format of <...> differs between versions 1 and 2;
1860// see the LoadSrcTgtVersion{1,2}() functions for a
1861// description of what's expected.
1862//
1863// stash <stash_id> <src_range>
1864// - (version 2+ only) load the given source range and stash
1865// the data in the given slot of the stash table.
1866//
1867// The creator of the transfer list will guarantee that no block
1868// is read (ie, used as the source for a patch or move) after it
1869// has been written.
1870//
1871// In version 2, the creator will guarantee that a given stash is
1872// loaded (with a stash command) before it's used in a
1873// move/bsdiff/imgdiff command.
1874//
1875// Within one command the source and target ranges may overlap so
1876// in general we need to read the entire source into memory before
1877// writing anything to the target blocks.
1878//
1879// All the patch data is concatenated into one patch_data file in
1880// the update package. It must be stored uncompressed because we
1881// memory-map it in directly from the archive. (Since patches are
1882// already compressed, we lose very little by not compressing
1883// their concatenation.)
1884//
1885// In version 3, commands that read data from the partition (i.e.
1886// move/bsdiff/imgdiff/stash) have one or more additional hashes
1887// before the range parameters, which are used to check if the
1888// command has already been completed and verify the integrity of
1889// the source data.
1890
1891Value* BlockImageVerifyFn(const char* name, State* state, int argc, Expr* argv[]) {
1892 // Commands which are not tested are set to NULL to skip them completely
1893 const Command commands[] = {
1894 { "bsdiff", PerformCommandDiff },
1895 { "erase", NULL },
1896 { "free", PerformCommandFree },
1897 { "imgdiff", PerformCommandDiff },
1898 { "move", PerformCommandMove },
1899 { "new", NULL },
1900 { "stash", PerformCommandStash },
1901 { "zero", NULL }
1902 };
1903
1904 // Perform a dry run without writing to test if an update can proceed
1905 return PerformBlockImageUpdate(name, state, argc, argv, commands,
1906 sizeof(commands) / sizeof(commands[0]), 1);
1907}
1908
1909Value* BlockImageUpdateFn(const char* name, State* state, int argc, Expr* argv[]) {
1910 const Command commands[] = {
1911 { "bsdiff", PerformCommandDiff },
1912 { "erase", PerformCommandErase },
1913 { "free", PerformCommandFree },
1914 { "imgdiff", PerformCommandDiff },
1915 { "move", PerformCommandMove },
1916 { "new", PerformCommandNew },
1917 { "stash", PerformCommandStash },
1918 { "zero", PerformCommandZero }
1919 };
1920
1921 return PerformBlockImageUpdate(name, state, argc, argv, commands,
1922 sizeof(commands) / sizeof(commands[0]), 0);
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001923}
1924
1925Value* RangeSha1Fn(const char* name, State* state, int argc, Expr* argv[]) {
1926 Value* blockdev_filename;
1927 Value* ranges;
1928 const uint8_t* digest = NULL;
1929 if (ReadValueArgs(state, argv, 2, &blockdev_filename, &ranges) < 0) {
1930 return NULL;
1931 }
1932
1933 if (blockdev_filename->type != VAL_STRING) {
1934 ErrorAbort(state, "blockdev_filename argument to %s must be string", name);
1935 goto done;
1936 }
1937 if (ranges->type != VAL_STRING) {
1938 ErrorAbort(state, "ranges argument to %s must be string", name);
1939 goto done;
1940 }
1941
Tao Baoba9a42a2015-06-23 23:23:33 -07001942 int fd;
1943 fd = open(blockdev_filename->data, O_RDWR);
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001944 if (fd < 0) {
Elliott Hughes1fdd4522015-03-23 13:33:57 -07001945 ErrorAbort(state, "open \"%s\" failed: %s", blockdev_filename->data, strerror(errno));
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001946 goto done;
1947 }
1948
Tao Baoba9a42a2015-06-23 23:23:33 -07001949 RangeSet* rs;
1950 rs = parse_range(ranges->data);
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001951 uint8_t buffer[BLOCKSIZE];
1952
1953 SHA_CTX ctx;
1954 SHA_init(&ctx);
1955
1956 int i, j;
1957 for (i = 0; i < rs->count; ++i) {
Elliott Hughes7bad7c42015-04-28 17:24:24 -07001958 if (!check_lseek(fd, (off64_t)rs->pos[i*2] * BLOCKSIZE, SEEK_SET)) {
Sami Tolvanen90221202014-12-09 16:39:47 +00001959 ErrorAbort(state, "failed to seek %s: %s", blockdev_filename->data,
1960 strerror(errno));
1961 goto done;
1962 }
1963
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001964 for (j = rs->pos[i*2]; j < rs->pos[i*2+1]; ++j) {
Sami Tolvanen90221202014-12-09 16:39:47 +00001965 if (read_all(fd, buffer, BLOCKSIZE) == -1) {
1966 ErrorAbort(state, "failed to read %s: %s", blockdev_filename->data,
1967 strerror(errno));
1968 goto done;
1969 }
1970
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001971 SHA_update(&ctx, buffer, BLOCKSIZE);
1972 }
1973 }
1974 digest = SHA_final(&ctx);
1975 close(fd);
1976
1977 done:
1978 FreeValue(blockdev_filename);
1979 FreeValue(ranges);
1980 if (digest == NULL) {
1981 return StringValue(strdup(""));
1982 } else {
1983 return StringValue(PrintSha1(digest));
1984 }
1985}
1986
1987void RegisterBlockImageFunctions() {
Sami Tolvanen90221202014-12-09 16:39:47 +00001988 RegisterFunction("block_image_verify", BlockImageVerifyFn);
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001989 RegisterFunction("block_image_update", BlockImageUpdateFn);
1990 RegisterFunction("range_sha1", RangeSha1Fn);
1991}