blob: 7b8721dbf1b3a8af3cea18c513e69dccdb8e47f4 [file] [log] [blame]
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <ctype.h>
18#include <errno.h>
Sami Tolvanen90221202014-12-09 16:39:47 +000019#include <dirent.h>
Doug Zongkerbc7ffed2014-08-15 14:31:52 -070020#include <fcntl.h>
21#include <inttypes.h>
22#include <pthread.h>
23#include <stdarg.h>
24#include <stdio.h>
25#include <stdlib.h>
26#include <string.h>
Sami Tolvanen90221202014-12-09 16:39:47 +000027#include <sys/stat.h>
Doug Zongkerbc7ffed2014-08-15 14:31:52 -070028#include <sys/types.h>
29#include <sys/wait.h>
30#include <sys/ioctl.h>
31#include <time.h>
32#include <unistd.h>
33
34#include "applypatch/applypatch.h"
35#include "edify/expr.h"
36#include "mincrypt/sha.h"
Sami Tolvanen90221202014-12-09 16:39:47 +000037#include "minzip/Hash.h"
Doug Zongkerbc7ffed2014-08-15 14:31:52 -070038#include "updater.h"
39
40#define BLOCKSIZE 4096
41
Sami Tolvanen6abd52f2015-06-10 15:52:04 +000042// Set this to 0 to interpret 'erase' transfers to mean do a
43// BLKDISCARD ioctl (the normal behavior). Set to 1 to interpret
44// erase to mean fill the region with zeroes.
45#define DEBUG_ERASE 0
46
Doug Zongkerbc7ffed2014-08-15 14:31:52 -070047#ifndef BLKDISCARD
48#define BLKDISCARD _IO(0x12,119)
49#endif
50
Sami Tolvanen90221202014-12-09 16:39:47 +000051#define STASH_DIRECTORY_BASE "/cache/recovery"
52#define STASH_DIRECTORY_MODE 0700
53#define STASH_FILE_MODE 0600
54
Doug Zongkerbc7ffed2014-08-15 14:31:52 -070055char* PrintSha1(const uint8_t* digest);
56
57typedef struct {
58 int count;
59 int size;
60 int pos[0];
61} RangeSet;
62
63static RangeSet* parse_range(char* text) {
64 char* save;
65 int num;
66 num = strtol(strtok_r(text, ",", &save), NULL, 0);
67
68 RangeSet* out = malloc(sizeof(RangeSet) + num * sizeof(int));
69 if (out == NULL) {
Doug Zongker52ae67d2014-09-08 12:22:09 -070070 fprintf(stderr, "failed to allocate range of %zu bytes\n",
Doug Zongkerbc7ffed2014-08-15 14:31:52 -070071 sizeof(RangeSet) + num * sizeof(int));
72 exit(1);
73 }
74 out->count = num / 2;
75 out->size = 0;
76 int i;
77 for (i = 0; i < num; ++i) {
78 out->pos[i] = strtol(strtok_r(NULL, ",", &save), NULL, 0);
79 if (i%2) {
80 out->size += out->pos[i];
81 } else {
82 out->size -= out->pos[i];
83 }
84 }
85
86 return out;
87}
88
Sami Tolvanen90221202014-12-09 16:39:47 +000089static int range_overlaps(RangeSet* r1, RangeSet* r2) {
90 int i, j, r1_0, r1_1, r2_0, r2_1;
91
92 if (!r1 || !r2) {
93 return 0;
94 }
95
96 for (i = 0; i < r1->count; ++i) {
97 r1_0 = r1->pos[i * 2];
98 r1_1 = r1->pos[i * 2 + 1];
99
100 for (j = 0; j < r2->count; ++j) {
101 r2_0 = r2->pos[j * 2];
102 r2_1 = r2->pos[j * 2 + 1];
103
104 if (!(r2_0 > r1_1 || r1_0 > r2_1)) {
105 return 1;
106 }
107 }
108 }
109
110 return 0;
111}
112
113static int read_all(int fd, uint8_t* data, size_t size) {
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700114 size_t so_far = 0;
115 while (so_far < size) {
Elliott Hughes2f5feed2015-04-28 17:24:24 -0700116 ssize_t r = TEMP_FAILURE_RETRY(read(fd, data+so_far, size-so_far));
117 if (r == -1) {
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700118 fprintf(stderr, "read failed: %s\n", strerror(errno));
Sami Tolvanen90221202014-12-09 16:39:47 +0000119 return -1;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700120 }
Elliott Hughes2f5feed2015-04-28 17:24:24 -0700121 so_far += r;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700122 }
Sami Tolvanen90221202014-12-09 16:39:47 +0000123 return 0;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700124}
125
Sami Tolvanen90221202014-12-09 16:39:47 +0000126static int write_all(int fd, const uint8_t* data, size_t size) {
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700127 size_t written = 0;
128 while (written < size) {
Elliott Hughes2f5feed2015-04-28 17:24:24 -0700129 ssize_t w = TEMP_FAILURE_RETRY(write(fd, data+written, size-written));
130 if (w == -1) {
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700131 fprintf(stderr, "write failed: %s\n", strerror(errno));
Sami Tolvanen90221202014-12-09 16:39:47 +0000132 return -1;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700133 }
Elliott Hughes2f5feed2015-04-28 17:24:24 -0700134 written += w;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700135 }
Sami Tolvanen90221202014-12-09 16:39:47 +0000136
137 if (fsync(fd) == -1) {
138 fprintf(stderr, "fsync failed: %s\n", strerror(errno));
139 return -1;
140 }
141
142 return 0;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700143}
144
Elliott Hughes2f5feed2015-04-28 17:24:24 -0700145static bool check_lseek(int fd, off64_t offset, int whence) {
146 off64_t rc = TEMP_FAILURE_RETRY(lseek64(fd, offset, whence));
147 if (rc == -1) {
148 fprintf(stderr, "lseek64 failed: %s\n", strerror(errno));
149 return false;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700150 }
Elliott Hughes2f5feed2015-04-28 17:24:24 -0700151 return true;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700152}
153
154static void allocate(size_t size, uint8_t** buffer, size_t* buffer_alloc) {
155 // if the buffer's big enough, reuse it.
156 if (size <= *buffer_alloc) return;
157
158 free(*buffer);
159
160 *buffer = (uint8_t*) malloc(size);
161 if (*buffer == NULL) {
Doug Zongker1d5d6092014-08-21 10:47:24 -0700162 fprintf(stderr, "failed to allocate %zu bytes\n", size);
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700163 exit(1);
164 }
165 *buffer_alloc = size;
166}
167
168typedef struct {
169 int fd;
170 RangeSet* tgt;
171 int p_block;
172 size_t p_remain;
173} RangeSinkState;
174
175static ssize_t RangeSinkWrite(const uint8_t* data, ssize_t size, void* token) {
176 RangeSinkState* rss = (RangeSinkState*) token;
177
178 if (rss->p_remain <= 0) {
179 fprintf(stderr, "range sink write overrun");
Sami Tolvanen90221202014-12-09 16:39:47 +0000180 return 0;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700181 }
182
183 ssize_t written = 0;
184 while (size > 0) {
185 size_t write_now = size;
Sami Tolvanen90221202014-12-09 16:39:47 +0000186
187 if (rss->p_remain < write_now) {
188 write_now = rss->p_remain;
189 }
190
191 if (write_all(rss->fd, data, write_now) == -1) {
192 break;
193 }
194
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700195 data += write_now;
196 size -= write_now;
197
198 rss->p_remain -= write_now;
199 written += write_now;
200
201 if (rss->p_remain == 0) {
202 // move to the next block
203 ++rss->p_block;
204 if (rss->p_block < rss->tgt->count) {
Sami Tolvanen90221202014-12-09 16:39:47 +0000205 rss->p_remain = (rss->tgt->pos[rss->p_block * 2 + 1] -
206 rss->tgt->pos[rss->p_block * 2]) * BLOCKSIZE;
207
Elliott Hughes2f5feed2015-04-28 17:24:24 -0700208 if (!check_lseek(rss->fd, (off64_t)rss->tgt->pos[rss->p_block*2] * BLOCKSIZE,
209 SEEK_SET)) {
Sami Tolvanen90221202014-12-09 16:39:47 +0000210 break;
211 }
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700212 } else {
213 // we can't write any more; return how many bytes have
214 // been written so far.
Sami Tolvanen90221202014-12-09 16:39:47 +0000215 break;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700216 }
217 }
218 }
219
220 return written;
221}
222
223// All of the data for all the 'new' transfers is contained in one
224// file in the update package, concatenated together in the order in
225// which transfers.list will need it. We want to stream it out of the
226// archive (it's compressed) without writing it to a temp file, but we
227// can't write each section until it's that transfer's turn to go.
228//
229// To achieve this, we expand the new data from the archive in a
230// background thread, and block that threads 'receive uncompressed
231// data' function until the main thread has reached a point where we
232// want some new data to be written. We signal the background thread
233// with the destination for the data and block the main thread,
234// waiting for the background thread to complete writing that section.
235// Then it signals the main thread to wake up and goes back to
236// blocking waiting for a transfer.
237//
238// NewThreadInfo is the struct used to pass information back and forth
239// between the two threads. When the main thread wants some data
240// written, it sets rss to the destination location and signals the
241// condition. When the background thread is done writing, it clears
242// rss and signals the condition again.
243
244typedef struct {
245 ZipArchive* za;
246 const ZipEntry* entry;
247
248 RangeSinkState* rss;
249
250 pthread_mutex_t mu;
251 pthread_cond_t cv;
252} NewThreadInfo;
253
254static bool receive_new_data(const unsigned char* data, int size, void* cookie) {
255 NewThreadInfo* nti = (NewThreadInfo*) cookie;
256
257 while (size > 0) {
258 // Wait for nti->rss to be non-NULL, indicating some of this
259 // data is wanted.
260 pthread_mutex_lock(&nti->mu);
261 while (nti->rss == NULL) {
262 pthread_cond_wait(&nti->cv, &nti->mu);
263 }
264 pthread_mutex_unlock(&nti->mu);
265
266 // At this point nti->rss is set, and we own it. The main
267 // thread is waiting for it to disappear from nti.
268 ssize_t written = RangeSinkWrite(data, size, nti->rss);
269 data += written;
270 size -= written;
271
272 if (nti->rss->p_block == nti->rss->tgt->count) {
273 // we have written all the bytes desired by this rss.
274
275 pthread_mutex_lock(&nti->mu);
276 nti->rss = NULL;
277 pthread_cond_broadcast(&nti->cv);
278 pthread_mutex_unlock(&nti->mu);
279 }
280 }
281
282 return true;
283}
284
285static void* unzip_new_data(void* cookie) {
286 NewThreadInfo* nti = (NewThreadInfo*) cookie;
287 mzProcessZipEntryContents(nti->za, nti->entry, receive_new_data, nti);
288 return NULL;
289}
290
Sami Tolvanen90221202014-12-09 16:39:47 +0000291static int ReadBlocks(RangeSet* src, uint8_t* buffer, int fd) {
292 int i;
293 size_t p = 0;
294 size_t size;
295
296 if (!src || !buffer) {
297 return -1;
298 }
299
300 for (i = 0; i < src->count; ++i) {
Elliott Hughes2f5feed2015-04-28 17:24:24 -0700301 if (!check_lseek(fd, (off64_t) src->pos[i * 2] * BLOCKSIZE, SEEK_SET)) {
Sami Tolvanen90221202014-12-09 16:39:47 +0000302 return -1;
303 }
304
305 size = (src->pos[i * 2 + 1] - src->pos[i * 2]) * BLOCKSIZE;
306
307 if (read_all(fd, buffer + p, size) == -1) {
308 return -1;
309 }
310
311 p += size;
312 }
313
314 return 0;
315}
316
317static int WriteBlocks(RangeSet* tgt, uint8_t* buffer, int fd) {
318 int i;
319 size_t p = 0;
320 size_t size;
321
322 if (!tgt || !buffer) {
323 return -1;
324 }
325
326 for (i = 0; i < tgt->count; ++i) {
Elliott Hughes2f5feed2015-04-28 17:24:24 -0700327 if (!check_lseek(fd, (off64_t) tgt->pos[i * 2] * BLOCKSIZE, SEEK_SET)) {
Sami Tolvanen90221202014-12-09 16:39:47 +0000328 return -1;
329 }
330
331 size = (tgt->pos[i * 2 + 1] - tgt->pos[i * 2]) * BLOCKSIZE;
332
333 if (write_all(fd, buffer + p, size) == -1) {
334 return -1;
335 }
336
337 p += size;
338 }
339
340 return 0;
341}
342
Doug Zongker52ae67d2014-09-08 12:22:09 -0700343// Do a source/target load for move/bsdiff/imgdiff in version 1.
344// 'wordsave' is the save_ptr of a strtok_r()-in-progress. We expect
345// to parse the remainder of the string as:
346//
347// <src_range> <tgt_range>
348//
349// The source range is loaded into the provided buffer, reallocating
350// it to make it larger if necessary. The target ranges are returned
351// in *tgt, if tgt is non-NULL.
352
Sami Tolvanen90221202014-12-09 16:39:47 +0000353static int LoadSrcTgtVersion1(char** wordsave, RangeSet** tgt, int* src_blocks,
Doug Zongker52ae67d2014-09-08 12:22:09 -0700354 uint8_t** buffer, size_t* buffer_alloc, int fd) {
355 char* word;
Sami Tolvanen90221202014-12-09 16:39:47 +0000356 int rc;
Doug Zongker52ae67d2014-09-08 12:22:09 -0700357
Sami Tolvanen90221202014-12-09 16:39:47 +0000358 word = strtok_r(NULL, " ", wordsave);
Doug Zongker52ae67d2014-09-08 12:22:09 -0700359 RangeSet* src = parse_range(word);
360
361 if (tgt != NULL) {
Sami Tolvanen90221202014-12-09 16:39:47 +0000362 word = strtok_r(NULL, " ", wordsave);
Doug Zongker52ae67d2014-09-08 12:22:09 -0700363 *tgt = parse_range(word);
364 }
365
366 allocate(src->size * BLOCKSIZE, buffer, buffer_alloc);
Sami Tolvanen90221202014-12-09 16:39:47 +0000367 rc = ReadBlocks(src, *buffer, fd);
368 *src_blocks = src->size;
369
370 free(src);
371 return rc;
372}
373
374static int VerifyBlocks(const char *expected, const uint8_t *buffer,
375 size_t blocks, int printerror) {
376 char* hexdigest = NULL;
377 int rc = -1;
378 uint8_t digest[SHA_DIGEST_SIZE];
379
380 if (!expected || !buffer) {
381 return rc;
Doug Zongker52ae67d2014-09-08 12:22:09 -0700382 }
383
Sami Tolvanen90221202014-12-09 16:39:47 +0000384 SHA_hash(buffer, blocks * BLOCKSIZE, digest);
385 hexdigest = PrintSha1(digest);
386
387 if (hexdigest != NULL) {
388 rc = strcmp(expected, hexdigest);
389
390 if (rc != 0 && printerror) {
391 fprintf(stderr, "failed to verify blocks (expected %s, read %s)\n",
392 expected, hexdigest);
393 }
394
395 free(hexdigest);
396 }
397
398 return rc;
399}
400
401static char* GetStashFileName(const char* base, const char* id, const char* postfix) {
402 char* fn;
403 int len;
404 int res;
405
406 if (base == NULL) {
407 return NULL;
408 }
409
410 if (id == NULL) {
411 id = "";
412 }
413
414 if (postfix == NULL) {
415 postfix = "";
416 }
417
418 len = strlen(STASH_DIRECTORY_BASE) + 1 + strlen(base) + 1 + strlen(id) + strlen(postfix) + 1;
419 fn = malloc(len);
420
421 if (fn == NULL) {
422 fprintf(stderr, "failed to malloc %d bytes for fn\n", len);
423 return NULL;
424 }
425
426 res = snprintf(fn, len, STASH_DIRECTORY_BASE "/%s/%s%s", base, id, postfix);
427
428 if (res < 0 || res >= len) {
429 fprintf(stderr, "failed to format file name (return value %d)\n", res);
430 free(fn);
431 return NULL;
432 }
433
434 return fn;
435}
436
437typedef void (*StashCallback)(const char*, void*);
438
439// Does a best effort enumeration of stash files. Ignores possible non-file
440// items in the stash directory and continues despite of errors. Calls the
441// 'callback' function for each file and passes 'data' to the function as a
442// parameter.
443
444static void EnumerateStash(const char* dirname, StashCallback callback, void* data) {
445 char* fn;
446 DIR* directory;
447 int len;
448 int res;
449 struct dirent* item;
450
451 if (dirname == NULL || callback == NULL) {
452 return;
453 }
454
455 directory = opendir(dirname);
456
457 if (directory == NULL) {
458 if (errno != ENOENT) {
Elliott Hughes1fdd4522015-03-23 13:33:57 -0700459 fprintf(stderr, "opendir \"%s\" failed: %s\n", dirname, strerror(errno));
Sami Tolvanen90221202014-12-09 16:39:47 +0000460 }
461 return;
462 }
463
464 while ((item = readdir(directory)) != NULL) {
465 if (item->d_type != DT_REG) {
466 continue;
467 }
468
469 len = strlen(dirname) + 1 + strlen(item->d_name) + 1;
470 fn = malloc(len);
471
472 if (fn == NULL) {
473 fprintf(stderr, "failed to malloc %d bytes for fn\n", len);
474 continue;
475 }
476
477 res = snprintf(fn, len, "%s/%s", dirname, item->d_name);
478
479 if (res < 0 || res >= len) {
480 fprintf(stderr, "failed to format file name (return value %d)\n", res);
481 free(fn);
482 continue;
483 }
484
485 callback(fn, data);
486 free(fn);
487 }
488
489 if (closedir(directory) == -1) {
Elliott Hughes1fdd4522015-03-23 13:33:57 -0700490 fprintf(stderr, "closedir \"%s\" failed: %s\n", dirname, strerror(errno));
Sami Tolvanen90221202014-12-09 16:39:47 +0000491 }
492}
493
494static void UpdateFileSize(const char* fn, void* data) {
495 int* size = (int*) data;
496 struct stat st;
497
498 if (!fn || !data) {
499 return;
500 }
501
502 if (stat(fn, &st) == -1) {
Elliott Hughes1fdd4522015-03-23 13:33:57 -0700503 fprintf(stderr, "stat \"%s\" failed: %s\n", fn, strerror(errno));
Sami Tolvanen90221202014-12-09 16:39:47 +0000504 return;
505 }
506
507 *size += st.st_size;
508}
509
510// Deletes the stash directory and all files in it. Assumes that it only
511// contains files. There is nothing we can do about unlikely, but possible
512// errors, so they are merely logged.
513
514static void DeleteFile(const char* fn, void* data) {
515 if (fn) {
516 fprintf(stderr, "deleting %s\n", fn);
517
518 if (unlink(fn) == -1 && errno != ENOENT) {
Elliott Hughes1fdd4522015-03-23 13:33:57 -0700519 fprintf(stderr, "unlink \"%s\" failed: %s\n", fn, strerror(errno));
Sami Tolvanen90221202014-12-09 16:39:47 +0000520 }
521 }
522}
523
524static void DeletePartial(const char* fn, void* data) {
525 if (fn && strstr(fn, ".partial") != NULL) {
526 DeleteFile(fn, data);
527 }
528}
529
530static void DeleteStash(const char* base) {
531 char* dirname;
532
533 if (base == NULL) {
534 return;
535 }
536
537 dirname = GetStashFileName(base, NULL, NULL);
538
539 if (dirname == NULL) {
540 return;
541 }
542
543 fprintf(stderr, "deleting stash %s\n", base);
544 EnumerateStash(dirname, DeleteFile, NULL);
545
546 if (rmdir(dirname) == -1) {
547 if (errno != ENOENT && errno != ENOTDIR) {
Elliott Hughes1fdd4522015-03-23 13:33:57 -0700548 fprintf(stderr, "rmdir \"%s\" failed: %s\n", dirname, strerror(errno));
Sami Tolvanen90221202014-12-09 16:39:47 +0000549 }
550 }
551
552 free(dirname);
553}
554
555static int LoadStash(const char* base, const char* id, int verify, int* blocks, uint8_t** buffer,
556 size_t* buffer_alloc, int printnoent) {
557 char *fn = NULL;
558 int blockcount = 0;
559 int fd = -1;
560 int rc = -1;
561 int res;
562 struct stat st;
563
564 if (!base || !id || !buffer || !buffer_alloc) {
565 goto lsout;
566 }
567
568 if (!blocks) {
569 blocks = &blockcount;
570 }
571
572 fn = GetStashFileName(base, id, NULL);
573
574 if (fn == NULL) {
575 goto lsout;
576 }
577
578 res = stat(fn, &st);
579
580 if (res == -1) {
581 if (errno != ENOENT || printnoent) {
Elliott Hughes1fdd4522015-03-23 13:33:57 -0700582 fprintf(stderr, "stat \"%s\" failed: %s\n", fn, strerror(errno));
Sami Tolvanen90221202014-12-09 16:39:47 +0000583 }
584 goto lsout;
585 }
586
587 fprintf(stderr, " loading %s\n", fn);
588
589 if ((st.st_size % BLOCKSIZE) != 0) {
590 fprintf(stderr, "%s size %zd not multiple of block size %d", fn, st.st_size, BLOCKSIZE);
591 goto lsout;
592 }
593
594 fd = TEMP_FAILURE_RETRY(open(fn, O_RDONLY));
595
596 if (fd == -1) {
Elliott Hughes1fdd4522015-03-23 13:33:57 -0700597 fprintf(stderr, "open \"%s\" failed: %s\n", fn, strerror(errno));
Sami Tolvanen90221202014-12-09 16:39:47 +0000598 goto lsout;
599 }
600
601 allocate(st.st_size, buffer, buffer_alloc);
602
603 if (read_all(fd, *buffer, st.st_size) == -1) {
604 goto lsout;
605 }
606
607 *blocks = st.st_size / BLOCKSIZE;
608
609 if (verify && VerifyBlocks(id, *buffer, *blocks, 1) != 0) {
610 fprintf(stderr, "unexpected contents in %s\n", fn);
611 DeleteFile(fn, NULL);
612 goto lsout;
613 }
614
615 rc = 0;
616
617lsout:
618 if (fd != -1) {
Elliott Hughesb5dabd22015-05-28 23:06:17 -0700619 close(fd);
Sami Tolvanen90221202014-12-09 16:39:47 +0000620 }
621
622 if (fn) {
623 free(fn);
624 }
625
626 return rc;
627}
628
629static int WriteStash(const char* base, const char* id, int blocks, uint8_t* buffer,
Sami Tolvanen43b748f2015-04-17 12:50:31 +0100630 int checkspace, int *exists) {
Sami Tolvanen90221202014-12-09 16:39:47 +0000631 char *fn = NULL;
632 char *cn = NULL;
633 int fd = -1;
634 int rc = -1;
635 int res;
Sami Tolvanen43b748f2015-04-17 12:50:31 +0100636 struct stat st;
Sami Tolvanen90221202014-12-09 16:39:47 +0000637
638 if (base == NULL || buffer == NULL) {
639 goto wsout;
640 }
641
642 if (checkspace && CacheSizeCheck(blocks * BLOCKSIZE) != 0) {
643 fprintf(stderr, "not enough space to write stash\n");
644 goto wsout;
645 }
646
647 fn = GetStashFileName(base, id, ".partial");
648 cn = GetStashFileName(base, id, NULL);
649
650 if (fn == NULL || cn == NULL) {
651 goto wsout;
652 }
653
Sami Tolvanen43b748f2015-04-17 12:50:31 +0100654 if (exists) {
655 res = stat(cn, &st);
656
657 if (res == 0) {
658 // The file already exists and since the name is the hash of the contents,
659 // it's safe to assume the contents are identical (accidental hash collisions
660 // are unlikely)
661 fprintf(stderr, " skipping %d existing blocks in %s\n", blocks, cn);
662 *exists = 1;
663 rc = 0;
664 goto wsout;
665 }
666
667 *exists = 0;
668 }
669
Sami Tolvanen90221202014-12-09 16:39:47 +0000670 fprintf(stderr, " writing %d blocks to %s\n", blocks, cn);
671
672 fd = TEMP_FAILURE_RETRY(open(fn, O_WRONLY | O_CREAT | O_TRUNC | O_SYNC, STASH_FILE_MODE));
673
674 if (fd == -1) {
Elliott Hughes1fdd4522015-03-23 13:33:57 -0700675 fprintf(stderr, "failed to create \"%s\": %s\n", fn, strerror(errno));
Sami Tolvanen90221202014-12-09 16:39:47 +0000676 goto wsout;
677 }
678
679 if (write_all(fd, buffer, blocks * BLOCKSIZE) == -1) {
680 goto wsout;
681 }
682
683 if (fsync(fd) == -1) {
Elliott Hughes1fdd4522015-03-23 13:33:57 -0700684 fprintf(stderr, "fsync \"%s\" failed: %s\n", fn, strerror(errno));
Sami Tolvanen90221202014-12-09 16:39:47 +0000685 goto wsout;
686 }
687
688 if (rename(fn, cn) == -1) {
Elliott Hughes1fdd4522015-03-23 13:33:57 -0700689 fprintf(stderr, "rename(\"%s\", \"%s\") failed: %s\n", fn, cn, strerror(errno));
Sami Tolvanen90221202014-12-09 16:39:47 +0000690 goto wsout;
691 }
692
693 rc = 0;
694
695wsout:
696 if (fd != -1) {
Elliott Hughes1857a7f2015-05-15 16:19:20 -0700697 close(fd);
Sami Tolvanen90221202014-12-09 16:39:47 +0000698 }
699
700 if (fn) {
701 free(fn);
702 }
703
704 if (cn) {
705 free(cn);
706 }
707
708 return rc;
709}
710
711// Creates a directory for storing stash files and checks if the /cache partition
712// hash enough space for the expected amount of blocks we need to store. Returns
713// >0 if we created the directory, zero if it existed already, and <0 of failure.
714
715static int CreateStash(State* state, int maxblocks, const char* blockdev, char** base) {
716 char* dirname = NULL;
717 const uint8_t* digest;
718 int rc = -1;
719 int res;
720 int size = 0;
721 SHA_CTX ctx;
722 struct stat st;
723
724 if (blockdev == NULL || base == NULL) {
725 goto csout;
726 }
727
728 // Stash directory should be different for each partition to avoid conflicts
729 // when updating multiple partitions at the same time, so we use the hash of
730 // the block device name as the base directory
731 SHA_init(&ctx);
732 SHA_update(&ctx, blockdev, strlen(blockdev));
733 digest = SHA_final(&ctx);
734 *base = PrintSha1(digest);
735
736 if (*base == NULL) {
737 goto csout;
738 }
739
740 dirname = GetStashFileName(*base, NULL, NULL);
741
742 if (dirname == NULL) {
743 goto csout;
744 }
745
746 res = stat(dirname, &st);
747
748 if (res == -1 && errno != ENOENT) {
Elliott Hughes1fdd4522015-03-23 13:33:57 -0700749 ErrorAbort(state, "stat \"%s\" failed: %s\n", dirname, strerror(errno));
Sami Tolvanen90221202014-12-09 16:39:47 +0000750 goto csout;
751 } else if (res != 0) {
752 fprintf(stderr, "creating stash %s\n", dirname);
753 res = mkdir(dirname, STASH_DIRECTORY_MODE);
754
755 if (res != 0) {
Elliott Hughes1fdd4522015-03-23 13:33:57 -0700756 ErrorAbort(state, "mkdir \"%s\" failed: %s\n", dirname, strerror(errno));
Sami Tolvanen90221202014-12-09 16:39:47 +0000757 goto csout;
758 }
759
760 if (CacheSizeCheck(maxblocks * BLOCKSIZE) != 0) {
761 ErrorAbort(state, "not enough space for stash\n");
762 goto csout;
763 }
764
765 rc = 1; // Created directory
766 goto csout;
767 }
768
769 fprintf(stderr, "using existing stash %s\n", dirname);
770
771 // If the directory already exists, calculate the space already allocated to
772 // stash files and check if there's enough for all required blocks. Delete any
773 // partially completed stash files first.
774
775 EnumerateStash(dirname, DeletePartial, NULL);
776 EnumerateStash(dirname, UpdateFileSize, &size);
777
778 size = (maxblocks * BLOCKSIZE) - size;
779
780 if (size > 0 && CacheSizeCheck(size) != 0) {
781 ErrorAbort(state, "not enough space for stash (%d more needed)\n", size);
782 goto csout;
783 }
784
785 rc = 0; // Using existing directory
786
787csout:
788 if (dirname) {
789 free(dirname);
790 }
791
792 return rc;
793}
794
795static int SaveStash(const char* base, char** wordsave, uint8_t** buffer, size_t* buffer_alloc,
796 int fd, int usehash, int* isunresumable) {
797 char *id = NULL;
798 int res = -1;
799 int blocks = 0;
800
801 if (!wordsave || !buffer || !buffer_alloc || !isunresumable) {
802 return -1;
803 }
804
805 id = strtok_r(NULL, " ", wordsave);
806
807 if (id == NULL) {
808 fprintf(stderr, "missing id field in stash command\n");
809 return -1;
810 }
811
812 if (usehash && LoadStash(base, id, 1, &blocks, buffer, buffer_alloc, 0) == 0) {
813 // Stash file already exists and has expected contents. Do not
814 // read from source again, as the source may have been already
815 // overwritten during a previous attempt.
816 return 0;
817 }
818
819 if (LoadSrcTgtVersion1(wordsave, NULL, &blocks, buffer, buffer_alloc, fd) == -1) {
820 return -1;
821 }
822
823 if (usehash && VerifyBlocks(id, *buffer, blocks, 1) != 0) {
824 // Source blocks have unexpected contents. If we actually need this
825 // data later, this is an unrecoverable error. However, the command
826 // that uses the data may have already completed previously, so the
827 // possible failure will occur during source block verification.
828 fprintf(stderr, "failed to load source blocks for stash %s\n", id);
829 return 0;
830 }
831
832 fprintf(stderr, "stashing %d blocks to %s\n", blocks, id);
Sami Tolvanen43b748f2015-04-17 12:50:31 +0100833 return WriteStash(base, id, blocks, *buffer, 0, NULL);
Sami Tolvanen90221202014-12-09 16:39:47 +0000834}
835
836static int FreeStash(const char* base, const char* id) {
837 char *fn = NULL;
838
839 if (base == NULL || id == NULL) {
840 return -1;
841 }
842
843 fn = GetStashFileName(base, id, NULL);
844
845 if (fn == NULL) {
846 return -1;
847 }
848
849 DeleteFile(fn, NULL);
850 free(fn);
851
852 return 0;
Doug Zongker52ae67d2014-09-08 12:22:09 -0700853}
854
855static void MoveRange(uint8_t* dest, RangeSet* locs, const uint8_t* source) {
856 // source contains packed data, which we want to move to the
857 // locations given in *locs in the dest buffer. source and dest
858 // may be the same buffer.
859
860 int start = locs->size;
861 int i;
862 for (i = locs->count-1; i >= 0; --i) {
863 int blocks = locs->pos[i*2+1] - locs->pos[i*2];
864 start -= blocks;
865 memmove(dest + (locs->pos[i*2] * BLOCKSIZE), source + (start * BLOCKSIZE),
866 blocks * BLOCKSIZE);
867 }
868}
869
870// Do a source/target load for move/bsdiff/imgdiff in version 2.
871// 'wordsave' is the save_ptr of a strtok_r()-in-progress. We expect
872// to parse the remainder of the string as one of:
873//
874// <tgt_range> <src_block_count> <src_range>
875// (loads data from source image only)
876//
877// <tgt_range> <src_block_count> - <[stash_id:stash_range] ...>
878// (loads data from stashes only)
879//
880// <tgt_range> <src_block_count> <src_range> <src_loc> <[stash_id:stash_range] ...>
881// (loads data from both source image and stashes)
882//
883// On return, buffer is filled with the loaded source data (rearranged
884// and combined with stashed data as necessary). buffer may be
885// reallocated if needed to accommodate the source data. *tgt is the
Sami Tolvanen90221202014-12-09 16:39:47 +0000886// target RangeSet. Any stashes required are loaded using LoadStash.
Doug Zongker52ae67d2014-09-08 12:22:09 -0700887
Sami Tolvanen90221202014-12-09 16:39:47 +0000888static int LoadSrcTgtVersion2(char** wordsave, RangeSet** tgt, int* src_blocks,
Doug Zongker52ae67d2014-09-08 12:22:09 -0700889 uint8_t** buffer, size_t* buffer_alloc, int fd,
Sami Tolvanen90221202014-12-09 16:39:47 +0000890 const char* stashbase, int* overlap) {
Doug Zongker52ae67d2014-09-08 12:22:09 -0700891 char* word;
Sami Tolvanen90221202014-12-09 16:39:47 +0000892 char* colonsave;
893 char* colon;
894 int id;
895 int res;
896 RangeSet* locs;
897 size_t stashalloc = 0;
898 uint8_t* stash = NULL;
Doug Zongker52ae67d2014-09-08 12:22:09 -0700899
900 if (tgt != NULL) {
Sami Tolvanen90221202014-12-09 16:39:47 +0000901 word = strtok_r(NULL, " ", wordsave);
Doug Zongker52ae67d2014-09-08 12:22:09 -0700902 *tgt = parse_range(word);
903 }
904
Sami Tolvanen90221202014-12-09 16:39:47 +0000905 word = strtok_r(NULL, " ", wordsave);
Doug Zongker52ae67d2014-09-08 12:22:09 -0700906 *src_blocks = strtol(word, NULL, 0);
907
908 allocate(*src_blocks * BLOCKSIZE, buffer, buffer_alloc);
909
Sami Tolvanen90221202014-12-09 16:39:47 +0000910 word = strtok_r(NULL, " ", wordsave);
Doug Zongker52ae67d2014-09-08 12:22:09 -0700911 if (word[0] == '-' && word[1] == '\0') {
912 // no source ranges, only stashes
913 } else {
914 RangeSet* src = parse_range(word);
Sami Tolvanen90221202014-12-09 16:39:47 +0000915 res = ReadBlocks(src, *buffer, fd);
Doug Zongker52ae67d2014-09-08 12:22:09 -0700916
Sami Tolvanen90221202014-12-09 16:39:47 +0000917 if (overlap && tgt) {
918 *overlap = range_overlaps(src, *tgt);
Doug Zongker52ae67d2014-09-08 12:22:09 -0700919 }
Sami Tolvanen90221202014-12-09 16:39:47 +0000920
Doug Zongker52ae67d2014-09-08 12:22:09 -0700921 free(src);
922
Sami Tolvanen90221202014-12-09 16:39:47 +0000923 if (res == -1) {
924 return -1;
Doug Zongker52ae67d2014-09-08 12:22:09 -0700925 }
926
Sami Tolvanen90221202014-12-09 16:39:47 +0000927 word = strtok_r(NULL, " ", wordsave);
928 if (word == NULL) {
929 // no stashes, only source range
930 return 0;
931 }
932
933 locs = parse_range(word);
Doug Zongker52ae67d2014-09-08 12:22:09 -0700934 MoveRange(*buffer, locs, *buffer);
Sami Tolvanen90221202014-12-09 16:39:47 +0000935 free(locs);
Doug Zongker52ae67d2014-09-08 12:22:09 -0700936 }
937
Sami Tolvanen90221202014-12-09 16:39:47 +0000938 while ((word = strtok_r(NULL, " ", wordsave)) != NULL) {
Doug Zongker52ae67d2014-09-08 12:22:09 -0700939 // Each word is a an index into the stash table, a colon, and
940 // then a rangeset describing where in the source block that
941 // stashed data should go.
Sami Tolvanen90221202014-12-09 16:39:47 +0000942 colonsave = NULL;
943 colon = strtok_r(word, ":", &colonsave);
944
945 res = LoadStash(stashbase, colon, 0, NULL, &stash, &stashalloc, 1);
946
947 if (res == -1) {
948 // These source blocks will fail verification if used later, but we
949 // will let the caller decide if this is a fatal failure
950 fprintf(stderr, "failed to load stash %s\n", colon);
951 continue;
952 }
953
Doug Zongker52ae67d2014-09-08 12:22:09 -0700954 colon = strtok_r(NULL, ":", &colonsave);
Sami Tolvanen90221202014-12-09 16:39:47 +0000955 locs = parse_range(colon);
956
957 MoveRange(*buffer, locs, stash);
Doug Zongker52ae67d2014-09-08 12:22:09 -0700958 free(locs);
959 }
Sami Tolvanen90221202014-12-09 16:39:47 +0000960
961 if (stash) {
962 free(stash);
963 }
964
965 return 0;
966}
967
968// Parameters for transfer list command functions
969typedef struct {
970 char* cmdname;
971 char* cpos;
972 char* freestash;
973 char* stashbase;
974 int canwrite;
975 int createdstash;
976 int fd;
977 int foundwrites;
978 int isunresumable;
979 int version;
980 int written;
981 NewThreadInfo nti;
982 pthread_t thread;
983 size_t bufsize;
984 uint8_t* buffer;
985 uint8_t* patch_start;
986} CommandParameters;
987
988// Do a source/target load for move/bsdiff/imgdiff in version 3.
989//
990// Parameters are the same as for LoadSrcTgtVersion2, except for 'onehash', which
991// tells the function whether to expect separate source and targe block hashes, or
992// if they are both the same and only one hash should be expected, and
993// 'isunresumable', which receives a non-zero value if block verification fails in
994// a way that the update cannot be resumed anymore.
995//
996// If the function is unable to load the necessary blocks or their contents don't
997// match the hashes, the return value is -1 and the command should be aborted.
998//
999// If the return value is 1, the command has already been completed according to
1000// the contents of the target blocks, and should not be performed again.
1001//
1002// If the return value is 0, source blocks have expected content and the command
1003// can be performed.
1004
1005static int LoadSrcTgtVersion3(CommandParameters* params, RangeSet** tgt, int* src_blocks,
1006 int onehash, int* overlap) {
1007 char* srchash = NULL;
1008 char* tgthash = NULL;
Sami Tolvanen43b748f2015-04-17 12:50:31 +01001009 int stash_exists = 0;
Sami Tolvanen90221202014-12-09 16:39:47 +00001010 int overlap_blocks = 0;
1011 int rc = -1;
1012 uint8_t* tgtbuffer = NULL;
1013
1014 if (!params|| !tgt || !src_blocks || !overlap) {
1015 goto v3out;
1016 }
1017
1018 srchash = strtok_r(NULL, " ", &params->cpos);
1019
1020 if (srchash == NULL) {
1021 fprintf(stderr, "missing source hash\n");
1022 goto v3out;
1023 }
1024
1025 if (onehash) {
1026 tgthash = srchash;
1027 } else {
1028 tgthash = strtok_r(NULL, " ", &params->cpos);
1029
1030 if (tgthash == NULL) {
1031 fprintf(stderr, "missing target hash\n");
1032 goto v3out;
1033 }
1034 }
1035
1036 if (LoadSrcTgtVersion2(&params->cpos, tgt, src_blocks, &params->buffer, &params->bufsize,
1037 params->fd, params->stashbase, overlap) == -1) {
1038 goto v3out;
1039 }
1040
1041 tgtbuffer = (uint8_t*) malloc((*tgt)->size * BLOCKSIZE);
1042
1043 if (tgtbuffer == NULL) {
1044 fprintf(stderr, "failed to allocate %d bytes\n", (*tgt)->size * BLOCKSIZE);
1045 goto v3out;
1046 }
1047
1048 if (ReadBlocks(*tgt, tgtbuffer, params->fd) == -1) {
1049 goto v3out;
1050 }
1051
1052 if (VerifyBlocks(tgthash, tgtbuffer, (*tgt)->size, 0) == 0) {
1053 // Target blocks already have expected content, command should be skipped
1054 rc = 1;
1055 goto v3out;
1056 }
1057
1058 if (VerifyBlocks(srchash, params->buffer, *src_blocks, 1) == 0) {
1059 // If source and target blocks overlap, stash the source blocks so we can
1060 // resume from possible write errors
1061 if (*overlap) {
1062 fprintf(stderr, "stashing %d overlapping blocks to %s\n", *src_blocks,
1063 srchash);
1064
Sami Tolvanen43b748f2015-04-17 12:50:31 +01001065 if (WriteStash(params->stashbase, srchash, *src_blocks, params->buffer, 1,
1066 &stash_exists) != 0) {
Sami Tolvanen90221202014-12-09 16:39:47 +00001067 fprintf(stderr, "failed to stash overlapping source blocks\n");
1068 goto v3out;
1069 }
1070
1071 // Can be deleted when the write has completed
Sami Tolvanen43b748f2015-04-17 12:50:31 +01001072 if (!stash_exists) {
1073 params->freestash = srchash;
1074 }
Sami Tolvanen90221202014-12-09 16:39:47 +00001075 }
1076
1077 // Source blocks have expected content, command can proceed
1078 rc = 0;
1079 goto v3out;
1080 }
1081
1082 if (*overlap && LoadStash(params->stashbase, srchash, 1, NULL, &params->buffer,
1083 &params->bufsize, 1) == 0) {
Sami Tolvanen43b748f2015-04-17 12:50:31 +01001084 // Overlapping source blocks were previously stashed, command can proceed.
1085 // We are recovering from an interrupted command, so we don't know if the
1086 // stash can safely be deleted after this command.
Sami Tolvanen90221202014-12-09 16:39:47 +00001087 rc = 0;
1088 goto v3out;
1089 }
1090
1091 // Valid source data not available, update cannot be resumed
1092 fprintf(stderr, "partition has unexpected contents\n");
1093 params->isunresumable = 1;
1094
1095v3out:
1096 if (tgtbuffer) {
1097 free(tgtbuffer);
1098 }
1099
1100 return rc;
1101}
1102
1103static int PerformCommandMove(CommandParameters* params) {
1104 int blocks = 0;
1105 int overlap = 0;
1106 int rc = -1;
1107 int status = 0;
1108 RangeSet* tgt = NULL;
1109
1110 if (!params) {
1111 goto pcmout;
1112 }
1113
1114 if (params->version == 1) {
1115 status = LoadSrcTgtVersion1(&params->cpos, &tgt, &blocks, &params->buffer,
1116 &params->bufsize, params->fd);
1117 } else if (params->version == 2) {
1118 status = LoadSrcTgtVersion2(&params->cpos, &tgt, &blocks, &params->buffer,
1119 &params->bufsize, params->fd, params->stashbase, NULL);
1120 } else if (params->version >= 3) {
1121 status = LoadSrcTgtVersion3(params, &tgt, &blocks, 1, &overlap);
1122 }
1123
1124 if (status == -1) {
1125 fprintf(stderr, "failed to read blocks for move\n");
1126 goto pcmout;
1127 }
1128
1129 if (status == 0) {
1130 params->foundwrites = 1;
1131 } else if (params->foundwrites) {
1132 fprintf(stderr, "warning: commands executed out of order [%s]\n", params->cmdname);
1133 }
1134
1135 if (params->canwrite) {
1136 if (status == 0) {
1137 fprintf(stderr, " moving %d blocks\n", blocks);
1138
1139 if (WriteBlocks(tgt, params->buffer, params->fd) == -1) {
1140 goto pcmout;
1141 }
1142 } else {
1143 fprintf(stderr, "skipping %d already moved blocks\n", blocks);
1144 }
1145
1146 }
1147
1148 if (params->freestash) {
1149 FreeStash(params->stashbase, params->freestash);
1150 params->freestash = NULL;
1151 }
1152
1153 params->written += tgt->size;
1154 rc = 0;
1155
1156pcmout:
1157 if (tgt) {
1158 free(tgt);
1159 }
1160
1161 return rc;
1162}
1163
1164static int PerformCommandStash(CommandParameters* params) {
1165 if (!params) {
1166 return -1;
1167 }
1168
1169 return SaveStash(params->stashbase, &params->cpos, &params->buffer, &params->bufsize,
1170 params->fd, (params->version >= 3), &params->isunresumable);
1171}
1172
1173static int PerformCommandFree(CommandParameters* params) {
1174 if (!params) {
1175 return -1;
1176 }
1177
1178 if (params->createdstash || params->canwrite) {
1179 return FreeStash(params->stashbase, params->cpos);
1180 }
1181
1182 return 0;
1183}
1184
1185static int PerformCommandZero(CommandParameters* params) {
1186 char* range = NULL;
1187 int i;
1188 int j;
1189 int rc = -1;
1190 RangeSet* tgt = NULL;
1191
1192 if (!params) {
1193 goto pczout;
1194 }
1195
1196 range = strtok_r(NULL, " ", &params->cpos);
1197
1198 if (range == NULL) {
1199 fprintf(stderr, "missing target blocks for zero\n");
1200 goto pczout;
1201 }
1202
1203 tgt = parse_range(range);
1204
1205 fprintf(stderr, " zeroing %d blocks\n", tgt->size);
1206
1207 allocate(BLOCKSIZE, &params->buffer, &params->bufsize);
1208 memset(params->buffer, 0, BLOCKSIZE);
1209
1210 if (params->canwrite) {
1211 for (i = 0; i < tgt->count; ++i) {
Elliott Hughes2f5feed2015-04-28 17:24:24 -07001212 if (!check_lseek(params->fd, (off64_t) tgt->pos[i * 2] * BLOCKSIZE, SEEK_SET)) {
Sami Tolvanen90221202014-12-09 16:39:47 +00001213 goto pczout;
1214 }
1215
1216 for (j = tgt->pos[i * 2]; j < tgt->pos[i * 2 + 1]; ++j) {
1217 if (write_all(params->fd, params->buffer, BLOCKSIZE) == -1) {
1218 goto pczout;
1219 }
1220 }
1221 }
1222 }
1223
1224 if (params->cmdname[0] == 'z') {
Sami Tolvanen6abd52f2015-06-10 15:52:04 +00001225 // Update only for the zero command, as the erase command will call
1226 // this if DEBUG_ERASE is defined.
Sami Tolvanen90221202014-12-09 16:39:47 +00001227 params->written += tgt->size;
1228 }
1229
1230 rc = 0;
1231
1232pczout:
1233 if (tgt) {
1234 free(tgt);
1235 }
1236
1237 return rc;
1238}
1239
1240static int PerformCommandNew(CommandParameters* params) {
1241 char* range = NULL;
1242 int rc = -1;
1243 RangeSet* tgt = NULL;
1244 RangeSinkState rss;
1245
1246 if (!params) {
1247 goto pcnout;
1248 }
1249
1250 range = strtok_r(NULL, " ", &params->cpos);
1251
1252 if (range == NULL) {
1253 goto pcnout;
1254 }
1255
1256 tgt = parse_range(range);
1257
1258 if (params->canwrite) {
1259 fprintf(stderr, " writing %d blocks of new data\n", tgt->size);
1260
1261 rss.fd = params->fd;
1262 rss.tgt = tgt;
1263 rss.p_block = 0;
1264 rss.p_remain = (tgt->pos[1] - tgt->pos[0]) * BLOCKSIZE;
1265
Elliott Hughes2f5feed2015-04-28 17:24:24 -07001266 if (!check_lseek(params->fd, (off64_t) tgt->pos[0] * BLOCKSIZE, SEEK_SET)) {
Sami Tolvanen90221202014-12-09 16:39:47 +00001267 goto pcnout;
1268 }
1269
1270 pthread_mutex_lock(&params->nti.mu);
1271 params->nti.rss = &rss;
1272 pthread_cond_broadcast(&params->nti.cv);
1273
1274 while (params->nti.rss) {
1275 pthread_cond_wait(&params->nti.cv, &params->nti.mu);
1276 }
1277
1278 pthread_mutex_unlock(&params->nti.mu);
1279 }
1280
1281 params->written += tgt->size;
1282 rc = 0;
1283
1284pcnout:
1285 if (tgt) {
1286 free(tgt);
1287 }
1288
1289 return rc;
1290}
1291
1292static int PerformCommandDiff(CommandParameters* params) {
1293 char* logparams = NULL;
1294 char* value = NULL;
1295 int blocks = 0;
1296 int overlap = 0;
1297 int rc = -1;
1298 int status = 0;
1299 RangeSet* tgt = NULL;
1300 RangeSinkState rss;
1301 size_t len = 0;
1302 size_t offset = 0;
1303 Value patch_value;
1304
1305 if (!params) {
1306 goto pcdout;
1307 }
1308
1309 logparams = strdup(params->cpos);
1310 value = strtok_r(NULL, " ", &params->cpos);
1311
1312 if (value == NULL) {
1313 fprintf(stderr, "missing patch offset for %s\n", params->cmdname);
1314 goto pcdout;
1315 }
1316
1317 offset = strtoul(value, NULL, 0);
1318
1319 value = strtok_r(NULL, " ", &params->cpos);
1320
1321 if (value == NULL) {
1322 fprintf(stderr, "missing patch length for %s\n", params->cmdname);
1323 goto pcdout;
1324 }
1325
1326 len = strtoul(value, NULL, 0);
1327
1328 if (params->version == 1) {
1329 status = LoadSrcTgtVersion1(&params->cpos, &tgt, &blocks, &params->buffer,
1330 &params->bufsize, params->fd);
1331 } else if (params->version == 2) {
1332 status = LoadSrcTgtVersion2(&params->cpos, &tgt, &blocks, &params->buffer,
1333 &params->bufsize, params->fd, params->stashbase, NULL);
1334 } else if (params->version >= 3) {
1335 status = LoadSrcTgtVersion3(params, &tgt, &blocks, 0, &overlap);
1336 }
1337
1338 if (status == -1) {
1339 fprintf(stderr, "failed to read blocks for diff\n");
1340 goto pcdout;
1341 }
1342
1343 if (status == 0) {
1344 params->foundwrites = 1;
1345 } else if (params->foundwrites) {
1346 fprintf(stderr, "warning: commands executed out of order [%s]\n", params->cmdname);
1347 }
1348
1349 if (params->canwrite) {
1350 if (status == 0) {
1351 fprintf(stderr, "patching %d blocks to %d\n", blocks, tgt->size);
1352
1353 patch_value.type = VAL_BLOB;
1354 patch_value.size = len;
1355 patch_value.data = (char*) (params->patch_start + offset);
1356
1357 rss.fd = params->fd;
1358 rss.tgt = tgt;
1359 rss.p_block = 0;
1360 rss.p_remain = (tgt->pos[1] - tgt->pos[0]) * BLOCKSIZE;
1361
Elliott Hughes2f5feed2015-04-28 17:24:24 -07001362 if (!check_lseek(params->fd, (off64_t) tgt->pos[0] * BLOCKSIZE, SEEK_SET)) {
Sami Tolvanen90221202014-12-09 16:39:47 +00001363 goto pcdout;
1364 }
1365
1366 if (params->cmdname[0] == 'i') { // imgdiff
1367 ApplyImagePatch(params->buffer, blocks * BLOCKSIZE, &patch_value,
1368 &RangeSinkWrite, &rss, NULL, NULL);
1369 } else {
1370 ApplyBSDiffPatch(params->buffer, blocks * BLOCKSIZE, &patch_value,
1371 0, &RangeSinkWrite, &rss, NULL);
1372 }
1373
1374 // We expect the output of the patcher to fill the tgt ranges exactly.
1375 if (rss.p_block != tgt->count || rss.p_remain != 0) {
1376 fprintf(stderr, "range sink underrun?\n");
1377 }
1378 } else {
1379 fprintf(stderr, "skipping %d blocks already patched to %d [%s]\n",
1380 blocks, tgt->size, logparams);
1381 }
1382 }
1383
1384 if (params->freestash) {
1385 FreeStash(params->stashbase, params->freestash);
1386 params->freestash = NULL;
1387 }
1388
1389 params->written += tgt->size;
1390 rc = 0;
1391
1392pcdout:
1393 if (logparams) {
1394 free(logparams);
1395 }
1396
1397 if (tgt) {
1398 free(tgt);
1399 }
1400
1401 return rc;
1402}
1403
1404static int PerformCommandErase(CommandParameters* params) {
1405 char* range = NULL;
1406 int i;
1407 int rc = -1;
1408 RangeSet* tgt = NULL;
1409 struct stat st;
1410 uint64_t blocks[2];
1411
Sami Tolvanen6abd52f2015-06-10 15:52:04 +00001412 if (DEBUG_ERASE) {
1413 return PerformCommandZero(params);
Sami Tolvanen90221202014-12-09 16:39:47 +00001414 }
1415
1416 if (!params) {
1417 goto pceout;
1418 }
1419
1420 if (fstat(params->fd, &st) == -1) {
Elliott Hughes1fdd4522015-03-23 13:33:57 -07001421 fprintf(stderr, "failed to fstat device to erase: %s\n", strerror(errno));
Sami Tolvanen90221202014-12-09 16:39:47 +00001422 goto pceout;
1423 }
1424
1425 if (!S_ISBLK(st.st_mode)) {
1426 fprintf(stderr, "not a block device; skipping erase\n");
Sami Tolvanen90221202014-12-09 16:39:47 +00001427 goto pceout;
1428 }
1429
1430 range = strtok_r(NULL, " ", &params->cpos);
1431
1432 if (range == NULL) {
1433 fprintf(stderr, "missing target blocks for zero\n");
1434 goto pceout;
1435 }
1436
1437 tgt = parse_range(range);
1438
1439 if (params->canwrite) {
1440 fprintf(stderr, " erasing %d blocks\n", tgt->size);
1441
1442 for (i = 0; i < tgt->count; ++i) {
1443 // offset in bytes
1444 blocks[0] = tgt->pos[i * 2] * (uint64_t) BLOCKSIZE;
1445 // length in bytes
1446 blocks[1] = (tgt->pos[i * 2 + 1] - tgt->pos[i * 2]) * (uint64_t) BLOCKSIZE;
1447
1448 if (ioctl(params->fd, BLKDISCARD, &blocks) == -1) {
Elliott Hughes1fdd4522015-03-23 13:33:57 -07001449 fprintf(stderr, "BLKDISCARD ioctl failed: %s\n", strerror(errno));
Sami Tolvanen92eea1b2015-04-27 11:24:29 +01001450 goto pceout;
Sami Tolvanen90221202014-12-09 16:39:47 +00001451 }
1452 }
1453 }
1454
1455 rc = 0;
1456
1457pceout:
1458 if (tgt) {
1459 free(tgt);
1460 }
1461
1462 return rc;
1463}
1464
1465// Definitions for transfer list command functions
1466typedef int (*CommandFunction)(CommandParameters*);
1467
1468typedef struct {
1469 const char* name;
1470 CommandFunction f;
1471} Command;
1472
1473// CompareCommands and CompareCommandNames are for the hash table
1474
1475static int CompareCommands(const void* c1, const void* c2) {
1476 return strcmp(((const Command*) c1)->name, ((const Command*) c2)->name);
1477}
1478
1479static int CompareCommandNames(const void* c1, const void* c2) {
1480 return strcmp(((const Command*) c1)->name, (const char*) c2);
1481}
1482
1483// HashString is used to hash command names for the hash table
1484
1485static unsigned int HashString(const char *s) {
1486 unsigned int hash = 0;
1487 if (s) {
1488 while (*s) {
1489 hash = hash * 33 + *s++;
1490 }
1491 }
1492 return hash;
Doug Zongker52ae67d2014-09-08 12:22:09 -07001493}
1494
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001495// args:
1496// - block device (or file) to modify in-place
1497// - transfer list (blob)
1498// - new data stream (filename within package.zip)
1499// - patch stream (filename within package.zip, must be uncompressed)
1500
Sami Tolvanen90221202014-12-09 16:39:47 +00001501static Value* PerformBlockImageUpdate(const char* name, State* state, int argc, Expr* argv[],
1502 const Command* commands, int cmdcount, int dryrun) {
1503
1504 char* line = NULL;
1505 char* linesave = NULL;
1506 char* logcmd = NULL;
Doug Zongker1d5d6092014-08-21 10:47:24 -07001507 char* transfer_list = NULL;
Sami Tolvanen90221202014-12-09 16:39:47 +00001508 CommandParameters params;
1509 const Command* cmd = NULL;
1510 const ZipEntry* new_entry = NULL;
1511 const ZipEntry* patch_entry = NULL;
1512 FILE* cmd_pipe = NULL;
1513 HashTable* cmdht = NULL;
1514 int i;
1515 int res;
1516 int rc = -1;
1517 int stash_max_blocks = 0;
1518 int total_blocks = 0;
1519 pthread_attr_t attr;
1520 unsigned int cmdhash;
1521 UpdaterInfo* ui = NULL;
1522 Value* blockdev_filename = NULL;
1523 Value* new_data_fn = NULL;
1524 Value* patch_data_fn = NULL;
1525 Value* transfer_list_value = NULL;
1526 ZipArchive* za = NULL;
1527
1528 memset(&params, 0, sizeof(params));
1529 params.canwrite = !dryrun;
1530
1531 fprintf(stderr, "performing %s\n", dryrun ? "verification" : "update");
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001532
Doug Zongker1d5d6092014-08-21 10:47:24 -07001533 if (ReadValueArgs(state, argv, 4, &blockdev_filename, &transfer_list_value,
Sami Tolvanen90221202014-12-09 16:39:47 +00001534 &new_data_fn, &patch_data_fn) < 0) {
1535 goto pbiudone;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001536 }
1537
1538 if (blockdev_filename->type != VAL_STRING) {
1539 ErrorAbort(state, "blockdev_filename argument to %s must be string", name);
Sami Tolvanen90221202014-12-09 16:39:47 +00001540 goto pbiudone;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001541 }
Doug Zongker1d5d6092014-08-21 10:47:24 -07001542 if (transfer_list_value->type != VAL_BLOB) {
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001543 ErrorAbort(state, "transfer_list argument to %s must be blob", name);
Sami Tolvanen90221202014-12-09 16:39:47 +00001544 goto pbiudone;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001545 }
1546 if (new_data_fn->type != VAL_STRING) {
1547 ErrorAbort(state, "new_data_fn argument to %s must be string", name);
Sami Tolvanen90221202014-12-09 16:39:47 +00001548 goto pbiudone;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001549 }
1550 if (patch_data_fn->type != VAL_STRING) {
1551 ErrorAbort(state, "patch_data_fn argument to %s must be string", name);
Sami Tolvanen90221202014-12-09 16:39:47 +00001552 goto pbiudone;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001553 }
1554
Sami Tolvanen90221202014-12-09 16:39:47 +00001555 ui = (UpdaterInfo*) state->cookie;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001556
Sami Tolvanen90221202014-12-09 16:39:47 +00001557 if (ui == NULL) {
1558 goto pbiudone;
1559 }
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001560
Sami Tolvanen90221202014-12-09 16:39:47 +00001561 cmd_pipe = ui->cmd_pipe;
1562 za = ui->package_zip;
1563
1564 if (cmd_pipe == NULL || za == NULL) {
1565 goto pbiudone;
1566 }
1567
1568 patch_entry = mzFindZipEntry(za, patch_data_fn->data);
1569
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001570 if (patch_entry == NULL) {
Sami Tolvanen90221202014-12-09 16:39:47 +00001571 fprintf(stderr, "%s(): no file \"%s\" in package", name, patch_data_fn->data);
1572 goto pbiudone;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001573 }
1574
Sami Tolvanen90221202014-12-09 16:39:47 +00001575 params.patch_start = ui->package_zip_addr + mzGetZipEntryOffset(patch_entry);
1576 new_entry = mzFindZipEntry(za, new_data_fn->data);
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001577
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001578 if (new_entry == NULL) {
Sami Tolvanen90221202014-12-09 16:39:47 +00001579 fprintf(stderr, "%s(): no file \"%s\" in package", name, new_data_fn->data);
1580 goto pbiudone;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001581 }
1582
Sami Tolvanen90221202014-12-09 16:39:47 +00001583 params.fd = TEMP_FAILURE_RETRY(open(blockdev_filename->data, O_RDWR));
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001584
Sami Tolvanen90221202014-12-09 16:39:47 +00001585 if (params.fd == -1) {
Elliott Hughes1fdd4522015-03-23 13:33:57 -07001586 fprintf(stderr, "open \"%s\" failed: %s\n", blockdev_filename->data, strerror(errno));
Sami Tolvanen90221202014-12-09 16:39:47 +00001587 goto pbiudone;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001588 }
1589
Sami Tolvanen90221202014-12-09 16:39:47 +00001590 if (params.canwrite) {
1591 params.nti.za = za;
1592 params.nti.entry = new_entry;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001593
Sami Tolvanen90221202014-12-09 16:39:47 +00001594 pthread_mutex_init(&params.nti.mu, NULL);
1595 pthread_cond_init(&params.nti.cv, NULL);
1596 pthread_attr_init(&attr);
1597 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
1598
Elliott Hughes1fdd4522015-03-23 13:33:57 -07001599 int error = pthread_create(&params.thread, &attr, unzip_new_data, &params.nti);
1600 if (error != 0) {
1601 fprintf(stderr, "pthread_create failed: %s\n", strerror(error));
Sami Tolvanen90221202014-12-09 16:39:47 +00001602 goto pbiudone;
1603 }
1604 }
1605
1606 // The data in transfer_list_value is not necessarily null-terminated, so we need
1607 // to copy it to a new buffer and add the null that strtok_r will need.
1608 transfer_list = malloc(transfer_list_value->size + 1);
1609
Doug Zongker1d5d6092014-08-21 10:47:24 -07001610 if (transfer_list == NULL) {
1611 fprintf(stderr, "failed to allocate %zd bytes for transfer list\n",
Sami Tolvanen90221202014-12-09 16:39:47 +00001612 transfer_list_value->size + 1);
1613 goto pbiudone;
Doug Zongker1d5d6092014-08-21 10:47:24 -07001614 }
Sami Tolvanen90221202014-12-09 16:39:47 +00001615
Doug Zongker1d5d6092014-08-21 10:47:24 -07001616 memcpy(transfer_list, transfer_list_value->data, transfer_list_value->size);
1617 transfer_list[transfer_list_value->size] = '\0';
1618
Sami Tolvanen90221202014-12-09 16:39:47 +00001619 // First line in transfer list is the version number
Doug Zongker1d5d6092014-08-21 10:47:24 -07001620 line = strtok_r(transfer_list, "\n", &linesave);
Sami Tolvanen90221202014-12-09 16:39:47 +00001621 params.version = strtol(line, NULL, 0);
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001622
Sami Tolvanen90221202014-12-09 16:39:47 +00001623 if (params.version < 1 || params.version > 3) {
1624 fprintf(stderr, "unexpected transfer list version [%s]\n", line);
1625 goto pbiudone;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001626 }
1627
Sami Tolvanen90221202014-12-09 16:39:47 +00001628 fprintf(stderr, "blockimg version is %d\n", params.version);
1629
1630 // Second line in transfer list is the total number of blocks we expect to write
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001631 line = strtok_r(NULL, "\n", &linesave);
Sami Tolvanen90221202014-12-09 16:39:47 +00001632 total_blocks = strtol(line, NULL, 0);
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001633
Sami Tolvanen90221202014-12-09 16:39:47 +00001634 if (total_blocks < 0) {
1635 ErrorAbort(state, "unexpected block count [%s]\n", line);
1636 goto pbiudone;
1637 } else if (total_blocks == 0) {
1638 rc = 0;
1639 goto pbiudone;
1640 }
1641
1642 if (params.version >= 2) {
1643 // Third line is how many stash entries are needed simultaneously
Doug Zongker52ae67d2014-09-08 12:22:09 -07001644 line = strtok_r(NULL, "\n", &linesave);
Sami Tolvanen90221202014-12-09 16:39:47 +00001645 fprintf(stderr, "maximum stash entries %s\n", line);
Doug Zongker52ae67d2014-09-08 12:22:09 -07001646
Sami Tolvanen90221202014-12-09 16:39:47 +00001647 // Fourth line is the maximum number of blocks that will be stashed simultaneously
1648 line = strtok_r(NULL, "\n", &linesave);
1649 stash_max_blocks = strtol(line, NULL, 0);
1650
1651 if (stash_max_blocks < 0) {
1652 ErrorAbort(state, "unexpected maximum stash blocks [%s]\n", line);
1653 goto pbiudone;
Doug Zongker52ae67d2014-09-08 12:22:09 -07001654 }
1655
Jesse Zhao1df64d32015-02-17 17:09:23 -08001656 if (stash_max_blocks >= 0) {
Sami Tolvanen90221202014-12-09 16:39:47 +00001657 res = CreateStash(state, stash_max_blocks, blockdev_filename->data,
1658 &params.stashbase);
1659
1660 if (res == -1) {
1661 goto pbiudone;
1662 }
1663
1664 params.createdstash = res;
1665 }
Doug Zongker52ae67d2014-09-08 12:22:09 -07001666 }
1667
Sami Tolvanen90221202014-12-09 16:39:47 +00001668 // Build a hash table of the available commands
1669 cmdht = mzHashTableCreate(cmdcount, NULL);
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001670
Sami Tolvanen90221202014-12-09 16:39:47 +00001671 for (i = 0; i < cmdcount; ++i) {
1672 cmdhash = HashString(commands[i].name);
1673 mzHashTableLookup(cmdht, cmdhash, (void*) &commands[i], CompareCommands, true);
1674 }
1675
1676 // Subsequent lines are all individual transfer commands
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001677 for (line = strtok_r(NULL, "\n", &linesave); line;
1678 line = strtok_r(NULL, "\n", &linesave)) {
Doug Zongker52ae67d2014-09-08 12:22:09 -07001679
Sami Tolvanen90221202014-12-09 16:39:47 +00001680 logcmd = strdup(line);
1681 params.cmdname = strtok_r(line, " ", &params.cpos);
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001682
Sami Tolvanen90221202014-12-09 16:39:47 +00001683 if (params.cmdname == NULL) {
1684 fprintf(stderr, "missing command [%s]\n", line);
1685 goto pbiudone;
1686 }
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001687
Sami Tolvanen90221202014-12-09 16:39:47 +00001688 cmdhash = HashString(params.cmdname);
1689 cmd = (const Command*) mzHashTableLookup(cmdht, cmdhash, params.cmdname,
1690 CompareCommandNames, false);
Doug Zongker52ae67d2014-09-08 12:22:09 -07001691
Sami Tolvanen90221202014-12-09 16:39:47 +00001692 if (cmd == NULL) {
1693 fprintf(stderr, "unexpected command [%s]\n", params.cmdname);
1694 goto pbiudone;
1695 }
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001696
Sami Tolvanen90221202014-12-09 16:39:47 +00001697 if (cmd->f != NULL && cmd->f(&params) == -1) {
1698 fprintf(stderr, "failed to execute command [%s]\n",
1699 logcmd ? logcmd : params.cmdname);
1700 goto pbiudone;
1701 }
1702
1703 if (logcmd) {
1704 free(logcmd);
1705 logcmd = NULL;
1706 }
1707
1708 if (params.canwrite) {
1709 fprintf(cmd_pipe, "set_progress %.4f\n", (double) params.written / total_blocks);
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001710 fflush(cmd_pipe);
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001711 }
1712 }
1713
Sami Tolvanen90221202014-12-09 16:39:47 +00001714 if (params.canwrite) {
1715 pthread_join(params.thread, NULL);
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001716
Sami Tolvanen90221202014-12-09 16:39:47 +00001717 fprintf(stderr, "wrote %d blocks; expected %d\n", params.written, total_blocks);
1718 fprintf(stderr, "max alloc needed was %zu\n", params.bufsize);
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001719
Sami Tolvanen90221202014-12-09 16:39:47 +00001720 // Delete stash only after successfully completing the update, as it
1721 // may contain blocks needed to complete the update later.
1722 DeleteStash(params.stashbase);
1723 } else {
1724 fprintf(stderr, "verified partition contents; update may be resumed\n");
1725 }
1726
1727 rc = 0;
1728
1729pbiudone:
1730 if (params.fd != -1) {
1731 if (fsync(params.fd) == -1) {
Elliott Hughes1fdd4522015-03-23 13:33:57 -07001732 fprintf(stderr, "fsync failed: %s\n", strerror(errno));
Sami Tolvanen90221202014-12-09 16:39:47 +00001733 }
Elliott Hughes1857a7f2015-05-15 16:19:20 -07001734 close(params.fd);
Sami Tolvanen90221202014-12-09 16:39:47 +00001735 }
1736
1737 if (logcmd) {
1738 free(logcmd);
1739 }
1740
1741 if (cmdht) {
1742 mzHashTableFree(cmdht);
1743 }
1744
1745 if (params.buffer) {
1746 free(params.buffer);
1747 }
1748
1749 if (transfer_list) {
1750 free(transfer_list);
1751 }
1752
1753 if (blockdev_filename) {
1754 FreeValue(blockdev_filename);
1755 }
1756
1757 if (transfer_list_value) {
1758 FreeValue(transfer_list_value);
1759 }
1760
1761 if (new_data_fn) {
1762 FreeValue(new_data_fn);
1763 }
1764
1765 if (patch_data_fn) {
1766 FreeValue(patch_data_fn);
1767 }
1768
1769 // Only delete the stash if the update cannot be resumed, or it's
1770 // a verification run and we created the stash.
1771 if (params.isunresumable || (!params.canwrite && params.createdstash)) {
1772 DeleteStash(params.stashbase);
1773 }
1774
1775 if (params.stashbase) {
1776 free(params.stashbase);
1777 }
1778
1779 return StringValue(rc == 0 ? strdup("t") : strdup(""));
1780}
1781
1782// The transfer list is a text file containing commands to
1783// transfer data from one place to another on the target
1784// partition. We parse it and execute the commands in order:
1785//
1786// zero [rangeset]
1787// - fill the indicated blocks with zeros
1788//
1789// new [rangeset]
1790// - fill the blocks with data read from the new_data file
1791//
1792// erase [rangeset]
1793// - mark the given blocks as empty
1794//
1795// move <...>
1796// bsdiff <patchstart> <patchlen> <...>
1797// imgdiff <patchstart> <patchlen> <...>
1798// - read the source blocks, apply a patch (or not in the
1799// case of move), write result to target blocks. bsdiff or
1800// imgdiff specifies the type of patch; move means no patch
1801// at all.
1802//
1803// The format of <...> differs between versions 1 and 2;
1804// see the LoadSrcTgtVersion{1,2}() functions for a
1805// description of what's expected.
1806//
1807// stash <stash_id> <src_range>
1808// - (version 2+ only) load the given source range and stash
1809// the data in the given slot of the stash table.
1810//
1811// The creator of the transfer list will guarantee that no block
1812// is read (ie, used as the source for a patch or move) after it
1813// has been written.
1814//
1815// In version 2, the creator will guarantee that a given stash is
1816// loaded (with a stash command) before it's used in a
1817// move/bsdiff/imgdiff command.
1818//
1819// Within one command the source and target ranges may overlap so
1820// in general we need to read the entire source into memory before
1821// writing anything to the target blocks.
1822//
1823// All the patch data is concatenated into one patch_data file in
1824// the update package. It must be stored uncompressed because we
1825// memory-map it in directly from the archive. (Since patches are
1826// already compressed, we lose very little by not compressing
1827// their concatenation.)
1828//
1829// In version 3, commands that read data from the partition (i.e.
1830// move/bsdiff/imgdiff/stash) have one or more additional hashes
1831// before the range parameters, which are used to check if the
1832// command has already been completed and verify the integrity of
1833// the source data.
1834
1835Value* BlockImageVerifyFn(const char* name, State* state, int argc, Expr* argv[]) {
1836 // Commands which are not tested are set to NULL to skip them completely
1837 const Command commands[] = {
1838 { "bsdiff", PerformCommandDiff },
1839 { "erase", NULL },
1840 { "free", PerformCommandFree },
1841 { "imgdiff", PerformCommandDiff },
1842 { "move", PerformCommandMove },
1843 { "new", NULL },
1844 { "stash", PerformCommandStash },
1845 { "zero", NULL }
1846 };
1847
1848 // Perform a dry run without writing to test if an update can proceed
1849 return PerformBlockImageUpdate(name, state, argc, argv, commands,
1850 sizeof(commands) / sizeof(commands[0]), 1);
1851}
1852
1853Value* BlockImageUpdateFn(const char* name, State* state, int argc, Expr* argv[]) {
1854 const Command commands[] = {
1855 { "bsdiff", PerformCommandDiff },
1856 { "erase", PerformCommandErase },
1857 { "free", PerformCommandFree },
1858 { "imgdiff", PerformCommandDiff },
1859 { "move", PerformCommandMove },
1860 { "new", PerformCommandNew },
1861 { "stash", PerformCommandStash },
1862 { "zero", PerformCommandZero }
1863 };
1864
1865 return PerformBlockImageUpdate(name, state, argc, argv, commands,
1866 sizeof(commands) / sizeof(commands[0]), 0);
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001867}
1868
1869Value* RangeSha1Fn(const char* name, State* state, int argc, Expr* argv[]) {
1870 Value* blockdev_filename;
1871 Value* ranges;
1872 const uint8_t* digest = NULL;
1873 if (ReadValueArgs(state, argv, 2, &blockdev_filename, &ranges) < 0) {
1874 return NULL;
1875 }
1876
1877 if (blockdev_filename->type != VAL_STRING) {
1878 ErrorAbort(state, "blockdev_filename argument to %s must be string", name);
1879 goto done;
1880 }
1881 if (ranges->type != VAL_STRING) {
1882 ErrorAbort(state, "ranges argument to %s must be string", name);
1883 goto done;
1884 }
1885
1886 int fd = open(blockdev_filename->data, O_RDWR);
1887 if (fd < 0) {
Elliott Hughes1fdd4522015-03-23 13:33:57 -07001888 ErrorAbort(state, "open \"%s\" failed: %s", blockdev_filename->data, strerror(errno));
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001889 goto done;
1890 }
1891
1892 RangeSet* rs = parse_range(ranges->data);
1893 uint8_t buffer[BLOCKSIZE];
1894
1895 SHA_CTX ctx;
1896 SHA_init(&ctx);
1897
1898 int i, j;
1899 for (i = 0; i < rs->count; ++i) {
Elliott Hughes2f5feed2015-04-28 17:24:24 -07001900 if (!check_lseek(fd, (off64_t)rs->pos[i*2] * BLOCKSIZE, SEEK_SET)) {
Sami Tolvanen90221202014-12-09 16:39:47 +00001901 ErrorAbort(state, "failed to seek %s: %s", blockdev_filename->data,
1902 strerror(errno));
1903 goto done;
1904 }
1905
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001906 for (j = rs->pos[i*2]; j < rs->pos[i*2+1]; ++j) {
Sami Tolvanen90221202014-12-09 16:39:47 +00001907 if (read_all(fd, buffer, BLOCKSIZE) == -1) {
1908 ErrorAbort(state, "failed to read %s: %s", blockdev_filename->data,
1909 strerror(errno));
1910 goto done;
1911 }
1912
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001913 SHA_update(&ctx, buffer, BLOCKSIZE);
1914 }
1915 }
1916 digest = SHA_final(&ctx);
1917 close(fd);
1918
1919 done:
1920 FreeValue(blockdev_filename);
1921 FreeValue(ranges);
1922 if (digest == NULL) {
1923 return StringValue(strdup(""));
1924 } else {
1925 return StringValue(PrintSha1(digest));
1926 }
1927}
1928
1929void RegisterBlockImageFunctions() {
Sami Tolvanen90221202014-12-09 16:39:47 +00001930 RegisterFunction("block_image_verify", BlockImageVerifyFn);
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001931 RegisterFunction("block_image_update", BlockImageUpdateFn);
1932 RegisterFunction("range_sha1", RangeSha1Fn);
1933}