blob: 091bedf5395870aef7aa789c89ecf1033633bcc5 [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>
Tao Bao0940fe12015-08-27 16:41:21 -070037#include <vector>
Tao Baoe6aa3322015-08-05 15:20:27 -070038
39#include <base/strings.h>
40
Doug Zongkerbc7ffed2014-08-15 14:31:52 -070041#include "applypatch/applypatch.h"
42#include "edify/expr.h"
43#include "mincrypt/sha.h"
Sami Tolvanen90221202014-12-09 16:39:47 +000044#include "minzip/Hash.h"
Tao Baoe6aa3322015-08-05 15:20:27 -070045#include "print_sha1.h"
Tao Bao0940fe12015-08-27 16:41:21 -070046#include "unique_fd.h"
47#include "updater.h"
Doug Zongkerbc7ffed2014-08-15 14:31:52 -070048
49#define BLOCKSIZE 4096
50
Sami Tolvanene82fa182015-06-10 15:58:12 +000051// Set this to 0 to interpret 'erase' transfers to mean do a
52// BLKDISCARD ioctl (the normal behavior). Set to 1 to interpret
53// erase to mean fill the region with zeroes.
54#define DEBUG_ERASE 0
55
Sami Tolvanen90221202014-12-09 16:39:47 +000056#define STASH_DIRECTORY_BASE "/cache/recovery"
57#define STASH_DIRECTORY_MODE 0700
58#define STASH_FILE_MODE 0600
59
Tao Bao0940fe12015-08-27 16:41:21 -070060struct RangeSet {
61 size_t count; // Limit is INT_MAX.
Shrinivas Sahukara6153df2015-08-19 13:01:45 +053062 size_t size;
Tao Bao0940fe12015-08-27 16:41:21 -070063 std::vector<size_t> pos; // Actual limit is INT_MAX.
64};
Doug Zongkerbc7ffed2014-08-15 14:31:52 -070065
Tao Bao0940fe12015-08-27 16:41:21 -070066static void parse_range(const char* range_text, RangeSet& rs) {
Sami Tolvanenf2bac042015-05-12 12:48:46 +010067
Tao Bao0940fe12015-08-27 16:41:21 -070068 if (range_text == nullptr) {
69 fprintf(stderr, "failed to parse range: null range\n");
70 exit(1);
Doug Zongkerbc7ffed2014-08-15 14:31:52 -070071 }
Sami Tolvanenf2bac042015-05-12 12:48:46 +010072
Tao Bao0940fe12015-08-27 16:41:21 -070073 std::vector<std::string> pieces = android::base::Split(std::string(range_text), ",");
74 long int val;
Sami Tolvanenf2bac042015-05-12 12:48:46 +010075
Tao Bao0940fe12015-08-27 16:41:21 -070076 if (pieces.size() < 3) {
Sami Tolvanenf2bac042015-05-12 12:48:46 +010077 goto err;
78 }
79
Shrinivas Sahukara6153df2015-08-19 13:01:45 +053080 errno = 0;
Tao Bao0940fe12015-08-27 16:41:21 -070081 val = strtol(pieces[0].c_str(), nullptr, 0);
Sami Tolvanenf2bac042015-05-12 12:48:46 +010082
Tao Bao0940fe12015-08-27 16:41:21 -070083 if (errno != 0 || val < 2 || val > INT_MAX) {
Sami Tolvanenf2bac042015-05-12 12:48:46 +010084 goto err;
85 } else if (val % 2) {
86 goto err; // must be even
Tao Bao0940fe12015-08-27 16:41:21 -070087 } else if (val != static_cast<long int>(pieces.size() - 1)) {
Sami Tolvanenf2bac042015-05-12 12:48:46 +010088 goto err;
89 }
90
Tao Bao0940fe12015-08-27 16:41:21 -070091 size_t num;
92 num = static_cast<size_t>(val);
Sami Tolvanenf2bac042015-05-12 12:48:46 +010093
Tao Bao0940fe12015-08-27 16:41:21 -070094 rs.pos.resize(num);
95 rs.count = num / 2;
96 rs.size = 0;
Sami Tolvanenf2bac042015-05-12 12:48:46 +010097
Tao Bao0940fe12015-08-27 16:41:21 -070098 for (size_t i = 0; i < num; i += 2) {
99 const char* token = pieces[i+1].c_str();
Shrinivas Sahukara6153df2015-08-19 13:01:45 +0530100 errno = 0;
Tao Bao0940fe12015-08-27 16:41:21 -0700101 val = strtol(token, nullptr, 0);
Shrinivas Sahukara6153df2015-08-19 13:01:45 +0530102 if (errno != 0 || val < 0 || val > INT_MAX) {
Sami Tolvanenf2bac042015-05-12 12:48:46 +0100103 goto err;
104 }
Tao Bao0940fe12015-08-27 16:41:21 -0700105 rs.pos[i] = static_cast<size_t>(val);
Sami Tolvanenf2bac042015-05-12 12:48:46 +0100106
Tao Bao0940fe12015-08-27 16:41:21 -0700107 token = pieces[i+2].c_str();
Shrinivas Sahukara6153df2015-08-19 13:01:45 +0530108 errno = 0;
Tao Bao0940fe12015-08-27 16:41:21 -0700109 val = strtol(token, nullptr, 0);
Shrinivas Sahukara6153df2015-08-19 13:01:45 +0530110 if (errno != 0 || val < 0 || val > INT_MAX) {
111 goto err;
112 }
Tao Bao0940fe12015-08-27 16:41:21 -0700113 rs.pos[i+1] = static_cast<size_t>(val);
Shrinivas Sahukara6153df2015-08-19 13:01:45 +0530114
Tao Bao0940fe12015-08-27 16:41:21 -0700115 if (rs.pos[i] >= rs.pos[i+1]) {
Shrinivas Sahukara6153df2015-08-19 13:01:45 +0530116 goto err; // empty or negative range
117 }
118
Tao Bao0940fe12015-08-27 16:41:21 -0700119 size_t sz = rs.pos[i+1] - rs.pos[i];
120 if (rs.size > SIZE_MAX - sz) {
Shrinivas Sahukara6153df2015-08-19 13:01:45 +0530121 goto err; // overflow
122 }
123
Tao Bao0940fe12015-08-27 16:41:21 -0700124 rs.size += sz;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700125 }
126
Tao Bao0940fe12015-08-27 16:41:21 -0700127 return;
Sami Tolvanenf2bac042015-05-12 12:48:46 +0100128
129err:
Tao Bao0940fe12015-08-27 16:41:21 -0700130 fprintf(stderr, "failed to parse range '%s'\n", range_text);
Sami Tolvanenf2bac042015-05-12 12:48:46 +0100131 exit(1);
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700132}
133
Tao Baoe6aa3322015-08-05 15:20:27 -0700134static bool range_overlaps(const RangeSet& r1, const RangeSet& r2) {
Shrinivas Sahukara6153df2015-08-19 13:01:45 +0530135 for (size_t i = 0; i < r1.count; ++i) {
136 size_t r1_0 = r1.pos[i * 2];
137 size_t r1_1 = r1.pos[i * 2 + 1];
Sami Tolvanen90221202014-12-09 16:39:47 +0000138
Shrinivas Sahukara6153df2015-08-19 13:01:45 +0530139 for (size_t j = 0; j < r2.count; ++j) {
140 size_t r2_0 = r2.pos[j * 2];
141 size_t r2_1 = r2.pos[j * 2 + 1];
Sami Tolvanen90221202014-12-09 16:39:47 +0000142
Tao Baoc0f56ad2015-06-25 14:00:31 -0700143 if (!(r2_0 >= r1_1 || r1_0 >= r2_1)) {
Tao Baoe6aa3322015-08-05 15:20:27 -0700144 return true;
Sami Tolvanen90221202014-12-09 16:39:47 +0000145 }
146 }
147 }
148
Tao Baoe6aa3322015-08-05 15:20:27 -0700149 return false;
Sami Tolvanen90221202014-12-09 16:39:47 +0000150}
151
152static int read_all(int fd, uint8_t* data, size_t size) {
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700153 size_t so_far = 0;
154 while (so_far < size) {
Elliott Hughes7bad7c42015-04-28 17:24:24 -0700155 ssize_t r = TEMP_FAILURE_RETRY(read(fd, data+so_far, size-so_far));
156 if (r == -1) {
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700157 fprintf(stderr, "read failed: %s\n", strerror(errno));
Sami Tolvanen90221202014-12-09 16:39:47 +0000158 return -1;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700159 }
Elliott Hughes7bad7c42015-04-28 17:24:24 -0700160 so_far += r;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700161 }
Sami Tolvanen90221202014-12-09 16:39:47 +0000162 return 0;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700163}
164
Sami Tolvanen90221202014-12-09 16:39:47 +0000165static int write_all(int fd, const uint8_t* data, size_t size) {
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700166 size_t written = 0;
167 while (written < size) {
Elliott Hughes7bad7c42015-04-28 17:24:24 -0700168 ssize_t w = TEMP_FAILURE_RETRY(write(fd, data+written, size-written));
169 if (w == -1) {
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700170 fprintf(stderr, "write failed: %s\n", strerror(errno));
Sami Tolvanen90221202014-12-09 16:39:47 +0000171 return -1;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700172 }
Elliott Hughes7bad7c42015-04-28 17:24:24 -0700173 written += w;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700174 }
Sami Tolvanen90221202014-12-09 16:39:47 +0000175
Sami Tolvanen90221202014-12-09 16:39:47 +0000176 return 0;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700177}
178
Elliott Hughes7bad7c42015-04-28 17:24:24 -0700179static bool check_lseek(int fd, off64_t offset, int whence) {
180 off64_t rc = TEMP_FAILURE_RETRY(lseek64(fd, offset, whence));
181 if (rc == -1) {
182 fprintf(stderr, "lseek64 failed: %s\n", strerror(errno));
183 return false;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700184 }
Elliott Hughes7bad7c42015-04-28 17:24:24 -0700185 return true;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700186}
187
188static void allocate(size_t size, uint8_t** buffer, size_t* buffer_alloc) {
189 // if the buffer's big enough, reuse it.
190 if (size <= *buffer_alloc) return;
191
192 free(*buffer);
193
194 *buffer = (uint8_t*) malloc(size);
Tao Bao0940fe12015-08-27 16:41:21 -0700195 if (*buffer == nullptr) {
Doug Zongker1d5d6092014-08-21 10:47:24 -0700196 fprintf(stderr, "failed to allocate %zu bytes\n", size);
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700197 exit(1);
198 }
199 *buffer_alloc = size;
200}
201
Tao Bao0940fe12015-08-27 16:41:21 -0700202struct RangeSinkState {
203 RangeSinkState(RangeSet& rs) : tgt(rs) { };
204
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700205 int fd;
Tao Bao0940fe12015-08-27 16:41:21 -0700206 const RangeSet& tgt;
Shrinivas Sahukara6153df2015-08-19 13:01:45 +0530207 size_t p_block;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700208 size_t p_remain;
Tao Bao0940fe12015-08-27 16:41:21 -0700209};
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700210
211static ssize_t RangeSinkWrite(const uint8_t* data, ssize_t size, void* token) {
Tao Bao0940fe12015-08-27 16:41:21 -0700212 RangeSinkState* rss = reinterpret_cast<RangeSinkState*>(token);
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700213
Tao Bao0940fe12015-08-27 16:41:21 -0700214 if (rss->p_remain == 0) {
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700215 fprintf(stderr, "range sink write overrun");
Sami Tolvanen90221202014-12-09 16:39:47 +0000216 return 0;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700217 }
218
219 ssize_t written = 0;
220 while (size > 0) {
221 size_t write_now = size;
Sami Tolvanen90221202014-12-09 16:39:47 +0000222
223 if (rss->p_remain < write_now) {
224 write_now = rss->p_remain;
225 }
226
227 if (write_all(rss->fd, data, write_now) == -1) {
228 break;
229 }
230
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700231 data += write_now;
232 size -= write_now;
233
234 rss->p_remain -= write_now;
235 written += write_now;
236
237 if (rss->p_remain == 0) {
238 // move to the next block
239 ++rss->p_block;
Tao Bao0940fe12015-08-27 16:41:21 -0700240 if (rss->p_block < rss->tgt.count) {
241 rss->p_remain = (rss->tgt.pos[rss->p_block * 2 + 1] -
242 rss->tgt.pos[rss->p_block * 2]) * BLOCKSIZE;
Sami Tolvanen90221202014-12-09 16:39:47 +0000243
Tao Bao0940fe12015-08-27 16:41:21 -0700244 if (!check_lseek(rss->fd, (off64_t)rss->tgt.pos[rss->p_block*2] * BLOCKSIZE,
Elliott Hughes7bad7c42015-04-28 17:24:24 -0700245 SEEK_SET)) {
Sami Tolvanen90221202014-12-09 16:39:47 +0000246 break;
247 }
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700248 } else {
249 // we can't write any more; return how many bytes have
250 // been written so far.
Sami Tolvanen90221202014-12-09 16:39:47 +0000251 break;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700252 }
253 }
254 }
255
256 return written;
257}
258
259// All of the data for all the 'new' transfers is contained in one
260// file in the update package, concatenated together in the order in
261// which transfers.list will need it. We want to stream it out of the
262// archive (it's compressed) without writing it to a temp file, but we
263// can't write each section until it's that transfer's turn to go.
264//
265// To achieve this, we expand the new data from the archive in a
266// background thread, and block that threads 'receive uncompressed
267// data' function until the main thread has reached a point where we
268// want some new data to be written. We signal the background thread
269// with the destination for the data and block the main thread,
270// waiting for the background thread to complete writing that section.
271// Then it signals the main thread to wake up and goes back to
272// blocking waiting for a transfer.
273//
274// NewThreadInfo is the struct used to pass information back and forth
275// between the two threads. When the main thread wants some data
276// written, it sets rss to the destination location and signals the
277// condition. When the background thread is done writing, it clears
278// rss and signals the condition again.
279
Tao Bao0940fe12015-08-27 16:41:21 -0700280struct NewThreadInfo {
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700281 ZipArchive* za;
282 const ZipEntry* entry;
283
284 RangeSinkState* rss;
285
286 pthread_mutex_t mu;
287 pthread_cond_t cv;
Tao Bao0940fe12015-08-27 16:41:21 -0700288};
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700289
290static bool receive_new_data(const unsigned char* data, int size, void* cookie) {
Tao Bao0940fe12015-08-27 16:41:21 -0700291 NewThreadInfo* nti = reinterpret_cast<NewThreadInfo*>(cookie);
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700292
293 while (size > 0) {
Tao Bao0940fe12015-08-27 16:41:21 -0700294 // Wait for nti->rss to be non-null, indicating some of this
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700295 // data is wanted.
296 pthread_mutex_lock(&nti->mu);
Tao Bao0940fe12015-08-27 16:41:21 -0700297 while (nti->rss == nullptr) {
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700298 pthread_cond_wait(&nti->cv, &nti->mu);
299 }
300 pthread_mutex_unlock(&nti->mu);
301
302 // At this point nti->rss is set, and we own it. The main
303 // thread is waiting for it to disappear from nti.
304 ssize_t written = RangeSinkWrite(data, size, nti->rss);
305 data += written;
306 size -= written;
307
Tao Bao0940fe12015-08-27 16:41:21 -0700308 if (nti->rss->p_block == nti->rss->tgt.count) {
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700309 // we have written all the bytes desired by this rss.
310
311 pthread_mutex_lock(&nti->mu);
Tao Bao0940fe12015-08-27 16:41:21 -0700312 nti->rss = nullptr;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700313 pthread_cond_broadcast(&nti->cv);
314 pthread_mutex_unlock(&nti->mu);
315 }
316 }
317
318 return true;
319}
320
321static void* unzip_new_data(void* cookie) {
322 NewThreadInfo* nti = (NewThreadInfo*) cookie;
323 mzProcessZipEntryContents(nti->za, nti->entry, receive_new_data, nti);
Tao Bao0940fe12015-08-27 16:41:21 -0700324 return nullptr;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -0700325}
326
Tao Bao0940fe12015-08-27 16:41:21 -0700327static int ReadBlocks(const RangeSet& src, uint8_t* buffer, int fd) {
Sami Tolvanen90221202014-12-09 16:39:47 +0000328 size_t p = 0;
Sami Tolvanen90221202014-12-09 16:39:47 +0000329
Tao Bao0940fe12015-08-27 16:41:21 -0700330 if (!buffer) {
Sami Tolvanen90221202014-12-09 16:39:47 +0000331 return -1;
332 }
333
Tao Bao0940fe12015-08-27 16:41:21 -0700334 for (size_t i = 0; i < src.count; ++i) {
335 if (!check_lseek(fd, (off64_t) src.pos[i * 2] * BLOCKSIZE, SEEK_SET)) {
Sami Tolvanen90221202014-12-09 16:39:47 +0000336 return -1;
337 }
338
Tao Bao0940fe12015-08-27 16:41:21 -0700339 size_t size = (src.pos[i * 2 + 1] - src.pos[i * 2]) * BLOCKSIZE;
Sami Tolvanen90221202014-12-09 16:39:47 +0000340
341 if (read_all(fd, buffer + p, size) == -1) {
342 return -1;
343 }
344
345 p += size;
346 }
347
348 return 0;
349}
350
Tao Bao0940fe12015-08-27 16:41:21 -0700351static int WriteBlocks(const RangeSet& tgt, uint8_t* buffer, int fd) {
352 if (!buffer) {
Sami Tolvanen90221202014-12-09 16:39:47 +0000353 return -1;
354 }
355
Tao Bao0940fe12015-08-27 16:41:21 -0700356 size_t p = 0;
357 for (size_t i = 0; i < tgt.count; ++i) {
358 if (!check_lseek(fd, (off64_t) tgt.pos[i * 2] * BLOCKSIZE, SEEK_SET)) {
Sami Tolvanen90221202014-12-09 16:39:47 +0000359 return -1;
360 }
361
Tao Bao0940fe12015-08-27 16:41:21 -0700362 size_t size = (tgt.pos[i * 2 + 1] - tgt.pos[i * 2]) * BLOCKSIZE;
Sami Tolvanen90221202014-12-09 16:39:47 +0000363
364 if (write_all(fd, buffer + p, size) == -1) {
365 return -1;
366 }
367
368 p += size;
369 }
370
371 return 0;
372}
373
Doug Zongker52ae67d2014-09-08 12:22:09 -0700374// Do a source/target load for move/bsdiff/imgdiff in version 1.
375// 'wordsave' is the save_ptr of a strtok_r()-in-progress. We expect
376// to parse the remainder of the string as:
377//
378// <src_range> <tgt_range>
379//
380// The source range is loaded into the provided buffer, reallocating
Tao Bao34847b22015-09-08 11:05:49 -0700381// it to make it larger if necessary.
Doug Zongker52ae67d2014-09-08 12:22:09 -0700382
Tao Bao34847b22015-09-08 11:05:49 -0700383static int LoadSrcTgtVersion1(char** wordsave, RangeSet& tgt, size_t& src_blocks,
Tao Bao0940fe12015-08-27 16:41:21 -0700384 uint8_t** buffer, size_t* buffer_alloc, int fd) {
385 char* word = strtok_r(nullptr, " ", wordsave);
386 RangeSet src;
387 parse_range(word, src);
Doug Zongker52ae67d2014-09-08 12:22:09 -0700388
Tao Bao34847b22015-09-08 11:05:49 -0700389 word = strtok_r(nullptr, " ", wordsave);
390 parse_range(word, tgt);
Doug Zongker52ae67d2014-09-08 12:22:09 -0700391
Tao Bao0940fe12015-08-27 16:41:21 -0700392 allocate(src.size * BLOCKSIZE, buffer, buffer_alloc);
393 int rc = ReadBlocks(src, *buffer, fd);
394 src_blocks = src.size;
Sami Tolvanen90221202014-12-09 16:39:47 +0000395
Sami Tolvanen90221202014-12-09 16:39:47 +0000396 return rc;
397}
398
Tao Bao0940fe12015-08-27 16:41:21 -0700399static int VerifyBlocks(const std::string& expected, const uint8_t* buffer,
400 const size_t blocks, bool printerror) {
Sami Tolvanen90221202014-12-09 16:39:47 +0000401 uint8_t digest[SHA_DIGEST_SIZE];
402
Tao Bao0940fe12015-08-27 16:41:21 -0700403 if (!buffer) {
404 return -1;
Doug Zongker52ae67d2014-09-08 12:22:09 -0700405 }
406
Sami Tolvanen90221202014-12-09 16:39:47 +0000407 SHA_hash(buffer, blocks * BLOCKSIZE, digest);
Sami Tolvanen90221202014-12-09 16:39:47 +0000408
Tao Baoe6aa3322015-08-05 15:20:27 -0700409 std::string hexdigest = print_sha1(digest);
Sami Tolvanen90221202014-12-09 16:39:47 +0000410
Tao Bao0940fe12015-08-27 16:41:21 -0700411 if (hexdigest != expected) {
412 if (printerror) {
413 fprintf(stderr, "failed to verify blocks (expected %s, read %s)\n",
414 expected.c_str(), hexdigest.c_str());
415 }
416 return -1;
Sami Tolvanen90221202014-12-09 16:39:47 +0000417 }
418
Tao Bao0940fe12015-08-27 16:41:21 -0700419 return 0;
Sami Tolvanen90221202014-12-09 16:39:47 +0000420}
421
Tao Bao0940fe12015-08-27 16:41:21 -0700422static std::string GetStashFileName(const std::string& base, const std::string& id,
423 const std::string& postfix) {
Tao Baoe6aa3322015-08-05 15:20:27 -0700424 if (base.empty()) {
425 return "";
Sami Tolvanen90221202014-12-09 16:39:47 +0000426 }
427
Tao Baoe6aa3322015-08-05 15:20:27 -0700428 std::string fn(STASH_DIRECTORY_BASE);
429 fn += "/" + base + "/" + id + postfix;
Sami Tolvanen90221202014-12-09 16:39:47 +0000430
431 return fn;
432}
433
Tao Baoe6aa3322015-08-05 15:20:27 -0700434typedef void (*StashCallback)(const std::string&, void*);
Sami Tolvanen90221202014-12-09 16:39:47 +0000435
436// Does a best effort enumeration of stash files. Ignores possible non-file
437// items in the stash directory and continues despite of errors. Calls the
438// 'callback' function for each file and passes 'data' to the function as a
439// parameter.
440
Tao Baoe6aa3322015-08-05 15:20:27 -0700441static void EnumerateStash(const std::string& dirname, StashCallback callback, void* data) {
Tao Bao0940fe12015-08-27 16:41:21 -0700442 if (dirname.empty() || callback == nullptr) {
Sami Tolvanen90221202014-12-09 16:39:47 +0000443 return;
444 }
445
Tao Baoe6aa3322015-08-05 15:20:27 -0700446 std::unique_ptr<DIR, int(*)(DIR*)> directory(opendir(dirname.c_str()), closedir);
Sami Tolvanen90221202014-12-09 16:39:47 +0000447
Tao Bao0940fe12015-08-27 16:41:21 -0700448 if (directory == nullptr) {
Sami Tolvanen90221202014-12-09 16:39:47 +0000449 if (errno != ENOENT) {
Tao Baoe6aa3322015-08-05 15:20:27 -0700450 fprintf(stderr, "opendir \"%s\" failed: %s\n", dirname.c_str(), strerror(errno));
Sami Tolvanen90221202014-12-09 16:39:47 +0000451 }
452 return;
453 }
454
Tao Baoe6aa3322015-08-05 15:20:27 -0700455 struct dirent* item;
Tao Bao0940fe12015-08-27 16:41:21 -0700456 while ((item = readdir(directory.get())) != nullptr) {
Sami Tolvanen90221202014-12-09 16:39:47 +0000457 if (item->d_type != DT_REG) {
458 continue;
459 }
460
Tao Baoe6aa3322015-08-05 15:20:27 -0700461 std::string fn = dirname + "/" + std::string(item->d_name);
Sami Tolvanen90221202014-12-09 16:39:47 +0000462 callback(fn, data);
Sami Tolvanen90221202014-12-09 16:39:47 +0000463 }
464}
465
Tao Baoe6aa3322015-08-05 15:20:27 -0700466static void UpdateFileSize(const std::string& fn, void* data) {
467 if (fn.empty() || !data) {
468 return;
469 }
470
Tao Bao0940fe12015-08-27 16:41:21 -0700471 struct stat sb;
472 if (stat(fn.c_str(), &sb) == -1) {
Tao Baoe6aa3322015-08-05 15:20:27 -0700473 fprintf(stderr, "stat \"%s\" failed: %s\n", fn.c_str(), strerror(errno));
Sami Tolvanen90221202014-12-09 16:39:47 +0000474 return;
475 }
476
Tao Baoe6aa3322015-08-05 15:20:27 -0700477 int* size = reinterpret_cast<int*>(data);
Tao Bao0940fe12015-08-27 16:41:21 -0700478 *size += sb.st_size;
Sami Tolvanen90221202014-12-09 16:39:47 +0000479}
480
481// Deletes the stash directory and all files in it. Assumes that it only
482// contains files. There is nothing we can do about unlikely, but possible
483// errors, so they are merely logged.
484
Tao Bao0940fe12015-08-27 16:41:21 -0700485static void DeleteFile(const std::string& fn, void* /* data */) {
Tao Baoe6aa3322015-08-05 15:20:27 -0700486 if (!fn.empty()) {
487 fprintf(stderr, "deleting %s\n", fn.c_str());
Sami Tolvanen90221202014-12-09 16:39:47 +0000488
Tao Baoe6aa3322015-08-05 15:20:27 -0700489 if (unlink(fn.c_str()) == -1 && errno != ENOENT) {
490 fprintf(stderr, "unlink \"%s\" failed: %s\n", fn.c_str(), strerror(errno));
Sami Tolvanen90221202014-12-09 16:39:47 +0000491 }
492 }
493}
494
Tao Baoe6aa3322015-08-05 15:20:27 -0700495static void DeletePartial(const std::string& fn, void* data) {
496 if (android::base::EndsWith(fn, ".partial")) {
Sami Tolvanen90221202014-12-09 16:39:47 +0000497 DeleteFile(fn, data);
498 }
499}
500
Tao Baoe6aa3322015-08-05 15:20:27 -0700501static void DeleteStash(const std::string& base) {
502 if (base.empty()) {
Sami Tolvanen90221202014-12-09 16:39:47 +0000503 return;
504 }
505
Tao Baoe6aa3322015-08-05 15:20:27 -0700506 fprintf(stderr, "deleting stash %s\n", base.c_str());
Sami Tolvanen90221202014-12-09 16:39:47 +0000507
Tao Baoe6aa3322015-08-05 15:20:27 -0700508 std::string dirname = GetStashFileName(base, "", "");
Tao Bao0940fe12015-08-27 16:41:21 -0700509 EnumerateStash(dirname, DeleteFile, nullptr);
Sami Tolvanen90221202014-12-09 16:39:47 +0000510
Tao Baoe6aa3322015-08-05 15:20:27 -0700511 if (rmdir(dirname.c_str()) == -1) {
Sami Tolvanen90221202014-12-09 16:39:47 +0000512 if (errno != ENOENT && errno != ENOTDIR) {
Tao Baoe6aa3322015-08-05 15:20:27 -0700513 fprintf(stderr, "rmdir \"%s\" failed: %s\n", dirname.c_str(), strerror(errno));
Sami Tolvanen90221202014-12-09 16:39:47 +0000514 }
515 }
Sami Tolvanen90221202014-12-09 16:39:47 +0000516}
517
Tao Bao0940fe12015-08-27 16:41:21 -0700518static int LoadStash(const std::string& base, const std::string& id, bool verify, size_t* blocks,
Tao Baoe6aa3322015-08-05 15:20:27 -0700519 uint8_t** buffer, size_t* buffer_alloc, bool printnoent) {
Tao Bao0940fe12015-08-27 16:41:21 -0700520 if (base.empty() || !buffer || !buffer_alloc) {
521 return -1;
Sami Tolvanen90221202014-12-09 16:39:47 +0000522 }
523
Tao Bao0940fe12015-08-27 16:41:21 -0700524 size_t blockcount = 0;
525
Sami Tolvanen90221202014-12-09 16:39:47 +0000526 if (!blocks) {
527 blocks = &blockcount;
528 }
529
Tao Bao0940fe12015-08-27 16:41:21 -0700530 std::string fn = GetStashFileName(base, id, "");
Sami Tolvanen90221202014-12-09 16:39:47 +0000531
Tao Bao0940fe12015-08-27 16:41:21 -0700532 struct stat sb;
533 int res = stat(fn.c_str(), &sb);
Sami Tolvanen90221202014-12-09 16:39:47 +0000534
535 if (res == -1) {
536 if (errno != ENOENT || printnoent) {
Tao Baoe6aa3322015-08-05 15:20:27 -0700537 fprintf(stderr, "stat \"%s\" failed: %s\n", fn.c_str(), strerror(errno));
Sami Tolvanen90221202014-12-09 16:39:47 +0000538 }
Tao Bao0940fe12015-08-27 16:41:21 -0700539 return -1;
Sami Tolvanen90221202014-12-09 16:39:47 +0000540 }
541
Tao Baoe6aa3322015-08-05 15:20:27 -0700542 fprintf(stderr, " loading %s\n", fn.c_str());
Sami Tolvanen90221202014-12-09 16:39:47 +0000543
Tao Bao0940fe12015-08-27 16:41:21 -0700544 if ((sb.st_size % BLOCKSIZE) != 0) {
Tao Baoba9a42a2015-06-23 23:23:33 -0700545 fprintf(stderr, "%s size %" PRId64 " not multiple of block size %d",
Tao Bao0940fe12015-08-27 16:41:21 -0700546 fn.c_str(), static_cast<int64_t>(sb.st_size), BLOCKSIZE);
547 return -1;
Sami Tolvanen90221202014-12-09 16:39:47 +0000548 }
549
Tao Bao0940fe12015-08-27 16:41:21 -0700550 int fd = TEMP_FAILURE_RETRY(open(fn.c_str(), O_RDONLY));
551 unique_fd fd_holder(fd);
Sami Tolvanen90221202014-12-09 16:39:47 +0000552
553 if (fd == -1) {
Tao Baoe6aa3322015-08-05 15:20:27 -0700554 fprintf(stderr, "open \"%s\" failed: %s\n", fn.c_str(), strerror(errno));
Tao Bao0940fe12015-08-27 16:41:21 -0700555 return -1;
Sami Tolvanen90221202014-12-09 16:39:47 +0000556 }
557
Tao Bao0940fe12015-08-27 16:41:21 -0700558 allocate(sb.st_size, buffer, buffer_alloc);
Sami Tolvanen90221202014-12-09 16:39:47 +0000559
Tao Bao0940fe12015-08-27 16:41:21 -0700560 if (read_all(fd, *buffer, sb.st_size) == -1) {
561 return -1;
Sami Tolvanen90221202014-12-09 16:39:47 +0000562 }
563
Tao Bao0940fe12015-08-27 16:41:21 -0700564 *blocks = sb.st_size / BLOCKSIZE;
Sami Tolvanen90221202014-12-09 16:39:47 +0000565
Tao Baoe6aa3322015-08-05 15:20:27 -0700566 if (verify && VerifyBlocks(id, *buffer, *blocks, true) != 0) {
567 fprintf(stderr, "unexpected contents in %s\n", fn.c_str());
Tao Bao0940fe12015-08-27 16:41:21 -0700568 DeleteFile(fn, nullptr);
569 return -1;
Sami Tolvanen90221202014-12-09 16:39:47 +0000570 }
571
Tao Bao0940fe12015-08-27 16:41:21 -0700572 return 0;
Sami Tolvanen90221202014-12-09 16:39:47 +0000573}
574
Tao Bao0940fe12015-08-27 16:41:21 -0700575static int WriteStash(const std::string& base, const std::string& id, int blocks, uint8_t* buffer,
576 bool checkspace, bool *exists) {
577 if (base.empty() || buffer == nullptr) {
578 return -1;
Sami Tolvanen90221202014-12-09 16:39:47 +0000579 }
580
581 if (checkspace && CacheSizeCheck(blocks * BLOCKSIZE) != 0) {
582 fprintf(stderr, "not enough space to write stash\n");
Tao Bao0940fe12015-08-27 16:41:21 -0700583 return -1;
Sami Tolvanen90221202014-12-09 16:39:47 +0000584 }
585
Tao Bao0940fe12015-08-27 16:41:21 -0700586 std::string fn = GetStashFileName(base, id, ".partial");
587 std::string cn = GetStashFileName(base, id, "");
Sami Tolvanen90221202014-12-09 16:39:47 +0000588
Sami Tolvanen43b748f2015-04-17 12:50:31 +0100589 if (exists) {
Tao Bao0940fe12015-08-27 16:41:21 -0700590 struct stat sb;
591 int res = stat(cn.c_str(), &sb);
Sami Tolvanen43b748f2015-04-17 12:50:31 +0100592
593 if (res == 0) {
594 // The file already exists and since the name is the hash of the contents,
595 // it's safe to assume the contents are identical (accidental hash collisions
596 // are unlikely)
Tao Baoe6aa3322015-08-05 15:20:27 -0700597 fprintf(stderr, " skipping %d existing blocks in %s\n", blocks, cn.c_str());
Tao Bao0940fe12015-08-27 16:41:21 -0700598 *exists = true;
599 return 0;
Sami Tolvanen43b748f2015-04-17 12:50:31 +0100600 }
601
Tao Bao0940fe12015-08-27 16:41:21 -0700602 *exists = false;
Sami Tolvanen43b748f2015-04-17 12:50:31 +0100603 }
604
Tao Baoe6aa3322015-08-05 15:20:27 -0700605 fprintf(stderr, " writing %d blocks to %s\n", blocks, cn.c_str());
Sami Tolvanen90221202014-12-09 16:39:47 +0000606
Tao Bao0940fe12015-08-27 16:41:21 -0700607 int fd = TEMP_FAILURE_RETRY(open(fn.c_str(), O_WRONLY | O_CREAT | O_TRUNC, STASH_FILE_MODE));
608 unique_fd fd_holder(fd);
Sami Tolvanen90221202014-12-09 16:39:47 +0000609
610 if (fd == -1) {
Tao Baoe6aa3322015-08-05 15:20:27 -0700611 fprintf(stderr, "failed to create \"%s\": %s\n", fn.c_str(), strerror(errno));
Tao Bao0940fe12015-08-27 16:41:21 -0700612 return -1;
Sami Tolvanen90221202014-12-09 16:39:47 +0000613 }
614
615 if (write_all(fd, buffer, blocks * BLOCKSIZE) == -1) {
Tao Bao0940fe12015-08-27 16:41:21 -0700616 return -1;
Sami Tolvanen90221202014-12-09 16:39:47 +0000617 }
618
619 if (fsync(fd) == -1) {
Tao Baoe6aa3322015-08-05 15:20:27 -0700620 fprintf(stderr, "fsync \"%s\" failed: %s\n", fn.c_str(), strerror(errno));
Tao Bao0940fe12015-08-27 16:41:21 -0700621 return -1;
Sami Tolvanen90221202014-12-09 16:39:47 +0000622 }
623
Tao Baoe6aa3322015-08-05 15:20:27 -0700624 if (rename(fn.c_str(), cn.c_str()) == -1) {
625 fprintf(stderr, "rename(\"%s\", \"%s\") failed: %s\n", fn.c_str(), cn.c_str(),
626 strerror(errno));
Tao Bao0940fe12015-08-27 16:41:21 -0700627 return -1;
Sami Tolvanen90221202014-12-09 16:39:47 +0000628 }
629
Tao Bao0940fe12015-08-27 16:41:21 -0700630 std::string dname = GetStashFileName(base, "", "");
631 int dfd = TEMP_FAILURE_RETRY(open(dname.c_str(), O_RDONLY | O_DIRECTORY));
632 unique_fd dfd_holder(dfd);
Tao Baodc392262015-07-31 15:56:44 -0700633
634 if (dfd == -1) {
Tao Baoe6aa3322015-08-05 15:20:27 -0700635 fprintf(stderr, "failed to open \"%s\" failed: %s\n", dname.c_str(), strerror(errno));
Tao Bao0940fe12015-08-27 16:41:21 -0700636 return -1;
Tao Baodc392262015-07-31 15:56:44 -0700637 }
638
639 if (fsync(dfd) == -1) {
Tao Baoe6aa3322015-08-05 15:20:27 -0700640 fprintf(stderr, "fsync \"%s\" failed: %s\n", dname.c_str(), strerror(errno));
Tao Bao0940fe12015-08-27 16:41:21 -0700641 return -1;
Tao Baodc392262015-07-31 15:56:44 -0700642 }
643
Tao Bao0940fe12015-08-27 16:41:21 -0700644 return 0;
Sami Tolvanen90221202014-12-09 16:39:47 +0000645}
646
647// Creates a directory for storing stash files and checks if the /cache partition
648// hash enough space for the expected amount of blocks we need to store. Returns
649// >0 if we created the directory, zero if it existed already, and <0 of failure.
650
Tao Bao0940fe12015-08-27 16:41:21 -0700651static int CreateStash(State* state, int maxblocks, const char* blockdev, std::string& base) {
652 if (blockdev == nullptr) {
Tao Baoe6aa3322015-08-05 15:20:27 -0700653 return -1;
Sami Tolvanen90221202014-12-09 16:39:47 +0000654 }
655
656 // Stash directory should be different for each partition to avoid conflicts
657 // when updating multiple partitions at the same time, so we use the hash of
658 // the block device name as the base directory
Tao Baoe6aa3322015-08-05 15:20:27 -0700659 SHA_CTX ctx;
Sami Tolvanen90221202014-12-09 16:39:47 +0000660 SHA_init(&ctx);
661 SHA_update(&ctx, blockdev, strlen(blockdev));
Tao Baoe6aa3322015-08-05 15:20:27 -0700662 const uint8_t* digest = SHA_final(&ctx);
663 base = print_sha1(digest);
Sami Tolvanen90221202014-12-09 16:39:47 +0000664
Tao Baoe6aa3322015-08-05 15:20:27 -0700665 std::string dirname = GetStashFileName(base, "", "");
Tao Bao0940fe12015-08-27 16:41:21 -0700666 struct stat sb;
667 int res = stat(dirname.c_str(), &sb);
Sami Tolvanen90221202014-12-09 16:39:47 +0000668
669 if (res == -1 && errno != ENOENT) {
Tao Baoe6aa3322015-08-05 15:20:27 -0700670 ErrorAbort(state, "stat \"%s\" failed: %s\n", dirname.c_str(), strerror(errno));
671 return -1;
Sami Tolvanen90221202014-12-09 16:39:47 +0000672 } else if (res != 0) {
Tao Baoe6aa3322015-08-05 15:20:27 -0700673 fprintf(stderr, "creating stash %s\n", dirname.c_str());
674 res = mkdir(dirname.c_str(), STASH_DIRECTORY_MODE);
Sami Tolvanen90221202014-12-09 16:39:47 +0000675
676 if (res != 0) {
Tao Baoe6aa3322015-08-05 15:20:27 -0700677 ErrorAbort(state, "mkdir \"%s\" failed: %s\n", dirname.c_str(), strerror(errno));
678 return -1;
Sami Tolvanen90221202014-12-09 16:39:47 +0000679 }
680
681 if (CacheSizeCheck(maxblocks * BLOCKSIZE) != 0) {
682 ErrorAbort(state, "not enough space for stash\n");
Tao Baoe6aa3322015-08-05 15:20:27 -0700683 return -1;
Sami Tolvanen90221202014-12-09 16:39:47 +0000684 }
685
Tao Baoe6aa3322015-08-05 15:20:27 -0700686 return 1; // Created directory
Sami Tolvanen90221202014-12-09 16:39:47 +0000687 }
688
Tao Baoe6aa3322015-08-05 15:20:27 -0700689 fprintf(stderr, "using existing stash %s\n", dirname.c_str());
Sami Tolvanen90221202014-12-09 16:39:47 +0000690
691 // If the directory already exists, calculate the space already allocated to
692 // stash files and check if there's enough for all required blocks. Delete any
693 // partially completed stash files first.
694
Tao Bao0940fe12015-08-27 16:41:21 -0700695 EnumerateStash(dirname, DeletePartial, nullptr);
Tao Baoe6aa3322015-08-05 15:20:27 -0700696 int size = 0;
Sami Tolvanen90221202014-12-09 16:39:47 +0000697 EnumerateStash(dirname, UpdateFileSize, &size);
698
Tao Bao0940fe12015-08-27 16:41:21 -0700699 size = maxblocks * BLOCKSIZE - size;
Sami Tolvanen90221202014-12-09 16:39:47 +0000700
701 if (size > 0 && CacheSizeCheck(size) != 0) {
702 ErrorAbort(state, "not enough space for stash (%d more needed)\n", size);
Tao Baoe6aa3322015-08-05 15:20:27 -0700703 return -1;
Sami Tolvanen90221202014-12-09 16:39:47 +0000704 }
705
Tao Baoe6aa3322015-08-05 15:20:27 -0700706 return 0; // Using existing directory
Sami Tolvanen90221202014-12-09 16:39:47 +0000707}
708
Tao Baoe6aa3322015-08-05 15:20:27 -0700709static int SaveStash(const std::string& base, char** wordsave, uint8_t** buffer,
Tao Bao0940fe12015-08-27 16:41:21 -0700710 size_t* buffer_alloc, int fd, bool usehash) {
Tao Bao9739a292015-08-05 12:16:20 -0700711 if (!wordsave || !buffer || !buffer_alloc) {
Sami Tolvanen90221202014-12-09 16:39:47 +0000712 return -1;
713 }
714
Tao Bao0940fe12015-08-27 16:41:21 -0700715 char *id_tok = strtok_r(nullptr, " ", wordsave);
716 if (id_tok == nullptr) {
Sami Tolvanen90221202014-12-09 16:39:47 +0000717 fprintf(stderr, "missing id field in stash command\n");
718 return -1;
719 }
Tao Bao0940fe12015-08-27 16:41:21 -0700720 std::string id(id_tok);
Sami Tolvanen90221202014-12-09 16:39:47 +0000721
Tao Bao0940fe12015-08-27 16:41:21 -0700722 size_t blocks = 0;
723 if (usehash && LoadStash(base, id, true, &blocks, buffer, buffer_alloc, false) == 0) {
Sami Tolvanen90221202014-12-09 16:39:47 +0000724 // Stash file already exists and has expected contents. Do not
725 // read from source again, as the source may have been already
726 // overwritten during a previous attempt.
727 return 0;
728 }
729
Tao Bao34847b22015-09-08 11:05:49 -0700730 char* word = strtok_r(nullptr, " ", wordsave);
731 RangeSet src;
732 parse_range(word, src);
733
734 allocate(src.size * BLOCKSIZE, buffer, buffer_alloc);
735 if (ReadBlocks(src, *buffer, fd) == -1) {
Sami Tolvanen90221202014-12-09 16:39:47 +0000736 return -1;
737 }
Tao Bao34847b22015-09-08 11:05:49 -0700738 blocks = src.size;
Sami Tolvanen90221202014-12-09 16:39:47 +0000739
Tao Baoe6aa3322015-08-05 15:20:27 -0700740 if (usehash && VerifyBlocks(id, *buffer, blocks, true) != 0) {
Sami Tolvanen90221202014-12-09 16:39:47 +0000741 // Source blocks have unexpected contents. If we actually need this
742 // data later, this is an unrecoverable error. However, the command
743 // that uses the data may have already completed previously, so the
744 // possible failure will occur during source block verification.
Tao Bao0940fe12015-08-27 16:41:21 -0700745 fprintf(stderr, "failed to load source blocks for stash %s\n", id.c_str());
Sami Tolvanen90221202014-12-09 16:39:47 +0000746 return 0;
747 }
748
Tao Bao0940fe12015-08-27 16:41:21 -0700749 fprintf(stderr, "stashing %zu blocks to %s\n", blocks, id.c_str());
750 return WriteStash(base, id, blocks, *buffer, false, nullptr);
Sami Tolvanen90221202014-12-09 16:39:47 +0000751}
752
Tao Baoe6aa3322015-08-05 15:20:27 -0700753static int FreeStash(const std::string& base, const char* id) {
Tao Bao0940fe12015-08-27 16:41:21 -0700754 if (base.empty() || id == nullptr) {
Sami Tolvanen90221202014-12-09 16:39:47 +0000755 return -1;
756 }
757
Tao Baoe6aa3322015-08-05 15:20:27 -0700758 std::string fn = GetStashFileName(base, std::string(id), "");
Sami Tolvanen90221202014-12-09 16:39:47 +0000759
Tao Bao0940fe12015-08-27 16:41:21 -0700760 DeleteFile(fn, nullptr);
Sami Tolvanen90221202014-12-09 16:39:47 +0000761
762 return 0;
Doug Zongker52ae67d2014-09-08 12:22:09 -0700763}
764
Tao Bao0940fe12015-08-27 16:41:21 -0700765static void MoveRange(uint8_t* dest, const RangeSet& locs, const uint8_t* source) {
Doug Zongker52ae67d2014-09-08 12:22:09 -0700766 // source contains packed data, which we want to move to the
767 // locations given in *locs in the dest buffer. source and dest
768 // may be the same buffer.
769
Tao Bao0940fe12015-08-27 16:41:21 -0700770 size_t start = locs.size;
771 for (int i = locs.count-1; i >= 0; --i) {
772 size_t blocks = locs.pos[i*2+1] - locs.pos[i*2];
Doug Zongker52ae67d2014-09-08 12:22:09 -0700773 start -= blocks;
Tao Bao0940fe12015-08-27 16:41:21 -0700774 memmove(dest + (locs.pos[i*2] * BLOCKSIZE), source + (start * BLOCKSIZE),
Doug Zongker52ae67d2014-09-08 12:22:09 -0700775 blocks * BLOCKSIZE);
776 }
777}
778
779// Do a source/target load for move/bsdiff/imgdiff in version 2.
780// 'wordsave' is the save_ptr of a strtok_r()-in-progress. We expect
781// to parse the remainder of the string as one of:
782//
783// <tgt_range> <src_block_count> <src_range>
784// (loads data from source image only)
785//
786// <tgt_range> <src_block_count> - <[stash_id:stash_range] ...>
787// (loads data from stashes only)
788//
789// <tgt_range> <src_block_count> <src_range> <src_loc> <[stash_id:stash_range] ...>
790// (loads data from both source image and stashes)
791//
792// On return, buffer is filled with the loaded source data (rearranged
793// and combined with stashed data as necessary). buffer may be
794// reallocated if needed to accommodate the source data. *tgt is the
Sami Tolvanen90221202014-12-09 16:39:47 +0000795// target RangeSet. Any stashes required are loaded using LoadStash.
Doug Zongker52ae67d2014-09-08 12:22:09 -0700796
Tao Bao34847b22015-09-08 11:05:49 -0700797static int LoadSrcTgtVersion2(char** wordsave, RangeSet& tgt, size_t& src_blocks, uint8_t** buffer,
Tao Bao0940fe12015-08-27 16:41:21 -0700798 size_t* buffer_alloc, int fd, const std::string& stashbase, bool* overlap) {
Doug Zongker52ae67d2014-09-08 12:22:09 -0700799 char* word;
Sami Tolvanen90221202014-12-09 16:39:47 +0000800 char* colonsave;
801 char* colon;
Tao Bao0940fe12015-08-27 16:41:21 -0700802 RangeSet locs;
Sami Tolvanen90221202014-12-09 16:39:47 +0000803 size_t stashalloc = 0;
Tao Bao0940fe12015-08-27 16:41:21 -0700804 uint8_t* stash = nullptr;
Doug Zongker52ae67d2014-09-08 12:22:09 -0700805
Tao Bao34847b22015-09-08 11:05:49 -0700806 word = strtok_r(nullptr, " ", wordsave);
807 parse_range(word, tgt);
Doug Zongker52ae67d2014-09-08 12:22:09 -0700808
Tao Bao0940fe12015-08-27 16:41:21 -0700809 word = strtok_r(nullptr, " ", wordsave);
810 src_blocks = strtol(word, nullptr, 0);
Doug Zongker52ae67d2014-09-08 12:22:09 -0700811
Tao Bao0940fe12015-08-27 16:41:21 -0700812 allocate(src_blocks * BLOCKSIZE, buffer, buffer_alloc);
Doug Zongker52ae67d2014-09-08 12:22:09 -0700813
Tao Bao0940fe12015-08-27 16:41:21 -0700814 word = strtok_r(nullptr, " ", wordsave);
Doug Zongker52ae67d2014-09-08 12:22:09 -0700815 if (word[0] == '-' && word[1] == '\0') {
816 // no source ranges, only stashes
817 } else {
Tao Bao0940fe12015-08-27 16:41:21 -0700818 RangeSet src;
819 parse_range(word, src);
820 int res = ReadBlocks(src, *buffer, fd);
Doug Zongker52ae67d2014-09-08 12:22:09 -0700821
Tao Bao34847b22015-09-08 11:05:49 -0700822 if (overlap) {
823 *overlap = range_overlaps(src, tgt);
Doug Zongker52ae67d2014-09-08 12:22:09 -0700824 }
Sami Tolvanen90221202014-12-09 16:39:47 +0000825
Sami Tolvanen90221202014-12-09 16:39:47 +0000826 if (res == -1) {
827 return -1;
Doug Zongker52ae67d2014-09-08 12:22:09 -0700828 }
829
Tao Bao0940fe12015-08-27 16:41:21 -0700830 word = strtok_r(nullptr, " ", wordsave);
831 if (word == nullptr) {
Sami Tolvanen90221202014-12-09 16:39:47 +0000832 // no stashes, only source range
833 return 0;
834 }
835
Tao Bao0940fe12015-08-27 16:41:21 -0700836 parse_range(word, locs);
Doug Zongker52ae67d2014-09-08 12:22:09 -0700837 MoveRange(*buffer, locs, *buffer);
838 }
839
Tao Bao0940fe12015-08-27 16:41:21 -0700840 while ((word = strtok_r(nullptr, " ", wordsave)) != nullptr) {
Doug Zongker52ae67d2014-09-08 12:22:09 -0700841 // Each word is a an index into the stash table, a colon, and
842 // then a rangeset describing where in the source block that
843 // stashed data should go.
Tao Bao0940fe12015-08-27 16:41:21 -0700844 colonsave = nullptr;
Sami Tolvanen90221202014-12-09 16:39:47 +0000845 colon = strtok_r(word, ":", &colonsave);
846
Tao Bao0940fe12015-08-27 16:41:21 -0700847 int res = LoadStash(stashbase, std::string(colon), false, nullptr, &stash, &stashalloc, true);
Sami Tolvanen90221202014-12-09 16:39:47 +0000848
849 if (res == -1) {
850 // These source blocks will fail verification if used later, but we
851 // will let the caller decide if this is a fatal failure
852 fprintf(stderr, "failed to load stash %s\n", colon);
853 continue;
854 }
855
Tao Bao0940fe12015-08-27 16:41:21 -0700856 colon = strtok_r(nullptr, ":", &colonsave);
857 parse_range(colon, locs);
Sami Tolvanen90221202014-12-09 16:39:47 +0000858
859 MoveRange(*buffer, locs, stash);
Doug Zongker52ae67d2014-09-08 12:22:09 -0700860 }
Sami Tolvanen90221202014-12-09 16:39:47 +0000861
862 if (stash) {
863 free(stash);
864 }
865
866 return 0;
867}
868
869// Parameters for transfer list command functions
Tao Bao0940fe12015-08-27 16:41:21 -0700870struct CommandParameters {
Sami Tolvanen90221202014-12-09 16:39:47 +0000871 char* cmdname;
872 char* cpos;
873 char* freestash;
Tao Baoe6aa3322015-08-05 15:20:27 -0700874 std::string stashbase;
875 bool canwrite;
Sami Tolvanen90221202014-12-09 16:39:47 +0000876 int createdstash;
877 int fd;
Tao Bao0940fe12015-08-27 16:41:21 -0700878 bool foundwrites;
Tao Baoe6aa3322015-08-05 15:20:27 -0700879 bool isunresumable;
Sami Tolvanen90221202014-12-09 16:39:47 +0000880 int version;
Tao Bao0940fe12015-08-27 16:41:21 -0700881 size_t written;
Sami Tolvanen90221202014-12-09 16:39:47 +0000882 NewThreadInfo nti;
883 pthread_t thread;
884 size_t bufsize;
885 uint8_t* buffer;
886 uint8_t* patch_start;
Tao Bao0940fe12015-08-27 16:41:21 -0700887};
Sami Tolvanen90221202014-12-09 16:39:47 +0000888
889// Do a source/target load for move/bsdiff/imgdiff in version 3.
890//
891// Parameters are the same as for LoadSrcTgtVersion2, except for 'onehash', which
892// tells the function whether to expect separate source and targe block hashes, or
893// if they are both the same and only one hash should be expected, and
894// 'isunresumable', which receives a non-zero value if block verification fails in
895// a way that the update cannot be resumed anymore.
896//
897// If the function is unable to load the necessary blocks or their contents don't
898// match the hashes, the return value is -1 and the command should be aborted.
899//
900// If the return value is 1, the command has already been completed according to
901// the contents of the target blocks, and should not be performed again.
902//
903// If the return value is 0, source blocks have expected content and the command
904// can be performed.
905
Tao Bao34847b22015-09-08 11:05:49 -0700906static int LoadSrcTgtVersion3(CommandParameters& params, RangeSet& tgt, size_t& src_blocks,
Tao Bao0940fe12015-08-27 16:41:21 -0700907 bool onehash, bool& overlap) {
Sami Tolvanen90221202014-12-09 16:39:47 +0000908
Tao Bao0940fe12015-08-27 16:41:21 -0700909 char* srchash = strtok_r(nullptr, " ", &params.cpos);
910 if (srchash == nullptr) {
Sami Tolvanen90221202014-12-09 16:39:47 +0000911 fprintf(stderr, "missing source hash\n");
Tao Bao0940fe12015-08-27 16:41:21 -0700912 return -1;
Sami Tolvanen90221202014-12-09 16:39:47 +0000913 }
914
Tao Bao0940fe12015-08-27 16:41:21 -0700915 char* tgthash = nullptr;
Sami Tolvanen90221202014-12-09 16:39:47 +0000916 if (onehash) {
917 tgthash = srchash;
918 } else {
Tao Bao0940fe12015-08-27 16:41:21 -0700919 tgthash = strtok_r(nullptr, " ", &params.cpos);
Sami Tolvanen90221202014-12-09 16:39:47 +0000920
Tao Bao0940fe12015-08-27 16:41:21 -0700921 if (tgthash == nullptr) {
Sami Tolvanen90221202014-12-09 16:39:47 +0000922 fprintf(stderr, "missing target hash\n");
Tao Bao0940fe12015-08-27 16:41:21 -0700923 return -1;
Sami Tolvanen90221202014-12-09 16:39:47 +0000924 }
925 }
926
Tao Bao0940fe12015-08-27 16:41:21 -0700927 if (LoadSrcTgtVersion2(&params.cpos, tgt, src_blocks, &params.buffer, &params.bufsize,
928 params.fd, params.stashbase, &overlap) == -1) {
929 return -1;
Sami Tolvanen90221202014-12-09 16:39:47 +0000930 }
931
Tao Bao34847b22015-09-08 11:05:49 -0700932 std::vector<uint8_t> tgtbuffer(tgt.size * BLOCKSIZE);
Sami Tolvanen90221202014-12-09 16:39:47 +0000933
Tao Bao34847b22015-09-08 11:05:49 -0700934 if (ReadBlocks(tgt, tgtbuffer.data(), params.fd) == -1) {
Tao Bao0940fe12015-08-27 16:41:21 -0700935 return -1;
Sami Tolvanen90221202014-12-09 16:39:47 +0000936 }
937
Tao Bao34847b22015-09-08 11:05:49 -0700938 if (VerifyBlocks(tgthash, tgtbuffer.data(), tgt.size, false) == 0) {
Sami Tolvanen90221202014-12-09 16:39:47 +0000939 // Target blocks already have expected content, command should be skipped
Tao Bao0940fe12015-08-27 16:41:21 -0700940 fprintf(stderr, "verified, to return 1");
941 return 1;
Sami Tolvanen90221202014-12-09 16:39:47 +0000942 }
943
Tao Bao0940fe12015-08-27 16:41:21 -0700944 if (VerifyBlocks(srchash, params.buffer, src_blocks, true) == 0) {
Sami Tolvanen90221202014-12-09 16:39:47 +0000945 // If source and target blocks overlap, stash the source blocks so we can
946 // resume from possible write errors
Tao Bao0940fe12015-08-27 16:41:21 -0700947 if (overlap) {
948 fprintf(stderr, "stashing %zu overlapping blocks to %s\n", src_blocks, srchash);
Sami Tolvanen90221202014-12-09 16:39:47 +0000949
Tao Bao0940fe12015-08-27 16:41:21 -0700950 bool stash_exists = false;
951 if (WriteStash(params.stashbase, std::string(srchash), src_blocks, params.buffer, true,
952 &stash_exists) != 0) {
Sami Tolvanen90221202014-12-09 16:39:47 +0000953 fprintf(stderr, "failed to stash overlapping source blocks\n");
Tao Bao0940fe12015-08-27 16:41:21 -0700954 return -1;
Sami Tolvanen90221202014-12-09 16:39:47 +0000955 }
956
957 // Can be deleted when the write has completed
Sami Tolvanen43b748f2015-04-17 12:50:31 +0100958 if (!stash_exists) {
Tao Bao0940fe12015-08-27 16:41:21 -0700959 params.freestash = srchash;
Sami Tolvanen43b748f2015-04-17 12:50:31 +0100960 }
Sami Tolvanen90221202014-12-09 16:39:47 +0000961 }
962
963 // Source blocks have expected content, command can proceed
Tao Bao0940fe12015-08-27 16:41:21 -0700964 return 0;
Sami Tolvanen90221202014-12-09 16:39:47 +0000965 }
966
Tao Bao0940fe12015-08-27 16:41:21 -0700967 if (overlap && LoadStash(params.stashbase, std::string(srchash), true, nullptr, &params.buffer,
968 &params.bufsize, true) == 0) {
Sami Tolvanen43b748f2015-04-17 12:50:31 +0100969 // Overlapping source blocks were previously stashed, command can proceed.
970 // We are recovering from an interrupted command, so we don't know if the
971 // stash can safely be deleted after this command.
Tao Bao0940fe12015-08-27 16:41:21 -0700972 return 0;
Sami Tolvanen90221202014-12-09 16:39:47 +0000973 }
974
975 // Valid source data not available, update cannot be resumed
976 fprintf(stderr, "partition has unexpected contents\n");
Tao Bao0940fe12015-08-27 16:41:21 -0700977 params.isunresumable = true;
Sami Tolvanen90221202014-12-09 16:39:47 +0000978
Tao Bao0940fe12015-08-27 16:41:21 -0700979 return -1;
Sami Tolvanen90221202014-12-09 16:39:47 +0000980}
981
Tao Bao0940fe12015-08-27 16:41:21 -0700982static int PerformCommandMove(CommandParameters& params) {
983 size_t blocks = 0;
Tao Baoe6aa3322015-08-05 15:20:27 -0700984 bool overlap = false;
Sami Tolvanen90221202014-12-09 16:39:47 +0000985 int status = 0;
Tao Bao0940fe12015-08-27 16:41:21 -0700986 RangeSet tgt;
Sami Tolvanen90221202014-12-09 16:39:47 +0000987
Tao Bao0940fe12015-08-27 16:41:21 -0700988 if (params.version == 1) {
Tao Bao34847b22015-09-08 11:05:49 -0700989 status = LoadSrcTgtVersion1(&params.cpos, tgt, blocks, &params.buffer,
Tao Bao0940fe12015-08-27 16:41:21 -0700990 &params.bufsize, params.fd);
991 } else if (params.version == 2) {
Tao Bao34847b22015-09-08 11:05:49 -0700992 status = LoadSrcTgtVersion2(&params.cpos, tgt, blocks, &params.buffer,
Tao Bao0940fe12015-08-27 16:41:21 -0700993 &params.bufsize, params.fd, params.stashbase, nullptr);
994 } else if (params.version >= 3) {
Tao Bao34847b22015-09-08 11:05:49 -0700995 status = LoadSrcTgtVersion3(params, tgt, blocks, true, overlap);
Sami Tolvanen90221202014-12-09 16:39:47 +0000996 }
997
998 if (status == -1) {
999 fprintf(stderr, "failed to read blocks for move\n");
Tao Bao0940fe12015-08-27 16:41:21 -07001000 return -1;
Sami Tolvanen90221202014-12-09 16:39:47 +00001001 }
1002
1003 if (status == 0) {
Tao Bao0940fe12015-08-27 16:41:21 -07001004 params.foundwrites = true;
1005 } else if (params.foundwrites) {
1006 fprintf(stderr, "warning: commands executed out of order [%s]\n", params.cmdname);
Sami Tolvanen90221202014-12-09 16:39:47 +00001007 }
1008
Tao Bao0940fe12015-08-27 16:41:21 -07001009 if (params.canwrite) {
Sami Tolvanen90221202014-12-09 16:39:47 +00001010 if (status == 0) {
Tao Bao0940fe12015-08-27 16:41:21 -07001011 fprintf(stderr, " moving %zu blocks\n", blocks);
Sami Tolvanen90221202014-12-09 16:39:47 +00001012
Tao Bao0940fe12015-08-27 16:41:21 -07001013 if (WriteBlocks(tgt, params.buffer, params.fd) == -1) {
1014 return -1;
Sami Tolvanen90221202014-12-09 16:39:47 +00001015 }
1016 } else {
Tao Bao0940fe12015-08-27 16:41:21 -07001017 fprintf(stderr, "skipping %zu already moved blocks\n", blocks);
Sami Tolvanen90221202014-12-09 16:39:47 +00001018 }
1019
1020 }
1021
Tao Bao0940fe12015-08-27 16:41:21 -07001022 if (params.freestash) {
1023 FreeStash(params.stashbase, params.freestash);
1024 params.freestash = nullptr;
Sami Tolvanen90221202014-12-09 16:39:47 +00001025 }
1026
Tao Bao0940fe12015-08-27 16:41:21 -07001027 params.written += tgt.size;
Sami Tolvanen90221202014-12-09 16:39:47 +00001028
Tao Bao0940fe12015-08-27 16:41:21 -07001029 return 0;
Sami Tolvanen90221202014-12-09 16:39:47 +00001030}
1031
Tao Bao0940fe12015-08-27 16:41:21 -07001032static int PerformCommandStash(CommandParameters& params) {
1033 return SaveStash(params.stashbase, &params.cpos, &params.buffer, &params.bufsize,
1034 params.fd, (params.version >= 3));
Sami Tolvanen90221202014-12-09 16:39:47 +00001035}
1036
Tao Bao0940fe12015-08-27 16:41:21 -07001037static int PerformCommandFree(CommandParameters& params) {
1038 if (params.createdstash || params.canwrite) {
1039 return FreeStash(params.stashbase, params.cpos);
Sami Tolvanen90221202014-12-09 16:39:47 +00001040 }
1041
1042 return 0;
1043}
1044
Tao Bao0940fe12015-08-27 16:41:21 -07001045static int PerformCommandZero(CommandParameters& params) {
1046 char* range = strtok_r(nullptr, " ", &params.cpos);
Sami Tolvanen90221202014-12-09 16:39:47 +00001047
Tao Bao0940fe12015-08-27 16:41:21 -07001048 if (range == nullptr) {
Sami Tolvanen90221202014-12-09 16:39:47 +00001049 fprintf(stderr, "missing target blocks for zero\n");
Tao Bao0940fe12015-08-27 16:41:21 -07001050 return -1;
Sami Tolvanen90221202014-12-09 16:39:47 +00001051 }
1052
Tao Bao0940fe12015-08-27 16:41:21 -07001053 RangeSet tgt;
1054 parse_range(range, tgt);
Sami Tolvanen90221202014-12-09 16:39:47 +00001055
Tao Bao0940fe12015-08-27 16:41:21 -07001056 fprintf(stderr, " zeroing %zu blocks\n", tgt.size);
Sami Tolvanen90221202014-12-09 16:39:47 +00001057
Tao Bao0940fe12015-08-27 16:41:21 -07001058 allocate(BLOCKSIZE, &params.buffer, &params.bufsize);
1059 memset(params.buffer, 0, BLOCKSIZE);
Sami Tolvanen90221202014-12-09 16:39:47 +00001060
Tao Bao0940fe12015-08-27 16:41:21 -07001061 if (params.canwrite) {
1062 for (size_t i = 0; i < tgt.count; ++i) {
1063 if (!check_lseek(params.fd, (off64_t) tgt.pos[i * 2] * BLOCKSIZE, SEEK_SET)) {
1064 return -1;
Sami Tolvanen90221202014-12-09 16:39:47 +00001065 }
1066
Tao Bao0940fe12015-08-27 16:41:21 -07001067 for (size_t j = tgt.pos[i * 2]; j < tgt.pos[i * 2 + 1]; ++j) {
1068 if (write_all(params.fd, params.buffer, BLOCKSIZE) == -1) {
1069 return -1;
Sami Tolvanen90221202014-12-09 16:39:47 +00001070 }
1071 }
1072 }
1073 }
1074
Tao Bao0940fe12015-08-27 16:41:21 -07001075 if (params.cmdname[0] == 'z') {
Sami Tolvanene82fa182015-06-10 15:58:12 +00001076 // Update only for the zero command, as the erase command will call
1077 // this if DEBUG_ERASE is defined.
Tao Bao0940fe12015-08-27 16:41:21 -07001078 params.written += tgt.size;
Sami Tolvanen90221202014-12-09 16:39:47 +00001079 }
1080
Tao Bao0940fe12015-08-27 16:41:21 -07001081 return 0;
Sami Tolvanen90221202014-12-09 16:39:47 +00001082}
1083
Tao Bao0940fe12015-08-27 16:41:21 -07001084static int PerformCommandNew(CommandParameters& params) {
1085 char* range = strtok_r(nullptr, " ", &params.cpos);
Sami Tolvanen90221202014-12-09 16:39:47 +00001086
Tao Bao0940fe12015-08-27 16:41:21 -07001087 if (range == nullptr) {
1088 return -1;
Sami Tolvanen90221202014-12-09 16:39:47 +00001089 }
1090
Tao Bao0940fe12015-08-27 16:41:21 -07001091 RangeSet tgt;
1092 parse_range(range, tgt);
Sami Tolvanen90221202014-12-09 16:39:47 +00001093
Tao Bao0940fe12015-08-27 16:41:21 -07001094 if (params.canwrite) {
1095 fprintf(stderr, " writing %zu blocks of new data\n", tgt.size);
Sami Tolvanen90221202014-12-09 16:39:47 +00001096
Tao Bao0940fe12015-08-27 16:41:21 -07001097 RangeSinkState rss(tgt);
1098 rss.fd = params.fd;
Sami Tolvanen90221202014-12-09 16:39:47 +00001099 rss.p_block = 0;
Tao Bao0940fe12015-08-27 16:41:21 -07001100 rss.p_remain = (tgt.pos[1] - tgt.pos[0]) * BLOCKSIZE;
Sami Tolvanen90221202014-12-09 16:39:47 +00001101
Tao Bao0940fe12015-08-27 16:41:21 -07001102 if (!check_lseek(params.fd, (off64_t) tgt.pos[0] * BLOCKSIZE, SEEK_SET)) {
1103 return -1;
Sami Tolvanen90221202014-12-09 16:39:47 +00001104 }
1105
Tao Bao0940fe12015-08-27 16:41:21 -07001106 pthread_mutex_lock(&params.nti.mu);
1107 params.nti.rss = &rss;
1108 pthread_cond_broadcast(&params.nti.cv);
Sami Tolvanen90221202014-12-09 16:39:47 +00001109
Tao Bao0940fe12015-08-27 16:41:21 -07001110 while (params.nti.rss) {
1111 pthread_cond_wait(&params.nti.cv, &params.nti.mu);
Sami Tolvanen90221202014-12-09 16:39:47 +00001112 }
1113
Tao Bao0940fe12015-08-27 16:41:21 -07001114 pthread_mutex_unlock(&params.nti.mu);
Sami Tolvanen90221202014-12-09 16:39:47 +00001115 }
1116
Tao Bao0940fe12015-08-27 16:41:21 -07001117 params.written += tgt.size;
Sami Tolvanen90221202014-12-09 16:39:47 +00001118
Tao Bao0940fe12015-08-27 16:41:21 -07001119 return 0;
Sami Tolvanen90221202014-12-09 16:39:47 +00001120}
1121
Tao Bao0940fe12015-08-27 16:41:21 -07001122static int PerformCommandDiff(CommandParameters& params) {
1123 char* value = nullptr;
Tao Baoe6aa3322015-08-05 15:20:27 -07001124 bool overlap = false;
Tao Bao0940fe12015-08-27 16:41:21 -07001125 RangeSet tgt;
1126
1127 const std::string logparams(params.cpos);
1128 value = strtok_r(nullptr, " ", &params.cpos);
1129
1130 if (value == nullptr) {
1131 fprintf(stderr, "missing patch offset for %s\n", params.cmdname);
1132 return -1;
1133 }
1134
1135 size_t offset = strtoul(value, nullptr, 0);
1136
1137 value = strtok_r(nullptr, " ", &params.cpos);
1138
1139 if (value == nullptr) {
1140 fprintf(stderr, "missing patch length for %s\n", params.cmdname);
1141 return -1;
1142 }
1143
1144 size_t len = strtoul(value, nullptr, 0);
1145
1146 size_t blocks = 0;
Sami Tolvanen90221202014-12-09 16:39:47 +00001147 int status = 0;
Tao Bao0940fe12015-08-27 16:41:21 -07001148 if (params.version == 1) {
Tao Bao34847b22015-09-08 11:05:49 -07001149 status = LoadSrcTgtVersion1(&params.cpos, tgt, blocks, &params.buffer,
Tao Bao0940fe12015-08-27 16:41:21 -07001150 &params.bufsize, params.fd);
1151 } else if (params.version == 2) {
Tao Bao34847b22015-09-08 11:05:49 -07001152 status = LoadSrcTgtVersion2(&params.cpos, tgt, blocks, &params.buffer,
Tao Bao0940fe12015-08-27 16:41:21 -07001153 &params.bufsize, params.fd, params.stashbase, nullptr);
1154 } else if (params.version >= 3) {
Tao Bao34847b22015-09-08 11:05:49 -07001155 status = LoadSrcTgtVersion3(params, tgt, blocks, false, overlap);
Sami Tolvanen90221202014-12-09 16:39:47 +00001156 }
1157
1158 if (status == -1) {
1159 fprintf(stderr, "failed to read blocks for diff\n");
Tao Bao0940fe12015-08-27 16:41:21 -07001160 return -1;
Sami Tolvanen90221202014-12-09 16:39:47 +00001161 }
1162
1163 if (status == 0) {
Tao Bao0940fe12015-08-27 16:41:21 -07001164 params.foundwrites = true;
1165 } else if (params.foundwrites) {
1166 fprintf(stderr, "warning: commands executed out of order [%s]\n", params.cmdname);
Sami Tolvanen90221202014-12-09 16:39:47 +00001167 }
1168
Tao Bao0940fe12015-08-27 16:41:21 -07001169 if (params.canwrite) {
Sami Tolvanen90221202014-12-09 16:39:47 +00001170 if (status == 0) {
Tao Bao0940fe12015-08-27 16:41:21 -07001171 fprintf(stderr, "patching %zu blocks to %zu\n", blocks, tgt.size);
Sami Tolvanen90221202014-12-09 16:39:47 +00001172
Tao Bao0940fe12015-08-27 16:41:21 -07001173 Value patch_value;
Sami Tolvanen90221202014-12-09 16:39:47 +00001174 patch_value.type = VAL_BLOB;
1175 patch_value.size = len;
Tao Bao0940fe12015-08-27 16:41:21 -07001176 patch_value.data = (char*) (params.patch_start + offset);
Sami Tolvanen90221202014-12-09 16:39:47 +00001177
Tao Bao0940fe12015-08-27 16:41:21 -07001178 RangeSinkState rss(tgt);
1179 rss.fd = params.fd;
Sami Tolvanen90221202014-12-09 16:39:47 +00001180 rss.p_block = 0;
Tao Bao0940fe12015-08-27 16:41:21 -07001181 rss.p_remain = (tgt.pos[1] - tgt.pos[0]) * BLOCKSIZE;
Sami Tolvanen90221202014-12-09 16:39:47 +00001182
Tao Bao0940fe12015-08-27 16:41:21 -07001183 if (!check_lseek(params.fd, (off64_t) tgt.pos[0] * BLOCKSIZE, SEEK_SET)) {
1184 return -1;
Sami Tolvanen90221202014-12-09 16:39:47 +00001185 }
1186
Tao Bao0940fe12015-08-27 16:41:21 -07001187 if (params.cmdname[0] == 'i') { // imgdiff
1188 ApplyImagePatch(params.buffer, blocks * BLOCKSIZE, &patch_value,
1189 &RangeSinkWrite, &rss, nullptr, nullptr);
Sami Tolvanen90221202014-12-09 16:39:47 +00001190 } else {
Tao Bao0940fe12015-08-27 16:41:21 -07001191 ApplyBSDiffPatch(params.buffer, blocks * BLOCKSIZE, &patch_value,
1192 0, &RangeSinkWrite, &rss, nullptr);
Sami Tolvanen90221202014-12-09 16:39:47 +00001193 }
1194
1195 // We expect the output of the patcher to fill the tgt ranges exactly.
Tao Bao0940fe12015-08-27 16:41:21 -07001196 if (rss.p_block != tgt.count || rss.p_remain != 0) {
Sami Tolvanen90221202014-12-09 16:39:47 +00001197 fprintf(stderr, "range sink underrun?\n");
1198 }
1199 } else {
Tao Bao0940fe12015-08-27 16:41:21 -07001200 fprintf(stderr, "skipping %zu blocks already patched to %zu [%s]\n",
1201 blocks, tgt.size, logparams.c_str());
Sami Tolvanen90221202014-12-09 16:39:47 +00001202 }
1203 }
1204
Tao Bao0940fe12015-08-27 16:41:21 -07001205 if (params.freestash) {
1206 FreeStash(params.stashbase, params.freestash);
1207 params.freestash = nullptr;
Sami Tolvanen90221202014-12-09 16:39:47 +00001208 }
1209
Tao Bao0940fe12015-08-27 16:41:21 -07001210 params.written += tgt.size;
Sami Tolvanen90221202014-12-09 16:39:47 +00001211
Tao Bao0940fe12015-08-27 16:41:21 -07001212 return 0;
Sami Tolvanen90221202014-12-09 16:39:47 +00001213}
1214
Tao Bao0940fe12015-08-27 16:41:21 -07001215static int PerformCommandErase(CommandParameters& params) {
Sami Tolvanene82fa182015-06-10 15:58:12 +00001216 if (DEBUG_ERASE) {
1217 return PerformCommandZero(params);
Sami Tolvanen90221202014-12-09 16:39:47 +00001218 }
1219
Tao Bao0940fe12015-08-27 16:41:21 -07001220 struct stat sb;
1221 if (fstat(params.fd, &sb) == -1) {
Elliott Hughes1fdd4522015-03-23 13:33:57 -07001222 fprintf(stderr, "failed to fstat device to erase: %s\n", strerror(errno));
Tao Bao0940fe12015-08-27 16:41:21 -07001223 return -1;
Sami Tolvanen90221202014-12-09 16:39:47 +00001224 }
1225
Tao Bao0940fe12015-08-27 16:41:21 -07001226 if (!S_ISBLK(sb.st_mode)) {
Sami Tolvanen90221202014-12-09 16:39:47 +00001227 fprintf(stderr, "not a block device; skipping erase\n");
Tao Bao0940fe12015-08-27 16:41:21 -07001228 return -1;
Sami Tolvanen90221202014-12-09 16:39:47 +00001229 }
1230
Tao Bao0940fe12015-08-27 16:41:21 -07001231 char* range = strtok_r(nullptr, " ", &params.cpos);
Sami Tolvanen90221202014-12-09 16:39:47 +00001232
Tao Bao0940fe12015-08-27 16:41:21 -07001233 if (range == nullptr) {
Tao Baoba9a42a2015-06-23 23:23:33 -07001234 fprintf(stderr, "missing target blocks for erase\n");
Tao Bao0940fe12015-08-27 16:41:21 -07001235 return -1;
Sami Tolvanen90221202014-12-09 16:39:47 +00001236 }
1237
Tao Bao0940fe12015-08-27 16:41:21 -07001238 RangeSet tgt;
1239 parse_range(range, tgt);
Sami Tolvanen90221202014-12-09 16:39:47 +00001240
Tao Bao0940fe12015-08-27 16:41:21 -07001241 if (params.canwrite) {
1242 fprintf(stderr, " erasing %zu blocks\n", tgt.size);
Sami Tolvanen90221202014-12-09 16:39:47 +00001243
Tao Bao0940fe12015-08-27 16:41:21 -07001244 for (size_t i = 0; i < tgt.count; ++i) {
1245 uint64_t blocks[2];
Sami Tolvanen90221202014-12-09 16:39:47 +00001246 // offset in bytes
Tao Bao0940fe12015-08-27 16:41:21 -07001247 blocks[0] = tgt.pos[i * 2] * (uint64_t) BLOCKSIZE;
Sami Tolvanen90221202014-12-09 16:39:47 +00001248 // length in bytes
Tao Bao0940fe12015-08-27 16:41:21 -07001249 blocks[1] = (tgt.pos[i * 2 + 1] - tgt.pos[i * 2]) * (uint64_t) BLOCKSIZE;
Sami Tolvanen90221202014-12-09 16:39:47 +00001250
Tao Bao0940fe12015-08-27 16:41:21 -07001251 if (ioctl(params.fd, BLKDISCARD, &blocks) == -1) {
Elliott Hughes1fdd4522015-03-23 13:33:57 -07001252 fprintf(stderr, "BLKDISCARD ioctl failed: %s\n", strerror(errno));
Tao Bao0940fe12015-08-27 16:41:21 -07001253 return -1;
Sami Tolvanen90221202014-12-09 16:39:47 +00001254 }
1255 }
1256 }
1257
Tao Bao0940fe12015-08-27 16:41:21 -07001258 return 0;
Sami Tolvanen90221202014-12-09 16:39:47 +00001259}
1260
1261// Definitions for transfer list command functions
Tao Bao0940fe12015-08-27 16:41:21 -07001262typedef int (*CommandFunction)(CommandParameters&);
Sami Tolvanen90221202014-12-09 16:39:47 +00001263
1264typedef struct {
1265 const char* name;
1266 CommandFunction f;
1267} Command;
1268
1269// CompareCommands and CompareCommandNames are for the hash table
1270
1271static int CompareCommands(const void* c1, const void* c2) {
1272 return strcmp(((const Command*) c1)->name, ((const Command*) c2)->name);
1273}
1274
1275static int CompareCommandNames(const void* c1, const void* c2) {
1276 return strcmp(((const Command*) c1)->name, (const char*) c2);
1277}
1278
1279// HashString is used to hash command names for the hash table
1280
1281static unsigned int HashString(const char *s) {
1282 unsigned int hash = 0;
1283 if (s) {
1284 while (*s) {
1285 hash = hash * 33 + *s++;
1286 }
1287 }
1288 return hash;
Doug Zongker52ae67d2014-09-08 12:22:09 -07001289}
1290
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001291// args:
1292// - block device (or file) to modify in-place
1293// - transfer list (blob)
1294// - new data stream (filename within package.zip)
1295// - patch stream (filename within package.zip, must be uncompressed)
1296
Tao Bao0940fe12015-08-27 16:41:21 -07001297static Value* PerformBlockImageUpdate(const char* name, State* state, int /* argc */, Expr* argv[],
1298 const Command* commands, size_t cmdcount, bool dryrun) {
Sami Tolvanen90221202014-12-09 16:39:47 +00001299
Tao Bao0940fe12015-08-27 16:41:21 -07001300 char* line = nullptr;
1301 char* linesave = nullptr;
1302 char* transfer_list = nullptr;
Sami Tolvanen90221202014-12-09 16:39:47 +00001303 CommandParameters params;
Tao Bao0940fe12015-08-27 16:41:21 -07001304 const Command* cmd = nullptr;
1305 const ZipEntry* new_entry = nullptr;
1306 const ZipEntry* patch_entry = nullptr;
1307 FILE* cmd_pipe = nullptr;
1308 HashTable* cmdht = nullptr;
Sami Tolvanen90221202014-12-09 16:39:47 +00001309 int res;
1310 int rc = -1;
Sami Tolvanen90221202014-12-09 16:39:47 +00001311 int total_blocks = 0;
1312 pthread_attr_t attr;
Tao Bao0940fe12015-08-27 16:41:21 -07001313 UpdaterInfo* ui = nullptr;
1314 Value* blockdev_filename = nullptr;
1315 Value* new_data_fn = nullptr;
1316 Value* patch_data_fn = nullptr;
1317 Value* transfer_list_value = nullptr;
1318 ZipArchive* za = nullptr;
Sami Tolvanen90221202014-12-09 16:39:47 +00001319
1320 memset(&params, 0, sizeof(params));
1321 params.canwrite = !dryrun;
1322
1323 fprintf(stderr, "performing %s\n", dryrun ? "verification" : "update");
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001324
Doug Zongker1d5d6092014-08-21 10:47:24 -07001325 if (ReadValueArgs(state, argv, 4, &blockdev_filename, &transfer_list_value,
Sami Tolvanen90221202014-12-09 16:39:47 +00001326 &new_data_fn, &patch_data_fn) < 0) {
1327 goto pbiudone;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001328 }
1329
1330 if (blockdev_filename->type != VAL_STRING) {
1331 ErrorAbort(state, "blockdev_filename argument to %s must be string", name);
Sami Tolvanen90221202014-12-09 16:39:47 +00001332 goto pbiudone;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001333 }
Doug Zongker1d5d6092014-08-21 10:47:24 -07001334 if (transfer_list_value->type != VAL_BLOB) {
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001335 ErrorAbort(state, "transfer_list argument to %s must be blob", name);
Sami Tolvanen90221202014-12-09 16:39:47 +00001336 goto pbiudone;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001337 }
1338 if (new_data_fn->type != VAL_STRING) {
1339 ErrorAbort(state, "new_data_fn argument to %s must be string", name);
Sami Tolvanen90221202014-12-09 16:39:47 +00001340 goto pbiudone;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001341 }
1342 if (patch_data_fn->type != VAL_STRING) {
1343 ErrorAbort(state, "patch_data_fn argument to %s must be string", name);
Sami Tolvanen90221202014-12-09 16:39:47 +00001344 goto pbiudone;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001345 }
1346
Sami Tolvanen90221202014-12-09 16:39:47 +00001347 ui = (UpdaterInfo*) state->cookie;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001348
Tao Bao0940fe12015-08-27 16:41:21 -07001349 if (ui == nullptr) {
Sami Tolvanen90221202014-12-09 16:39:47 +00001350 goto pbiudone;
1351 }
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001352
Sami Tolvanen90221202014-12-09 16:39:47 +00001353 cmd_pipe = ui->cmd_pipe;
1354 za = ui->package_zip;
1355
Tao Bao0940fe12015-08-27 16:41:21 -07001356 if (cmd_pipe == nullptr || za == nullptr) {
Sami Tolvanen90221202014-12-09 16:39:47 +00001357 goto pbiudone;
1358 }
1359
1360 patch_entry = mzFindZipEntry(za, patch_data_fn->data);
1361
Tao Bao0940fe12015-08-27 16:41:21 -07001362 if (patch_entry == nullptr) {
Sami Tolvanen90221202014-12-09 16:39:47 +00001363 fprintf(stderr, "%s(): no file \"%s\" in package", name, patch_data_fn->data);
1364 goto pbiudone;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001365 }
1366
Sami Tolvanen90221202014-12-09 16:39:47 +00001367 params.patch_start = ui->package_zip_addr + mzGetZipEntryOffset(patch_entry);
1368 new_entry = mzFindZipEntry(za, new_data_fn->data);
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001369
Tao Bao0940fe12015-08-27 16:41:21 -07001370 if (new_entry == nullptr) {
Sami Tolvanen90221202014-12-09 16:39:47 +00001371 fprintf(stderr, "%s(): no file \"%s\" in package", name, new_data_fn->data);
1372 goto pbiudone;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001373 }
1374
Sami Tolvanen90221202014-12-09 16:39:47 +00001375 params.fd = TEMP_FAILURE_RETRY(open(blockdev_filename->data, O_RDWR));
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001376
Sami Tolvanen90221202014-12-09 16:39:47 +00001377 if (params.fd == -1) {
Elliott Hughes1fdd4522015-03-23 13:33:57 -07001378 fprintf(stderr, "open \"%s\" failed: %s\n", blockdev_filename->data, strerror(errno));
Sami Tolvanen90221202014-12-09 16:39:47 +00001379 goto pbiudone;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001380 }
1381
Sami Tolvanen90221202014-12-09 16:39:47 +00001382 if (params.canwrite) {
1383 params.nti.za = za;
1384 params.nti.entry = new_entry;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001385
Tao Bao0940fe12015-08-27 16:41:21 -07001386 pthread_mutex_init(&params.nti.mu, nullptr);
1387 pthread_cond_init(&params.nti.cv, nullptr);
Sami Tolvanen90221202014-12-09 16:39:47 +00001388 pthread_attr_init(&attr);
1389 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
1390
Elliott Hughes1fdd4522015-03-23 13:33:57 -07001391 int error = pthread_create(&params.thread, &attr, unzip_new_data, &params.nti);
1392 if (error != 0) {
1393 fprintf(stderr, "pthread_create failed: %s\n", strerror(error));
Sami Tolvanen90221202014-12-09 16:39:47 +00001394 goto pbiudone;
1395 }
1396 }
1397
1398 // The data in transfer_list_value is not necessarily null-terminated, so we need
1399 // to copy it to a new buffer and add the null that strtok_r will need.
Tao Baoba9a42a2015-06-23 23:23:33 -07001400 transfer_list = reinterpret_cast<char*>(malloc(transfer_list_value->size + 1));
Sami Tolvanen90221202014-12-09 16:39:47 +00001401
Tao Bao0940fe12015-08-27 16:41:21 -07001402 if (transfer_list == nullptr) {
Doug Zongker1d5d6092014-08-21 10:47:24 -07001403 fprintf(stderr, "failed to allocate %zd bytes for transfer list\n",
Sami Tolvanen90221202014-12-09 16:39:47 +00001404 transfer_list_value->size + 1);
1405 goto pbiudone;
Doug Zongker1d5d6092014-08-21 10:47:24 -07001406 }
Sami Tolvanen90221202014-12-09 16:39:47 +00001407
Doug Zongker1d5d6092014-08-21 10:47:24 -07001408 memcpy(transfer_list, transfer_list_value->data, transfer_list_value->size);
1409 transfer_list[transfer_list_value->size] = '\0';
1410
Sami Tolvanen90221202014-12-09 16:39:47 +00001411 // First line in transfer list is the version number
Doug Zongker1d5d6092014-08-21 10:47:24 -07001412 line = strtok_r(transfer_list, "\n", &linesave);
Tao Bao0940fe12015-08-27 16:41:21 -07001413 params.version = strtol(line, nullptr, 0);
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001414
Sami Tolvanen90221202014-12-09 16:39:47 +00001415 if (params.version < 1 || params.version > 3) {
1416 fprintf(stderr, "unexpected transfer list version [%s]\n", line);
1417 goto pbiudone;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001418 }
1419
Sami Tolvanen90221202014-12-09 16:39:47 +00001420 fprintf(stderr, "blockimg version is %d\n", params.version);
1421
1422 // Second line in transfer list is the total number of blocks we expect to write
Tao Bao0940fe12015-08-27 16:41:21 -07001423 line = strtok_r(nullptr, "\n", &linesave);
1424 total_blocks = strtol(line, nullptr, 0);
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001425
Sami Tolvanen90221202014-12-09 16:39:47 +00001426 if (total_blocks < 0) {
1427 ErrorAbort(state, "unexpected block count [%s]\n", line);
1428 goto pbiudone;
1429 } else if (total_blocks == 0) {
1430 rc = 0;
1431 goto pbiudone;
1432 }
1433
1434 if (params.version >= 2) {
1435 // Third line is how many stash entries are needed simultaneously
Tao Bao0940fe12015-08-27 16:41:21 -07001436 line = strtok_r(nullptr, "\n", &linesave);
Sami Tolvanen90221202014-12-09 16:39:47 +00001437 fprintf(stderr, "maximum stash entries %s\n", line);
Doug Zongker52ae67d2014-09-08 12:22:09 -07001438
Sami Tolvanen90221202014-12-09 16:39:47 +00001439 // Fourth line is the maximum number of blocks that will be stashed simultaneously
Tao Bao0940fe12015-08-27 16:41:21 -07001440 line = strtok_r(nullptr, "\n", &linesave);
1441 int stash_max_blocks = strtol(line, nullptr, 0);
Sami Tolvanen90221202014-12-09 16:39:47 +00001442
1443 if (stash_max_blocks < 0) {
1444 ErrorAbort(state, "unexpected maximum stash blocks [%s]\n", line);
1445 goto pbiudone;
Doug Zongker52ae67d2014-09-08 12:22:09 -07001446 }
1447
Jesse Zhao1df64d32015-02-17 17:09:23 -08001448 if (stash_max_blocks >= 0) {
Tao Baoe6aa3322015-08-05 15:20:27 -07001449 res = CreateStash(state, stash_max_blocks, blockdev_filename->data, params.stashbase);
Sami Tolvanen90221202014-12-09 16:39:47 +00001450
1451 if (res == -1) {
1452 goto pbiudone;
1453 }
1454
1455 params.createdstash = res;
1456 }
Doug Zongker52ae67d2014-09-08 12:22:09 -07001457 }
1458
Sami Tolvanen90221202014-12-09 16:39:47 +00001459 // Build a hash table of the available commands
Tao Bao0940fe12015-08-27 16:41:21 -07001460 cmdht = mzHashTableCreate(cmdcount, nullptr);
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001461
Tao Bao0940fe12015-08-27 16:41:21 -07001462 for (size_t i = 0; i < cmdcount; ++i) {
1463 unsigned int cmdhash = HashString(commands[i].name);
Sami Tolvanen90221202014-12-09 16:39:47 +00001464 mzHashTableLookup(cmdht, cmdhash, (void*) &commands[i], CompareCommands, true);
1465 }
1466
1467 // Subsequent lines are all individual transfer commands
Tao Bao0940fe12015-08-27 16:41:21 -07001468 for (line = strtok_r(nullptr, "\n", &linesave); line;
1469 line = strtok_r(nullptr, "\n", &linesave)) {
Doug Zongker52ae67d2014-09-08 12:22:09 -07001470
Tao Bao0940fe12015-08-27 16:41:21 -07001471 const std::string logcmd(line);
Sami Tolvanen90221202014-12-09 16:39:47 +00001472 params.cmdname = strtok_r(line, " ", &params.cpos);
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001473
Tao Bao0940fe12015-08-27 16:41:21 -07001474 if (params.cmdname == nullptr) {
Sami Tolvanen90221202014-12-09 16:39:47 +00001475 fprintf(stderr, "missing command [%s]\n", line);
1476 goto pbiudone;
1477 }
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001478
Tao Bao0940fe12015-08-27 16:41:21 -07001479 unsigned int cmdhash = HashString(params.cmdname);
Sami Tolvanen90221202014-12-09 16:39:47 +00001480 cmd = (const Command*) mzHashTableLookup(cmdht, cmdhash, params.cmdname,
1481 CompareCommandNames, false);
Doug Zongker52ae67d2014-09-08 12:22:09 -07001482
Tao Bao0940fe12015-08-27 16:41:21 -07001483 if (cmd == nullptr) {
Sami Tolvanen90221202014-12-09 16:39:47 +00001484 fprintf(stderr, "unexpected command [%s]\n", params.cmdname);
1485 goto pbiudone;
1486 }
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001487
Tao Bao0940fe12015-08-27 16:41:21 -07001488 if (cmd->f != nullptr && cmd->f(params) == -1) {
1489 fprintf(stderr, "failed to execute command [%s]\n", logcmd.c_str());
Sami Tolvanen90221202014-12-09 16:39:47 +00001490 goto pbiudone;
1491 }
1492
Sami Tolvanen90221202014-12-09 16:39:47 +00001493 if (params.canwrite) {
Tao Bao187efff2015-07-27 14:07:08 -07001494 if (fsync(params.fd) == -1) {
1495 fprintf(stderr, "fsync failed: %s\n", strerror(errno));
1496 goto pbiudone;
1497 }
Sami Tolvanen90221202014-12-09 16:39:47 +00001498 fprintf(cmd_pipe, "set_progress %.4f\n", (double) params.written / total_blocks);
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001499 fflush(cmd_pipe);
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001500 }
1501 }
1502
Sami Tolvanen90221202014-12-09 16:39:47 +00001503 if (params.canwrite) {
Tao Bao0940fe12015-08-27 16:41:21 -07001504 pthread_join(params.thread, nullptr);
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001505
Tao Bao0940fe12015-08-27 16:41:21 -07001506 fprintf(stderr, "wrote %zu blocks; expected %d\n", params.written, total_blocks);
Sami Tolvanen90221202014-12-09 16:39:47 +00001507 fprintf(stderr, "max alloc needed was %zu\n", params.bufsize);
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001508
Sami Tolvanen90221202014-12-09 16:39:47 +00001509 // Delete stash only after successfully completing the update, as it
1510 // may contain blocks needed to complete the update later.
1511 DeleteStash(params.stashbase);
1512 } else {
1513 fprintf(stderr, "verified partition contents; update may be resumed\n");
1514 }
1515
1516 rc = 0;
1517
1518pbiudone:
1519 if (params.fd != -1) {
1520 if (fsync(params.fd) == -1) {
Elliott Hughes1fdd4522015-03-23 13:33:57 -07001521 fprintf(stderr, "fsync failed: %s\n", strerror(errno));
Sami Tolvanen90221202014-12-09 16:39:47 +00001522 }
Elliott Hughesb47afed2015-05-15 16:19:20 -07001523 close(params.fd);
Sami Tolvanen90221202014-12-09 16:39:47 +00001524 }
1525
Sami Tolvanen90221202014-12-09 16:39:47 +00001526 if (cmdht) {
1527 mzHashTableFree(cmdht);
1528 }
1529
1530 if (params.buffer) {
1531 free(params.buffer);
1532 }
1533
1534 if (transfer_list) {
1535 free(transfer_list);
1536 }
1537
1538 if (blockdev_filename) {
1539 FreeValue(blockdev_filename);
1540 }
1541
1542 if (transfer_list_value) {
1543 FreeValue(transfer_list_value);
1544 }
1545
1546 if (new_data_fn) {
1547 FreeValue(new_data_fn);
1548 }
1549
1550 if (patch_data_fn) {
1551 FreeValue(patch_data_fn);
1552 }
1553
1554 // Only delete the stash if the update cannot be resumed, or it's
1555 // a verification run and we created the stash.
1556 if (params.isunresumable || (!params.canwrite && params.createdstash)) {
1557 DeleteStash(params.stashbase);
1558 }
1559
Sami Tolvanen90221202014-12-09 16:39:47 +00001560 return StringValue(rc == 0 ? strdup("t") : strdup(""));
1561}
1562
1563// The transfer list is a text file containing commands to
1564// transfer data from one place to another on the target
1565// partition. We parse it and execute the commands in order:
1566//
1567// zero [rangeset]
1568// - fill the indicated blocks with zeros
1569//
1570// new [rangeset]
1571// - fill the blocks with data read from the new_data file
1572//
1573// erase [rangeset]
1574// - mark the given blocks as empty
1575//
1576// move <...>
1577// bsdiff <patchstart> <patchlen> <...>
1578// imgdiff <patchstart> <patchlen> <...>
1579// - read the source blocks, apply a patch (or not in the
1580// case of move), write result to target blocks. bsdiff or
1581// imgdiff specifies the type of patch; move means no patch
1582// at all.
1583//
1584// The format of <...> differs between versions 1 and 2;
1585// see the LoadSrcTgtVersion{1,2}() functions for a
1586// description of what's expected.
1587//
1588// stash <stash_id> <src_range>
1589// - (version 2+ only) load the given source range and stash
1590// the data in the given slot of the stash table.
1591//
1592// The creator of the transfer list will guarantee that no block
1593// is read (ie, used as the source for a patch or move) after it
1594// has been written.
1595//
1596// In version 2, the creator will guarantee that a given stash is
1597// loaded (with a stash command) before it's used in a
1598// move/bsdiff/imgdiff command.
1599//
1600// Within one command the source and target ranges may overlap so
1601// in general we need to read the entire source into memory before
1602// writing anything to the target blocks.
1603//
1604// All the patch data is concatenated into one patch_data file in
1605// the update package. It must be stored uncompressed because we
1606// memory-map it in directly from the archive. (Since patches are
1607// already compressed, we lose very little by not compressing
1608// their concatenation.)
1609//
1610// In version 3, commands that read data from the partition (i.e.
1611// move/bsdiff/imgdiff/stash) have one or more additional hashes
1612// before the range parameters, which are used to check if the
1613// command has already been completed and verify the integrity of
1614// the source data.
1615
1616Value* BlockImageVerifyFn(const char* name, State* state, int argc, Expr* argv[]) {
Tao Bao0940fe12015-08-27 16:41:21 -07001617 // Commands which are not tested are set to nullptr to skip them completely
Sami Tolvanen90221202014-12-09 16:39:47 +00001618 const Command commands[] = {
1619 { "bsdiff", PerformCommandDiff },
Tao Bao0940fe12015-08-27 16:41:21 -07001620 { "erase", nullptr },
Sami Tolvanen90221202014-12-09 16:39:47 +00001621 { "free", PerformCommandFree },
1622 { "imgdiff", PerformCommandDiff },
1623 { "move", PerformCommandMove },
Tao Bao0940fe12015-08-27 16:41:21 -07001624 { "new", nullptr },
Sami Tolvanen90221202014-12-09 16:39:47 +00001625 { "stash", PerformCommandStash },
Tao Bao0940fe12015-08-27 16:41:21 -07001626 { "zero", nullptr }
Sami Tolvanen90221202014-12-09 16:39:47 +00001627 };
1628
1629 // Perform a dry run without writing to test if an update can proceed
1630 return PerformBlockImageUpdate(name, state, argc, argv, commands,
Tao Baoe6aa3322015-08-05 15:20:27 -07001631 sizeof(commands) / sizeof(commands[0]), true);
Sami Tolvanen90221202014-12-09 16:39:47 +00001632}
1633
1634Value* BlockImageUpdateFn(const char* name, State* state, int argc, Expr* argv[]) {
1635 const Command commands[] = {
1636 { "bsdiff", PerformCommandDiff },
1637 { "erase", PerformCommandErase },
1638 { "free", PerformCommandFree },
1639 { "imgdiff", PerformCommandDiff },
1640 { "move", PerformCommandMove },
1641 { "new", PerformCommandNew },
1642 { "stash", PerformCommandStash },
1643 { "zero", PerformCommandZero }
1644 };
1645
1646 return PerformBlockImageUpdate(name, state, argc, argv, commands,
Tao Baoe6aa3322015-08-05 15:20:27 -07001647 sizeof(commands) / sizeof(commands[0]), false);
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001648}
1649
Tao Bao0940fe12015-08-27 16:41:21 -07001650Value* RangeSha1Fn(const char* name, State* state, int /* argc */, Expr* argv[]) {
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001651 Value* blockdev_filename;
1652 Value* ranges;
Tao Bao0940fe12015-08-27 16:41:21 -07001653 const uint8_t* digest = nullptr;
1654 RangeSet rs;
1655
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001656 if (ReadValueArgs(state, argv, 2, &blockdev_filename, &ranges) < 0) {
Tao Bao0940fe12015-08-27 16:41:21 -07001657 return nullptr;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001658 }
1659
1660 if (blockdev_filename->type != VAL_STRING) {
1661 ErrorAbort(state, "blockdev_filename argument to %s must be string", name);
1662 goto done;
1663 }
1664 if (ranges->type != VAL_STRING) {
1665 ErrorAbort(state, "ranges argument to %s must be string", name);
1666 goto done;
1667 }
1668
Tao Baoba9a42a2015-06-23 23:23:33 -07001669 int fd;
1670 fd = open(blockdev_filename->data, O_RDWR);
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001671 if (fd < 0) {
Elliott Hughes1fdd4522015-03-23 13:33:57 -07001672 ErrorAbort(state, "open \"%s\" failed: %s", blockdev_filename->data, strerror(errno));
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001673 goto done;
1674 }
1675
Tao Bao0940fe12015-08-27 16:41:21 -07001676 parse_range(ranges->data, rs);
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001677 uint8_t buffer[BLOCKSIZE];
1678
1679 SHA_CTX ctx;
1680 SHA_init(&ctx);
1681
Tao Bao0940fe12015-08-27 16:41:21 -07001682 for (size_t i = 0; i < rs.count; ++i) {
1683 if (!check_lseek(fd, (off64_t)rs.pos[i*2] * BLOCKSIZE, SEEK_SET)) {
Sami Tolvanen90221202014-12-09 16:39:47 +00001684 ErrorAbort(state, "failed to seek %s: %s", blockdev_filename->data,
Tao Bao0940fe12015-08-27 16:41:21 -07001685 strerror(errno));
Sami Tolvanen90221202014-12-09 16:39:47 +00001686 goto done;
1687 }
1688
Tao Bao0940fe12015-08-27 16:41:21 -07001689 for (size_t j = rs.pos[i*2]; j < rs.pos[i*2+1]; ++j) {
Sami Tolvanen90221202014-12-09 16:39:47 +00001690 if (read_all(fd, buffer, BLOCKSIZE) == -1) {
1691 ErrorAbort(state, "failed to read %s: %s", blockdev_filename->data,
Tao Bao0940fe12015-08-27 16:41:21 -07001692 strerror(errno));
Sami Tolvanen90221202014-12-09 16:39:47 +00001693 goto done;
1694 }
1695
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001696 SHA_update(&ctx, buffer, BLOCKSIZE);
1697 }
1698 }
1699 digest = SHA_final(&ctx);
1700 close(fd);
1701
1702 done:
1703 FreeValue(blockdev_filename);
1704 FreeValue(ranges);
Tao Bao0940fe12015-08-27 16:41:21 -07001705 if (digest == nullptr) {
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001706 return StringValue(strdup(""));
1707 } else {
Tao Baoe6aa3322015-08-05 15:20:27 -07001708 return StringValue(strdup(print_sha1(digest).c_str()));
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001709 }
1710}
1711
1712void RegisterBlockImageFunctions() {
Sami Tolvanen90221202014-12-09 16:39:47 +00001713 RegisterFunction("block_image_verify", BlockImageVerifyFn);
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001714 RegisterFunction("block_image_update", BlockImageUpdateFn);
1715 RegisterFunction("range_sha1", RangeSha1Fn);
1716}