blob: 7da9adf5f1014d4ebf92ce5729cd13b714975ff9 [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
Tao Baoe6aa3322015-08-05 15:20:27 -070035#include <memory>
36#include <string>
37
38#include <base/strings.h>
39
Doug Zongkerbc7ffed2014-08-15 14:31:52 -070040#include "applypatch/applypatch.h"
41#include "edify/expr.h"
42#include "mincrypt/sha.h"
Sami Tolvanen90221202014-12-09 16:39:47 +000043#include "minzip/Hash.h"
Doug Zongkerbc7ffed2014-08-15 14:31:52 -070044#include "updater.h"
Tao Baoe6aa3322015-08-05 15:20:27 -070045#include "print_sha1.h"
Doug Zongkerbc7ffed2014-08-15 14:31:52 -070046
47#define BLOCKSIZE 4096
48
Sami Tolvanene82fa182015-06-10 15:58:12 +000049// Set this to 0 to interpret 'erase' transfers to mean do a
50// BLKDISCARD ioctl (the normal behavior). Set to 1 to interpret
51// erase to mean fill the region with zeroes.
52#define DEBUG_ERASE 0
53
Sami Tolvanen90221202014-12-09 16:39:47 +000054#define STASH_DIRECTORY_BASE "/cache/recovery"
55#define STASH_DIRECTORY_MODE 0700
56#define STASH_FILE_MODE 0600
57
Doug Zongkerbc7ffed2014-08-15 14:31:52 -070058typedef struct {
Shrinivas Sahukara6153df2015-08-19 13:01:45 +053059 size_t count;
60 size_t size;
61 size_t pos[0]; // Actual limit is INT_MAX.
Doug Zongkerbc7ffed2014-08-15 14:31:52 -070062} RangeSet;
63
Sami Tolvanenf2bac042015-05-12 12:48:46 +010064#define RANGESET_MAX_POINTS \
65 ((int)((INT_MAX / sizeof(int)) - sizeof(RangeSet)))
66
Doug Zongkerbc7ffed2014-08-15 14:31:52 -070067static RangeSet* parse_range(char* text) {
68 char* save;
Sami Tolvanenf2bac042015-05-12 12:48:46 +010069 char* token;
Tao Baoba9a42a2015-06-23 23:23:33 -070070 int num;
Sami Tolvanenf2bac042015-05-12 12:48:46 +010071 long int val;
72 RangeSet* out = NULL;
73 size_t bufsize;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -070074
Sami Tolvanenf2bac042015-05-12 12:48:46 +010075 if (!text) {
76 goto err;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -070077 }
Sami Tolvanenf2bac042015-05-12 12:48:46 +010078
79 token = strtok_r(text, ",", &save);
80
81 if (!token) {
82 goto err;
83 }
84
Shrinivas Sahukara6153df2015-08-19 13:01:45 +053085 errno = 0;
Sami Tolvanenf2bac042015-05-12 12:48:46 +010086 val = strtol(token, NULL, 0);
87
Shrinivas Sahukara6153df2015-08-19 13:01:45 +053088 if (errno != 0 || val < 2 || val > RANGESET_MAX_POINTS) {
Sami Tolvanenf2bac042015-05-12 12:48:46 +010089 goto err;
90 } else if (val % 2) {
91 goto err; // must be even
92 }
93
94 num = (int) val;
Shrinivas Sahukara6153df2015-08-19 13:01:45 +053095 bufsize = sizeof(RangeSet) + num * sizeof(size_t);
Sami Tolvanenf2bac042015-05-12 12:48:46 +010096
Tao Baoba9a42a2015-06-23 23:23:33 -070097 out = reinterpret_cast<RangeSet*>(malloc(bufsize));
Sami Tolvanenf2bac042015-05-12 12:48:46 +010098
99 if (!out) {
100 fprintf(stderr, "failed to allocate range of %zu bytes\n", bufsize);
101 goto err;
102 }
103
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700104 out->count = num / 2;
105 out->size = 0;
Sami Tolvanenf2bac042015-05-12 12:48:46 +0100106
Shrinivas Sahukara6153df2015-08-19 13:01:45 +0530107 for (int i = 0; i < num; i += 2) {
Sami Tolvanenf2bac042015-05-12 12:48:46 +0100108 token = strtok_r(NULL, ",", &save);
109
110 if (!token) {
111 goto err;
112 }
113
Shrinivas Sahukara6153df2015-08-19 13:01:45 +0530114 errno = 0;
Sami Tolvanenf2bac042015-05-12 12:48:46 +0100115 val = strtol(token, NULL, 0);
116
Shrinivas Sahukara6153df2015-08-19 13:01:45 +0530117 if (errno != 0 || val < 0 || val > INT_MAX) {
Sami Tolvanenf2bac042015-05-12 12:48:46 +0100118 goto err;
119 }
120
Shrinivas Sahukara6153df2015-08-19 13:01:45 +0530121 out->pos[i] = static_cast<size_t>(val);
Sami Tolvanenf2bac042015-05-12 12:48:46 +0100122
Shrinivas Sahukara6153df2015-08-19 13:01:45 +0530123 token = strtok_r(NULL, ",", &save);
Sami Tolvanenf2bac042015-05-12 12:48:46 +0100124
Shrinivas Sahukara6153df2015-08-19 13:01:45 +0530125 if (!token) {
126 goto err;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700127 }
Shrinivas Sahukara6153df2015-08-19 13:01:45 +0530128
129 errno = 0;
130 val = strtol(token, NULL, 0);
131
132 if (errno != 0 || val < 0 || val > INT_MAX) {
133 goto err;
134 }
135
136 out->pos[i+1] = static_cast<size_t>(val);
137
138 if (out->pos[i] >= out->pos[i+1]) {
139 goto err; // empty or negative range
140 }
141
142 size_t rs = out->pos[i+1] - out->pos[i];
143 if (out->size > SIZE_MAX - rs) {
144 goto err; // overflow
145 }
146
147 out->size += rs;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700148 }
149
Shrinivas Sahukara6153df2015-08-19 13:01:45 +0530150 if (out->size == 0) {
Sami Tolvanenf2bac042015-05-12 12:48:46 +0100151 goto err;
152 }
153
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700154 return out;
Sami Tolvanenf2bac042015-05-12 12:48:46 +0100155
156err:
157 fprintf(stderr, "failed to parse range '%s'\n", text ? text : "NULL");
158 exit(1);
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700159}
160
Tao Baoe6aa3322015-08-05 15:20:27 -0700161static bool range_overlaps(const RangeSet& r1, const RangeSet& r2) {
Shrinivas Sahukara6153df2015-08-19 13:01:45 +0530162 for (size_t i = 0; i < r1.count; ++i) {
163 size_t r1_0 = r1.pos[i * 2];
164 size_t r1_1 = r1.pos[i * 2 + 1];
Sami Tolvanen90221202014-12-09 16:39:47 +0000165
Shrinivas Sahukara6153df2015-08-19 13:01:45 +0530166 for (size_t j = 0; j < r2.count; ++j) {
167 size_t r2_0 = r2.pos[j * 2];
168 size_t r2_1 = r2.pos[j * 2 + 1];
Sami Tolvanen90221202014-12-09 16:39:47 +0000169
Tao Baoc0f56ad2015-06-25 14:00:31 -0700170 if (!(r2_0 >= r1_1 || r1_0 >= r2_1)) {
Tao Baoe6aa3322015-08-05 15:20:27 -0700171 return true;
Sami Tolvanen90221202014-12-09 16:39:47 +0000172 }
173 }
174 }
175
Tao Baoe6aa3322015-08-05 15:20:27 -0700176 return false;
Sami Tolvanen90221202014-12-09 16:39:47 +0000177}
178
179static int read_all(int fd, uint8_t* data, size_t size) {
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700180 size_t so_far = 0;
181 while (so_far < size) {
Elliott Hughes7bad7c42015-04-28 17:24:24 -0700182 ssize_t r = TEMP_FAILURE_RETRY(read(fd, data+so_far, size-so_far));
183 if (r == -1) {
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700184 fprintf(stderr, "read failed: %s\n", strerror(errno));
Sami Tolvanen90221202014-12-09 16:39:47 +0000185 return -1;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700186 }
Elliott Hughes7bad7c42015-04-28 17:24:24 -0700187 so_far += r;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700188 }
Sami Tolvanen90221202014-12-09 16:39:47 +0000189 return 0;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700190}
191
Sami Tolvanen90221202014-12-09 16:39:47 +0000192static int write_all(int fd, const uint8_t* data, size_t size) {
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700193 size_t written = 0;
194 while (written < size) {
Elliott Hughes7bad7c42015-04-28 17:24:24 -0700195 ssize_t w = TEMP_FAILURE_RETRY(write(fd, data+written, size-written));
196 if (w == -1) {
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700197 fprintf(stderr, "write failed: %s\n", strerror(errno));
Sami Tolvanen90221202014-12-09 16:39:47 +0000198 return -1;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700199 }
Elliott Hughes7bad7c42015-04-28 17:24:24 -0700200 written += w;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700201 }
Sami Tolvanen90221202014-12-09 16:39:47 +0000202
Sami Tolvanen90221202014-12-09 16:39:47 +0000203 return 0;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700204}
205
Elliott Hughes7bad7c42015-04-28 17:24:24 -0700206static bool check_lseek(int fd, off64_t offset, int whence) {
207 off64_t rc = TEMP_FAILURE_RETRY(lseek64(fd, offset, whence));
208 if (rc == -1) {
209 fprintf(stderr, "lseek64 failed: %s\n", strerror(errno));
210 return false;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700211 }
Elliott Hughes7bad7c42015-04-28 17:24:24 -0700212 return true;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700213}
214
215static void allocate(size_t size, uint8_t** buffer, size_t* buffer_alloc) {
216 // if the buffer's big enough, reuse it.
217 if (size <= *buffer_alloc) return;
218
219 free(*buffer);
220
221 *buffer = (uint8_t*) malloc(size);
222 if (*buffer == NULL) {
Doug Zongker1d5d6092014-08-21 10:47:24 -0700223 fprintf(stderr, "failed to allocate %zu bytes\n", size);
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700224 exit(1);
225 }
226 *buffer_alloc = size;
227}
228
229typedef struct {
230 int fd;
231 RangeSet* tgt;
Shrinivas Sahukara6153df2015-08-19 13:01:45 +0530232 size_t p_block;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700233 size_t p_remain;
234} RangeSinkState;
235
236static ssize_t RangeSinkWrite(const uint8_t* data, ssize_t size, void* token) {
237 RangeSinkState* rss = (RangeSinkState*) token;
238
239 if (rss->p_remain <= 0) {
240 fprintf(stderr, "range sink write overrun");
Sami Tolvanen90221202014-12-09 16:39:47 +0000241 return 0;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700242 }
243
244 ssize_t written = 0;
245 while (size > 0) {
246 size_t write_now = size;
Sami Tolvanen90221202014-12-09 16:39:47 +0000247
248 if (rss->p_remain < write_now) {
249 write_now = rss->p_remain;
250 }
251
252 if (write_all(rss->fd, data, write_now) == -1) {
253 break;
254 }
255
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700256 data += write_now;
257 size -= write_now;
258
259 rss->p_remain -= write_now;
260 written += write_now;
261
262 if (rss->p_remain == 0) {
263 // move to the next block
264 ++rss->p_block;
265 if (rss->p_block < rss->tgt->count) {
Sami Tolvanen90221202014-12-09 16:39:47 +0000266 rss->p_remain = (rss->tgt->pos[rss->p_block * 2 + 1] -
267 rss->tgt->pos[rss->p_block * 2]) * BLOCKSIZE;
268
Elliott Hughes7bad7c42015-04-28 17:24:24 -0700269 if (!check_lseek(rss->fd, (off64_t)rss->tgt->pos[rss->p_block*2] * BLOCKSIZE,
270 SEEK_SET)) {
Sami Tolvanen90221202014-12-09 16:39:47 +0000271 break;
272 }
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700273 } else {
274 // we can't write any more; return how many bytes have
275 // been written so far.
Sami Tolvanen90221202014-12-09 16:39:47 +0000276 break;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700277 }
278 }
279 }
280
281 return written;
282}
283
284// All of the data for all the 'new' transfers is contained in one
285// file in the update package, concatenated together in the order in
286// which transfers.list will need it. We want to stream it out of the
287// archive (it's compressed) without writing it to a temp file, but we
288// can't write each section until it's that transfer's turn to go.
289//
290// To achieve this, we expand the new data from the archive in a
291// background thread, and block that threads 'receive uncompressed
292// data' function until the main thread has reached a point where we
293// want some new data to be written. We signal the background thread
294// with the destination for the data and block the main thread,
295// waiting for the background thread to complete writing that section.
296// Then it signals the main thread to wake up and goes back to
297// blocking waiting for a transfer.
298//
299// NewThreadInfo is the struct used to pass information back and forth
300// between the two threads. When the main thread wants some data
301// written, it sets rss to the destination location and signals the
302// condition. When the background thread is done writing, it clears
303// rss and signals the condition again.
304
305typedef struct {
306 ZipArchive* za;
307 const ZipEntry* entry;
308
309 RangeSinkState* rss;
310
311 pthread_mutex_t mu;
312 pthread_cond_t cv;
313} NewThreadInfo;
314
315static bool receive_new_data(const unsigned char* data, int size, void* cookie) {
316 NewThreadInfo* nti = (NewThreadInfo*) cookie;
317
318 while (size > 0) {
319 // Wait for nti->rss to be non-NULL, indicating some of this
320 // data is wanted.
321 pthread_mutex_lock(&nti->mu);
322 while (nti->rss == NULL) {
323 pthread_cond_wait(&nti->cv, &nti->mu);
324 }
325 pthread_mutex_unlock(&nti->mu);
326
327 // At this point nti->rss is set, and we own it. The main
328 // thread is waiting for it to disappear from nti.
329 ssize_t written = RangeSinkWrite(data, size, nti->rss);
330 data += written;
331 size -= written;
332
333 if (nti->rss->p_block == nti->rss->tgt->count) {
334 // we have written all the bytes desired by this rss.
335
336 pthread_mutex_lock(&nti->mu);
337 nti->rss = NULL;
338 pthread_cond_broadcast(&nti->cv);
339 pthread_mutex_unlock(&nti->mu);
340 }
341 }
342
343 return true;
344}
345
346static void* unzip_new_data(void* cookie) {
347 NewThreadInfo* nti = (NewThreadInfo*) cookie;
348 mzProcessZipEntryContents(nti->za, nti->entry, receive_new_data, nti);
349 return NULL;
350}
351
Sami Tolvanen90221202014-12-09 16:39:47 +0000352static int ReadBlocks(RangeSet* src, uint8_t* buffer, int fd) {
Sami Tolvanen90221202014-12-09 16:39:47 +0000353 size_t p = 0;
Sami Tolvanen90221202014-12-09 16:39:47 +0000354
355 if (!src || !buffer) {
356 return -1;
357 }
358
Shrinivas Sahukara6153df2015-08-19 13:01:45 +0530359 for (size_t i = 0; i < src->count; ++i) {
Elliott Hughes7bad7c42015-04-28 17:24:24 -0700360 if (!check_lseek(fd, (off64_t) src->pos[i * 2] * BLOCKSIZE, SEEK_SET)) {
Sami Tolvanen90221202014-12-09 16:39:47 +0000361 return -1;
362 }
363
Shrinivas Sahukara6153df2015-08-19 13:01:45 +0530364 size_t size = (src->pos[i * 2 + 1] - src->pos[i * 2]) * BLOCKSIZE;
Sami Tolvanen90221202014-12-09 16:39:47 +0000365
366 if (read_all(fd, buffer + p, size) == -1) {
367 return -1;
368 }
369
370 p += size;
371 }
372
373 return 0;
374}
375
376static int WriteBlocks(RangeSet* tgt, uint8_t* buffer, int fd) {
Sami Tolvanen90221202014-12-09 16:39:47 +0000377 size_t p = 0;
Sami Tolvanen90221202014-12-09 16:39:47 +0000378
379 if (!tgt || !buffer) {
380 return -1;
381 }
382
Shrinivas Sahukara6153df2015-08-19 13:01:45 +0530383 for (size_t i = 0; i < tgt->count; ++i) {
Elliott Hughes7bad7c42015-04-28 17:24:24 -0700384 if (!check_lseek(fd, (off64_t) tgt->pos[i * 2] * BLOCKSIZE, SEEK_SET)) {
Sami Tolvanen90221202014-12-09 16:39:47 +0000385 return -1;
386 }
387
Shrinivas Sahukara6153df2015-08-19 13:01:45 +0530388 size_t size = (tgt->pos[i * 2 + 1] - tgt->pos[i * 2]) * BLOCKSIZE;
Sami Tolvanen90221202014-12-09 16:39:47 +0000389
390 if (write_all(fd, buffer + p, size) == -1) {
391 return -1;
392 }
393
394 p += size;
395 }
396
397 return 0;
398}
399
Doug Zongker52ae67d2014-09-08 12:22:09 -0700400// Do a source/target load for move/bsdiff/imgdiff in version 1.
401// 'wordsave' is the save_ptr of a strtok_r()-in-progress. We expect
402// to parse the remainder of the string as:
403//
404// <src_range> <tgt_range>
405//
406// The source range is loaded into the provided buffer, reallocating
407// it to make it larger if necessary. The target ranges are returned
408// in *tgt, if tgt is non-NULL.
409
Sami Tolvanen90221202014-12-09 16:39:47 +0000410static int LoadSrcTgtVersion1(char** wordsave, RangeSet** tgt, int* src_blocks,
Tao Baoe6aa3322015-08-05 15:20:27 -0700411 uint8_t** buffer, size_t* buffer_alloc, int fd) {
Doug Zongker52ae67d2014-09-08 12:22:09 -0700412 char* word;
Sami Tolvanen90221202014-12-09 16:39:47 +0000413 int rc;
Doug Zongker52ae67d2014-09-08 12:22:09 -0700414
Sami Tolvanen90221202014-12-09 16:39:47 +0000415 word = strtok_r(NULL, " ", wordsave);
Doug Zongker52ae67d2014-09-08 12:22:09 -0700416 RangeSet* src = parse_range(word);
417
418 if (tgt != NULL) {
Sami Tolvanen90221202014-12-09 16:39:47 +0000419 word = strtok_r(NULL, " ", wordsave);
Doug Zongker52ae67d2014-09-08 12:22:09 -0700420 *tgt = parse_range(word);
421 }
422
423 allocate(src->size * BLOCKSIZE, buffer, buffer_alloc);
Sami Tolvanen90221202014-12-09 16:39:47 +0000424 rc = ReadBlocks(src, *buffer, fd);
425 *src_blocks = src->size;
426
427 free(src);
428 return rc;
429}
430
431static int VerifyBlocks(const char *expected, const uint8_t *buffer,
Tao Baoe6aa3322015-08-05 15:20:27 -0700432 size_t blocks, bool printerror) {
Sami Tolvanen90221202014-12-09 16:39:47 +0000433 int rc = -1;
434 uint8_t digest[SHA_DIGEST_SIZE];
435
436 if (!expected || !buffer) {
437 return rc;
Doug Zongker52ae67d2014-09-08 12:22:09 -0700438 }
439
Sami Tolvanen90221202014-12-09 16:39:47 +0000440 SHA_hash(buffer, blocks * BLOCKSIZE, digest);
Sami Tolvanen90221202014-12-09 16:39:47 +0000441
Tao Baoe6aa3322015-08-05 15:20:27 -0700442 std::string hexdigest = print_sha1(digest);
Sami Tolvanen90221202014-12-09 16:39:47 +0000443
Tao Baoe6aa3322015-08-05 15:20:27 -0700444 rc = hexdigest != std::string(expected);
Sami Tolvanen90221202014-12-09 16:39:47 +0000445
Tao Baoe6aa3322015-08-05 15:20:27 -0700446 if (rc != 0 && printerror) {
447 fprintf(stderr, "failed to verify blocks (expected %s, read %s)\n",
448 expected, hexdigest.c_str());
Sami Tolvanen90221202014-12-09 16:39:47 +0000449 }
450
451 return rc;
452}
453
Tao Baoe6aa3322015-08-05 15:20:27 -0700454static std::string GetStashFileName(const std::string& base, const std::string id,
455 const std::string postfix) {
456 if (base.empty()) {
457 return "";
Sami Tolvanen90221202014-12-09 16:39:47 +0000458 }
459
Tao Baoe6aa3322015-08-05 15:20:27 -0700460 std::string fn(STASH_DIRECTORY_BASE);
461 fn += "/" + base + "/" + id + postfix;
Sami Tolvanen90221202014-12-09 16:39:47 +0000462
463 return fn;
464}
465
Tao Baoe6aa3322015-08-05 15:20:27 -0700466typedef void (*StashCallback)(const std::string&, void*);
Sami Tolvanen90221202014-12-09 16:39:47 +0000467
468// Does a best effort enumeration of stash files. Ignores possible non-file
469// items in the stash directory and continues despite of errors. Calls the
470// 'callback' function for each file and passes 'data' to the function as a
471// parameter.
472
Tao Baoe6aa3322015-08-05 15:20:27 -0700473static void EnumerateStash(const std::string& dirname, StashCallback callback, void* data) {
474 if (dirname.empty() || callback == NULL) {
Sami Tolvanen90221202014-12-09 16:39:47 +0000475 return;
476 }
477
Tao Baoe6aa3322015-08-05 15:20:27 -0700478 std::unique_ptr<DIR, int(*)(DIR*)> directory(opendir(dirname.c_str()), closedir);
Sami Tolvanen90221202014-12-09 16:39:47 +0000479
480 if (directory == NULL) {
481 if (errno != ENOENT) {
Tao Baoe6aa3322015-08-05 15:20:27 -0700482 fprintf(stderr, "opendir \"%s\" failed: %s\n", dirname.c_str(), strerror(errno));
Sami Tolvanen90221202014-12-09 16:39:47 +0000483 }
484 return;
485 }
486
Tao Baoe6aa3322015-08-05 15:20:27 -0700487 struct dirent* item;
488 while ((item = readdir(directory.get())) != NULL) {
Sami Tolvanen90221202014-12-09 16:39:47 +0000489 if (item->d_type != DT_REG) {
490 continue;
491 }
492
Tao Baoe6aa3322015-08-05 15:20:27 -0700493 std::string fn = dirname + "/" + std::string(item->d_name);
Sami Tolvanen90221202014-12-09 16:39:47 +0000494 callback(fn, data);
Sami Tolvanen90221202014-12-09 16:39:47 +0000495 }
496}
497
Tao Baoe6aa3322015-08-05 15:20:27 -0700498static void UpdateFileSize(const std::string& fn, void* data) {
499 if (fn.empty() || !data) {
500 return;
501 }
502
Sami Tolvanen90221202014-12-09 16:39:47 +0000503 struct stat st;
Tao Baoe6aa3322015-08-05 15:20:27 -0700504 if (stat(fn.c_str(), &st) == -1) {
505 fprintf(stderr, "stat \"%s\" failed: %s\n", fn.c_str(), strerror(errno));
Sami Tolvanen90221202014-12-09 16:39:47 +0000506 return;
507 }
508
Tao Baoe6aa3322015-08-05 15:20:27 -0700509 int* size = reinterpret_cast<int*>(data);
Sami Tolvanen90221202014-12-09 16:39:47 +0000510 *size += st.st_size;
511}
512
513// Deletes the stash directory and all files in it. Assumes that it only
514// contains files. There is nothing we can do about unlikely, but possible
515// errors, so they are merely logged.
516
Tao Baoe6aa3322015-08-05 15:20:27 -0700517static void DeleteFile(const std::string& fn, void* data) {
518 if (!fn.empty()) {
519 fprintf(stderr, "deleting %s\n", fn.c_str());
Sami Tolvanen90221202014-12-09 16:39:47 +0000520
Tao Baoe6aa3322015-08-05 15:20:27 -0700521 if (unlink(fn.c_str()) == -1 && errno != ENOENT) {
522 fprintf(stderr, "unlink \"%s\" failed: %s\n", fn.c_str(), strerror(errno));
Sami Tolvanen90221202014-12-09 16:39:47 +0000523 }
524 }
525}
526
Tao Baoe6aa3322015-08-05 15:20:27 -0700527static void DeletePartial(const std::string& fn, void* data) {
528 if (android::base::EndsWith(fn, ".partial")) {
Sami Tolvanen90221202014-12-09 16:39:47 +0000529 DeleteFile(fn, data);
530 }
531}
532
Tao Baoe6aa3322015-08-05 15:20:27 -0700533static void DeleteStash(const std::string& base) {
534 if (base.empty()) {
Sami Tolvanen90221202014-12-09 16:39:47 +0000535 return;
536 }
537
Tao Baoe6aa3322015-08-05 15:20:27 -0700538 fprintf(stderr, "deleting stash %s\n", base.c_str());
Sami Tolvanen90221202014-12-09 16:39:47 +0000539
Tao Baoe6aa3322015-08-05 15:20:27 -0700540 std::string dirname = GetStashFileName(base, "", "");
Sami Tolvanen90221202014-12-09 16:39:47 +0000541 EnumerateStash(dirname, DeleteFile, NULL);
542
Tao Baoe6aa3322015-08-05 15:20:27 -0700543 if (rmdir(dirname.c_str()) == -1) {
Sami Tolvanen90221202014-12-09 16:39:47 +0000544 if (errno != ENOENT && errno != ENOTDIR) {
Tao Baoe6aa3322015-08-05 15:20:27 -0700545 fprintf(stderr, "rmdir \"%s\" failed: %s\n", dirname.c_str(), strerror(errno));
Sami Tolvanen90221202014-12-09 16:39:47 +0000546 }
547 }
Sami Tolvanen90221202014-12-09 16:39:47 +0000548}
549
Tao Baoe6aa3322015-08-05 15:20:27 -0700550static int LoadStash(const std::string& base, const char* id, int verify, int* blocks,
551 uint8_t** buffer, size_t* buffer_alloc, bool printnoent) {
552 std::string fn;
Sami Tolvanen90221202014-12-09 16:39:47 +0000553 int blockcount = 0;
554 int fd = -1;
555 int rc = -1;
556 int res;
557 struct stat st;
558
Tao Baoe6aa3322015-08-05 15:20:27 -0700559 if (base.empty() || !id || !buffer || !buffer_alloc) {
Sami Tolvanen90221202014-12-09 16:39:47 +0000560 goto lsout;
561 }
562
563 if (!blocks) {
564 blocks = &blockcount;
565 }
566
Tao Baoe6aa3322015-08-05 15:20:27 -0700567 fn = GetStashFileName(base, std::string(id), "");
Sami Tolvanen90221202014-12-09 16:39:47 +0000568
Tao Baoe6aa3322015-08-05 15:20:27 -0700569 res = stat(fn.c_str(), &st);
Sami Tolvanen90221202014-12-09 16:39:47 +0000570
571 if (res == -1) {
572 if (errno != ENOENT || printnoent) {
Tao Baoe6aa3322015-08-05 15:20:27 -0700573 fprintf(stderr, "stat \"%s\" failed: %s\n", fn.c_str(), strerror(errno));
Sami Tolvanen90221202014-12-09 16:39:47 +0000574 }
575 goto lsout;
576 }
577
Tao Baoe6aa3322015-08-05 15:20:27 -0700578 fprintf(stderr, " loading %s\n", fn.c_str());
Sami Tolvanen90221202014-12-09 16:39:47 +0000579
580 if ((st.st_size % BLOCKSIZE) != 0) {
Tao Baoba9a42a2015-06-23 23:23:33 -0700581 fprintf(stderr, "%s size %" PRId64 " not multiple of block size %d",
Tao Baoe6aa3322015-08-05 15:20:27 -0700582 fn.c_str(), static_cast<int64_t>(st.st_size), BLOCKSIZE);
Sami Tolvanen90221202014-12-09 16:39:47 +0000583 goto lsout;
584 }
585
Tao Baoe6aa3322015-08-05 15:20:27 -0700586 fd = TEMP_FAILURE_RETRY(open(fn.c_str(), O_RDONLY));
Sami Tolvanen90221202014-12-09 16:39:47 +0000587
588 if (fd == -1) {
Tao Baoe6aa3322015-08-05 15:20:27 -0700589 fprintf(stderr, "open \"%s\" failed: %s\n", fn.c_str(), strerror(errno));
Sami Tolvanen90221202014-12-09 16:39:47 +0000590 goto lsout;
591 }
592
593 allocate(st.st_size, buffer, buffer_alloc);
594
595 if (read_all(fd, *buffer, st.st_size) == -1) {
596 goto lsout;
597 }
598
599 *blocks = st.st_size / BLOCKSIZE;
600
Tao Baoe6aa3322015-08-05 15:20:27 -0700601 if (verify && VerifyBlocks(id, *buffer, *blocks, true) != 0) {
602 fprintf(stderr, "unexpected contents in %s\n", fn.c_str());
Sami Tolvanen90221202014-12-09 16:39:47 +0000603 DeleteFile(fn, NULL);
604 goto lsout;
605 }
606
607 rc = 0;
608
609lsout:
610 if (fd != -1) {
Elliott Hughesb3ac6762015-05-28 23:06:17 -0700611 close(fd);
Sami Tolvanen90221202014-12-09 16:39:47 +0000612 }
613
Sami Tolvanen90221202014-12-09 16:39:47 +0000614 return rc;
615}
616
Tao Baoe6aa3322015-08-05 15:20:27 -0700617static int WriteStash(const std::string& base, const char* id, int blocks,
618 uint8_t* buffer, bool checkspace, int *exists) {
619 std::string fn;
620 std::string cn;
621 std::string dname;
Sami Tolvanen90221202014-12-09 16:39:47 +0000622 int fd = -1;
623 int rc = -1;
Tao Baodc392262015-07-31 15:56:44 -0700624 int dfd = -1;
Sami Tolvanen90221202014-12-09 16:39:47 +0000625 int res;
Sami Tolvanen43b748f2015-04-17 12:50:31 +0100626 struct stat st;
Sami Tolvanen90221202014-12-09 16:39:47 +0000627
Tao Baoe6aa3322015-08-05 15:20:27 -0700628 if (base.empty() || buffer == NULL) {
Sami Tolvanen90221202014-12-09 16:39:47 +0000629 goto wsout;
630 }
631
632 if (checkspace && CacheSizeCheck(blocks * BLOCKSIZE) != 0) {
633 fprintf(stderr, "not enough space to write stash\n");
634 goto wsout;
635 }
636
Tao Baoe6aa3322015-08-05 15:20:27 -0700637 fn = GetStashFileName(base, std::string(id), ".partial");
638 cn = GetStashFileName(base, std::string(id), "");
Sami Tolvanen90221202014-12-09 16:39:47 +0000639
Sami Tolvanen43b748f2015-04-17 12:50:31 +0100640 if (exists) {
Tao Baoe6aa3322015-08-05 15:20:27 -0700641 res = stat(cn.c_str(), &st);
Sami Tolvanen43b748f2015-04-17 12:50:31 +0100642
643 if (res == 0) {
644 // The file already exists and since the name is the hash of the contents,
645 // it's safe to assume the contents are identical (accidental hash collisions
646 // are unlikely)
Tao Baoe6aa3322015-08-05 15:20:27 -0700647 fprintf(stderr, " skipping %d existing blocks in %s\n", blocks, cn.c_str());
Sami Tolvanen43b748f2015-04-17 12:50:31 +0100648 *exists = 1;
649 rc = 0;
650 goto wsout;
651 }
652
653 *exists = 0;
654 }
655
Tao Baoe6aa3322015-08-05 15:20:27 -0700656 fprintf(stderr, " writing %d blocks to %s\n", blocks, cn.c_str());
Sami Tolvanen90221202014-12-09 16:39:47 +0000657
Tao Baoe6aa3322015-08-05 15:20:27 -0700658 fd = TEMP_FAILURE_RETRY(open(fn.c_str(), O_WRONLY | O_CREAT | O_TRUNC, STASH_FILE_MODE));
Sami Tolvanen90221202014-12-09 16:39:47 +0000659
660 if (fd == -1) {
Tao Baoe6aa3322015-08-05 15:20:27 -0700661 fprintf(stderr, "failed to create \"%s\": %s\n", fn.c_str(), strerror(errno));
Sami Tolvanen90221202014-12-09 16:39:47 +0000662 goto wsout;
663 }
664
665 if (write_all(fd, buffer, blocks * BLOCKSIZE) == -1) {
666 goto wsout;
667 }
668
669 if (fsync(fd) == -1) {
Tao Baoe6aa3322015-08-05 15:20:27 -0700670 fprintf(stderr, "fsync \"%s\" failed: %s\n", fn.c_str(), strerror(errno));
Sami Tolvanen90221202014-12-09 16:39:47 +0000671 goto wsout;
672 }
673
Tao Baoe6aa3322015-08-05 15:20:27 -0700674 if (rename(fn.c_str(), cn.c_str()) == -1) {
675 fprintf(stderr, "rename(\"%s\", \"%s\") failed: %s\n", fn.c_str(), cn.c_str(),
676 strerror(errno));
Sami Tolvanen90221202014-12-09 16:39:47 +0000677 goto wsout;
678 }
679
Tao Baoe6aa3322015-08-05 15:20:27 -0700680 dname = GetStashFileName(base, "", "");
681 dfd = TEMP_FAILURE_RETRY(open(dname.c_str(), O_RDONLY | O_DIRECTORY));
Tao Baodc392262015-07-31 15:56:44 -0700682
683 if (dfd == -1) {
Tao Baoe6aa3322015-08-05 15:20:27 -0700684 fprintf(stderr, "failed to open \"%s\" failed: %s\n", dname.c_str(), strerror(errno));
Tao Baodc392262015-07-31 15:56:44 -0700685 goto wsout;
686 }
687
688 if (fsync(dfd) == -1) {
Tao Baoe6aa3322015-08-05 15:20:27 -0700689 fprintf(stderr, "fsync \"%s\" failed: %s\n", dname.c_str(), strerror(errno));
Tao Baodc392262015-07-31 15:56:44 -0700690 goto wsout;
691 }
692
Sami Tolvanen90221202014-12-09 16:39:47 +0000693 rc = 0;
694
695wsout:
696 if (fd != -1) {
Elliott Hughesb47afed2015-05-15 16:19:20 -0700697 close(fd);
Sami Tolvanen90221202014-12-09 16:39:47 +0000698 }
699
Tao Baodc392262015-07-31 15:56:44 -0700700 if (dfd != -1) {
701 close(dfd);
702 }
703
Sami Tolvanen90221202014-12-09 16:39:47 +0000704 return rc;
705}
706
707// Creates a directory for storing stash files and checks if the /cache partition
708// hash enough space for the expected amount of blocks we need to store. Returns
709// >0 if we created the directory, zero if it existed already, and <0 of failure.
710
Tao Baoe6aa3322015-08-05 15:20:27 -0700711static int CreateStash(State* state, int maxblocks, const char* blockdev,
712 std::string& base) {
713 if (blockdev == NULL) {
714 return -1;
Sami Tolvanen90221202014-12-09 16:39:47 +0000715 }
716
717 // Stash directory should be different for each partition to avoid conflicts
718 // when updating multiple partitions at the same time, so we use the hash of
719 // the block device name as the base directory
Tao Baoe6aa3322015-08-05 15:20:27 -0700720 SHA_CTX ctx;
Sami Tolvanen90221202014-12-09 16:39:47 +0000721 SHA_init(&ctx);
722 SHA_update(&ctx, blockdev, strlen(blockdev));
Tao Baoe6aa3322015-08-05 15:20:27 -0700723 const uint8_t* digest = SHA_final(&ctx);
724 base = print_sha1(digest);
Sami Tolvanen90221202014-12-09 16:39:47 +0000725
Tao Baoe6aa3322015-08-05 15:20:27 -0700726 std::string dirname = GetStashFileName(base, "", "");
727 struct stat st;
728 int res = stat(dirname.c_str(), &st);
Sami Tolvanen90221202014-12-09 16:39:47 +0000729
730 if (res == -1 && errno != ENOENT) {
Tao Baoe6aa3322015-08-05 15:20:27 -0700731 ErrorAbort(state, "stat \"%s\" failed: %s\n", dirname.c_str(), strerror(errno));
732 return -1;
Sami Tolvanen90221202014-12-09 16:39:47 +0000733 } else if (res != 0) {
Tao Baoe6aa3322015-08-05 15:20:27 -0700734 fprintf(stderr, "creating stash %s\n", dirname.c_str());
735 res = mkdir(dirname.c_str(), STASH_DIRECTORY_MODE);
Sami Tolvanen90221202014-12-09 16:39:47 +0000736
737 if (res != 0) {
Tao Baoe6aa3322015-08-05 15:20:27 -0700738 ErrorAbort(state, "mkdir \"%s\" failed: %s\n", dirname.c_str(), strerror(errno));
739 return -1;
Sami Tolvanen90221202014-12-09 16:39:47 +0000740 }
741
742 if (CacheSizeCheck(maxblocks * BLOCKSIZE) != 0) {
743 ErrorAbort(state, "not enough space for stash\n");
Tao Baoe6aa3322015-08-05 15:20:27 -0700744 return -1;
Sami Tolvanen90221202014-12-09 16:39:47 +0000745 }
746
Tao Baoe6aa3322015-08-05 15:20:27 -0700747 return 1; // Created directory
Sami Tolvanen90221202014-12-09 16:39:47 +0000748 }
749
Tao Baoe6aa3322015-08-05 15:20:27 -0700750 fprintf(stderr, "using existing stash %s\n", dirname.c_str());
Sami Tolvanen90221202014-12-09 16:39:47 +0000751
752 // If the directory already exists, calculate the space already allocated to
753 // stash files and check if there's enough for all required blocks. Delete any
754 // partially completed stash files first.
755
756 EnumerateStash(dirname, DeletePartial, NULL);
Tao Baoe6aa3322015-08-05 15:20:27 -0700757 int size = 0;
Sami Tolvanen90221202014-12-09 16:39:47 +0000758 EnumerateStash(dirname, UpdateFileSize, &size);
759
760 size = (maxblocks * BLOCKSIZE) - size;
761
762 if (size > 0 && CacheSizeCheck(size) != 0) {
763 ErrorAbort(state, "not enough space for stash (%d more needed)\n", size);
Tao Baoe6aa3322015-08-05 15:20:27 -0700764 return -1;
Sami Tolvanen90221202014-12-09 16:39:47 +0000765 }
766
Tao Baoe6aa3322015-08-05 15:20:27 -0700767 return 0; // Using existing directory
Sami Tolvanen90221202014-12-09 16:39:47 +0000768}
769
Tao Baoe6aa3322015-08-05 15:20:27 -0700770static int SaveStash(const std::string& base, char** wordsave, uint8_t** buffer,
Tao Bao9739a292015-08-05 12:16:20 -0700771 size_t* buffer_alloc, int fd, bool usehash) {
772 if (!wordsave || !buffer || !buffer_alloc) {
Sami Tolvanen90221202014-12-09 16:39:47 +0000773 return -1;
774 }
775
Tao Baoe6aa3322015-08-05 15:20:27 -0700776 char *id = strtok_r(NULL, " ", wordsave);
Sami Tolvanen90221202014-12-09 16:39:47 +0000777 if (id == NULL) {
778 fprintf(stderr, "missing id field in stash command\n");
779 return -1;
780 }
781
Tao Baoe6aa3322015-08-05 15:20:27 -0700782 int blocks = 0;
783 if (usehash && LoadStash(base, id, 1, &blocks, buffer, buffer_alloc, false) == 0) {
Sami Tolvanen90221202014-12-09 16:39:47 +0000784 // Stash file already exists and has expected contents. Do not
785 // read from source again, as the source may have been already
786 // overwritten during a previous attempt.
787 return 0;
788 }
789
790 if (LoadSrcTgtVersion1(wordsave, NULL, &blocks, buffer, buffer_alloc, fd) == -1) {
791 return -1;
792 }
793
Tao Baoe6aa3322015-08-05 15:20:27 -0700794 if (usehash && VerifyBlocks(id, *buffer, blocks, true) != 0) {
Sami Tolvanen90221202014-12-09 16:39:47 +0000795 // Source blocks have unexpected contents. If we actually need this
796 // data later, this is an unrecoverable error. However, the command
797 // that uses the data may have already completed previously, so the
798 // possible failure will occur during source block verification.
799 fprintf(stderr, "failed to load source blocks for stash %s\n", id);
800 return 0;
801 }
802
803 fprintf(stderr, "stashing %d blocks to %s\n", blocks, id);
Tao Baoe6aa3322015-08-05 15:20:27 -0700804 return WriteStash(base, id, blocks, *buffer, false, NULL);
Sami Tolvanen90221202014-12-09 16:39:47 +0000805}
806
Tao Baoe6aa3322015-08-05 15:20:27 -0700807static int FreeStash(const std::string& base, const char* id) {
808 if (base.empty() || id == NULL) {
Sami Tolvanen90221202014-12-09 16:39:47 +0000809 return -1;
810 }
811
Tao Baoe6aa3322015-08-05 15:20:27 -0700812 std::string fn = GetStashFileName(base, std::string(id), "");
Sami Tolvanen90221202014-12-09 16:39:47 +0000813
814 DeleteFile(fn, NULL);
Sami Tolvanen90221202014-12-09 16:39:47 +0000815
816 return 0;
Doug Zongker52ae67d2014-09-08 12:22:09 -0700817}
818
819static void MoveRange(uint8_t* dest, RangeSet* locs, const uint8_t* source) {
820 // source contains packed data, which we want to move to the
821 // locations given in *locs in the dest buffer. source and dest
822 // may be the same buffer.
823
824 int start = locs->size;
825 int i;
826 for (i = locs->count-1; i >= 0; --i) {
827 int blocks = locs->pos[i*2+1] - locs->pos[i*2];
828 start -= blocks;
829 memmove(dest + (locs->pos[i*2] * BLOCKSIZE), source + (start * BLOCKSIZE),
830 blocks * BLOCKSIZE);
831 }
832}
833
834// Do a source/target load for move/bsdiff/imgdiff in version 2.
835// 'wordsave' is the save_ptr of a strtok_r()-in-progress. We expect
836// to parse the remainder of the string as one of:
837//
838// <tgt_range> <src_block_count> <src_range>
839// (loads data from source image only)
840//
841// <tgt_range> <src_block_count> - <[stash_id:stash_range] ...>
842// (loads data from stashes only)
843//
844// <tgt_range> <src_block_count> <src_range> <src_loc> <[stash_id:stash_range] ...>
845// (loads data from both source image and stashes)
846//
847// On return, buffer is filled with the loaded source data (rearranged
848// and combined with stashed data as necessary). buffer may be
849// reallocated if needed to accommodate the source data. *tgt is the
Sami Tolvanen90221202014-12-09 16:39:47 +0000850// target RangeSet. Any stashes required are loaded using LoadStash.
Doug Zongker52ae67d2014-09-08 12:22:09 -0700851
Sami Tolvanen90221202014-12-09 16:39:47 +0000852static int LoadSrcTgtVersion2(char** wordsave, RangeSet** tgt, int* src_blocks,
Tao Baoe6aa3322015-08-05 15:20:27 -0700853 uint8_t** buffer, size_t* buffer_alloc, int fd,
854 const std::string& stashbase, bool* overlap) {
Doug Zongker52ae67d2014-09-08 12:22:09 -0700855 char* word;
Sami Tolvanen90221202014-12-09 16:39:47 +0000856 char* colonsave;
857 char* colon;
Sami Tolvanen90221202014-12-09 16:39:47 +0000858 int res;
859 RangeSet* locs;
860 size_t stashalloc = 0;
861 uint8_t* stash = NULL;
Doug Zongker52ae67d2014-09-08 12:22:09 -0700862
863 if (tgt != NULL) {
Sami Tolvanen90221202014-12-09 16:39:47 +0000864 word = strtok_r(NULL, " ", wordsave);
Doug Zongker52ae67d2014-09-08 12:22:09 -0700865 *tgt = parse_range(word);
866 }
867
Sami Tolvanen90221202014-12-09 16:39:47 +0000868 word = strtok_r(NULL, " ", wordsave);
Doug Zongker52ae67d2014-09-08 12:22:09 -0700869 *src_blocks = strtol(word, NULL, 0);
870
871 allocate(*src_blocks * BLOCKSIZE, buffer, buffer_alloc);
872
Sami Tolvanen90221202014-12-09 16:39:47 +0000873 word = strtok_r(NULL, " ", wordsave);
Doug Zongker52ae67d2014-09-08 12:22:09 -0700874 if (word[0] == '-' && word[1] == '\0') {
875 // no source ranges, only stashes
876 } else {
877 RangeSet* src = parse_range(word);
Sami Tolvanen90221202014-12-09 16:39:47 +0000878 res = ReadBlocks(src, *buffer, fd);
Doug Zongker52ae67d2014-09-08 12:22:09 -0700879
Sami Tolvanen90221202014-12-09 16:39:47 +0000880 if (overlap && tgt) {
Tao Baoe6aa3322015-08-05 15:20:27 -0700881 *overlap = range_overlaps(*src, **tgt);
Doug Zongker52ae67d2014-09-08 12:22:09 -0700882 }
Sami Tolvanen90221202014-12-09 16:39:47 +0000883
Doug Zongker52ae67d2014-09-08 12:22:09 -0700884 free(src);
885
Sami Tolvanen90221202014-12-09 16:39:47 +0000886 if (res == -1) {
887 return -1;
Doug Zongker52ae67d2014-09-08 12:22:09 -0700888 }
889
Sami Tolvanen90221202014-12-09 16:39:47 +0000890 word = strtok_r(NULL, " ", wordsave);
891 if (word == NULL) {
892 // no stashes, only source range
893 return 0;
894 }
895
896 locs = parse_range(word);
Doug Zongker52ae67d2014-09-08 12:22:09 -0700897 MoveRange(*buffer, locs, *buffer);
Sami Tolvanen90221202014-12-09 16:39:47 +0000898 free(locs);
Doug Zongker52ae67d2014-09-08 12:22:09 -0700899 }
900
Sami Tolvanen90221202014-12-09 16:39:47 +0000901 while ((word = strtok_r(NULL, " ", wordsave)) != NULL) {
Doug Zongker52ae67d2014-09-08 12:22:09 -0700902 // Each word is a an index into the stash table, a colon, and
903 // then a rangeset describing where in the source block that
904 // stashed data should go.
Sami Tolvanen90221202014-12-09 16:39:47 +0000905 colonsave = NULL;
906 colon = strtok_r(word, ":", &colonsave);
907
Tao Baoe6aa3322015-08-05 15:20:27 -0700908 res = LoadStash(stashbase, colon, 0, NULL, &stash, &stashalloc, true);
Sami Tolvanen90221202014-12-09 16:39:47 +0000909
910 if (res == -1) {
911 // These source blocks will fail verification if used later, but we
912 // will let the caller decide if this is a fatal failure
913 fprintf(stderr, "failed to load stash %s\n", colon);
914 continue;
915 }
916
Doug Zongker52ae67d2014-09-08 12:22:09 -0700917 colon = strtok_r(NULL, ":", &colonsave);
Sami Tolvanen90221202014-12-09 16:39:47 +0000918 locs = parse_range(colon);
919
920 MoveRange(*buffer, locs, stash);
Doug Zongker52ae67d2014-09-08 12:22:09 -0700921 free(locs);
922 }
Sami Tolvanen90221202014-12-09 16:39:47 +0000923
924 if (stash) {
925 free(stash);
926 }
927
928 return 0;
929}
930
931// Parameters for transfer list command functions
932typedef struct {
933 char* cmdname;
934 char* cpos;
935 char* freestash;
Tao Baoe6aa3322015-08-05 15:20:27 -0700936 std::string stashbase;
937 bool canwrite;
Sami Tolvanen90221202014-12-09 16:39:47 +0000938 int createdstash;
939 int fd;
940 int foundwrites;
Tao Baoe6aa3322015-08-05 15:20:27 -0700941 bool isunresumable;
Sami Tolvanen90221202014-12-09 16:39:47 +0000942 int version;
943 int written;
944 NewThreadInfo nti;
945 pthread_t thread;
946 size_t bufsize;
947 uint8_t* buffer;
948 uint8_t* patch_start;
949} CommandParameters;
950
951// Do a source/target load for move/bsdiff/imgdiff in version 3.
952//
953// Parameters are the same as for LoadSrcTgtVersion2, except for 'onehash', which
954// tells the function whether to expect separate source and targe block hashes, or
955// if they are both the same and only one hash should be expected, and
956// 'isunresumable', which receives a non-zero value if block verification fails in
957// a way that the update cannot be resumed anymore.
958//
959// If the function is unable to load the necessary blocks or their contents don't
960// match the hashes, the return value is -1 and the command should be aborted.
961//
962// If the return value is 1, the command has already been completed according to
963// the contents of the target blocks, and should not be performed again.
964//
965// If the return value is 0, source blocks have expected content and the command
966// can be performed.
967
968static int LoadSrcTgtVersion3(CommandParameters* params, RangeSet** tgt, int* src_blocks,
Tao Baoe6aa3322015-08-05 15:20:27 -0700969 int onehash, bool* overlap) {
Sami Tolvanen90221202014-12-09 16:39:47 +0000970 char* srchash = NULL;
971 char* tgthash = NULL;
Sami Tolvanen43b748f2015-04-17 12:50:31 +0100972 int stash_exists = 0;
Sami Tolvanen90221202014-12-09 16:39:47 +0000973 int rc = -1;
974 uint8_t* tgtbuffer = NULL;
975
976 if (!params|| !tgt || !src_blocks || !overlap) {
977 goto v3out;
978 }
979
980 srchash = strtok_r(NULL, " ", &params->cpos);
981
982 if (srchash == NULL) {
983 fprintf(stderr, "missing source hash\n");
984 goto v3out;
985 }
986
987 if (onehash) {
988 tgthash = srchash;
989 } else {
990 tgthash = strtok_r(NULL, " ", &params->cpos);
991
992 if (tgthash == NULL) {
993 fprintf(stderr, "missing target hash\n");
994 goto v3out;
995 }
996 }
997
998 if (LoadSrcTgtVersion2(&params->cpos, tgt, src_blocks, &params->buffer, &params->bufsize,
999 params->fd, params->stashbase, overlap) == -1) {
1000 goto v3out;
1001 }
1002
1003 tgtbuffer = (uint8_t*) malloc((*tgt)->size * BLOCKSIZE);
1004
1005 if (tgtbuffer == NULL) {
1006 fprintf(stderr, "failed to allocate %d bytes\n", (*tgt)->size * BLOCKSIZE);
1007 goto v3out;
1008 }
1009
1010 if (ReadBlocks(*tgt, tgtbuffer, params->fd) == -1) {
1011 goto v3out;
1012 }
1013
Tao Baoe6aa3322015-08-05 15:20:27 -07001014 if (VerifyBlocks(tgthash, tgtbuffer, (*tgt)->size, false) == 0) {
Sami Tolvanen90221202014-12-09 16:39:47 +00001015 // Target blocks already have expected content, command should be skipped
1016 rc = 1;
1017 goto v3out;
1018 }
1019
Tao Baoe6aa3322015-08-05 15:20:27 -07001020 if (VerifyBlocks(srchash, params->buffer, *src_blocks, true) == 0) {
Sami Tolvanen90221202014-12-09 16:39:47 +00001021 // If source and target blocks overlap, stash the source blocks so we can
1022 // resume from possible write errors
1023 if (*overlap) {
1024 fprintf(stderr, "stashing %d overlapping blocks to %s\n", *src_blocks,
1025 srchash);
1026
Tao Baoe6aa3322015-08-05 15:20:27 -07001027 if (WriteStash(params->stashbase, srchash, *src_blocks, params->buffer, true,
Sami Tolvanen43b748f2015-04-17 12:50:31 +01001028 &stash_exists) != 0) {
Sami Tolvanen90221202014-12-09 16:39:47 +00001029 fprintf(stderr, "failed to stash overlapping source blocks\n");
1030 goto v3out;
1031 }
1032
1033 // Can be deleted when the write has completed
Sami Tolvanen43b748f2015-04-17 12:50:31 +01001034 if (!stash_exists) {
1035 params->freestash = srchash;
1036 }
Sami Tolvanen90221202014-12-09 16:39:47 +00001037 }
1038
1039 // Source blocks have expected content, command can proceed
1040 rc = 0;
1041 goto v3out;
1042 }
1043
1044 if (*overlap && LoadStash(params->stashbase, srchash, 1, NULL, &params->buffer,
Tao Baoe6aa3322015-08-05 15:20:27 -07001045 &params->bufsize, true) == 0) {
Sami Tolvanen43b748f2015-04-17 12:50:31 +01001046 // Overlapping source blocks were previously stashed, command can proceed.
1047 // We are recovering from an interrupted command, so we don't know if the
1048 // stash can safely be deleted after this command.
Sami Tolvanen90221202014-12-09 16:39:47 +00001049 rc = 0;
1050 goto v3out;
1051 }
1052
1053 // Valid source data not available, update cannot be resumed
1054 fprintf(stderr, "partition has unexpected contents\n");
Tao Baoe6aa3322015-08-05 15:20:27 -07001055 params->isunresumable = true;
Sami Tolvanen90221202014-12-09 16:39:47 +00001056
1057v3out:
1058 if (tgtbuffer) {
1059 free(tgtbuffer);
1060 }
1061
1062 return rc;
1063}
1064
1065static int PerformCommandMove(CommandParameters* params) {
1066 int blocks = 0;
Tao Baoe6aa3322015-08-05 15:20:27 -07001067 bool overlap = false;
Sami Tolvanen90221202014-12-09 16:39:47 +00001068 int rc = -1;
1069 int status = 0;
1070 RangeSet* tgt = NULL;
1071
1072 if (!params) {
1073 goto pcmout;
1074 }
1075
1076 if (params->version == 1) {
1077 status = LoadSrcTgtVersion1(&params->cpos, &tgt, &blocks, &params->buffer,
1078 &params->bufsize, params->fd);
1079 } else if (params->version == 2) {
1080 status = LoadSrcTgtVersion2(&params->cpos, &tgt, &blocks, &params->buffer,
1081 &params->bufsize, params->fd, params->stashbase, NULL);
1082 } else if (params->version >= 3) {
1083 status = LoadSrcTgtVersion3(params, &tgt, &blocks, 1, &overlap);
1084 }
1085
1086 if (status == -1) {
1087 fprintf(stderr, "failed to read blocks for move\n");
1088 goto pcmout;
1089 }
1090
1091 if (status == 0) {
1092 params->foundwrites = 1;
1093 } else if (params->foundwrites) {
1094 fprintf(stderr, "warning: commands executed out of order [%s]\n", params->cmdname);
1095 }
1096
1097 if (params->canwrite) {
1098 if (status == 0) {
1099 fprintf(stderr, " moving %d blocks\n", blocks);
1100
1101 if (WriteBlocks(tgt, params->buffer, params->fd) == -1) {
1102 goto pcmout;
1103 }
1104 } else {
1105 fprintf(stderr, "skipping %d already moved blocks\n", blocks);
1106 }
1107
1108 }
1109
1110 if (params->freestash) {
1111 FreeStash(params->stashbase, params->freestash);
1112 params->freestash = NULL;
1113 }
1114
1115 params->written += tgt->size;
1116 rc = 0;
1117
1118pcmout:
1119 if (tgt) {
1120 free(tgt);
1121 }
1122
1123 return rc;
1124}
1125
1126static int PerformCommandStash(CommandParameters* params) {
1127 if (!params) {
1128 return -1;
1129 }
1130
1131 return SaveStash(params->stashbase, &params->cpos, &params->buffer, &params->bufsize,
Tao Bao9739a292015-08-05 12:16:20 -07001132 params->fd, (params->version >= 3));
Sami Tolvanen90221202014-12-09 16:39:47 +00001133}
1134
1135static int PerformCommandFree(CommandParameters* params) {
1136 if (!params) {
1137 return -1;
1138 }
1139
1140 if (params->createdstash || params->canwrite) {
1141 return FreeStash(params->stashbase, params->cpos);
1142 }
1143
1144 return 0;
1145}
1146
1147static int PerformCommandZero(CommandParameters* params) {
1148 char* range = NULL;
Sami Tolvanen90221202014-12-09 16:39:47 +00001149 int rc = -1;
1150 RangeSet* tgt = NULL;
1151
1152 if (!params) {
1153 goto pczout;
1154 }
1155
1156 range = strtok_r(NULL, " ", &params->cpos);
1157
1158 if (range == NULL) {
1159 fprintf(stderr, "missing target blocks for zero\n");
1160 goto pczout;
1161 }
1162
1163 tgt = parse_range(range);
1164
1165 fprintf(stderr, " zeroing %d blocks\n", tgt->size);
1166
1167 allocate(BLOCKSIZE, &params->buffer, &params->bufsize);
1168 memset(params->buffer, 0, BLOCKSIZE);
1169
1170 if (params->canwrite) {
Shrinivas Sahukara6153df2015-08-19 13:01:45 +05301171 for (size_t i = 0; i < tgt->count; ++i) {
Elliott Hughes7bad7c42015-04-28 17:24:24 -07001172 if (!check_lseek(params->fd, (off64_t) tgt->pos[i * 2] * BLOCKSIZE, SEEK_SET)) {
Sami Tolvanen90221202014-12-09 16:39:47 +00001173 goto pczout;
1174 }
1175
Shrinivas Sahukara6153df2015-08-19 13:01:45 +05301176 for (size_t j = tgt->pos[i * 2]; j < tgt->pos[i * 2 + 1]; ++j) {
Sami Tolvanen90221202014-12-09 16:39:47 +00001177 if (write_all(params->fd, params->buffer, BLOCKSIZE) == -1) {
1178 goto pczout;
1179 }
1180 }
1181 }
1182 }
1183
1184 if (params->cmdname[0] == 'z') {
Sami Tolvanene82fa182015-06-10 15:58:12 +00001185 // Update only for the zero command, as the erase command will call
1186 // this if DEBUG_ERASE is defined.
Sami Tolvanen90221202014-12-09 16:39:47 +00001187 params->written += tgt->size;
1188 }
1189
1190 rc = 0;
1191
1192pczout:
1193 if (tgt) {
1194 free(tgt);
1195 }
1196
1197 return rc;
1198}
1199
1200static int PerformCommandNew(CommandParameters* params) {
1201 char* range = NULL;
1202 int rc = -1;
1203 RangeSet* tgt = NULL;
1204 RangeSinkState rss;
1205
1206 if (!params) {
1207 goto pcnout;
1208 }
1209
1210 range = strtok_r(NULL, " ", &params->cpos);
1211
1212 if (range == NULL) {
1213 goto pcnout;
1214 }
1215
1216 tgt = parse_range(range);
1217
1218 if (params->canwrite) {
1219 fprintf(stderr, " writing %d blocks of new data\n", tgt->size);
1220
1221 rss.fd = params->fd;
1222 rss.tgt = tgt;
1223 rss.p_block = 0;
1224 rss.p_remain = (tgt->pos[1] - tgt->pos[0]) * BLOCKSIZE;
1225
Elliott Hughes7bad7c42015-04-28 17:24:24 -07001226 if (!check_lseek(params->fd, (off64_t) tgt->pos[0] * BLOCKSIZE, SEEK_SET)) {
Sami Tolvanen90221202014-12-09 16:39:47 +00001227 goto pcnout;
1228 }
1229
1230 pthread_mutex_lock(&params->nti.mu);
1231 params->nti.rss = &rss;
1232 pthread_cond_broadcast(&params->nti.cv);
1233
1234 while (params->nti.rss) {
1235 pthread_cond_wait(&params->nti.cv, &params->nti.mu);
1236 }
1237
1238 pthread_mutex_unlock(&params->nti.mu);
1239 }
1240
1241 params->written += tgt->size;
1242 rc = 0;
1243
1244pcnout:
1245 if (tgt) {
1246 free(tgt);
1247 }
1248
1249 return rc;
1250}
1251
1252static int PerformCommandDiff(CommandParameters* params) {
1253 char* logparams = NULL;
1254 char* value = NULL;
1255 int blocks = 0;
Tao Baoe6aa3322015-08-05 15:20:27 -07001256 bool overlap = false;
Sami Tolvanen90221202014-12-09 16:39:47 +00001257 int rc = -1;
1258 int status = 0;
1259 RangeSet* tgt = NULL;
1260 RangeSinkState rss;
1261 size_t len = 0;
1262 size_t offset = 0;
1263 Value patch_value;
1264
1265 if (!params) {
1266 goto pcdout;
1267 }
1268
1269 logparams = strdup(params->cpos);
1270 value = strtok_r(NULL, " ", &params->cpos);
1271
1272 if (value == NULL) {
1273 fprintf(stderr, "missing patch offset for %s\n", params->cmdname);
1274 goto pcdout;
1275 }
1276
1277 offset = strtoul(value, NULL, 0);
1278
1279 value = strtok_r(NULL, " ", &params->cpos);
1280
1281 if (value == NULL) {
1282 fprintf(stderr, "missing patch length for %s\n", params->cmdname);
1283 goto pcdout;
1284 }
1285
1286 len = strtoul(value, NULL, 0);
1287
1288 if (params->version == 1) {
1289 status = LoadSrcTgtVersion1(&params->cpos, &tgt, &blocks, &params->buffer,
1290 &params->bufsize, params->fd);
1291 } else if (params->version == 2) {
1292 status = LoadSrcTgtVersion2(&params->cpos, &tgt, &blocks, &params->buffer,
1293 &params->bufsize, params->fd, params->stashbase, NULL);
1294 } else if (params->version >= 3) {
1295 status = LoadSrcTgtVersion3(params, &tgt, &blocks, 0, &overlap);
1296 }
1297
1298 if (status == -1) {
1299 fprintf(stderr, "failed to read blocks for diff\n");
1300 goto pcdout;
1301 }
1302
1303 if (status == 0) {
1304 params->foundwrites = 1;
1305 } else if (params->foundwrites) {
1306 fprintf(stderr, "warning: commands executed out of order [%s]\n", params->cmdname);
1307 }
1308
1309 if (params->canwrite) {
1310 if (status == 0) {
1311 fprintf(stderr, "patching %d blocks to %d\n", blocks, tgt->size);
1312
1313 patch_value.type = VAL_BLOB;
1314 patch_value.size = len;
1315 patch_value.data = (char*) (params->patch_start + offset);
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 pcdout;
1324 }
1325
1326 if (params->cmdname[0] == 'i') { // imgdiff
1327 ApplyImagePatch(params->buffer, blocks * BLOCKSIZE, &patch_value,
1328 &RangeSinkWrite, &rss, NULL, NULL);
1329 } else {
1330 ApplyBSDiffPatch(params->buffer, blocks * BLOCKSIZE, &patch_value,
1331 0, &RangeSinkWrite, &rss, NULL);
1332 }
1333
1334 // We expect the output of the patcher to fill the tgt ranges exactly.
1335 if (rss.p_block != tgt->count || rss.p_remain != 0) {
1336 fprintf(stderr, "range sink underrun?\n");
1337 }
1338 } else {
1339 fprintf(stderr, "skipping %d blocks already patched to %d [%s]\n",
1340 blocks, tgt->size, logparams);
1341 }
1342 }
1343
1344 if (params->freestash) {
1345 FreeStash(params->stashbase, params->freestash);
1346 params->freestash = NULL;
1347 }
1348
1349 params->written += tgt->size;
1350 rc = 0;
1351
1352pcdout:
1353 if (logparams) {
1354 free(logparams);
1355 }
1356
1357 if (tgt) {
1358 free(tgt);
1359 }
1360
1361 return rc;
1362}
1363
1364static int PerformCommandErase(CommandParameters* params) {
1365 char* range = NULL;
Sami Tolvanen90221202014-12-09 16:39:47 +00001366 int rc = -1;
1367 RangeSet* tgt = NULL;
1368 struct stat st;
1369 uint64_t blocks[2];
1370
Sami Tolvanene82fa182015-06-10 15:58:12 +00001371 if (DEBUG_ERASE) {
1372 return PerformCommandZero(params);
Sami Tolvanen90221202014-12-09 16:39:47 +00001373 }
1374
1375 if (!params) {
1376 goto pceout;
1377 }
1378
1379 if (fstat(params->fd, &st) == -1) {
Elliott Hughes1fdd4522015-03-23 13:33:57 -07001380 fprintf(stderr, "failed to fstat device to erase: %s\n", strerror(errno));
Sami Tolvanen90221202014-12-09 16:39:47 +00001381 goto pceout;
1382 }
1383
1384 if (!S_ISBLK(st.st_mode)) {
1385 fprintf(stderr, "not a block device; skipping erase\n");
Sami Tolvanen90221202014-12-09 16:39:47 +00001386 goto pceout;
1387 }
1388
1389 range = strtok_r(NULL, " ", &params->cpos);
1390
1391 if (range == NULL) {
Tao Baoba9a42a2015-06-23 23:23:33 -07001392 fprintf(stderr, "missing target blocks for erase\n");
Sami Tolvanen90221202014-12-09 16:39:47 +00001393 goto pceout;
1394 }
1395
1396 tgt = parse_range(range);
1397
1398 if (params->canwrite) {
1399 fprintf(stderr, " erasing %d blocks\n", tgt->size);
1400
Shrinivas Sahukara6153df2015-08-19 13:01:45 +05301401 for (size_t i = 0; i < tgt->count; ++i) {
Sami Tolvanen90221202014-12-09 16:39:47 +00001402 // offset in bytes
1403 blocks[0] = tgt->pos[i * 2] * (uint64_t) BLOCKSIZE;
1404 // length in bytes
1405 blocks[1] = (tgt->pos[i * 2 + 1] - tgt->pos[i * 2]) * (uint64_t) BLOCKSIZE;
1406
1407 if (ioctl(params->fd, BLKDISCARD, &blocks) == -1) {
Elliott Hughes1fdd4522015-03-23 13:33:57 -07001408 fprintf(stderr, "BLKDISCARD ioctl failed: %s\n", strerror(errno));
Sami Tolvanencc2428c2015-04-27 11:24:29 +01001409 goto pceout;
Sami Tolvanen90221202014-12-09 16:39:47 +00001410 }
1411 }
1412 }
1413
1414 rc = 0;
1415
1416pceout:
1417 if (tgt) {
1418 free(tgt);
1419 }
1420
1421 return rc;
1422}
1423
1424// Definitions for transfer list command functions
1425typedef int (*CommandFunction)(CommandParameters*);
1426
1427typedef struct {
1428 const char* name;
1429 CommandFunction f;
1430} Command;
1431
1432// CompareCommands and CompareCommandNames are for the hash table
1433
1434static int CompareCommands(const void* c1, const void* c2) {
1435 return strcmp(((const Command*) c1)->name, ((const Command*) c2)->name);
1436}
1437
1438static int CompareCommandNames(const void* c1, const void* c2) {
1439 return strcmp(((const Command*) c1)->name, (const char*) c2);
1440}
1441
1442// HashString is used to hash command names for the hash table
1443
1444static unsigned int HashString(const char *s) {
1445 unsigned int hash = 0;
1446 if (s) {
1447 while (*s) {
1448 hash = hash * 33 + *s++;
1449 }
1450 }
1451 return hash;
Doug Zongker52ae67d2014-09-08 12:22:09 -07001452}
1453
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001454// args:
1455// - block device (or file) to modify in-place
1456// - transfer list (blob)
1457// - new data stream (filename within package.zip)
1458// - patch stream (filename within package.zip, must be uncompressed)
1459
Sami Tolvanen90221202014-12-09 16:39:47 +00001460static Value* PerformBlockImageUpdate(const char* name, State* state, int argc, Expr* argv[],
Tao Baoe6aa3322015-08-05 15:20:27 -07001461 const Command* commands, int cmdcount, bool dryrun) {
Sami Tolvanen90221202014-12-09 16:39:47 +00001462
1463 char* line = NULL;
1464 char* linesave = NULL;
1465 char* logcmd = NULL;
Doug Zongker1d5d6092014-08-21 10:47:24 -07001466 char* transfer_list = NULL;
Sami Tolvanen90221202014-12-09 16:39:47 +00001467 CommandParameters params;
1468 const Command* cmd = NULL;
1469 const ZipEntry* new_entry = NULL;
1470 const ZipEntry* patch_entry = NULL;
1471 FILE* cmd_pipe = NULL;
1472 HashTable* cmdht = NULL;
1473 int i;
1474 int res;
1475 int rc = -1;
1476 int stash_max_blocks = 0;
1477 int total_blocks = 0;
1478 pthread_attr_t attr;
1479 unsigned int cmdhash;
1480 UpdaterInfo* ui = NULL;
1481 Value* blockdev_filename = NULL;
1482 Value* new_data_fn = NULL;
1483 Value* patch_data_fn = NULL;
1484 Value* transfer_list_value = NULL;
1485 ZipArchive* za = NULL;
1486
1487 memset(&params, 0, sizeof(params));
1488 params.canwrite = !dryrun;
1489
1490 fprintf(stderr, "performing %s\n", dryrun ? "verification" : "update");
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001491
Doug Zongker1d5d6092014-08-21 10:47:24 -07001492 if (ReadValueArgs(state, argv, 4, &blockdev_filename, &transfer_list_value,
Sami Tolvanen90221202014-12-09 16:39:47 +00001493 &new_data_fn, &patch_data_fn) < 0) {
1494 goto pbiudone;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001495 }
1496
1497 if (blockdev_filename->type != VAL_STRING) {
1498 ErrorAbort(state, "blockdev_filename argument to %s must be string", name);
Sami Tolvanen90221202014-12-09 16:39:47 +00001499 goto pbiudone;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001500 }
Doug Zongker1d5d6092014-08-21 10:47:24 -07001501 if (transfer_list_value->type != VAL_BLOB) {
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001502 ErrorAbort(state, "transfer_list argument to %s must be blob", name);
Sami Tolvanen90221202014-12-09 16:39:47 +00001503 goto pbiudone;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001504 }
1505 if (new_data_fn->type != VAL_STRING) {
1506 ErrorAbort(state, "new_data_fn argument to %s must be string", name);
Sami Tolvanen90221202014-12-09 16:39:47 +00001507 goto pbiudone;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001508 }
1509 if (patch_data_fn->type != VAL_STRING) {
1510 ErrorAbort(state, "patch_data_fn argument to %s must be string", name);
Sami Tolvanen90221202014-12-09 16:39:47 +00001511 goto pbiudone;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001512 }
1513
Sami Tolvanen90221202014-12-09 16:39:47 +00001514 ui = (UpdaterInfo*) state->cookie;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001515
Sami Tolvanen90221202014-12-09 16:39:47 +00001516 if (ui == NULL) {
1517 goto pbiudone;
1518 }
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001519
Sami Tolvanen90221202014-12-09 16:39:47 +00001520 cmd_pipe = ui->cmd_pipe;
1521 za = ui->package_zip;
1522
1523 if (cmd_pipe == NULL || za == NULL) {
1524 goto pbiudone;
1525 }
1526
1527 patch_entry = mzFindZipEntry(za, patch_data_fn->data);
1528
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001529 if (patch_entry == NULL) {
Sami Tolvanen90221202014-12-09 16:39:47 +00001530 fprintf(stderr, "%s(): no file \"%s\" in package", name, patch_data_fn->data);
1531 goto pbiudone;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001532 }
1533
Sami Tolvanen90221202014-12-09 16:39:47 +00001534 params.patch_start = ui->package_zip_addr + mzGetZipEntryOffset(patch_entry);
1535 new_entry = mzFindZipEntry(za, new_data_fn->data);
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001536
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001537 if (new_entry == NULL) {
Sami Tolvanen90221202014-12-09 16:39:47 +00001538 fprintf(stderr, "%s(): no file \"%s\" in package", name, new_data_fn->data);
1539 goto pbiudone;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001540 }
1541
Sami Tolvanen90221202014-12-09 16:39:47 +00001542 params.fd = TEMP_FAILURE_RETRY(open(blockdev_filename->data, O_RDWR));
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001543
Sami Tolvanen90221202014-12-09 16:39:47 +00001544 if (params.fd == -1) {
Elliott Hughes1fdd4522015-03-23 13:33:57 -07001545 fprintf(stderr, "open \"%s\" failed: %s\n", blockdev_filename->data, strerror(errno));
Sami Tolvanen90221202014-12-09 16:39:47 +00001546 goto pbiudone;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001547 }
1548
Sami Tolvanen90221202014-12-09 16:39:47 +00001549 if (params.canwrite) {
1550 params.nti.za = za;
1551 params.nti.entry = new_entry;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001552
Sami Tolvanen90221202014-12-09 16:39:47 +00001553 pthread_mutex_init(&params.nti.mu, NULL);
1554 pthread_cond_init(&params.nti.cv, NULL);
1555 pthread_attr_init(&attr);
1556 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
1557
Elliott Hughes1fdd4522015-03-23 13:33:57 -07001558 int error = pthread_create(&params.thread, &attr, unzip_new_data, &params.nti);
1559 if (error != 0) {
1560 fprintf(stderr, "pthread_create failed: %s\n", strerror(error));
Sami Tolvanen90221202014-12-09 16:39:47 +00001561 goto pbiudone;
1562 }
1563 }
1564
1565 // The data in transfer_list_value is not necessarily null-terminated, so we need
1566 // to copy it to a new buffer and add the null that strtok_r will need.
Tao Baoba9a42a2015-06-23 23:23:33 -07001567 transfer_list = reinterpret_cast<char*>(malloc(transfer_list_value->size + 1));
Sami Tolvanen90221202014-12-09 16:39:47 +00001568
Doug Zongker1d5d6092014-08-21 10:47:24 -07001569 if (transfer_list == NULL) {
1570 fprintf(stderr, "failed to allocate %zd bytes for transfer list\n",
Sami Tolvanen90221202014-12-09 16:39:47 +00001571 transfer_list_value->size + 1);
1572 goto pbiudone;
Doug Zongker1d5d6092014-08-21 10:47:24 -07001573 }
Sami Tolvanen90221202014-12-09 16:39:47 +00001574
Doug Zongker1d5d6092014-08-21 10:47:24 -07001575 memcpy(transfer_list, transfer_list_value->data, transfer_list_value->size);
1576 transfer_list[transfer_list_value->size] = '\0';
1577
Sami Tolvanen90221202014-12-09 16:39:47 +00001578 // First line in transfer list is the version number
Doug Zongker1d5d6092014-08-21 10:47:24 -07001579 line = strtok_r(transfer_list, "\n", &linesave);
Sami Tolvanen90221202014-12-09 16:39:47 +00001580 params.version = strtol(line, NULL, 0);
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001581
Sami Tolvanen90221202014-12-09 16:39:47 +00001582 if (params.version < 1 || params.version > 3) {
1583 fprintf(stderr, "unexpected transfer list version [%s]\n", line);
1584 goto pbiudone;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001585 }
1586
Sami Tolvanen90221202014-12-09 16:39:47 +00001587 fprintf(stderr, "blockimg version is %d\n", params.version);
1588
1589 // Second line in transfer list is the total number of blocks we expect to write
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001590 line = strtok_r(NULL, "\n", &linesave);
Sami Tolvanen90221202014-12-09 16:39:47 +00001591 total_blocks = strtol(line, NULL, 0);
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001592
Sami Tolvanen90221202014-12-09 16:39:47 +00001593 if (total_blocks < 0) {
1594 ErrorAbort(state, "unexpected block count [%s]\n", line);
1595 goto pbiudone;
1596 } else if (total_blocks == 0) {
1597 rc = 0;
1598 goto pbiudone;
1599 }
1600
1601 if (params.version >= 2) {
1602 // Third line is how many stash entries are needed simultaneously
Doug Zongker52ae67d2014-09-08 12:22:09 -07001603 line = strtok_r(NULL, "\n", &linesave);
Sami Tolvanen90221202014-12-09 16:39:47 +00001604 fprintf(stderr, "maximum stash entries %s\n", line);
Doug Zongker52ae67d2014-09-08 12:22:09 -07001605
Sami Tolvanen90221202014-12-09 16:39:47 +00001606 // Fourth line is the maximum number of blocks that will be stashed simultaneously
1607 line = strtok_r(NULL, "\n", &linesave);
1608 stash_max_blocks = strtol(line, NULL, 0);
1609
1610 if (stash_max_blocks < 0) {
1611 ErrorAbort(state, "unexpected maximum stash blocks [%s]\n", line);
1612 goto pbiudone;
Doug Zongker52ae67d2014-09-08 12:22:09 -07001613 }
1614
Jesse Zhao1df64d32015-02-17 17:09:23 -08001615 if (stash_max_blocks >= 0) {
Tao Baoe6aa3322015-08-05 15:20:27 -07001616 res = CreateStash(state, stash_max_blocks, blockdev_filename->data, params.stashbase);
Sami Tolvanen90221202014-12-09 16:39:47 +00001617
1618 if (res == -1) {
1619 goto pbiudone;
1620 }
1621
1622 params.createdstash = res;
1623 }
Doug Zongker52ae67d2014-09-08 12:22:09 -07001624 }
1625
Sami Tolvanen90221202014-12-09 16:39:47 +00001626 // Build a hash table of the available commands
1627 cmdht = mzHashTableCreate(cmdcount, NULL);
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001628
Sami Tolvanen90221202014-12-09 16:39:47 +00001629 for (i = 0; i < cmdcount; ++i) {
1630 cmdhash = HashString(commands[i].name);
1631 mzHashTableLookup(cmdht, cmdhash, (void*) &commands[i], CompareCommands, true);
1632 }
1633
1634 // Subsequent lines are all individual transfer commands
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001635 for (line = strtok_r(NULL, "\n", &linesave); line;
1636 line = strtok_r(NULL, "\n", &linesave)) {
Doug Zongker52ae67d2014-09-08 12:22:09 -07001637
Sami Tolvanen90221202014-12-09 16:39:47 +00001638 logcmd = strdup(line);
1639 params.cmdname = strtok_r(line, " ", &params.cpos);
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001640
Sami Tolvanen90221202014-12-09 16:39:47 +00001641 if (params.cmdname == NULL) {
1642 fprintf(stderr, "missing command [%s]\n", line);
1643 goto pbiudone;
1644 }
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001645
Sami Tolvanen90221202014-12-09 16:39:47 +00001646 cmdhash = HashString(params.cmdname);
1647 cmd = (const Command*) mzHashTableLookup(cmdht, cmdhash, params.cmdname,
1648 CompareCommandNames, false);
Doug Zongker52ae67d2014-09-08 12:22:09 -07001649
Sami Tolvanen90221202014-12-09 16:39:47 +00001650 if (cmd == NULL) {
1651 fprintf(stderr, "unexpected command [%s]\n", params.cmdname);
1652 goto pbiudone;
1653 }
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001654
Sami Tolvanen90221202014-12-09 16:39:47 +00001655 if (cmd->f != NULL && cmd->f(&params) == -1) {
1656 fprintf(stderr, "failed to execute command [%s]\n",
1657 logcmd ? logcmd : params.cmdname);
1658 goto pbiudone;
1659 }
1660
1661 if (logcmd) {
1662 free(logcmd);
1663 logcmd = NULL;
1664 }
1665
1666 if (params.canwrite) {
Tao Bao187efff2015-07-27 14:07:08 -07001667 if (fsync(params.fd) == -1) {
1668 fprintf(stderr, "fsync failed: %s\n", strerror(errno));
1669 goto pbiudone;
1670 }
Sami Tolvanen90221202014-12-09 16:39:47 +00001671 fprintf(cmd_pipe, "set_progress %.4f\n", (double) params.written / total_blocks);
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001672 fflush(cmd_pipe);
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001673 }
1674 }
1675
Sami Tolvanen90221202014-12-09 16:39:47 +00001676 if (params.canwrite) {
1677 pthread_join(params.thread, NULL);
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001678
Sami Tolvanen90221202014-12-09 16:39:47 +00001679 fprintf(stderr, "wrote %d blocks; expected %d\n", params.written, total_blocks);
1680 fprintf(stderr, "max alloc needed was %zu\n", params.bufsize);
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001681
Sami Tolvanen90221202014-12-09 16:39:47 +00001682 // Delete stash only after successfully completing the update, as it
1683 // may contain blocks needed to complete the update later.
1684 DeleteStash(params.stashbase);
1685 } else {
1686 fprintf(stderr, "verified partition contents; update may be resumed\n");
1687 }
1688
1689 rc = 0;
1690
1691pbiudone:
1692 if (params.fd != -1) {
1693 if (fsync(params.fd) == -1) {
Elliott Hughes1fdd4522015-03-23 13:33:57 -07001694 fprintf(stderr, "fsync failed: %s\n", strerror(errno));
Sami Tolvanen90221202014-12-09 16:39:47 +00001695 }
Elliott Hughesb47afed2015-05-15 16:19:20 -07001696 close(params.fd);
Sami Tolvanen90221202014-12-09 16:39:47 +00001697 }
1698
1699 if (logcmd) {
1700 free(logcmd);
1701 }
1702
1703 if (cmdht) {
1704 mzHashTableFree(cmdht);
1705 }
1706
1707 if (params.buffer) {
1708 free(params.buffer);
1709 }
1710
1711 if (transfer_list) {
1712 free(transfer_list);
1713 }
1714
1715 if (blockdev_filename) {
1716 FreeValue(blockdev_filename);
1717 }
1718
1719 if (transfer_list_value) {
1720 FreeValue(transfer_list_value);
1721 }
1722
1723 if (new_data_fn) {
1724 FreeValue(new_data_fn);
1725 }
1726
1727 if (patch_data_fn) {
1728 FreeValue(patch_data_fn);
1729 }
1730
1731 // Only delete the stash if the update cannot be resumed, or it's
1732 // a verification run and we created the stash.
1733 if (params.isunresumable || (!params.canwrite && params.createdstash)) {
1734 DeleteStash(params.stashbase);
1735 }
1736
Sami Tolvanen90221202014-12-09 16:39:47 +00001737 return StringValue(rc == 0 ? strdup("t") : strdup(""));
1738}
1739
1740// The transfer list is a text file containing commands to
1741// transfer data from one place to another on the target
1742// partition. We parse it and execute the commands in order:
1743//
1744// zero [rangeset]
1745// - fill the indicated blocks with zeros
1746//
1747// new [rangeset]
1748// - fill the blocks with data read from the new_data file
1749//
1750// erase [rangeset]
1751// - mark the given blocks as empty
1752//
1753// move <...>
1754// bsdiff <patchstart> <patchlen> <...>
1755// imgdiff <patchstart> <patchlen> <...>
1756// - read the source blocks, apply a patch (or not in the
1757// case of move), write result to target blocks. bsdiff or
1758// imgdiff specifies the type of patch; move means no patch
1759// at all.
1760//
1761// The format of <...> differs between versions 1 and 2;
1762// see the LoadSrcTgtVersion{1,2}() functions for a
1763// description of what's expected.
1764//
1765// stash <stash_id> <src_range>
1766// - (version 2+ only) load the given source range and stash
1767// the data in the given slot of the stash table.
1768//
1769// The creator of the transfer list will guarantee that no block
1770// is read (ie, used as the source for a patch or move) after it
1771// has been written.
1772//
1773// In version 2, the creator will guarantee that a given stash is
1774// loaded (with a stash command) before it's used in a
1775// move/bsdiff/imgdiff command.
1776//
1777// Within one command the source and target ranges may overlap so
1778// in general we need to read the entire source into memory before
1779// writing anything to the target blocks.
1780//
1781// All the patch data is concatenated into one patch_data file in
1782// the update package. It must be stored uncompressed because we
1783// memory-map it in directly from the archive. (Since patches are
1784// already compressed, we lose very little by not compressing
1785// their concatenation.)
1786//
1787// In version 3, commands that read data from the partition (i.e.
1788// move/bsdiff/imgdiff/stash) have one or more additional hashes
1789// before the range parameters, which are used to check if the
1790// command has already been completed and verify the integrity of
1791// the source data.
1792
1793Value* BlockImageVerifyFn(const char* name, State* state, int argc, Expr* argv[]) {
1794 // Commands which are not tested are set to NULL to skip them completely
1795 const Command commands[] = {
1796 { "bsdiff", PerformCommandDiff },
1797 { "erase", NULL },
1798 { "free", PerformCommandFree },
1799 { "imgdiff", PerformCommandDiff },
1800 { "move", PerformCommandMove },
1801 { "new", NULL },
1802 { "stash", PerformCommandStash },
1803 { "zero", NULL }
1804 };
1805
1806 // Perform a dry run without writing to test if an update can proceed
1807 return PerformBlockImageUpdate(name, state, argc, argv, commands,
Tao Baoe6aa3322015-08-05 15:20:27 -07001808 sizeof(commands) / sizeof(commands[0]), true);
Sami Tolvanen90221202014-12-09 16:39:47 +00001809}
1810
1811Value* BlockImageUpdateFn(const char* name, State* state, int argc, Expr* argv[]) {
1812 const Command commands[] = {
1813 { "bsdiff", PerformCommandDiff },
1814 { "erase", PerformCommandErase },
1815 { "free", PerformCommandFree },
1816 { "imgdiff", PerformCommandDiff },
1817 { "move", PerformCommandMove },
1818 { "new", PerformCommandNew },
1819 { "stash", PerformCommandStash },
1820 { "zero", PerformCommandZero }
1821 };
1822
1823 return PerformBlockImageUpdate(name, state, argc, argv, commands,
Tao Baoe6aa3322015-08-05 15:20:27 -07001824 sizeof(commands) / sizeof(commands[0]), false);
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001825}
1826
1827Value* RangeSha1Fn(const char* name, State* state, int argc, Expr* argv[]) {
1828 Value* blockdev_filename;
1829 Value* ranges;
1830 const uint8_t* digest = NULL;
1831 if (ReadValueArgs(state, argv, 2, &blockdev_filename, &ranges) < 0) {
1832 return NULL;
1833 }
1834
1835 if (blockdev_filename->type != VAL_STRING) {
1836 ErrorAbort(state, "blockdev_filename argument to %s must be string", name);
1837 goto done;
1838 }
1839 if (ranges->type != VAL_STRING) {
1840 ErrorAbort(state, "ranges argument to %s must be string", name);
1841 goto done;
1842 }
1843
Tao Baoba9a42a2015-06-23 23:23:33 -07001844 int fd;
1845 fd = open(blockdev_filename->data, O_RDWR);
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001846 if (fd < 0) {
Elliott Hughes1fdd4522015-03-23 13:33:57 -07001847 ErrorAbort(state, "open \"%s\" failed: %s", blockdev_filename->data, strerror(errno));
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001848 goto done;
1849 }
1850
Tao Baoba9a42a2015-06-23 23:23:33 -07001851 RangeSet* rs;
1852 rs = parse_range(ranges->data);
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001853 uint8_t buffer[BLOCKSIZE];
1854
1855 SHA_CTX ctx;
1856 SHA_init(&ctx);
1857
Shrinivas Sahukara6153df2015-08-19 13:01:45 +05301858 for (size_t i = 0; i < rs->count; ++i) {
Elliott Hughes7bad7c42015-04-28 17:24:24 -07001859 if (!check_lseek(fd, (off64_t)rs->pos[i*2] * BLOCKSIZE, SEEK_SET)) {
Sami Tolvanen90221202014-12-09 16:39:47 +00001860 ErrorAbort(state, "failed to seek %s: %s", blockdev_filename->data,
1861 strerror(errno));
1862 goto done;
1863 }
1864
Shrinivas Sahukara6153df2015-08-19 13:01:45 +05301865 for (size_t j = rs->pos[i*2]; j < rs->pos[i*2+1]; ++j) {
Sami Tolvanen90221202014-12-09 16:39:47 +00001866 if (read_all(fd, buffer, BLOCKSIZE) == -1) {
1867 ErrorAbort(state, "failed to read %s: %s", blockdev_filename->data,
1868 strerror(errno));
1869 goto done;
1870 }
1871
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001872 SHA_update(&ctx, buffer, BLOCKSIZE);
1873 }
1874 }
1875 digest = SHA_final(&ctx);
1876 close(fd);
1877
1878 done:
1879 FreeValue(blockdev_filename);
1880 FreeValue(ranges);
1881 if (digest == NULL) {
1882 return StringValue(strdup(""));
1883 } else {
Tao Baoe6aa3322015-08-05 15:20:27 -07001884 return StringValue(strdup(print_sha1(digest).c_str()));
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001885 }
1886}
1887
1888void RegisterBlockImageFunctions() {
Sami Tolvanen90221202014-12-09 16:39:47 +00001889 RegisterFunction("block_image_verify", BlockImageVerifyFn);
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001890 RegisterFunction("block_image_update", BlockImageUpdateFn);
1891 RegisterFunction("range_sha1", RangeSha1Fn);
1892}