blob: 5f5f9bd7208e97acca40b04499dc64993114001e [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
381// it to make it larger if necessary. The target ranges are returned
Tao Bao0940fe12015-08-27 16:41:21 -0700382// in *tgt, if tgt is non-null.
Doug Zongker52ae67d2014-09-08 12:22:09 -0700383
Tao Bao0940fe12015-08-27 16:41:21 -0700384static int LoadSrcTgtVersion1(char** wordsave, RangeSet* tgt, size_t& src_blocks,
385 uint8_t** buffer, size_t* buffer_alloc, int fd) {
386 char* word = strtok_r(nullptr, " ", wordsave);
387 RangeSet src;
388 parse_range(word, src);
Doug Zongker52ae67d2014-09-08 12:22:09 -0700389
Tao Bao0940fe12015-08-27 16:41:21 -0700390 if (tgt != nullptr) {
391 word = strtok_r(nullptr, " ", wordsave);
392 parse_range(word, *tgt);
Doug Zongker52ae67d2014-09-08 12:22:09 -0700393 }
394
Tao Bao0940fe12015-08-27 16:41:21 -0700395 allocate(src.size * BLOCKSIZE, buffer, buffer_alloc);
396 int rc = ReadBlocks(src, *buffer, fd);
397 src_blocks = src.size;
Sami Tolvanen90221202014-12-09 16:39:47 +0000398
Sami Tolvanen90221202014-12-09 16:39:47 +0000399 return rc;
400}
401
Tao Bao0940fe12015-08-27 16:41:21 -0700402static int VerifyBlocks(const std::string& expected, const uint8_t* buffer,
403 const size_t blocks, bool printerror) {
Sami Tolvanen90221202014-12-09 16:39:47 +0000404 uint8_t digest[SHA_DIGEST_SIZE];
405
Tao Bao0940fe12015-08-27 16:41:21 -0700406 if (!buffer) {
407 return -1;
Doug Zongker52ae67d2014-09-08 12:22:09 -0700408 }
409
Sami Tolvanen90221202014-12-09 16:39:47 +0000410 SHA_hash(buffer, blocks * BLOCKSIZE, digest);
Sami Tolvanen90221202014-12-09 16:39:47 +0000411
Tao Baoe6aa3322015-08-05 15:20:27 -0700412 std::string hexdigest = print_sha1(digest);
Sami Tolvanen90221202014-12-09 16:39:47 +0000413
Tao Bao0940fe12015-08-27 16:41:21 -0700414 if (hexdigest != expected) {
415 if (printerror) {
416 fprintf(stderr, "failed to verify blocks (expected %s, read %s)\n",
417 expected.c_str(), hexdigest.c_str());
418 }
419 return -1;
Sami Tolvanen90221202014-12-09 16:39:47 +0000420 }
421
Tao Bao0940fe12015-08-27 16:41:21 -0700422 return 0;
Sami Tolvanen90221202014-12-09 16:39:47 +0000423}
424
Tao Bao0940fe12015-08-27 16:41:21 -0700425static std::string GetStashFileName(const std::string& base, const std::string& id,
426 const std::string& postfix) {
Tao Baoe6aa3322015-08-05 15:20:27 -0700427 if (base.empty()) {
428 return "";
Sami Tolvanen90221202014-12-09 16:39:47 +0000429 }
430
Tao Baoe6aa3322015-08-05 15:20:27 -0700431 std::string fn(STASH_DIRECTORY_BASE);
432 fn += "/" + base + "/" + id + postfix;
Sami Tolvanen90221202014-12-09 16:39:47 +0000433
434 return fn;
435}
436
Tao Baoe6aa3322015-08-05 15:20:27 -0700437typedef void (*StashCallback)(const std::string&, void*);
Sami Tolvanen90221202014-12-09 16:39:47 +0000438
439// Does a best effort enumeration of stash files. Ignores possible non-file
440// items in the stash directory and continues despite of errors. Calls the
441// 'callback' function for each file and passes 'data' to the function as a
442// parameter.
443
Tao Baoe6aa3322015-08-05 15:20:27 -0700444static void EnumerateStash(const std::string& dirname, StashCallback callback, void* data) {
Tao Bao0940fe12015-08-27 16:41:21 -0700445 if (dirname.empty() || callback == nullptr) {
Sami Tolvanen90221202014-12-09 16:39:47 +0000446 return;
447 }
448
Tao Baoe6aa3322015-08-05 15:20:27 -0700449 std::unique_ptr<DIR, int(*)(DIR*)> directory(opendir(dirname.c_str()), closedir);
Sami Tolvanen90221202014-12-09 16:39:47 +0000450
Tao Bao0940fe12015-08-27 16:41:21 -0700451 if (directory == nullptr) {
Sami Tolvanen90221202014-12-09 16:39:47 +0000452 if (errno != ENOENT) {
Tao Baoe6aa3322015-08-05 15:20:27 -0700453 fprintf(stderr, "opendir \"%s\" failed: %s\n", dirname.c_str(), strerror(errno));
Sami Tolvanen90221202014-12-09 16:39:47 +0000454 }
455 return;
456 }
457
Tao Baoe6aa3322015-08-05 15:20:27 -0700458 struct dirent* item;
Tao Bao0940fe12015-08-27 16:41:21 -0700459 while ((item = readdir(directory.get())) != nullptr) {
Sami Tolvanen90221202014-12-09 16:39:47 +0000460 if (item->d_type != DT_REG) {
461 continue;
462 }
463
Tao Baoe6aa3322015-08-05 15:20:27 -0700464 std::string fn = dirname + "/" + std::string(item->d_name);
Sami Tolvanen90221202014-12-09 16:39:47 +0000465 callback(fn, data);
Sami Tolvanen90221202014-12-09 16:39:47 +0000466 }
467}
468
Tao Baoe6aa3322015-08-05 15:20:27 -0700469static void UpdateFileSize(const std::string& fn, void* data) {
470 if (fn.empty() || !data) {
471 return;
472 }
473
Tao Bao0940fe12015-08-27 16:41:21 -0700474 struct stat sb;
475 if (stat(fn.c_str(), &sb) == -1) {
Tao Baoe6aa3322015-08-05 15:20:27 -0700476 fprintf(stderr, "stat \"%s\" failed: %s\n", fn.c_str(), strerror(errno));
Sami Tolvanen90221202014-12-09 16:39:47 +0000477 return;
478 }
479
Tao Baoe6aa3322015-08-05 15:20:27 -0700480 int* size = reinterpret_cast<int*>(data);
Tao Bao0940fe12015-08-27 16:41:21 -0700481 *size += sb.st_size;
Sami Tolvanen90221202014-12-09 16:39:47 +0000482}
483
484// Deletes the stash directory and all files in it. Assumes that it only
485// contains files. There is nothing we can do about unlikely, but possible
486// errors, so they are merely logged.
487
Tao Bao0940fe12015-08-27 16:41:21 -0700488static void DeleteFile(const std::string& fn, void* /* data */) {
Tao Baoe6aa3322015-08-05 15:20:27 -0700489 if (!fn.empty()) {
490 fprintf(stderr, "deleting %s\n", fn.c_str());
Sami Tolvanen90221202014-12-09 16:39:47 +0000491
Tao Baoe6aa3322015-08-05 15:20:27 -0700492 if (unlink(fn.c_str()) == -1 && errno != ENOENT) {
493 fprintf(stderr, "unlink \"%s\" failed: %s\n", fn.c_str(), strerror(errno));
Sami Tolvanen90221202014-12-09 16:39:47 +0000494 }
495 }
496}
497
Tao Baoe6aa3322015-08-05 15:20:27 -0700498static void DeletePartial(const std::string& fn, void* data) {
499 if (android::base::EndsWith(fn, ".partial")) {
Sami Tolvanen90221202014-12-09 16:39:47 +0000500 DeleteFile(fn, data);
501 }
502}
503
Tao Baoe6aa3322015-08-05 15:20:27 -0700504static void DeleteStash(const std::string& base) {
505 if (base.empty()) {
Sami Tolvanen90221202014-12-09 16:39:47 +0000506 return;
507 }
508
Tao Baoe6aa3322015-08-05 15:20:27 -0700509 fprintf(stderr, "deleting stash %s\n", base.c_str());
Sami Tolvanen90221202014-12-09 16:39:47 +0000510
Tao Baoe6aa3322015-08-05 15:20:27 -0700511 std::string dirname = GetStashFileName(base, "", "");
Tao Bao0940fe12015-08-27 16:41:21 -0700512 EnumerateStash(dirname, DeleteFile, nullptr);
Sami Tolvanen90221202014-12-09 16:39:47 +0000513
Tao Baoe6aa3322015-08-05 15:20:27 -0700514 if (rmdir(dirname.c_str()) == -1) {
Sami Tolvanen90221202014-12-09 16:39:47 +0000515 if (errno != ENOENT && errno != ENOTDIR) {
Tao Baoe6aa3322015-08-05 15:20:27 -0700516 fprintf(stderr, "rmdir \"%s\" failed: %s\n", dirname.c_str(), strerror(errno));
Sami Tolvanen90221202014-12-09 16:39:47 +0000517 }
518 }
Sami Tolvanen90221202014-12-09 16:39:47 +0000519}
520
Tao Bao0940fe12015-08-27 16:41:21 -0700521static int LoadStash(const std::string& base, const std::string& id, bool verify, size_t* blocks,
Tao Baoe6aa3322015-08-05 15:20:27 -0700522 uint8_t** buffer, size_t* buffer_alloc, bool printnoent) {
Tao Bao0940fe12015-08-27 16:41:21 -0700523 if (base.empty() || !buffer || !buffer_alloc) {
524 return -1;
Sami Tolvanen90221202014-12-09 16:39:47 +0000525 }
526
Tao Bao0940fe12015-08-27 16:41:21 -0700527 size_t blockcount = 0;
528
Sami Tolvanen90221202014-12-09 16:39:47 +0000529 if (!blocks) {
530 blocks = &blockcount;
531 }
532
Tao Bao0940fe12015-08-27 16:41:21 -0700533 std::string fn = GetStashFileName(base, id, "");
Sami Tolvanen90221202014-12-09 16:39:47 +0000534
Tao Bao0940fe12015-08-27 16:41:21 -0700535 struct stat sb;
536 int res = stat(fn.c_str(), &sb);
Sami Tolvanen90221202014-12-09 16:39:47 +0000537
538 if (res == -1) {
539 if (errno != ENOENT || printnoent) {
Tao Baoe6aa3322015-08-05 15:20:27 -0700540 fprintf(stderr, "stat \"%s\" failed: %s\n", fn.c_str(), strerror(errno));
Sami Tolvanen90221202014-12-09 16:39:47 +0000541 }
Tao Bao0940fe12015-08-27 16:41:21 -0700542 return -1;
Sami Tolvanen90221202014-12-09 16:39:47 +0000543 }
544
Tao Baoe6aa3322015-08-05 15:20:27 -0700545 fprintf(stderr, " loading %s\n", fn.c_str());
Sami Tolvanen90221202014-12-09 16:39:47 +0000546
Tao Bao0940fe12015-08-27 16:41:21 -0700547 if ((sb.st_size % BLOCKSIZE) != 0) {
Tao Baoba9a42a2015-06-23 23:23:33 -0700548 fprintf(stderr, "%s size %" PRId64 " not multiple of block size %d",
Tao Bao0940fe12015-08-27 16:41:21 -0700549 fn.c_str(), static_cast<int64_t>(sb.st_size), BLOCKSIZE);
550 return -1;
Sami Tolvanen90221202014-12-09 16:39:47 +0000551 }
552
Tao Bao0940fe12015-08-27 16:41:21 -0700553 int fd = TEMP_FAILURE_RETRY(open(fn.c_str(), O_RDONLY));
554 unique_fd fd_holder(fd);
Sami Tolvanen90221202014-12-09 16:39:47 +0000555
556 if (fd == -1) {
Tao Baoe6aa3322015-08-05 15:20:27 -0700557 fprintf(stderr, "open \"%s\" failed: %s\n", fn.c_str(), strerror(errno));
Tao Bao0940fe12015-08-27 16:41:21 -0700558 return -1;
Sami Tolvanen90221202014-12-09 16:39:47 +0000559 }
560
Tao Bao0940fe12015-08-27 16:41:21 -0700561 allocate(sb.st_size, buffer, buffer_alloc);
Sami Tolvanen90221202014-12-09 16:39:47 +0000562
Tao Bao0940fe12015-08-27 16:41:21 -0700563 if (read_all(fd, *buffer, sb.st_size) == -1) {
564 return -1;
Sami Tolvanen90221202014-12-09 16:39:47 +0000565 }
566
Tao Bao0940fe12015-08-27 16:41:21 -0700567 *blocks = sb.st_size / BLOCKSIZE;
Sami Tolvanen90221202014-12-09 16:39:47 +0000568
Tao Baoe6aa3322015-08-05 15:20:27 -0700569 if (verify && VerifyBlocks(id, *buffer, *blocks, true) != 0) {
570 fprintf(stderr, "unexpected contents in %s\n", fn.c_str());
Tao Bao0940fe12015-08-27 16:41:21 -0700571 DeleteFile(fn, nullptr);
572 return -1;
Sami Tolvanen90221202014-12-09 16:39:47 +0000573 }
574
Tao Bao0940fe12015-08-27 16:41:21 -0700575 return 0;
Sami Tolvanen90221202014-12-09 16:39:47 +0000576}
577
Tao Bao0940fe12015-08-27 16:41:21 -0700578static int WriteStash(const std::string& base, const std::string& id, int blocks, uint8_t* buffer,
579 bool checkspace, bool *exists) {
580 if (base.empty() || buffer == nullptr) {
581 return -1;
Sami Tolvanen90221202014-12-09 16:39:47 +0000582 }
583
584 if (checkspace && CacheSizeCheck(blocks * BLOCKSIZE) != 0) {
585 fprintf(stderr, "not enough space to write stash\n");
Tao Bao0940fe12015-08-27 16:41:21 -0700586 return -1;
Sami Tolvanen90221202014-12-09 16:39:47 +0000587 }
588
Tao Bao0940fe12015-08-27 16:41:21 -0700589 std::string fn = GetStashFileName(base, id, ".partial");
590 std::string cn = GetStashFileName(base, id, "");
Sami Tolvanen90221202014-12-09 16:39:47 +0000591
Sami Tolvanen43b748f2015-04-17 12:50:31 +0100592 if (exists) {
Tao Bao0940fe12015-08-27 16:41:21 -0700593 struct stat sb;
594 int res = stat(cn.c_str(), &sb);
Sami Tolvanen43b748f2015-04-17 12:50:31 +0100595
596 if (res == 0) {
597 // The file already exists and since the name is the hash of the contents,
598 // it's safe to assume the contents are identical (accidental hash collisions
599 // are unlikely)
Tao Baoe6aa3322015-08-05 15:20:27 -0700600 fprintf(stderr, " skipping %d existing blocks in %s\n", blocks, cn.c_str());
Tao Bao0940fe12015-08-27 16:41:21 -0700601 *exists = true;
602 return 0;
Sami Tolvanen43b748f2015-04-17 12:50:31 +0100603 }
604
Tao Bao0940fe12015-08-27 16:41:21 -0700605 *exists = false;
Sami Tolvanen43b748f2015-04-17 12:50:31 +0100606 }
607
Tao Baoe6aa3322015-08-05 15:20:27 -0700608 fprintf(stderr, " writing %d blocks to %s\n", blocks, cn.c_str());
Sami Tolvanen90221202014-12-09 16:39:47 +0000609
Tao Bao0940fe12015-08-27 16:41:21 -0700610 int fd = TEMP_FAILURE_RETRY(open(fn.c_str(), O_WRONLY | O_CREAT | O_TRUNC, STASH_FILE_MODE));
611 unique_fd fd_holder(fd);
Sami Tolvanen90221202014-12-09 16:39:47 +0000612
613 if (fd == -1) {
Tao Baoe6aa3322015-08-05 15:20:27 -0700614 fprintf(stderr, "failed to create \"%s\": %s\n", fn.c_str(), strerror(errno));
Tao Bao0940fe12015-08-27 16:41:21 -0700615 return -1;
Sami Tolvanen90221202014-12-09 16:39:47 +0000616 }
617
618 if (write_all(fd, buffer, blocks * BLOCKSIZE) == -1) {
Tao Bao0940fe12015-08-27 16:41:21 -0700619 return -1;
Sami Tolvanen90221202014-12-09 16:39:47 +0000620 }
621
622 if (fsync(fd) == -1) {
Tao Baoe6aa3322015-08-05 15:20:27 -0700623 fprintf(stderr, "fsync \"%s\" failed: %s\n", fn.c_str(), strerror(errno));
Tao Bao0940fe12015-08-27 16:41:21 -0700624 return -1;
Sami Tolvanen90221202014-12-09 16:39:47 +0000625 }
626
Tao Baoe6aa3322015-08-05 15:20:27 -0700627 if (rename(fn.c_str(), cn.c_str()) == -1) {
628 fprintf(stderr, "rename(\"%s\", \"%s\") failed: %s\n", fn.c_str(), cn.c_str(),
629 strerror(errno));
Tao Bao0940fe12015-08-27 16:41:21 -0700630 return -1;
Sami Tolvanen90221202014-12-09 16:39:47 +0000631 }
632
Tao Bao0940fe12015-08-27 16:41:21 -0700633 std::string dname = GetStashFileName(base, "", "");
634 int dfd = TEMP_FAILURE_RETRY(open(dname.c_str(), O_RDONLY | O_DIRECTORY));
635 unique_fd dfd_holder(dfd);
Tao Baodc392262015-07-31 15:56:44 -0700636
637 if (dfd == -1) {
Tao Baoe6aa3322015-08-05 15:20:27 -0700638 fprintf(stderr, "failed to open \"%s\" failed: %s\n", dname.c_str(), strerror(errno));
Tao Bao0940fe12015-08-27 16:41:21 -0700639 return -1;
Tao Baodc392262015-07-31 15:56:44 -0700640 }
641
642 if (fsync(dfd) == -1) {
Tao Baoe6aa3322015-08-05 15:20:27 -0700643 fprintf(stderr, "fsync \"%s\" failed: %s\n", dname.c_str(), strerror(errno));
Tao Bao0940fe12015-08-27 16:41:21 -0700644 return -1;
Tao Baodc392262015-07-31 15:56:44 -0700645 }
646
Tao Bao0940fe12015-08-27 16:41:21 -0700647 return 0;
Sami Tolvanen90221202014-12-09 16:39:47 +0000648}
649
650// Creates a directory for storing stash files and checks if the /cache partition
651// hash enough space for the expected amount of blocks we need to store. Returns
652// >0 if we created the directory, zero if it existed already, and <0 of failure.
653
Tao Bao0940fe12015-08-27 16:41:21 -0700654static int CreateStash(State* state, int maxblocks, const char* blockdev, std::string& base) {
655 if (blockdev == nullptr) {
Tao Baoe6aa3322015-08-05 15:20:27 -0700656 return -1;
Sami Tolvanen90221202014-12-09 16:39:47 +0000657 }
658
659 // Stash directory should be different for each partition to avoid conflicts
660 // when updating multiple partitions at the same time, so we use the hash of
661 // the block device name as the base directory
Tao Baoe6aa3322015-08-05 15:20:27 -0700662 SHA_CTX ctx;
Sami Tolvanen90221202014-12-09 16:39:47 +0000663 SHA_init(&ctx);
664 SHA_update(&ctx, blockdev, strlen(blockdev));
Tao Baoe6aa3322015-08-05 15:20:27 -0700665 const uint8_t* digest = SHA_final(&ctx);
666 base = print_sha1(digest);
Sami Tolvanen90221202014-12-09 16:39:47 +0000667
Tao Baoe6aa3322015-08-05 15:20:27 -0700668 std::string dirname = GetStashFileName(base, "", "");
Tao Bao0940fe12015-08-27 16:41:21 -0700669 struct stat sb;
670 int res = stat(dirname.c_str(), &sb);
Sami Tolvanen90221202014-12-09 16:39:47 +0000671
672 if (res == -1 && errno != ENOENT) {
Tao Baoe6aa3322015-08-05 15:20:27 -0700673 ErrorAbort(state, "stat \"%s\" failed: %s\n", dirname.c_str(), strerror(errno));
674 return -1;
Sami Tolvanen90221202014-12-09 16:39:47 +0000675 } else if (res != 0) {
Tao Baoe6aa3322015-08-05 15:20:27 -0700676 fprintf(stderr, "creating stash %s\n", dirname.c_str());
677 res = mkdir(dirname.c_str(), STASH_DIRECTORY_MODE);
Sami Tolvanen90221202014-12-09 16:39:47 +0000678
679 if (res != 0) {
Tao Baoe6aa3322015-08-05 15:20:27 -0700680 ErrorAbort(state, "mkdir \"%s\" failed: %s\n", dirname.c_str(), strerror(errno));
681 return -1;
Sami Tolvanen90221202014-12-09 16:39:47 +0000682 }
683
684 if (CacheSizeCheck(maxblocks * BLOCKSIZE) != 0) {
685 ErrorAbort(state, "not enough space for stash\n");
Tao Baoe6aa3322015-08-05 15:20:27 -0700686 return -1;
Sami Tolvanen90221202014-12-09 16:39:47 +0000687 }
688
Tao Baoe6aa3322015-08-05 15:20:27 -0700689 return 1; // Created directory
Sami Tolvanen90221202014-12-09 16:39:47 +0000690 }
691
Tao Baoe6aa3322015-08-05 15:20:27 -0700692 fprintf(stderr, "using existing stash %s\n", dirname.c_str());
Sami Tolvanen90221202014-12-09 16:39:47 +0000693
694 // If the directory already exists, calculate the space already allocated to
695 // stash files and check if there's enough for all required blocks. Delete any
696 // partially completed stash files first.
697
Tao Bao0940fe12015-08-27 16:41:21 -0700698 EnumerateStash(dirname, DeletePartial, nullptr);
Tao Baoe6aa3322015-08-05 15:20:27 -0700699 int size = 0;
Sami Tolvanen90221202014-12-09 16:39:47 +0000700 EnumerateStash(dirname, UpdateFileSize, &size);
701
Tao Bao0940fe12015-08-27 16:41:21 -0700702 size = maxblocks * BLOCKSIZE - size;
Sami Tolvanen90221202014-12-09 16:39:47 +0000703
704 if (size > 0 && CacheSizeCheck(size) != 0) {
705 ErrorAbort(state, "not enough space for stash (%d more needed)\n", size);
Tao Baoe6aa3322015-08-05 15:20:27 -0700706 return -1;
Sami Tolvanen90221202014-12-09 16:39:47 +0000707 }
708
Tao Baoe6aa3322015-08-05 15:20:27 -0700709 return 0; // Using existing directory
Sami Tolvanen90221202014-12-09 16:39:47 +0000710}
711
Tao Baoe6aa3322015-08-05 15:20:27 -0700712static int SaveStash(const std::string& base, char** wordsave, uint8_t** buffer,
Tao Bao0940fe12015-08-27 16:41:21 -0700713 size_t* buffer_alloc, int fd, bool usehash) {
Tao Bao9739a292015-08-05 12:16:20 -0700714 if (!wordsave || !buffer || !buffer_alloc) {
Sami Tolvanen90221202014-12-09 16:39:47 +0000715 return -1;
716 }
717
Tao Bao0940fe12015-08-27 16:41:21 -0700718 char *id_tok = strtok_r(nullptr, " ", wordsave);
719 if (id_tok == nullptr) {
Sami Tolvanen90221202014-12-09 16:39:47 +0000720 fprintf(stderr, "missing id field in stash command\n");
721 return -1;
722 }
Tao Bao0940fe12015-08-27 16:41:21 -0700723 std::string id(id_tok);
Sami Tolvanen90221202014-12-09 16:39:47 +0000724
Tao Bao0940fe12015-08-27 16:41:21 -0700725 size_t blocks = 0;
726 if (usehash && LoadStash(base, id, true, &blocks, buffer, buffer_alloc, false) == 0) {
Sami Tolvanen90221202014-12-09 16:39:47 +0000727 // Stash file already exists and has expected contents. Do not
728 // read from source again, as the source may have been already
729 // overwritten during a previous attempt.
730 return 0;
731 }
732
Tao Bao0940fe12015-08-27 16:41:21 -0700733 if (LoadSrcTgtVersion1(wordsave, nullptr, blocks, buffer, buffer_alloc, fd) == -1) {
Sami Tolvanen90221202014-12-09 16:39:47 +0000734 return -1;
735 }
736
Tao Baoe6aa3322015-08-05 15:20:27 -0700737 if (usehash && VerifyBlocks(id, *buffer, blocks, true) != 0) {
Sami Tolvanen90221202014-12-09 16:39:47 +0000738 // Source blocks have unexpected contents. If we actually need this
739 // data later, this is an unrecoverable error. However, the command
740 // that uses the data may have already completed previously, so the
741 // possible failure will occur during source block verification.
Tao Bao0940fe12015-08-27 16:41:21 -0700742 fprintf(stderr, "failed to load source blocks for stash %s\n", id.c_str());
Sami Tolvanen90221202014-12-09 16:39:47 +0000743 return 0;
744 }
745
Tao Bao0940fe12015-08-27 16:41:21 -0700746 fprintf(stderr, "stashing %zu blocks to %s\n", blocks, id.c_str());
747 return WriteStash(base, id, blocks, *buffer, false, nullptr);
Sami Tolvanen90221202014-12-09 16:39:47 +0000748}
749
Tao Baoe6aa3322015-08-05 15:20:27 -0700750static int FreeStash(const std::string& base, const char* id) {
Tao Bao0940fe12015-08-27 16:41:21 -0700751 if (base.empty() || id == nullptr) {
Sami Tolvanen90221202014-12-09 16:39:47 +0000752 return -1;
753 }
754
Tao Baoe6aa3322015-08-05 15:20:27 -0700755 std::string fn = GetStashFileName(base, std::string(id), "");
Sami Tolvanen90221202014-12-09 16:39:47 +0000756
Tao Bao0940fe12015-08-27 16:41:21 -0700757 DeleteFile(fn, nullptr);
Sami Tolvanen90221202014-12-09 16:39:47 +0000758
759 return 0;
Doug Zongker52ae67d2014-09-08 12:22:09 -0700760}
761
Tao Bao0940fe12015-08-27 16:41:21 -0700762static void MoveRange(uint8_t* dest, const RangeSet& locs, const uint8_t* source) {
Doug Zongker52ae67d2014-09-08 12:22:09 -0700763 // source contains packed data, which we want to move to the
764 // locations given in *locs in the dest buffer. source and dest
765 // may be the same buffer.
766
Tao Bao0940fe12015-08-27 16:41:21 -0700767 size_t start = locs.size;
768 for (int i = locs.count-1; i >= 0; --i) {
769 size_t blocks = locs.pos[i*2+1] - locs.pos[i*2];
Doug Zongker52ae67d2014-09-08 12:22:09 -0700770 start -= blocks;
Tao Bao0940fe12015-08-27 16:41:21 -0700771 memmove(dest + (locs.pos[i*2] * BLOCKSIZE), source + (start * BLOCKSIZE),
Doug Zongker52ae67d2014-09-08 12:22:09 -0700772 blocks * BLOCKSIZE);
773 }
774}
775
776// Do a source/target load for move/bsdiff/imgdiff in version 2.
777// 'wordsave' is the save_ptr of a strtok_r()-in-progress. We expect
778// to parse the remainder of the string as one of:
779//
780// <tgt_range> <src_block_count> <src_range>
781// (loads data from source image only)
782//
783// <tgt_range> <src_block_count> - <[stash_id:stash_range] ...>
784// (loads data from stashes only)
785//
786// <tgt_range> <src_block_count> <src_range> <src_loc> <[stash_id:stash_range] ...>
787// (loads data from both source image and stashes)
788//
789// On return, buffer is filled with the loaded source data (rearranged
790// and combined with stashed data as necessary). buffer may be
791// reallocated if needed to accommodate the source data. *tgt is the
Sami Tolvanen90221202014-12-09 16:39:47 +0000792// target RangeSet. Any stashes required are loaded using LoadStash.
Doug Zongker52ae67d2014-09-08 12:22:09 -0700793
Tao Bao0940fe12015-08-27 16:41:21 -0700794static int LoadSrcTgtVersion2(char** wordsave, RangeSet* tgt, size_t& src_blocks, uint8_t** buffer,
795 size_t* buffer_alloc, int fd, const std::string& stashbase, bool* overlap) {
Doug Zongker52ae67d2014-09-08 12:22:09 -0700796 char* word;
Sami Tolvanen90221202014-12-09 16:39:47 +0000797 char* colonsave;
798 char* colon;
Tao Bao0940fe12015-08-27 16:41:21 -0700799 RangeSet locs;
Sami Tolvanen90221202014-12-09 16:39:47 +0000800 size_t stashalloc = 0;
Tao Bao0940fe12015-08-27 16:41:21 -0700801 uint8_t* stash = nullptr;
Doug Zongker52ae67d2014-09-08 12:22:09 -0700802
Tao Bao0940fe12015-08-27 16:41:21 -0700803 if (tgt != nullptr) {
804 word = strtok_r(nullptr, " ", wordsave);
805 parse_range(word, *tgt);
Doug Zongker52ae67d2014-09-08 12:22:09 -0700806 }
807
Tao Bao0940fe12015-08-27 16:41:21 -0700808 word = strtok_r(nullptr, " ", wordsave);
809 src_blocks = strtol(word, nullptr, 0);
Doug Zongker52ae67d2014-09-08 12:22:09 -0700810
Tao Bao0940fe12015-08-27 16:41:21 -0700811 allocate(src_blocks * BLOCKSIZE, buffer, buffer_alloc);
Doug Zongker52ae67d2014-09-08 12:22:09 -0700812
Tao Bao0940fe12015-08-27 16:41:21 -0700813 word = strtok_r(nullptr, " ", wordsave);
Doug Zongker52ae67d2014-09-08 12:22:09 -0700814 if (word[0] == '-' && word[1] == '\0') {
815 // no source ranges, only stashes
816 } else {
Tao Bao0940fe12015-08-27 16:41:21 -0700817 RangeSet src;
818 parse_range(word, src);
819 int res = ReadBlocks(src, *buffer, fd);
Doug Zongker52ae67d2014-09-08 12:22:09 -0700820
Sami Tolvanen90221202014-12-09 16:39:47 +0000821 if (overlap && tgt) {
Tao Bao0940fe12015-08-27 16:41:21 -0700822 *overlap = range_overlaps(src, *tgt);
Doug Zongker52ae67d2014-09-08 12:22:09 -0700823 }
Sami Tolvanen90221202014-12-09 16:39:47 +0000824
Sami Tolvanen90221202014-12-09 16:39:47 +0000825 if (res == -1) {
826 return -1;
Doug Zongker52ae67d2014-09-08 12:22:09 -0700827 }
828
Tao Bao0940fe12015-08-27 16:41:21 -0700829 word = strtok_r(nullptr, " ", wordsave);
830 if (word == nullptr) {
Sami Tolvanen90221202014-12-09 16:39:47 +0000831 // no stashes, only source range
832 return 0;
833 }
834
Tao Bao0940fe12015-08-27 16:41:21 -0700835 parse_range(word, locs);
Doug Zongker52ae67d2014-09-08 12:22:09 -0700836 MoveRange(*buffer, locs, *buffer);
837 }
838
Tao Bao0940fe12015-08-27 16:41:21 -0700839 while ((word = strtok_r(nullptr, " ", wordsave)) != nullptr) {
Doug Zongker52ae67d2014-09-08 12:22:09 -0700840 // Each word is a an index into the stash table, a colon, and
841 // then a rangeset describing where in the source block that
842 // stashed data should go.
Tao Bao0940fe12015-08-27 16:41:21 -0700843 colonsave = nullptr;
Sami Tolvanen90221202014-12-09 16:39:47 +0000844 colon = strtok_r(word, ":", &colonsave);
845
Tao Bao0940fe12015-08-27 16:41:21 -0700846 int res = LoadStash(stashbase, std::string(colon), false, nullptr, &stash, &stashalloc, true);
Sami Tolvanen90221202014-12-09 16:39:47 +0000847
848 if (res == -1) {
849 // These source blocks will fail verification if used later, but we
850 // will let the caller decide if this is a fatal failure
851 fprintf(stderr, "failed to load stash %s\n", colon);
852 continue;
853 }
854
Tao Bao0940fe12015-08-27 16:41:21 -0700855 colon = strtok_r(nullptr, ":", &colonsave);
856 parse_range(colon, locs);
Sami Tolvanen90221202014-12-09 16:39:47 +0000857
858 MoveRange(*buffer, locs, stash);
Doug Zongker52ae67d2014-09-08 12:22:09 -0700859 }
Sami Tolvanen90221202014-12-09 16:39:47 +0000860
861 if (stash) {
862 free(stash);
863 }
864
865 return 0;
866}
867
868// Parameters for transfer list command functions
Tao Bao0940fe12015-08-27 16:41:21 -0700869struct CommandParameters {
Sami Tolvanen90221202014-12-09 16:39:47 +0000870 char* cmdname;
871 char* cpos;
872 char* freestash;
Tao Baoe6aa3322015-08-05 15:20:27 -0700873 std::string stashbase;
874 bool canwrite;
Sami Tolvanen90221202014-12-09 16:39:47 +0000875 int createdstash;
876 int fd;
Tao Bao0940fe12015-08-27 16:41:21 -0700877 bool foundwrites;
Tao Baoe6aa3322015-08-05 15:20:27 -0700878 bool isunresumable;
Sami Tolvanen90221202014-12-09 16:39:47 +0000879 int version;
Tao Bao0940fe12015-08-27 16:41:21 -0700880 size_t written;
Sami Tolvanen90221202014-12-09 16:39:47 +0000881 NewThreadInfo nti;
882 pthread_t thread;
883 size_t bufsize;
884 uint8_t* buffer;
885 uint8_t* patch_start;
Tao Bao0940fe12015-08-27 16:41:21 -0700886};
Sami Tolvanen90221202014-12-09 16:39:47 +0000887
888// Do a source/target load for move/bsdiff/imgdiff in version 3.
889//
890// Parameters are the same as for LoadSrcTgtVersion2, except for 'onehash', which
891// tells the function whether to expect separate source and targe block hashes, or
892// if they are both the same and only one hash should be expected, and
893// 'isunresumable', which receives a non-zero value if block verification fails in
894// a way that the update cannot be resumed anymore.
895//
896// If the function is unable to load the necessary blocks or their contents don't
897// match the hashes, the return value is -1 and the command should be aborted.
898//
899// If the return value is 1, the command has already been completed according to
900// the contents of the target blocks, and should not be performed again.
901//
902// If the return value is 0, source blocks have expected content and the command
903// can be performed.
904
Tao Bao0940fe12015-08-27 16:41:21 -0700905static int LoadSrcTgtVersion3(CommandParameters& params, RangeSet* tgt, size_t& src_blocks,
906 bool onehash, bool& overlap) {
Sami Tolvanen90221202014-12-09 16:39:47 +0000907
Tao Bao0940fe12015-08-27 16:41:21 -0700908 if (!tgt) {
909 return -1;
Sami Tolvanen90221202014-12-09 16:39:47 +0000910 }
911
Tao Bao0940fe12015-08-27 16:41:21 -0700912 char* srchash = strtok_r(nullptr, " ", &params.cpos);
913 if (srchash == nullptr) {
Sami Tolvanen90221202014-12-09 16:39:47 +0000914 fprintf(stderr, "missing source hash\n");
Tao Bao0940fe12015-08-27 16:41:21 -0700915 return -1;
Sami Tolvanen90221202014-12-09 16:39:47 +0000916 }
917
Tao Bao0940fe12015-08-27 16:41:21 -0700918 char* tgthash = nullptr;
Sami Tolvanen90221202014-12-09 16:39:47 +0000919 if (onehash) {
920 tgthash = srchash;
921 } else {
Tao Bao0940fe12015-08-27 16:41:21 -0700922 tgthash = strtok_r(nullptr, " ", &params.cpos);
Sami Tolvanen90221202014-12-09 16:39:47 +0000923
Tao Bao0940fe12015-08-27 16:41:21 -0700924 if (tgthash == nullptr) {
Sami Tolvanen90221202014-12-09 16:39:47 +0000925 fprintf(stderr, "missing target hash\n");
Tao Bao0940fe12015-08-27 16:41:21 -0700926 return -1;
Sami Tolvanen90221202014-12-09 16:39:47 +0000927 }
928 }
929
Tao Bao0940fe12015-08-27 16:41:21 -0700930 if (LoadSrcTgtVersion2(&params.cpos, tgt, src_blocks, &params.buffer, &params.bufsize,
931 params.fd, params.stashbase, &overlap) == -1) {
932 return -1;
Sami Tolvanen90221202014-12-09 16:39:47 +0000933 }
934
Tao Bao0940fe12015-08-27 16:41:21 -0700935 std::vector<uint8_t> tgtbuffer(tgt->size * BLOCKSIZE);
Sami Tolvanen90221202014-12-09 16:39:47 +0000936
Tao Bao0940fe12015-08-27 16:41:21 -0700937 if (ReadBlocks(*tgt, tgtbuffer.data(), params.fd) == -1) {
938 return -1;
Sami Tolvanen90221202014-12-09 16:39:47 +0000939 }
940
Tao Bao0940fe12015-08-27 16:41:21 -0700941 if (VerifyBlocks(tgthash, tgtbuffer.data(), tgt->size, false) == 0) {
Sami Tolvanen90221202014-12-09 16:39:47 +0000942 // Target blocks already have expected content, command should be skipped
Tao Bao0940fe12015-08-27 16:41:21 -0700943 fprintf(stderr, "verified, to return 1");
944 return 1;
Sami Tolvanen90221202014-12-09 16:39:47 +0000945 }
946
Tao Bao0940fe12015-08-27 16:41:21 -0700947 if (VerifyBlocks(srchash, params.buffer, src_blocks, true) == 0) {
Sami Tolvanen90221202014-12-09 16:39:47 +0000948 // If source and target blocks overlap, stash the source blocks so we can
949 // resume from possible write errors
Tao Bao0940fe12015-08-27 16:41:21 -0700950 if (overlap) {
951 fprintf(stderr, "stashing %zu overlapping blocks to %s\n", src_blocks, srchash);
Sami Tolvanen90221202014-12-09 16:39:47 +0000952
Tao Bao0940fe12015-08-27 16:41:21 -0700953 bool stash_exists = false;
954 if (WriteStash(params.stashbase, std::string(srchash), src_blocks, params.buffer, true,
955 &stash_exists) != 0) {
Sami Tolvanen90221202014-12-09 16:39:47 +0000956 fprintf(stderr, "failed to stash overlapping source blocks\n");
Tao Bao0940fe12015-08-27 16:41:21 -0700957 return -1;
Sami Tolvanen90221202014-12-09 16:39:47 +0000958 }
959
960 // Can be deleted when the write has completed
Sami Tolvanen43b748f2015-04-17 12:50:31 +0100961 if (!stash_exists) {
Tao Bao0940fe12015-08-27 16:41:21 -0700962 params.freestash = srchash;
Sami Tolvanen43b748f2015-04-17 12:50:31 +0100963 }
Sami Tolvanen90221202014-12-09 16:39:47 +0000964 }
965
966 // Source blocks have expected content, command can proceed
Tao Bao0940fe12015-08-27 16:41:21 -0700967 return 0;
Sami Tolvanen90221202014-12-09 16:39:47 +0000968 }
969
Tao Bao0940fe12015-08-27 16:41:21 -0700970 if (overlap && LoadStash(params.stashbase, std::string(srchash), true, nullptr, &params.buffer,
971 &params.bufsize, true) == 0) {
Sami Tolvanen43b748f2015-04-17 12:50:31 +0100972 // Overlapping source blocks were previously stashed, command can proceed.
973 // We are recovering from an interrupted command, so we don't know if the
974 // stash can safely be deleted after this command.
Tao Bao0940fe12015-08-27 16:41:21 -0700975 return 0;
Sami Tolvanen90221202014-12-09 16:39:47 +0000976 }
977
978 // Valid source data not available, update cannot be resumed
979 fprintf(stderr, "partition has unexpected contents\n");
Tao Bao0940fe12015-08-27 16:41:21 -0700980 params.isunresumable = true;
Sami Tolvanen90221202014-12-09 16:39:47 +0000981
Tao Bao0940fe12015-08-27 16:41:21 -0700982 return -1;
Sami Tolvanen90221202014-12-09 16:39:47 +0000983}
984
Tao Bao0940fe12015-08-27 16:41:21 -0700985static int PerformCommandMove(CommandParameters& params) {
986 size_t blocks = 0;
Tao Baoe6aa3322015-08-05 15:20:27 -0700987 bool overlap = false;
Sami Tolvanen90221202014-12-09 16:39:47 +0000988 int status = 0;
Tao Bao0940fe12015-08-27 16:41:21 -0700989 RangeSet tgt;
Sami Tolvanen90221202014-12-09 16:39:47 +0000990
Tao Bao0940fe12015-08-27 16:41:21 -0700991 if (params.version == 1) {
992 status = LoadSrcTgtVersion1(&params.cpos, &tgt, blocks, &params.buffer,
993 &params.bufsize, params.fd);
994 } else if (params.version == 2) {
995 status = LoadSrcTgtVersion2(&params.cpos, &tgt, blocks, &params.buffer,
996 &params.bufsize, params.fd, params.stashbase, nullptr);
997 } else if (params.version >= 3) {
998 status = LoadSrcTgtVersion3(params, &tgt, blocks, true, overlap);
Sami Tolvanen90221202014-12-09 16:39:47 +0000999 }
1000
1001 if (status == -1) {
1002 fprintf(stderr, "failed to read blocks for move\n");
Tao Bao0940fe12015-08-27 16:41:21 -07001003 return -1;
Sami Tolvanen90221202014-12-09 16:39:47 +00001004 }
1005
1006 if (status == 0) {
Tao Bao0940fe12015-08-27 16:41:21 -07001007 params.foundwrites = true;
1008 } else if (params.foundwrites) {
1009 fprintf(stderr, "warning: commands executed out of order [%s]\n", params.cmdname);
Sami Tolvanen90221202014-12-09 16:39:47 +00001010 }
1011
Tao Bao0940fe12015-08-27 16:41:21 -07001012 if (params.canwrite) {
Sami Tolvanen90221202014-12-09 16:39:47 +00001013 if (status == 0) {
Tao Bao0940fe12015-08-27 16:41:21 -07001014 fprintf(stderr, " moving %zu blocks\n", blocks);
Sami Tolvanen90221202014-12-09 16:39:47 +00001015
Tao Bao0940fe12015-08-27 16:41:21 -07001016 if (WriteBlocks(tgt, params.buffer, params.fd) == -1) {
1017 return -1;
Sami Tolvanen90221202014-12-09 16:39:47 +00001018 }
1019 } else {
Tao Bao0940fe12015-08-27 16:41:21 -07001020 fprintf(stderr, "skipping %zu already moved blocks\n", blocks);
Sami Tolvanen90221202014-12-09 16:39:47 +00001021 }
1022
1023 }
1024
Tao Bao0940fe12015-08-27 16:41:21 -07001025 if (params.freestash) {
1026 FreeStash(params.stashbase, params.freestash);
1027 params.freestash = nullptr;
Sami Tolvanen90221202014-12-09 16:39:47 +00001028 }
1029
Tao Bao0940fe12015-08-27 16:41:21 -07001030 params.written += tgt.size;
Sami Tolvanen90221202014-12-09 16:39:47 +00001031
Tao Bao0940fe12015-08-27 16:41:21 -07001032 return 0;
Sami Tolvanen90221202014-12-09 16:39:47 +00001033}
1034
Tao Bao0940fe12015-08-27 16:41:21 -07001035static int PerformCommandStash(CommandParameters& params) {
1036 return SaveStash(params.stashbase, &params.cpos, &params.buffer, &params.bufsize,
1037 params.fd, (params.version >= 3));
Sami Tolvanen90221202014-12-09 16:39:47 +00001038}
1039
Tao Bao0940fe12015-08-27 16:41:21 -07001040static int PerformCommandFree(CommandParameters& params) {
1041 if (params.createdstash || params.canwrite) {
1042 return FreeStash(params.stashbase, params.cpos);
Sami Tolvanen90221202014-12-09 16:39:47 +00001043 }
1044
1045 return 0;
1046}
1047
Tao Bao0940fe12015-08-27 16:41:21 -07001048static int PerformCommandZero(CommandParameters& params) {
1049 char* range = strtok_r(nullptr, " ", &params.cpos);
Sami Tolvanen90221202014-12-09 16:39:47 +00001050
Tao Bao0940fe12015-08-27 16:41:21 -07001051 if (range == nullptr) {
Sami Tolvanen90221202014-12-09 16:39:47 +00001052 fprintf(stderr, "missing target blocks for zero\n");
Tao Bao0940fe12015-08-27 16:41:21 -07001053 return -1;
Sami Tolvanen90221202014-12-09 16:39:47 +00001054 }
1055
Tao Bao0940fe12015-08-27 16:41:21 -07001056 RangeSet tgt;
1057 parse_range(range, tgt);
Sami Tolvanen90221202014-12-09 16:39:47 +00001058
Tao Bao0940fe12015-08-27 16:41:21 -07001059 fprintf(stderr, " zeroing %zu blocks\n", tgt.size);
Sami Tolvanen90221202014-12-09 16:39:47 +00001060
Tao Bao0940fe12015-08-27 16:41:21 -07001061 allocate(BLOCKSIZE, &params.buffer, &params.bufsize);
1062 memset(params.buffer, 0, BLOCKSIZE);
Sami Tolvanen90221202014-12-09 16:39:47 +00001063
Tao Bao0940fe12015-08-27 16:41:21 -07001064 if (params.canwrite) {
1065 for (size_t i = 0; i < tgt.count; ++i) {
1066 if (!check_lseek(params.fd, (off64_t) tgt.pos[i * 2] * BLOCKSIZE, SEEK_SET)) {
1067 return -1;
Sami Tolvanen90221202014-12-09 16:39:47 +00001068 }
1069
Tao Bao0940fe12015-08-27 16:41:21 -07001070 for (size_t j = tgt.pos[i * 2]; j < tgt.pos[i * 2 + 1]; ++j) {
1071 if (write_all(params.fd, params.buffer, BLOCKSIZE) == -1) {
1072 return -1;
Sami Tolvanen90221202014-12-09 16:39:47 +00001073 }
1074 }
1075 }
1076 }
1077
Tao Bao0940fe12015-08-27 16:41:21 -07001078 if (params.cmdname[0] == 'z') {
Sami Tolvanene82fa182015-06-10 15:58:12 +00001079 // Update only for the zero command, as the erase command will call
1080 // this if DEBUG_ERASE is defined.
Tao Bao0940fe12015-08-27 16:41:21 -07001081 params.written += tgt.size;
Sami Tolvanen90221202014-12-09 16:39:47 +00001082 }
1083
Tao Bao0940fe12015-08-27 16:41:21 -07001084 return 0;
Sami Tolvanen90221202014-12-09 16:39:47 +00001085}
1086
Tao Bao0940fe12015-08-27 16:41:21 -07001087static int PerformCommandNew(CommandParameters& params) {
1088 char* range = strtok_r(nullptr, " ", &params.cpos);
Sami Tolvanen90221202014-12-09 16:39:47 +00001089
Tao Bao0940fe12015-08-27 16:41:21 -07001090 if (range == nullptr) {
1091 return -1;
Sami Tolvanen90221202014-12-09 16:39:47 +00001092 }
1093
Tao Bao0940fe12015-08-27 16:41:21 -07001094 RangeSet tgt;
1095 parse_range(range, tgt);
Sami Tolvanen90221202014-12-09 16:39:47 +00001096
Tao Bao0940fe12015-08-27 16:41:21 -07001097 if (params.canwrite) {
1098 fprintf(stderr, " writing %zu blocks of new data\n", tgt.size);
Sami Tolvanen90221202014-12-09 16:39:47 +00001099
Tao Bao0940fe12015-08-27 16:41:21 -07001100 RangeSinkState rss(tgt);
1101 rss.fd = params.fd;
Sami Tolvanen90221202014-12-09 16:39:47 +00001102 rss.p_block = 0;
Tao Bao0940fe12015-08-27 16:41:21 -07001103 rss.p_remain = (tgt.pos[1] - tgt.pos[0]) * BLOCKSIZE;
Sami Tolvanen90221202014-12-09 16:39:47 +00001104
Tao Bao0940fe12015-08-27 16:41:21 -07001105 if (!check_lseek(params.fd, (off64_t) tgt.pos[0] * BLOCKSIZE, SEEK_SET)) {
1106 return -1;
Sami Tolvanen90221202014-12-09 16:39:47 +00001107 }
1108
Tao Bao0940fe12015-08-27 16:41:21 -07001109 pthread_mutex_lock(&params.nti.mu);
1110 params.nti.rss = &rss;
1111 pthread_cond_broadcast(&params.nti.cv);
Sami Tolvanen90221202014-12-09 16:39:47 +00001112
Tao Bao0940fe12015-08-27 16:41:21 -07001113 while (params.nti.rss) {
1114 pthread_cond_wait(&params.nti.cv, &params.nti.mu);
Sami Tolvanen90221202014-12-09 16:39:47 +00001115 }
1116
Tao Bao0940fe12015-08-27 16:41:21 -07001117 pthread_mutex_unlock(&params.nti.mu);
Sami Tolvanen90221202014-12-09 16:39:47 +00001118 }
1119
Tao Bao0940fe12015-08-27 16:41:21 -07001120 params.written += tgt.size;
Sami Tolvanen90221202014-12-09 16:39:47 +00001121
Tao Bao0940fe12015-08-27 16:41:21 -07001122 return 0;
Sami Tolvanen90221202014-12-09 16:39:47 +00001123}
1124
Tao Bao0940fe12015-08-27 16:41:21 -07001125static int PerformCommandDiff(CommandParameters& params) {
1126 char* value = nullptr;
Tao Baoe6aa3322015-08-05 15:20:27 -07001127 bool overlap = false;
Tao Bao0940fe12015-08-27 16:41:21 -07001128 RangeSet tgt;
1129
1130 const std::string logparams(params.cpos);
1131 value = strtok_r(nullptr, " ", &params.cpos);
1132
1133 if (value == nullptr) {
1134 fprintf(stderr, "missing patch offset for %s\n", params.cmdname);
1135 return -1;
1136 }
1137
1138 size_t offset = strtoul(value, nullptr, 0);
1139
1140 value = strtok_r(nullptr, " ", &params.cpos);
1141
1142 if (value == nullptr) {
1143 fprintf(stderr, "missing patch length for %s\n", params.cmdname);
1144 return -1;
1145 }
1146
1147 size_t len = strtoul(value, nullptr, 0);
1148
1149 size_t blocks = 0;
Sami Tolvanen90221202014-12-09 16:39:47 +00001150 int status = 0;
Tao Bao0940fe12015-08-27 16:41:21 -07001151 if (params.version == 1) {
1152 status = LoadSrcTgtVersion1(&params.cpos, &tgt, blocks, &params.buffer,
1153 &params.bufsize, params.fd);
1154 } else if (params.version == 2) {
1155 status = LoadSrcTgtVersion2(&params.cpos, &tgt, blocks, &params.buffer,
1156 &params.bufsize, params.fd, params.stashbase, nullptr);
1157 } else if (params.version >= 3) {
1158 status = LoadSrcTgtVersion3(params, &tgt, blocks, false, overlap);
Sami Tolvanen90221202014-12-09 16:39:47 +00001159 }
1160
1161 if (status == -1) {
1162 fprintf(stderr, "failed to read blocks for diff\n");
Tao Bao0940fe12015-08-27 16:41:21 -07001163 return -1;
Sami Tolvanen90221202014-12-09 16:39:47 +00001164 }
1165
1166 if (status == 0) {
Tao Bao0940fe12015-08-27 16:41:21 -07001167 params.foundwrites = true;
1168 } else if (params.foundwrites) {
1169 fprintf(stderr, "warning: commands executed out of order [%s]\n", params.cmdname);
Sami Tolvanen90221202014-12-09 16:39:47 +00001170 }
1171
Tao Bao0940fe12015-08-27 16:41:21 -07001172 if (params.canwrite) {
Sami Tolvanen90221202014-12-09 16:39:47 +00001173 if (status == 0) {
Tao Bao0940fe12015-08-27 16:41:21 -07001174 fprintf(stderr, "patching %zu blocks to %zu\n", blocks, tgt.size);
Sami Tolvanen90221202014-12-09 16:39:47 +00001175
Tao Bao0940fe12015-08-27 16:41:21 -07001176 Value patch_value;
Sami Tolvanen90221202014-12-09 16:39:47 +00001177 patch_value.type = VAL_BLOB;
1178 patch_value.size = len;
Tao Bao0940fe12015-08-27 16:41:21 -07001179 patch_value.data = (char*) (params.patch_start + offset);
Sami Tolvanen90221202014-12-09 16:39:47 +00001180
Tao Bao0940fe12015-08-27 16:41:21 -07001181 RangeSinkState rss(tgt);
1182 rss.fd = params.fd;
Sami Tolvanen90221202014-12-09 16:39:47 +00001183 rss.p_block = 0;
Tao Bao0940fe12015-08-27 16:41:21 -07001184 rss.p_remain = (tgt.pos[1] - tgt.pos[0]) * BLOCKSIZE;
Sami Tolvanen90221202014-12-09 16:39:47 +00001185
Tao Bao0940fe12015-08-27 16:41:21 -07001186 if (!check_lseek(params.fd, (off64_t) tgt.pos[0] * BLOCKSIZE, SEEK_SET)) {
1187 return -1;
Sami Tolvanen90221202014-12-09 16:39:47 +00001188 }
1189
Tao Bao0940fe12015-08-27 16:41:21 -07001190 if (params.cmdname[0] == 'i') { // imgdiff
1191 ApplyImagePatch(params.buffer, blocks * BLOCKSIZE, &patch_value,
1192 &RangeSinkWrite, &rss, nullptr, nullptr);
Sami Tolvanen90221202014-12-09 16:39:47 +00001193 } else {
Tao Bao0940fe12015-08-27 16:41:21 -07001194 ApplyBSDiffPatch(params.buffer, blocks * BLOCKSIZE, &patch_value,
1195 0, &RangeSinkWrite, &rss, nullptr);
Sami Tolvanen90221202014-12-09 16:39:47 +00001196 }
1197
1198 // We expect the output of the patcher to fill the tgt ranges exactly.
Tao Bao0940fe12015-08-27 16:41:21 -07001199 if (rss.p_block != tgt.count || rss.p_remain != 0) {
Sami Tolvanen90221202014-12-09 16:39:47 +00001200 fprintf(stderr, "range sink underrun?\n");
1201 }
1202 } else {
Tao Bao0940fe12015-08-27 16:41:21 -07001203 fprintf(stderr, "skipping %zu blocks already patched to %zu [%s]\n",
1204 blocks, tgt.size, logparams.c_str());
Sami Tolvanen90221202014-12-09 16:39:47 +00001205 }
1206 }
1207
Tao Bao0940fe12015-08-27 16:41:21 -07001208 if (params.freestash) {
1209 FreeStash(params.stashbase, params.freestash);
1210 params.freestash = nullptr;
Sami Tolvanen90221202014-12-09 16:39:47 +00001211 }
1212
Tao Bao0940fe12015-08-27 16:41:21 -07001213 params.written += tgt.size;
Sami Tolvanen90221202014-12-09 16:39:47 +00001214
Tao Bao0940fe12015-08-27 16:41:21 -07001215 return 0;
Sami Tolvanen90221202014-12-09 16:39:47 +00001216}
1217
Tao Bao0940fe12015-08-27 16:41:21 -07001218static int PerformCommandErase(CommandParameters& params) {
Sami Tolvanene82fa182015-06-10 15:58:12 +00001219 if (DEBUG_ERASE) {
1220 return PerformCommandZero(params);
Sami Tolvanen90221202014-12-09 16:39:47 +00001221 }
1222
Tao Bao0940fe12015-08-27 16:41:21 -07001223 struct stat sb;
1224 if (fstat(params.fd, &sb) == -1) {
Elliott Hughes1fdd4522015-03-23 13:33:57 -07001225 fprintf(stderr, "failed to fstat device to erase: %s\n", strerror(errno));
Tao Bao0940fe12015-08-27 16:41:21 -07001226 return -1;
Sami Tolvanen90221202014-12-09 16:39:47 +00001227 }
1228
Tao Bao0940fe12015-08-27 16:41:21 -07001229 if (!S_ISBLK(sb.st_mode)) {
Sami Tolvanen90221202014-12-09 16:39:47 +00001230 fprintf(stderr, "not a block device; skipping erase\n");
Tao Bao0940fe12015-08-27 16:41:21 -07001231 return -1;
Sami Tolvanen90221202014-12-09 16:39:47 +00001232 }
1233
Tao Bao0940fe12015-08-27 16:41:21 -07001234 char* range = strtok_r(nullptr, " ", &params.cpos);
Sami Tolvanen90221202014-12-09 16:39:47 +00001235
Tao Bao0940fe12015-08-27 16:41:21 -07001236 if (range == nullptr) {
Tao Baoba9a42a2015-06-23 23:23:33 -07001237 fprintf(stderr, "missing target blocks for erase\n");
Tao Bao0940fe12015-08-27 16:41:21 -07001238 return -1;
Sami Tolvanen90221202014-12-09 16:39:47 +00001239 }
1240
Tao Bao0940fe12015-08-27 16:41:21 -07001241 RangeSet tgt;
1242 parse_range(range, tgt);
Sami Tolvanen90221202014-12-09 16:39:47 +00001243
Tao Bao0940fe12015-08-27 16:41:21 -07001244 if (params.canwrite) {
1245 fprintf(stderr, " erasing %zu blocks\n", tgt.size);
Sami Tolvanen90221202014-12-09 16:39:47 +00001246
Tao Bao0940fe12015-08-27 16:41:21 -07001247 for (size_t i = 0; i < tgt.count; ++i) {
1248 uint64_t blocks[2];
Sami Tolvanen90221202014-12-09 16:39:47 +00001249 // offset in bytes
Tao Bao0940fe12015-08-27 16:41:21 -07001250 blocks[0] = tgt.pos[i * 2] * (uint64_t) BLOCKSIZE;
Sami Tolvanen90221202014-12-09 16:39:47 +00001251 // length in bytes
Tao Bao0940fe12015-08-27 16:41:21 -07001252 blocks[1] = (tgt.pos[i * 2 + 1] - tgt.pos[i * 2]) * (uint64_t) BLOCKSIZE;
Sami Tolvanen90221202014-12-09 16:39:47 +00001253
Tao Bao0940fe12015-08-27 16:41:21 -07001254 if (ioctl(params.fd, BLKDISCARD, &blocks) == -1) {
Elliott Hughes1fdd4522015-03-23 13:33:57 -07001255 fprintf(stderr, "BLKDISCARD ioctl failed: %s\n", strerror(errno));
Tao Bao0940fe12015-08-27 16:41:21 -07001256 return -1;
Sami Tolvanen90221202014-12-09 16:39:47 +00001257 }
1258 }
1259 }
1260
Tao Bao0940fe12015-08-27 16:41:21 -07001261 return 0;
Sami Tolvanen90221202014-12-09 16:39:47 +00001262}
1263
1264// Definitions for transfer list command functions
Tao Bao0940fe12015-08-27 16:41:21 -07001265typedef int (*CommandFunction)(CommandParameters&);
Sami Tolvanen90221202014-12-09 16:39:47 +00001266
1267typedef struct {
1268 const char* name;
1269 CommandFunction f;
1270} Command;
1271
1272// CompareCommands and CompareCommandNames are for the hash table
1273
1274static int CompareCommands(const void* c1, const void* c2) {
1275 return strcmp(((const Command*) c1)->name, ((const Command*) c2)->name);
1276}
1277
1278static int CompareCommandNames(const void* c1, const void* c2) {
1279 return strcmp(((const Command*) c1)->name, (const char*) c2);
1280}
1281
1282// HashString is used to hash command names for the hash table
1283
1284static unsigned int HashString(const char *s) {
1285 unsigned int hash = 0;
1286 if (s) {
1287 while (*s) {
1288 hash = hash * 33 + *s++;
1289 }
1290 }
1291 return hash;
Doug Zongker52ae67d2014-09-08 12:22:09 -07001292}
1293
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001294// args:
1295// - block device (or file) to modify in-place
1296// - transfer list (blob)
1297// - new data stream (filename within package.zip)
1298// - patch stream (filename within package.zip, must be uncompressed)
1299
Tao Bao0940fe12015-08-27 16:41:21 -07001300static Value* PerformBlockImageUpdate(const char* name, State* state, int /* argc */, Expr* argv[],
1301 const Command* commands, size_t cmdcount, bool dryrun) {
Sami Tolvanen90221202014-12-09 16:39:47 +00001302
Tao Bao0940fe12015-08-27 16:41:21 -07001303 char* line = nullptr;
1304 char* linesave = nullptr;
1305 char* transfer_list = nullptr;
Sami Tolvanen90221202014-12-09 16:39:47 +00001306 CommandParameters params;
Tao Bao0940fe12015-08-27 16:41:21 -07001307 const Command* cmd = nullptr;
1308 const ZipEntry* new_entry = nullptr;
1309 const ZipEntry* patch_entry = nullptr;
1310 FILE* cmd_pipe = nullptr;
1311 HashTable* cmdht = nullptr;
Sami Tolvanen90221202014-12-09 16:39:47 +00001312 int res;
1313 int rc = -1;
Sami Tolvanen90221202014-12-09 16:39:47 +00001314 int total_blocks = 0;
1315 pthread_attr_t attr;
Tao Bao0940fe12015-08-27 16:41:21 -07001316 UpdaterInfo* ui = nullptr;
1317 Value* blockdev_filename = nullptr;
1318 Value* new_data_fn = nullptr;
1319 Value* patch_data_fn = nullptr;
1320 Value* transfer_list_value = nullptr;
1321 ZipArchive* za = nullptr;
Sami Tolvanen90221202014-12-09 16:39:47 +00001322
1323 memset(&params, 0, sizeof(params));
1324 params.canwrite = !dryrun;
1325
1326 fprintf(stderr, "performing %s\n", dryrun ? "verification" : "update");
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001327
Doug Zongker1d5d6092014-08-21 10:47:24 -07001328 if (ReadValueArgs(state, argv, 4, &blockdev_filename, &transfer_list_value,
Sami Tolvanen90221202014-12-09 16:39:47 +00001329 &new_data_fn, &patch_data_fn) < 0) {
1330 goto pbiudone;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001331 }
1332
1333 if (blockdev_filename->type != VAL_STRING) {
1334 ErrorAbort(state, "blockdev_filename argument to %s must be string", name);
Sami Tolvanen90221202014-12-09 16:39:47 +00001335 goto pbiudone;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001336 }
Doug Zongker1d5d6092014-08-21 10:47:24 -07001337 if (transfer_list_value->type != VAL_BLOB) {
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001338 ErrorAbort(state, "transfer_list argument to %s must be blob", name);
Sami Tolvanen90221202014-12-09 16:39:47 +00001339 goto pbiudone;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001340 }
1341 if (new_data_fn->type != VAL_STRING) {
1342 ErrorAbort(state, "new_data_fn argument to %s must be string", name);
Sami Tolvanen90221202014-12-09 16:39:47 +00001343 goto pbiudone;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001344 }
1345 if (patch_data_fn->type != VAL_STRING) {
1346 ErrorAbort(state, "patch_data_fn argument to %s must be string", name);
Sami Tolvanen90221202014-12-09 16:39:47 +00001347 goto pbiudone;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001348 }
1349
Sami Tolvanen90221202014-12-09 16:39:47 +00001350 ui = (UpdaterInfo*) state->cookie;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001351
Tao Bao0940fe12015-08-27 16:41:21 -07001352 if (ui == nullptr) {
Sami Tolvanen90221202014-12-09 16:39:47 +00001353 goto pbiudone;
1354 }
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001355
Sami Tolvanen90221202014-12-09 16:39:47 +00001356 cmd_pipe = ui->cmd_pipe;
1357 za = ui->package_zip;
1358
Tao Bao0940fe12015-08-27 16:41:21 -07001359 if (cmd_pipe == nullptr || za == nullptr) {
Sami Tolvanen90221202014-12-09 16:39:47 +00001360 goto pbiudone;
1361 }
1362
1363 patch_entry = mzFindZipEntry(za, patch_data_fn->data);
1364
Tao Bao0940fe12015-08-27 16:41:21 -07001365 if (patch_entry == nullptr) {
Sami Tolvanen90221202014-12-09 16:39:47 +00001366 fprintf(stderr, "%s(): no file \"%s\" in package", name, patch_data_fn->data);
1367 goto pbiudone;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001368 }
1369
Sami Tolvanen90221202014-12-09 16:39:47 +00001370 params.patch_start = ui->package_zip_addr + mzGetZipEntryOffset(patch_entry);
1371 new_entry = mzFindZipEntry(za, new_data_fn->data);
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001372
Tao Bao0940fe12015-08-27 16:41:21 -07001373 if (new_entry == nullptr) {
Sami Tolvanen90221202014-12-09 16:39:47 +00001374 fprintf(stderr, "%s(): no file \"%s\" in package", name, new_data_fn->data);
1375 goto pbiudone;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001376 }
1377
Sami Tolvanen90221202014-12-09 16:39:47 +00001378 params.fd = TEMP_FAILURE_RETRY(open(blockdev_filename->data, O_RDWR));
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001379
Sami Tolvanen90221202014-12-09 16:39:47 +00001380 if (params.fd == -1) {
Elliott Hughes1fdd4522015-03-23 13:33:57 -07001381 fprintf(stderr, "open \"%s\" failed: %s\n", blockdev_filename->data, strerror(errno));
Sami Tolvanen90221202014-12-09 16:39:47 +00001382 goto pbiudone;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001383 }
1384
Sami Tolvanen90221202014-12-09 16:39:47 +00001385 if (params.canwrite) {
1386 params.nti.za = za;
1387 params.nti.entry = new_entry;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001388
Tao Bao0940fe12015-08-27 16:41:21 -07001389 pthread_mutex_init(&params.nti.mu, nullptr);
1390 pthread_cond_init(&params.nti.cv, nullptr);
Sami Tolvanen90221202014-12-09 16:39:47 +00001391 pthread_attr_init(&attr);
1392 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
1393
Elliott Hughes1fdd4522015-03-23 13:33:57 -07001394 int error = pthread_create(&params.thread, &attr, unzip_new_data, &params.nti);
1395 if (error != 0) {
1396 fprintf(stderr, "pthread_create failed: %s\n", strerror(error));
Sami Tolvanen90221202014-12-09 16:39:47 +00001397 goto pbiudone;
1398 }
1399 }
1400
1401 // The data in transfer_list_value is not necessarily null-terminated, so we need
1402 // to copy it to a new buffer and add the null that strtok_r will need.
Tao Baoba9a42a2015-06-23 23:23:33 -07001403 transfer_list = reinterpret_cast<char*>(malloc(transfer_list_value->size + 1));
Sami Tolvanen90221202014-12-09 16:39:47 +00001404
Tao Bao0940fe12015-08-27 16:41:21 -07001405 if (transfer_list == nullptr) {
Doug Zongker1d5d6092014-08-21 10:47:24 -07001406 fprintf(stderr, "failed to allocate %zd bytes for transfer list\n",
Sami Tolvanen90221202014-12-09 16:39:47 +00001407 transfer_list_value->size + 1);
1408 goto pbiudone;
Doug Zongker1d5d6092014-08-21 10:47:24 -07001409 }
Sami Tolvanen90221202014-12-09 16:39:47 +00001410
Doug Zongker1d5d6092014-08-21 10:47:24 -07001411 memcpy(transfer_list, transfer_list_value->data, transfer_list_value->size);
1412 transfer_list[transfer_list_value->size] = '\0';
1413
Sami Tolvanen90221202014-12-09 16:39:47 +00001414 // First line in transfer list is the version number
Doug Zongker1d5d6092014-08-21 10:47:24 -07001415 line = strtok_r(transfer_list, "\n", &linesave);
Tao Bao0940fe12015-08-27 16:41:21 -07001416 params.version = strtol(line, nullptr, 0);
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001417
Sami Tolvanen90221202014-12-09 16:39:47 +00001418 if (params.version < 1 || params.version > 3) {
1419 fprintf(stderr, "unexpected transfer list version [%s]\n", line);
1420 goto pbiudone;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001421 }
1422
Sami Tolvanen90221202014-12-09 16:39:47 +00001423 fprintf(stderr, "blockimg version is %d\n", params.version);
1424
1425 // Second line in transfer list is the total number of blocks we expect to write
Tao Bao0940fe12015-08-27 16:41:21 -07001426 line = strtok_r(nullptr, "\n", &linesave);
1427 total_blocks = strtol(line, nullptr, 0);
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001428
Sami Tolvanen90221202014-12-09 16:39:47 +00001429 if (total_blocks < 0) {
1430 ErrorAbort(state, "unexpected block count [%s]\n", line);
1431 goto pbiudone;
1432 } else if (total_blocks == 0) {
1433 rc = 0;
1434 goto pbiudone;
1435 }
1436
1437 if (params.version >= 2) {
1438 // Third line is how many stash entries are needed simultaneously
Tao Bao0940fe12015-08-27 16:41:21 -07001439 line = strtok_r(nullptr, "\n", &linesave);
Sami Tolvanen90221202014-12-09 16:39:47 +00001440 fprintf(stderr, "maximum stash entries %s\n", line);
Doug Zongker52ae67d2014-09-08 12:22:09 -07001441
Sami Tolvanen90221202014-12-09 16:39:47 +00001442 // Fourth line is the maximum number of blocks that will be stashed simultaneously
Tao Bao0940fe12015-08-27 16:41:21 -07001443 line = strtok_r(nullptr, "\n", &linesave);
1444 int stash_max_blocks = strtol(line, nullptr, 0);
Sami Tolvanen90221202014-12-09 16:39:47 +00001445
1446 if (stash_max_blocks < 0) {
1447 ErrorAbort(state, "unexpected maximum stash blocks [%s]\n", line);
1448 goto pbiudone;
Doug Zongker52ae67d2014-09-08 12:22:09 -07001449 }
1450
Jesse Zhao1df64d32015-02-17 17:09:23 -08001451 if (stash_max_blocks >= 0) {
Tao Baoe6aa3322015-08-05 15:20:27 -07001452 res = CreateStash(state, stash_max_blocks, blockdev_filename->data, params.stashbase);
Sami Tolvanen90221202014-12-09 16:39:47 +00001453
1454 if (res == -1) {
1455 goto pbiudone;
1456 }
1457
1458 params.createdstash = res;
1459 }
Doug Zongker52ae67d2014-09-08 12:22:09 -07001460 }
1461
Sami Tolvanen90221202014-12-09 16:39:47 +00001462 // Build a hash table of the available commands
Tao Bao0940fe12015-08-27 16:41:21 -07001463 cmdht = mzHashTableCreate(cmdcount, nullptr);
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001464
Tao Bao0940fe12015-08-27 16:41:21 -07001465 for (size_t i = 0; i < cmdcount; ++i) {
1466 unsigned int cmdhash = HashString(commands[i].name);
Sami Tolvanen90221202014-12-09 16:39:47 +00001467 mzHashTableLookup(cmdht, cmdhash, (void*) &commands[i], CompareCommands, true);
1468 }
1469
1470 // Subsequent lines are all individual transfer commands
Tao Bao0940fe12015-08-27 16:41:21 -07001471 for (line = strtok_r(nullptr, "\n", &linesave); line;
1472 line = strtok_r(nullptr, "\n", &linesave)) {
Doug Zongker52ae67d2014-09-08 12:22:09 -07001473
Tao Bao0940fe12015-08-27 16:41:21 -07001474 const std::string logcmd(line);
Sami Tolvanen90221202014-12-09 16:39:47 +00001475 params.cmdname = strtok_r(line, " ", &params.cpos);
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001476
Tao Bao0940fe12015-08-27 16:41:21 -07001477 if (params.cmdname == nullptr) {
Sami Tolvanen90221202014-12-09 16:39:47 +00001478 fprintf(stderr, "missing command [%s]\n", line);
1479 goto pbiudone;
1480 }
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001481
Tao Bao0940fe12015-08-27 16:41:21 -07001482 unsigned int cmdhash = HashString(params.cmdname);
Sami Tolvanen90221202014-12-09 16:39:47 +00001483 cmd = (const Command*) mzHashTableLookup(cmdht, cmdhash, params.cmdname,
1484 CompareCommandNames, false);
Doug Zongker52ae67d2014-09-08 12:22:09 -07001485
Tao Bao0940fe12015-08-27 16:41:21 -07001486 if (cmd == nullptr) {
Sami Tolvanen90221202014-12-09 16:39:47 +00001487 fprintf(stderr, "unexpected command [%s]\n", params.cmdname);
1488 goto pbiudone;
1489 }
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001490
Tao Bao0940fe12015-08-27 16:41:21 -07001491 if (cmd->f != nullptr && cmd->f(params) == -1) {
1492 fprintf(stderr, "failed to execute command [%s]\n", logcmd.c_str());
Sami Tolvanen90221202014-12-09 16:39:47 +00001493 goto pbiudone;
1494 }
1495
Sami Tolvanen90221202014-12-09 16:39:47 +00001496 if (params.canwrite) {
Tao Bao187efff2015-07-27 14:07:08 -07001497 if (fsync(params.fd) == -1) {
1498 fprintf(stderr, "fsync failed: %s\n", strerror(errno));
1499 goto pbiudone;
1500 }
Sami Tolvanen90221202014-12-09 16:39:47 +00001501 fprintf(cmd_pipe, "set_progress %.4f\n", (double) params.written / total_blocks);
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001502 fflush(cmd_pipe);
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001503 }
1504 }
1505
Sami Tolvanen90221202014-12-09 16:39:47 +00001506 if (params.canwrite) {
Tao Bao0940fe12015-08-27 16:41:21 -07001507 pthread_join(params.thread, nullptr);
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001508
Tao Bao0940fe12015-08-27 16:41:21 -07001509 fprintf(stderr, "wrote %zu blocks; expected %d\n", params.written, total_blocks);
Sami Tolvanen90221202014-12-09 16:39:47 +00001510 fprintf(stderr, "max alloc needed was %zu\n", params.bufsize);
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001511
Sami Tolvanen90221202014-12-09 16:39:47 +00001512 // Delete stash only after successfully completing the update, as it
1513 // may contain blocks needed to complete the update later.
1514 DeleteStash(params.stashbase);
1515 } else {
1516 fprintf(stderr, "verified partition contents; update may be resumed\n");
1517 }
1518
1519 rc = 0;
1520
1521pbiudone:
1522 if (params.fd != -1) {
1523 if (fsync(params.fd) == -1) {
Elliott Hughes1fdd4522015-03-23 13:33:57 -07001524 fprintf(stderr, "fsync failed: %s\n", strerror(errno));
Sami Tolvanen90221202014-12-09 16:39:47 +00001525 }
Elliott Hughesb47afed2015-05-15 16:19:20 -07001526 close(params.fd);
Sami Tolvanen90221202014-12-09 16:39:47 +00001527 }
1528
Sami Tolvanen90221202014-12-09 16:39:47 +00001529 if (cmdht) {
1530 mzHashTableFree(cmdht);
1531 }
1532
1533 if (params.buffer) {
1534 free(params.buffer);
1535 }
1536
1537 if (transfer_list) {
1538 free(transfer_list);
1539 }
1540
1541 if (blockdev_filename) {
1542 FreeValue(blockdev_filename);
1543 }
1544
1545 if (transfer_list_value) {
1546 FreeValue(transfer_list_value);
1547 }
1548
1549 if (new_data_fn) {
1550 FreeValue(new_data_fn);
1551 }
1552
1553 if (patch_data_fn) {
1554 FreeValue(patch_data_fn);
1555 }
1556
1557 // Only delete the stash if the update cannot be resumed, or it's
1558 // a verification run and we created the stash.
1559 if (params.isunresumable || (!params.canwrite && params.createdstash)) {
1560 DeleteStash(params.stashbase);
1561 }
1562
Sami Tolvanen90221202014-12-09 16:39:47 +00001563 return StringValue(rc == 0 ? strdup("t") : strdup(""));
1564}
1565
1566// The transfer list is a text file containing commands to
1567// transfer data from one place to another on the target
1568// partition. We parse it and execute the commands in order:
1569//
1570// zero [rangeset]
1571// - fill the indicated blocks with zeros
1572//
1573// new [rangeset]
1574// - fill the blocks with data read from the new_data file
1575//
1576// erase [rangeset]
1577// - mark the given blocks as empty
1578//
1579// move <...>
1580// bsdiff <patchstart> <patchlen> <...>
1581// imgdiff <patchstart> <patchlen> <...>
1582// - read the source blocks, apply a patch (or not in the
1583// case of move), write result to target blocks. bsdiff or
1584// imgdiff specifies the type of patch; move means no patch
1585// at all.
1586//
1587// The format of <...> differs between versions 1 and 2;
1588// see the LoadSrcTgtVersion{1,2}() functions for a
1589// description of what's expected.
1590//
1591// stash <stash_id> <src_range>
1592// - (version 2+ only) load the given source range and stash
1593// the data in the given slot of the stash table.
1594//
1595// The creator of the transfer list will guarantee that no block
1596// is read (ie, used as the source for a patch or move) after it
1597// has been written.
1598//
1599// In version 2, the creator will guarantee that a given stash is
1600// loaded (with a stash command) before it's used in a
1601// move/bsdiff/imgdiff command.
1602//
1603// Within one command the source and target ranges may overlap so
1604// in general we need to read the entire source into memory before
1605// writing anything to the target blocks.
1606//
1607// All the patch data is concatenated into one patch_data file in
1608// the update package. It must be stored uncompressed because we
1609// memory-map it in directly from the archive. (Since patches are
1610// already compressed, we lose very little by not compressing
1611// their concatenation.)
1612//
1613// In version 3, commands that read data from the partition (i.e.
1614// move/bsdiff/imgdiff/stash) have one or more additional hashes
1615// before the range parameters, which are used to check if the
1616// command has already been completed and verify the integrity of
1617// the source data.
1618
1619Value* BlockImageVerifyFn(const char* name, State* state, int argc, Expr* argv[]) {
Tao Bao0940fe12015-08-27 16:41:21 -07001620 // Commands which are not tested are set to nullptr to skip them completely
Sami Tolvanen90221202014-12-09 16:39:47 +00001621 const Command commands[] = {
1622 { "bsdiff", PerformCommandDiff },
Tao Bao0940fe12015-08-27 16:41:21 -07001623 { "erase", nullptr },
Sami Tolvanen90221202014-12-09 16:39:47 +00001624 { "free", PerformCommandFree },
1625 { "imgdiff", PerformCommandDiff },
1626 { "move", PerformCommandMove },
Tao Bao0940fe12015-08-27 16:41:21 -07001627 { "new", nullptr },
Sami Tolvanen90221202014-12-09 16:39:47 +00001628 { "stash", PerformCommandStash },
Tao Bao0940fe12015-08-27 16:41:21 -07001629 { "zero", nullptr }
Sami Tolvanen90221202014-12-09 16:39:47 +00001630 };
1631
1632 // Perform a dry run without writing to test if an update can proceed
1633 return PerformBlockImageUpdate(name, state, argc, argv, commands,
Tao Baoe6aa3322015-08-05 15:20:27 -07001634 sizeof(commands) / sizeof(commands[0]), true);
Sami Tolvanen90221202014-12-09 16:39:47 +00001635}
1636
1637Value* BlockImageUpdateFn(const char* name, State* state, int argc, Expr* argv[]) {
1638 const Command commands[] = {
1639 { "bsdiff", PerformCommandDiff },
1640 { "erase", PerformCommandErase },
1641 { "free", PerformCommandFree },
1642 { "imgdiff", PerformCommandDiff },
1643 { "move", PerformCommandMove },
1644 { "new", PerformCommandNew },
1645 { "stash", PerformCommandStash },
1646 { "zero", PerformCommandZero }
1647 };
1648
1649 return PerformBlockImageUpdate(name, state, argc, argv, commands,
Tao Baoe6aa3322015-08-05 15:20:27 -07001650 sizeof(commands) / sizeof(commands[0]), false);
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001651}
1652
Tao Bao0940fe12015-08-27 16:41:21 -07001653Value* RangeSha1Fn(const char* name, State* state, int /* argc */, Expr* argv[]) {
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001654 Value* blockdev_filename;
1655 Value* ranges;
Tao Bao0940fe12015-08-27 16:41:21 -07001656 const uint8_t* digest = nullptr;
1657 RangeSet rs;
1658
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001659 if (ReadValueArgs(state, argv, 2, &blockdev_filename, &ranges) < 0) {
Tao Bao0940fe12015-08-27 16:41:21 -07001660 return nullptr;
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001661 }
1662
1663 if (blockdev_filename->type != VAL_STRING) {
1664 ErrorAbort(state, "blockdev_filename argument to %s must be string", name);
1665 goto done;
1666 }
1667 if (ranges->type != VAL_STRING) {
1668 ErrorAbort(state, "ranges argument to %s must be string", name);
1669 goto done;
1670 }
1671
Tao Baoba9a42a2015-06-23 23:23:33 -07001672 int fd;
1673 fd = open(blockdev_filename->data, O_RDWR);
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001674 if (fd < 0) {
Elliott Hughes1fdd4522015-03-23 13:33:57 -07001675 ErrorAbort(state, "open \"%s\" failed: %s", blockdev_filename->data, strerror(errno));
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001676 goto done;
1677 }
1678
Tao Bao0940fe12015-08-27 16:41:21 -07001679 parse_range(ranges->data, rs);
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001680 uint8_t buffer[BLOCKSIZE];
1681
1682 SHA_CTX ctx;
1683 SHA_init(&ctx);
1684
Tao Bao0940fe12015-08-27 16:41:21 -07001685 for (size_t i = 0; i < rs.count; ++i) {
1686 if (!check_lseek(fd, (off64_t)rs.pos[i*2] * BLOCKSIZE, SEEK_SET)) {
Sami Tolvanen90221202014-12-09 16:39:47 +00001687 ErrorAbort(state, "failed to seek %s: %s", blockdev_filename->data,
Tao Bao0940fe12015-08-27 16:41:21 -07001688 strerror(errno));
Sami Tolvanen90221202014-12-09 16:39:47 +00001689 goto done;
1690 }
1691
Tao Bao0940fe12015-08-27 16:41:21 -07001692 for (size_t j = rs.pos[i*2]; j < rs.pos[i*2+1]; ++j) {
Sami Tolvanen90221202014-12-09 16:39:47 +00001693 if (read_all(fd, buffer, BLOCKSIZE) == -1) {
1694 ErrorAbort(state, "failed to read %s: %s", blockdev_filename->data,
Tao Bao0940fe12015-08-27 16:41:21 -07001695 strerror(errno));
Sami Tolvanen90221202014-12-09 16:39:47 +00001696 goto done;
1697 }
1698
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001699 SHA_update(&ctx, buffer, BLOCKSIZE);
1700 }
1701 }
1702 digest = SHA_final(&ctx);
1703 close(fd);
1704
1705 done:
1706 FreeValue(blockdev_filename);
1707 FreeValue(ranges);
Tao Bao0940fe12015-08-27 16:41:21 -07001708 if (digest == nullptr) {
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001709 return StringValue(strdup(""));
1710 } else {
Tao Baoe6aa3322015-08-05 15:20:27 -07001711 return StringValue(strdup(print_sha1(digest).c_str()));
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001712 }
1713}
1714
1715void RegisterBlockImageFunctions() {
Sami Tolvanen90221202014-12-09 16:39:47 +00001716 RegisterFunction("block_image_verify", BlockImageVerifyFn);
Doug Zongkerbc7ffed2014-08-15 14:31:52 -07001717 RegisterFunction("block_image_update", BlockImageUpdateFn);
1718 RegisterFunction("range_sha1", RangeSha1Fn);
1719}