diff options
Diffstat (limited to 'updater')
-rw-r--r-- | updater/blockimg.cpp | 1131 | ||||
-rw-r--r-- | updater/include/updater/rangeset.h | 95 | ||||
-rw-r--r-- | updater/install.cpp | 16 |
3 files changed, 611 insertions, 631 deletions
diff --git a/updater/blockimg.cpp b/updater/blockimg.cpp index c614ccc47..8199447a9 100644 --- a/updater/blockimg.cpp +++ b/updater/blockimg.cpp @@ -18,6 +18,7 @@ #include <errno.h> #include <dirent.h> #include <fcntl.h> +#include <inttypes.h> #include <linux/fs.h> #include <pthread.h> #include <stdarg.h> @@ -49,9 +50,10 @@ #include "edify/expr.h" #include "error_code.h" -#include "updater/install.h" #include "ota_io.h" #include "print_sha1.h" +#include "updater/install.h" +#include "updater/rangeset.h" #include "updater/updater.h" // Set this to 0 to interpret 'erase' transfers to mean do a @@ -64,100 +66,10 @@ static constexpr const char* STASH_DIRECTORY_BASE = "/cache/recovery"; static constexpr mode_t STASH_DIRECTORY_MODE = 0700; static constexpr mode_t STASH_FILE_MODE = 0600; -struct RangeSet { - size_t count; // Limit is INT_MAX. - size_t size; - std::vector<size_t> pos; // Actual limit is INT_MAX. - - // Get the block number for the ith(starting from 0) block in the range set. - int get_block(size_t idx) const { - if (idx >= size) { - LOG(ERROR) << "index: " << idx << " is greater than range set size: " << size; - return -1; - } - for (size_t i = 0; i < pos.size(); i += 2) { - if (idx < pos[i + 1] - pos[i]) { - return pos[i] + idx; - } - idx -= (pos[i + 1] - pos[i]); - } - return -1; - } -}; - static CauseCode failure_type = kNoCause; static bool is_retry = false; static std::unordered_map<std::string, RangeSet> stash_map; -static RangeSet parse_range(const std::string& range_text) { - RangeSet rs; - - std::vector<std::string> pieces = android::base::Split(range_text, ","); - if (pieces.size() < 3) { - goto err; - } - - size_t num; - if (!android::base::ParseUint(pieces[0], &num, static_cast<size_t>(INT_MAX))) { - goto err; - } - - if (num == 0 || num % 2) { - goto err; // must be even - } else if (num != pieces.size() - 1) { - goto err; - } - - rs.pos.resize(num); - rs.count = num / 2; - rs.size = 0; - - for (size_t i = 0; i < num; i += 2) { - if (!android::base::ParseUint(pieces[i + 1], &rs.pos[i], static_cast<size_t>(INT_MAX))) { - goto err; - } - - if (!android::base::ParseUint(pieces[i + 2], &rs.pos[i + 1], static_cast<size_t>(INT_MAX))) { - goto err; - } - - if (rs.pos[i] >= rs.pos[i + 1]) { - goto err; // empty or negative range - } - - size_t sz = rs.pos[i + 1] - rs.pos[i]; - if (rs.size > SIZE_MAX - sz) { - goto err; // overflow - } - - rs.size += sz; - } - - return rs; - -err: - LOG(ERROR) << "failed to parse range '" << range_text << "'"; - exit(EXIT_FAILURE); -} - -static bool range_overlaps(const RangeSet& r1, const RangeSet& r2) { - for (size_t i = 0; i < r1.count; ++i) { - size_t r1_0 = r1.pos[i * 2]; - size_t r1_1 = r1.pos[i * 2 + 1]; - - for (size_t j = 0; j < r2.count; ++j) { - size_t r2_0 = r2.pos[j * 2]; - size_t r2_1 = r2.pos[j * 2 + 1]; - - if (!(r2_0 >= r1_1 || r1_0 >= r2_1)) { - return true; - } - } - } - - return false; -} - static int read_all(int fd, uint8_t* data, size_t size) { size_t so_far = 0; while (so_far < size) { @@ -231,128 +143,135 @@ static void allocate(size_t size, std::vector<uint8_t>& buffer) { buffer.resize(size); } -struct RangeSinkState { - explicit RangeSinkState(RangeSet& rs) : tgt(rs) { }; - - int fd; - const RangeSet& tgt; - size_t p_block; - size_t p_remain; -}; - -static ssize_t RangeSinkWrite(const uint8_t* data, ssize_t size, void* token) { - RangeSinkState* rss = reinterpret_cast<RangeSinkState*>(token); +/** + * RangeSinkWriter reads data from the given FD, and writes them to the destination specified by the + * given RangeSet. + */ +class RangeSinkWriter { + public: + RangeSinkWriter(int fd, const RangeSet& tgt) + : fd_(fd), tgt_(tgt), next_range_(0), current_range_left_(0) { + CHECK_NE(tgt.count, static_cast<size_t>(0)); + }; + + bool Finished() const { + return next_range_ == tgt_.count && current_range_left_ == 0; + } - if (rss->p_remain == 0) { - LOG(ERROR) << "range sink write overrun"; - return 0; + size_t Write(const uint8_t* data, size_t size) { + if (Finished()) { + LOG(ERROR) << "range sink write overrun; can't write " << size << " bytes"; + return 0; } - ssize_t written = 0; + size_t written = 0; while (size > 0) { - size_t write_now = size; - - if (rss->p_remain < write_now) { - write_now = rss->p_remain; - } + // Move to the next range as needed. + if (current_range_left_ == 0) { + if (next_range_ < tgt_.count) { + off64_t offset = static_cast<off64_t>(tgt_.pos[next_range_ * 2]) * BLOCKSIZE; + current_range_left_ = + (tgt_.pos[next_range_ * 2 + 1] - tgt_.pos[next_range_ * 2]) * BLOCKSIZE; + next_range_++; + if (!discard_blocks(fd_, offset, current_range_left_)) { + break; + } - if (write_all(rss->fd, data, write_now) == -1) { + if (!check_lseek(fd_, offset, SEEK_SET)) { break; + } + } else { + // We can't write any more; return how many bytes have been written so far. + break; } + } - data += write_now; - size -= write_now; - - rss->p_remain -= write_now; - written += write_now; + size_t write_now = size; + if (current_range_left_ < write_now) { + write_now = current_range_left_; + } - if (rss->p_remain == 0) { - // move to the next block - ++rss->p_block; - if (rss->p_block < rss->tgt.count) { - rss->p_remain = (rss->tgt.pos[rss->p_block * 2 + 1] - - rss->tgt.pos[rss->p_block * 2]) * BLOCKSIZE; + if (write_all(fd_, data, write_now) == -1) { + break; + } - off64_t offset = static_cast<off64_t>(rss->tgt.pos[rss->p_block*2]) * BLOCKSIZE; - if (!discard_blocks(rss->fd, offset, rss->p_remain)) { - break; - } + data += write_now; + size -= write_now; - if (!check_lseek(rss->fd, offset, SEEK_SET)) { - break; - } - - } else { - // we can't write any more; return how many bytes have - // been written so far. - break; - } - } + current_range_left_ -= write_now; + written += write_now; } return written; -} + } -// All of the data for all the 'new' transfers is contained in one -// file in the update package, concatenated together in the order in -// which transfers.list will need it. We want to stream it out of the -// archive (it's compressed) without writing it to a temp file, but we -// can't write each section until it's that transfer's turn to go. -// -// To achieve this, we expand the new data from the archive in a -// background thread, and block that threads 'receive uncompressed -// data' function until the main thread has reached a point where we -// want some new data to be written. We signal the background thread -// with the destination for the data and block the main thread, -// waiting for the background thread to complete writing that section. -// Then it signals the main thread to wake up and goes back to -// blocking waiting for a transfer. -// -// NewThreadInfo is the struct used to pass information back and forth -// between the two threads. When the main thread wants some data -// written, it sets rss to the destination location and signals the -// condition. When the background thread is done writing, it clears -// rss and signals the condition again. + private: + // The input data. + int fd_; + // The destination for the data. + const RangeSet& tgt_; + // The next range that we should write to. + size_t next_range_; + // The number of bytes to write before moving to the next range. + size_t current_range_left_; +}; +/** + * All of the data for all the 'new' transfers is contained in one file in the update package, + * concatenated together in the order in which transfers.list will need it. We want to stream it out + * of the archive (it's compressed) without writing it to a temp file, but we can't write each + * section until it's that transfer's turn to go. + * + * To achieve this, we expand the new data from the archive in a background thread, and block that + * threads 'receive uncompressed data' function until the main thread has reached a point where we + * want some new data to be written. We signal the background thread with the destination for the + * data and block the main thread, waiting for the background thread to complete writing that + * section. Then it signals the main thread to wake up and goes back to blocking waiting for a + * transfer. + * + * NewThreadInfo is the struct used to pass information back and forth between the two threads. When + * the main thread wants some data written, it sets writer to the destination location and signals + * the condition. When the background thread is done writing, it clears writer and signals the + * condition again. + */ struct NewThreadInfo { - ZipArchiveHandle za; - ZipEntry entry; + ZipArchiveHandle za; + ZipEntry entry; - RangeSinkState* rss; + RangeSinkWriter* writer; - pthread_mutex_t mu; - pthread_cond_t cv; + pthread_mutex_t mu; + pthread_cond_t cv; }; static bool receive_new_data(const uint8_t* data, size_t size, void* cookie) { - NewThreadInfo* nti = reinterpret_cast<NewThreadInfo*>(cookie); + NewThreadInfo* nti = static_cast<NewThreadInfo*>(cookie); - while (size > 0) { - // Wait for nti->rss to be non-null, indicating some of this - // data is wanted. - pthread_mutex_lock(&nti->mu); - while (nti->rss == nullptr) { - pthread_cond_wait(&nti->cv, &nti->mu); - } - pthread_mutex_unlock(&nti->mu); + while (size > 0) { + // Wait for nti->writer to be non-null, indicating some of this data is wanted. + pthread_mutex_lock(&nti->mu); + while (nti->writer == nullptr) { + pthread_cond_wait(&nti->cv, &nti->mu); + } + pthread_mutex_unlock(&nti->mu); - // At this point nti->rss is set, and we own it. The main - // thread is waiting for it to disappear from nti. - ssize_t written = RangeSinkWrite(data, size, nti->rss); - data += written; - size -= written; + // At this point nti->writer is set, and we own it. The main thread is waiting for it to + // disappear from nti. + size_t written = nti->writer->Write(data, size); + data += written; + size -= written; - if (nti->rss->p_block == nti->rss->tgt.count) { - // we have written all the bytes desired by this rss. + if (nti->writer->Finished()) { + // We have written all the bytes desired by this writer. - pthread_mutex_lock(&nti->mu); - nti->rss = nullptr; - pthread_cond_broadcast(&nti->cv); - pthread_mutex_unlock(&nti->mu); - } + pthread_mutex_lock(&nti->mu); + nti->writer = nullptr; + pthread_cond_broadcast(&nti->cv); + pthread_mutex_unlock(&nti->mu); } + } - return true; + return true; } static void* unzip_new_data(void* cookie) { @@ -383,28 +302,26 @@ static int ReadBlocks(const RangeSet& src, std::vector<uint8_t>& buffer, int fd) } static int WriteBlocks(const RangeSet& tgt, const std::vector<uint8_t>& buffer, int fd) { - const uint8_t* data = buffer.data(); - - size_t p = 0; - for (size_t i = 0; i < tgt.count; ++i) { - off64_t offset = static_cast<off64_t>(tgt.pos[i * 2]) * BLOCKSIZE; - size_t size = (tgt.pos[i * 2 + 1] - tgt.pos[i * 2]) * BLOCKSIZE; - if (!discard_blocks(fd, offset, size)) { - return -1; - } - - if (!check_lseek(fd, offset, SEEK_SET)) { - return -1; - } + size_t written = 0; + for (size_t i = 0; i < tgt.count; ++i) { + off64_t offset = static_cast<off64_t>(tgt.pos[i * 2]) * BLOCKSIZE; + size_t size = (tgt.pos[i * 2 + 1] - tgt.pos[i * 2]) * BLOCKSIZE; + if (!discard_blocks(fd, offset, size)) { + return -1; + } - if (write_all(fd, data + p, size) == -1) { - return -1; - } + if (!check_lseek(fd, offset, SEEK_SET)) { + return -1; + } - p += size; + if (write_all(fd, buffer.data() + written, size) == -1) { + return -1; } - return 0; + written += size; + } + + return 0; } // Parameters for transfer list command functions @@ -463,7 +380,7 @@ static void PrintHashForCorruptedSourceBlocks(const CommandParameters& params, return; } - RangeSet src = parse_range(params.tokens[pos++]); + RangeSet src = RangeSet::Parse(params.tokens[pos++]); RangeSet locs; // If there's no stashed blocks, content in the buffer is consecutive and has the same @@ -477,17 +394,15 @@ static void PrintHashForCorruptedSourceBlocks(const CommandParameters& params, // Example: for the tokens <4,63946,63947,63948,63979> <4,6,7,8,39> <stashed_blocks>; // We want to print SHA-1 for the data in buffer[6], buffer[8], buffer[9] ... buffer[38]; // this corresponds to the 32 src blocks #63946, #63948, #63949 ... #63978. - locs = parse_range(params.tokens[pos++]); + locs = RangeSet::Parse(params.tokens[pos++]); CHECK_EQ(src.size, locs.size); CHECK_EQ(locs.pos.size() % 2, static_cast<size_t>(0)); } LOG(INFO) << "printing hash in hex for " << src.size << " source blocks"; for (size_t i = 0; i < src.size; i++) { - int block_num = src.get_block(i); - CHECK_NE(block_num, -1); - int buffer_index = locs.get_block(i); - CHECK_NE(buffer_index, -1); + size_t block_num = src.GetBlockNumber(i); + size_t buffer_index = locs.GetBlockNumber(i); CHECK_LE((buffer_index + 1) * BLOCKSIZE, buffer.size()); uint8_t digest[SHA_DIGEST_LENGTH]; @@ -506,8 +421,7 @@ static void PrintHashForCorruptedStashedBlocks(const std::string& id, CHECK_EQ(src.size * BLOCKSIZE, buffer.size()); for (size_t i = 0; i < src.size; i++) { - int block_num = src.get_block(i); - CHECK_NE(block_num, -1); + size_t block_num = src.GetBlockNumber(i); uint8_t digest[SHA_DIGEST_LENGTH]; SHA1(buffer.data() + i * BLOCKSIZE, BLOCKSIZE, digest); @@ -696,7 +610,7 @@ static int LoadStash(CommandParameters& params, const std::string& id, bool veri } static int WriteStash(const std::string& base, const std::string& id, int blocks, - std::vector<uint8_t>& buffer, bool checkspace, bool *exists) { + std::vector<uint8_t>& buffer, bool checkspace, bool* exists) { if (base.empty()) { return -1; } @@ -883,96 +797,81 @@ static void MoveRange(std::vector<uint8_t>& dest, const RangeSet& locs, } } -// Do a source/target load for move/bsdiff/imgdiff in version 2. -// We expect to parse the remainder of the parameter tokens as one of: -// -// <tgt_range> <src_block_count> <src_range> -// (loads data from source image only) -// -// <tgt_range> <src_block_count> - <[stash_id:stash_range] ...> -// (loads data from stashes only) -// -// <tgt_range> <src_block_count> <src_range> <src_loc> <[stash_id:stash_range] ...> -// (loads data from both source image and stashes) -// -// On return, params.buffer is filled with the loaded source data (rearranged and combined with -// stashed data as necessary). buffer may be reallocated if needed to accommodate the source data. -// *tgt is the target RangeSet. Any stashes required are loaded using LoadStash. - -static int LoadSrcTgtVersion2(CommandParameters& params, RangeSet& tgt, size_t& src_blocks, - bool* overlap) { - - // At least it needs to provide three parameters: <tgt_range>, - // <src_block_count> and "-"/<src_range>. - if (params.cpos + 2 >= params.tokens.size()) { - LOG(ERROR) << "invalid parameters"; - return -1; - } - - // <tgt_range> - tgt = parse_range(params.tokens[params.cpos++]); - - // <src_block_count> - const std::string& token = params.tokens[params.cpos++]; - if (!android::base::ParseUint(token.c_str(), &src_blocks)) { - LOG(ERROR) << "invalid src_block_count \"" << token << "\""; - return -1; - } - - allocate(src_blocks * BLOCKSIZE, params.buffer); - - // "-" or <src_range> [<src_loc>] - if (params.tokens[params.cpos] == "-") { - // no source ranges, only stashes - params.cpos++; - } else { - RangeSet src = parse_range(params.tokens[params.cpos++]); - int res = ReadBlocks(src, params.buffer, params.fd); - - if (overlap) { - *overlap = range_overlaps(src, tgt); - } +/** + * We expect to parse the remainder of the parameter tokens as one of: + * + * <src_block_count> <src_range> + * (loads data from source image only) + * + * <src_block_count> - <[stash_id:stash_range] ...> + * (loads data from stashes only) + * + * <src_block_count> <src_range> <src_loc> <[stash_id:stash_range] ...> + * (loads data from both source image and stashes) + * + * On return, params.buffer is filled with the loaded source data (rearranged and combined with + * stashed data as necessary). buffer may be reallocated if needed to accommodate the source data. + * tgt is the target RangeSet for detecting overlaps. Any stashes required are loaded using + * LoadStash. + */ +static int LoadSourceBlocks(CommandParameters& params, const RangeSet& tgt, size_t* src_blocks, + bool* overlap) { + CHECK(src_blocks != nullptr); + CHECK(overlap != nullptr); + + // <src_block_count> + const std::string& token = params.tokens[params.cpos++]; + if (!android::base::ParseUint(token, src_blocks)) { + LOG(ERROR) << "invalid src_block_count \"" << token << "\""; + return -1; + } - if (res == -1) { - return -1; - } + allocate(*src_blocks * BLOCKSIZE, params.buffer); - if (params.cpos >= params.tokens.size()) { - // no stashes, only source range - return 0; - } + // "-" or <src_range> [<src_loc>] + if (params.tokens[params.cpos] == "-") { + // no source ranges, only stashes + params.cpos++; + } else { + RangeSet src = RangeSet::Parse(params.tokens[params.cpos++]); + *overlap = src.Overlaps(tgt); - RangeSet locs = parse_range(params.tokens[params.cpos++]); - MoveRange(params.buffer, locs, params.buffer); + if (ReadBlocks(src, params.buffer, params.fd) == -1) { + return -1; } - // <[stash_id:stash_range]> - while (params.cpos < params.tokens.size()) { - // Each word is a an index into the stash table, a colon, and - // then a rangeset describing where in the source block that - // stashed data should go. - std::vector<std::string> tokens = android::base::Split(params.tokens[params.cpos++], ":"); - if (tokens.size() != 2) { - LOG(ERROR) << "invalid parameter"; - return -1; - } - - std::vector<uint8_t> stash; - int res = LoadStash(params, tokens[0], false, nullptr, stash, true); + if (params.cpos >= params.tokens.size()) { + // no stashes, only source range + return 0; + } - if (res == -1) { - // These source blocks will fail verification if used later, but we - // will let the caller decide if this is a fatal failure - LOG(ERROR) << "failed to load stash " << tokens[0]; - continue; - } + RangeSet locs = RangeSet::Parse(params.tokens[params.cpos++]); + MoveRange(params.buffer, locs, params.buffer); + } - RangeSet locs = parse_range(tokens[1]); + // <[stash_id:stash_range]> + while (params.cpos < params.tokens.size()) { + // Each word is a an index into the stash table, a colon, and then a RangeSet describing where + // in the source block that stashed data should go. + std::vector<std::string> tokens = android::base::Split(params.tokens[params.cpos++], ":"); + if (tokens.size() != 2) { + LOG(ERROR) << "invalid parameter"; + return -1; + } - MoveRange(params.buffer, locs, stash); + std::vector<uint8_t> stash; + if (LoadStash(params, tokens[0], false, nullptr, stash, true) == -1) { + // These source blocks will fail verification if used later, but we + // will let the caller decide if this is a fatal failure + LOG(ERROR) << "failed to load stash " << tokens[0]; + continue; } - return 0; + RangeSet locs = RangeSet::Parse(tokens[1]); + MoveRange(params.buffer, locs, stash); + } + + return 0; } /** @@ -989,9 +888,8 @@ static int LoadSrcTgtVersion2(CommandParameters& params, RangeSet& tgt, size_t& * <tgt_range> <src_block_count> <src_range> <src_loc> <[stash_id:stash_range] ...> * (loads data from both source image and stashes) * - * Parameters are the same as for LoadSrcTgtVersion2, except for 'onehash', which tells the function - * whether to expect separate source and targe block hashes, or if they are both the same and only - * one hash should be expected, and 'isunresumable', which receives a non-zero value if block + * 'onehash' tells whether to expect separate source and targe block hashes, or if they are both the + * same and only one hash should be expected. params.isunresumable will be set to true if block * verification fails in a way that the update cannot be resumed anymore. * * If the function is unable to load the necessary blocks or their contents don't match the hashes, @@ -1002,87 +900,100 @@ static int LoadSrcTgtVersion2(CommandParameters& params, RangeSet& tgt, size_t& * * If the return value is 0, source blocks have expected content and the command can be performed. */ -static int LoadSrcTgtVersion3(CommandParameters& params, RangeSet& tgt, size_t& src_blocks, - bool onehash, bool& overlap) { - if (params.cpos >= params.tokens.size()) { - LOG(ERROR) << "missing source hash"; - return -1; - } +static int LoadSrcTgtVersion3(CommandParameters& params, RangeSet& tgt, size_t* src_blocks, + bool onehash, bool* overlap) { + CHECK(src_blocks != nullptr); + CHECK(overlap != nullptr); - std::string srchash = params.tokens[params.cpos++]; - std::string tgthash; + if (params.cpos >= params.tokens.size()) { + LOG(ERROR) << "missing source hash"; + return -1; + } - if (onehash) { - tgthash = srchash; - } else { - if (params.cpos >= params.tokens.size()) { - LOG(ERROR) << "missing target hash"; - return -1; - } - tgthash = params.tokens[params.cpos++]; - } + std::string srchash = params.tokens[params.cpos++]; + std::string tgthash; - if (LoadSrcTgtVersion2(params, tgt, src_blocks, &overlap) == -1) { - return -1; + if (onehash) { + tgthash = srchash; + } else { + if (params.cpos >= params.tokens.size()) { + LOG(ERROR) << "missing target hash"; + return -1; } + tgthash = params.tokens[params.cpos++]; + } - std::vector<uint8_t> tgtbuffer(tgt.size * BLOCKSIZE); + // At least it needs to provide three parameters: <tgt_range>, <src_block_count> and + // "-"/<src_range>. + if (params.cpos + 2 >= params.tokens.size()) { + LOG(ERROR) << "invalid parameters"; + return -1; + } - if (ReadBlocks(tgt, tgtbuffer, params.fd) == -1) { - return -1; - } + // <tgt_range> + tgt = RangeSet::Parse(params.tokens[params.cpos++]); - if (VerifyBlocks(tgthash, tgtbuffer, tgt.size, false) == 0) { - // Target blocks already have expected content, command should be skipped. - return 1; - } + std::vector<uint8_t> tgtbuffer(tgt.size * BLOCKSIZE); + if (ReadBlocks(tgt, tgtbuffer, params.fd) == -1) { + return -1; + } - if (VerifyBlocks(srchash, params.buffer, src_blocks, true) == 0) { - // If source and target blocks overlap, stash the source blocks so we can - // resume from possible write errors. In verify mode, we can skip stashing - // because the source blocks won't be overwritten. - if (overlap && params.canwrite) { - LOG(INFO) << "stashing " << src_blocks << " overlapping blocks to " << srchash; + // Return now if target blocks already have expected content. + if (VerifyBlocks(tgthash, tgtbuffer, tgt.size, false) == 0) { + return 1; + } - bool stash_exists = false; - if (WriteStash(params.stashbase, srchash, src_blocks, params.buffer, true, - &stash_exists) != 0) { - LOG(ERROR) << "failed to stash overlapping source blocks"; - return -1; - } + // Load source blocks. + if (LoadSourceBlocks(params, tgt, src_blocks, overlap) == -1) { + return -1; + } - params.stashed += src_blocks; - // Can be deleted when the write has completed. - if (!stash_exists) { - params.freestash = srchash; - } - } + if (VerifyBlocks(srchash, params.buffer, *src_blocks, true) == 0) { + // If source and target blocks overlap, stash the source blocks so we can + // resume from possible write errors. In verify mode, we can skip stashing + // because the source blocks won't be overwritten. + if (*overlap && params.canwrite) { + LOG(INFO) << "stashing " << *src_blocks << " overlapping blocks to " << srchash; + + bool stash_exists = false; + if (WriteStash(params.stashbase, srchash, *src_blocks, params.buffer, true, + &stash_exists) != 0) { + LOG(ERROR) << "failed to stash overlapping source blocks"; + return -1; + } - // Source blocks have expected content, command can proceed. - return 0; + params.stashed += *src_blocks; + // Can be deleted when the write has completed. + if (!stash_exists) { + params.freestash = srchash; + } } - if (overlap && LoadStash(params, srchash, true, nullptr, params.buffer, true) == 0) { - // Overlapping source blocks were previously stashed, command can proceed. - // We are recovering from an interrupted command, so we don't know if the - // stash can safely be deleted after this command. - return 0; - } + // Source blocks have expected content, command can proceed. + return 0; + } + + if (*overlap && LoadStash(params, srchash, true, nullptr, params.buffer, true) == 0) { + // Overlapping source blocks were previously stashed, command can proceed. We are recovering + // from an interrupted command, so we don't know if the stash can safely be deleted after this + // command. + return 0; + } - // Valid source data not available, update cannot be resumed. - LOG(ERROR) << "partition has unexpected contents"; - PrintHashForCorruptedSourceBlocks(params, params.buffer); + // Valid source data not available, update cannot be resumed. + LOG(ERROR) << "partition has unexpected contents"; + PrintHashForCorruptedSourceBlocks(params, params.buffer); - params.isunresumable = true; + params.isunresumable = true; - return -1; + return -1; } static int PerformCommandMove(CommandParameters& params) { size_t blocks = 0; bool overlap = false; RangeSet tgt; - int status = LoadSrcTgtVersion3(params, tgt, blocks, true, overlap); + int status = LoadSrcTgtVersion3(params, tgt, &blocks, true, &overlap); if (status == -1) { LOG(ERROR) << "failed to read blocks for move"; @@ -1132,7 +1043,7 @@ static int PerformCommandStash(CommandParameters& params) { return 0; } - RangeSet src = parse_range(params.tokens[params.cpos++]); + RangeSet src = RangeSet::Parse(params.tokens[params.cpos++]); allocate(src.size * BLOCKSIZE, params.buffer); if (ReadBlocks(src, params.buffer, params.fd) == -1) { @@ -1183,7 +1094,7 @@ static int PerformCommandZero(CommandParameters& params) { return -1; } - RangeSet tgt = parse_range(params.tokens[params.cpos++]); + RangeSet tgt = RangeSet::Parse(params.tokens[params.cpos++]); LOG(INFO) << " zeroing " << tgt.size << " blocks"; @@ -1220,134 +1131,111 @@ static int PerformCommandZero(CommandParameters& params) { } static int PerformCommandNew(CommandParameters& params) { + if (params.cpos >= params.tokens.size()) { + LOG(ERROR) << "missing target blocks for new"; + return -1; + } - if (params.cpos >= params.tokens.size()) { - LOG(ERROR) << "missing target blocks for new"; - return -1; - } - - RangeSet tgt = parse_range(params.tokens[params.cpos++]); - - if (params.canwrite) { - LOG(INFO) << " writing " << tgt.size << " blocks of new data"; - - RangeSinkState rss(tgt); - rss.fd = params.fd; - rss.p_block = 0; - rss.p_remain = (tgt.pos[1] - tgt.pos[0]) * BLOCKSIZE; - - off64_t offset = static_cast<off64_t>(tgt.pos[0]) * BLOCKSIZE; - if (!discard_blocks(params.fd, offset, tgt.size * BLOCKSIZE)) { - return -1; - } - - if (!check_lseek(params.fd, offset, SEEK_SET)) { - return -1; - } + RangeSet tgt = RangeSet::Parse(params.tokens[params.cpos++]); - pthread_mutex_lock(¶ms.nti.mu); - params.nti.rss = &rss; - pthread_cond_broadcast(¶ms.nti.cv); + if (params.canwrite) { + LOG(INFO) << " writing " << tgt.size << " blocks of new data"; - while (params.nti.rss) { - pthread_cond_wait(¶ms.nti.cv, ¶ms.nti.mu); - } + RangeSinkWriter writer(params.fd, tgt); + pthread_mutex_lock(¶ms.nti.mu); + params.nti.writer = &writer; + pthread_cond_broadcast(¶ms.nti.cv); - pthread_mutex_unlock(¶ms.nti.mu); + while (params.nti.writer != nullptr) { + pthread_cond_wait(¶ms.nti.cv, ¶ms.nti.mu); } - params.written += tgt.size; + pthread_mutex_unlock(¶ms.nti.mu); + } - return 0; + params.written += tgt.size; + + return 0; } static int PerformCommandDiff(CommandParameters& params) { + // <offset> <length> + if (params.cpos + 1 >= params.tokens.size()) { + LOG(ERROR) << "missing patch offset or length for " << params.cmdname; + return -1; + } - // <offset> <length> - if (params.cpos + 1 >= params.tokens.size()) { - LOG(ERROR) << "missing patch offset or length for " << params.cmdname; - return -1; - } + size_t offset; + if (!android::base::ParseUint(params.tokens[params.cpos++], &offset)) { + LOG(ERROR) << "invalid patch offset"; + return -1; + } - size_t offset; - if (!android::base::ParseUint(params.tokens[params.cpos++].c_str(), &offset)) { - LOG(ERROR) << "invalid patch offset"; - return -1; - } + size_t len; + if (!android::base::ParseUint(params.tokens[params.cpos++], &len)) { + LOG(ERROR) << "invalid patch len"; + return -1; + } - size_t len; - if (!android::base::ParseUint(params.tokens[params.cpos++].c_str(), &len)) { - LOG(ERROR) << "invalid patch len"; - return -1; - } + RangeSet tgt; + size_t blocks = 0; + bool overlap = false; + int status = LoadSrcTgtVersion3(params, tgt, &blocks, false, &overlap); - RangeSet tgt; - size_t blocks = 0; - bool overlap = false; - int status = LoadSrcTgtVersion3(params, tgt, blocks, false, overlap); + if (status == -1) { + LOG(ERROR) << "failed to read blocks for diff"; + return -1; + } - if (status == -1) { - LOG(ERROR) << "failed to read blocks for diff"; - return -1; - } + if (status == 0) { + params.foundwrites = true; + } else if (params.foundwrites) { + LOG(WARNING) << "warning: commands executed out of order [" << params.cmdname << "]"; + } + if (params.canwrite) { if (status == 0) { - params.foundwrites = true; - } else if (params.foundwrites) { - LOG(WARNING) << "warning: commands executed out of order [" << params.cmdname << "]"; - } - - if (params.canwrite) { - if (status == 0) { - LOG(INFO) << "patching " << blocks << " blocks to " << tgt.size; - Value patch_value(VAL_BLOB, - std::string(reinterpret_cast<const char*>(params.patch_start + offset), len)); - RangeSinkState rss(tgt); - rss.fd = params.fd; - rss.p_block = 0; - rss.p_remain = (tgt.pos[1] - tgt.pos[0]) * BLOCKSIZE; - - off64_t offset = static_cast<off64_t>(tgt.pos[0]) * BLOCKSIZE; - if (!discard_blocks(params.fd, offset, rss.p_remain)) { - return -1; - } - - if (!check_lseek(params.fd, offset, SEEK_SET)) { - return -1; - } - - if (params.cmdname[0] == 'i') { // imgdiff - if (ApplyImagePatch(params.buffer.data(), blocks * BLOCKSIZE, &patch_value, - &RangeSinkWrite, &rss, nullptr, nullptr) != 0) { - LOG(ERROR) << "Failed to apply image patch."; - return -1; - } - } else { - if (ApplyBSDiffPatch(params.buffer.data(), blocks * BLOCKSIZE, &patch_value, - 0, &RangeSinkWrite, &rss, nullptr) != 0) { - LOG(ERROR) << "Failed to apply bsdiff patch."; - return -1; - } - } - - // We expect the output of the patcher to fill the tgt ranges exactly. - if (rss.p_block != tgt.count || rss.p_remain != 0) { - LOG(ERROR) << "range sink underrun?"; - } - } else { - LOG(INFO) << "skipping " << blocks << " blocks already patched to " << tgt.size - << " [" << params.cmdline << "]"; + LOG(INFO) << "patching " << blocks << " blocks to " << tgt.size; + Value patch_value( + VAL_BLOB, std::string(reinterpret_cast<const char*>(params.patch_start + offset), len)); + + RangeSinkWriter writer(params.fd, tgt); + if (params.cmdname[0] == 'i') { // imgdiff + if (ApplyImagePatch(params.buffer.data(), blocks * BLOCKSIZE, &patch_value, + std::bind(&RangeSinkWriter::Write, &writer, std::placeholders::_1, + std::placeholders::_2), + nullptr, nullptr) != 0) { + LOG(ERROR) << "Failed to apply image patch."; + return -1; } - } + } else { + if (ApplyBSDiffPatch(params.buffer.data(), blocks * BLOCKSIZE, &patch_value, 0, + std::bind(&RangeSinkWriter::Write, &writer, std::placeholders::_1, + std::placeholders::_2), + nullptr) != 0) { + LOG(ERROR) << "Failed to apply bsdiff patch."; + return -1; + } + } - if (!params.freestash.empty()) { - FreeStash(params.stashbase, params.freestash); - params.freestash.clear(); + // We expect the output of the patcher to fill the tgt ranges exactly. + if (!writer.Finished()) { + LOG(ERROR) << "range sink underrun?"; + } + } else { + LOG(INFO) << "skipping " << blocks << " blocks already patched to " << tgt.size << " [" + << params.cmdline << "]"; } + } - params.written += tgt.size; + if (!params.freestash.empty()) { + FreeStash(params.stashbase, params.freestash); + params.freestash.clear(); + } - return 0; + params.written += tgt.size; + + return 0; } static int PerformCommandErase(CommandParameters& params) { @@ -1371,7 +1259,7 @@ static int PerformCommandErase(CommandParameters& params) { return -1; } - RangeSet tgt = parse_range(params.tokens[params.cpos++]); + RangeSet tgt = RangeSet::Parse(params.tokens[params.cpos++]); if (params.canwrite) { LOG(INFO) << " erasing " << tgt.size << " blocks"; @@ -1429,10 +1317,10 @@ static Value* PerformBlockImageUpdate(const char* name, State* state, return nullptr; } - const Value* blockdev_filename = args[0].get(); - const Value* transfer_list_value = args[1].get(); - const Value* new_data_fn = args[2].get(); - const Value* patch_data_fn = args[3].get(); + const std::unique_ptr<Value>& blockdev_filename = args[0]; + const std::unique_ptr<Value>& transfer_list_value = args[1]; + const std::unique_ptr<Value>& new_data_fn = args[2]; + const std::unique_ptr<Value>& patch_data_fn = args[3]; if (blockdev_filename->type != VAL_STRING) { ErrorAbort(state, kArgsParsingFailure, "blockdev_filename argument to %s must be string", name); @@ -1722,64 +1610,62 @@ Value* BlockImageUpdateFn(const char* name, State* state, } Value* RangeSha1Fn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) { - if (argv.size() != 2) { - ErrorAbort(state, kArgsParsingFailure, "range_sha1 expects 2 arguments, got %zu", - argv.size()); - return StringValue(""); - } + if (argv.size() != 2) { + ErrorAbort(state, kArgsParsingFailure, "range_sha1 expects 2 arguments, got %zu", argv.size()); + return StringValue(""); + } - std::vector<std::unique_ptr<Value>> args; - if (!ReadValueArgs(state, argv, &args)) { - return nullptr; - } + std::vector<std::unique_ptr<Value>> args; + if (!ReadValueArgs(state, argv, &args)) { + return nullptr; + } - const Value* blockdev_filename = args[0].get(); - const Value* ranges = args[1].get(); + const std::unique_ptr<Value>& blockdev_filename = args[0]; + const std::unique_ptr<Value>& ranges = args[1]; - if (blockdev_filename->type != VAL_STRING) { - ErrorAbort(state, kArgsParsingFailure, "blockdev_filename argument to %s must be string", - name); - return StringValue(""); - } - if (ranges->type != VAL_STRING) { - ErrorAbort(state, kArgsParsingFailure, "ranges argument to %s must be string", name); - return StringValue(""); - } + if (blockdev_filename->type != VAL_STRING) { + ErrorAbort(state, kArgsParsingFailure, "blockdev_filename argument to %s must be string", name); + return StringValue(""); + } + if (ranges->type != VAL_STRING) { + ErrorAbort(state, kArgsParsingFailure, "ranges argument to %s must be string", name); + return StringValue(""); + } - android::base::unique_fd fd(ota_open(blockdev_filename->data.c_str(), O_RDWR)); - if (fd == -1) { - ErrorAbort(state, kFileOpenFailure, "open \"%s\" failed: %s", - blockdev_filename->data.c_str(), strerror(errno)); - return StringValue(""); - } + android::base::unique_fd fd(ota_open(blockdev_filename->data.c_str(), O_RDWR)); + if (fd == -1) { + ErrorAbort(state, kFileOpenFailure, "open \"%s\" failed: %s", blockdev_filename->data.c_str(), + strerror(errno)); + return StringValue(""); + } - RangeSet rs = parse_range(ranges->data); + RangeSet rs = RangeSet::Parse(ranges->data); - SHA_CTX ctx; - SHA1_Init(&ctx); + SHA_CTX ctx; + SHA1_Init(&ctx); - std::vector<uint8_t> buffer(BLOCKSIZE); - for (size_t i = 0; i < rs.count; ++i) { - if (!check_lseek(fd, (off64_t)rs.pos[i*2] * BLOCKSIZE, SEEK_SET)) { - ErrorAbort(state, kLseekFailure, "failed to seek %s: %s", - blockdev_filename->data.c_str(), strerror(errno)); - return StringValue(""); - } + std::vector<uint8_t> buffer(BLOCKSIZE); + for (size_t i = 0; i < rs.count; ++i) { + if (!check_lseek(fd, (off64_t)rs.pos[i * 2] * BLOCKSIZE, SEEK_SET)) { + ErrorAbort(state, kLseekFailure, "failed to seek %s: %s", blockdev_filename->data.c_str(), + strerror(errno)); + return StringValue(""); + } - for (size_t j = rs.pos[i*2]; j < rs.pos[i*2+1]; ++j) { - if (read_all(fd, buffer, BLOCKSIZE) == -1) { - ErrorAbort(state, kFreadFailure, "failed to read %s: %s", - blockdev_filename->data.c_str(), strerror(errno)); - return StringValue(""); - } + for (size_t j = rs.pos[i * 2]; j < rs.pos[i * 2 + 1]; ++j) { + if (read_all(fd, buffer, BLOCKSIZE) == -1) { + ErrorAbort(state, kFreadFailure, "failed to read %s: %s", blockdev_filename->data.c_str(), + strerror(errno)); + return StringValue(""); + } - SHA1_Update(&ctx, buffer.data(), BLOCKSIZE); - } + SHA1_Update(&ctx, buffer.data(), BLOCKSIZE); } - uint8_t digest[SHA_DIGEST_LENGTH]; - SHA1_Final(digest, &ctx); + } + uint8_t digest[SHA_DIGEST_LENGTH]; + SHA1_Final(digest, &ctx); - return StringValue(print_sha1(digest)); + return StringValue(print_sha1(digest)); } // This function checks if a device has been remounted R/W prior to an incremental @@ -1789,145 +1675,144 @@ Value* RangeSha1Fn(const char* name, State* state, const std::vector<std::unique Value* CheckFirstBlockFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) { - if (argv.size() != 1) { - ErrorAbort(state, kArgsParsingFailure, "check_first_block expects 1 argument, got %zu", - argv.size()); - return StringValue(""); - } + if (argv.size() != 1) { + ErrorAbort(state, kArgsParsingFailure, "check_first_block expects 1 argument, got %zu", + argv.size()); + return StringValue(""); + } - std::vector<std::unique_ptr<Value>> args; - if (!ReadValueArgs(state, argv, &args)) { - return nullptr; - } + std::vector<std::unique_ptr<Value>> args; + if (!ReadValueArgs(state, argv, &args)) { + return nullptr; + } - const Value* arg_filename = args[0].get(); + const std::unique_ptr<Value>& arg_filename = args[0]; - if (arg_filename->type != VAL_STRING) { - ErrorAbort(state, kArgsParsingFailure, "filename argument to %s must be string", name); - return StringValue(""); - } + if (arg_filename->type != VAL_STRING) { + ErrorAbort(state, kArgsParsingFailure, "filename argument to %s must be string", name); + return StringValue(""); + } - android::base::unique_fd fd(ota_open(arg_filename->data.c_str(), O_RDONLY)); - if (fd == -1) { - ErrorAbort(state, kFileOpenFailure, "open \"%s\" failed: %s", arg_filename->data.c_str(), - strerror(errno)); - return StringValue(""); - } + android::base::unique_fd fd(ota_open(arg_filename->data.c_str(), O_RDONLY)); + if (fd == -1) { + ErrorAbort(state, kFileOpenFailure, "open \"%s\" failed: %s", arg_filename->data.c_str(), + strerror(errno)); + return StringValue(""); + } - RangeSet blk0 {1 /*count*/, 1/*size*/, std::vector<size_t> {0, 1}/*position*/}; - std::vector<uint8_t> block0_buffer(BLOCKSIZE); + RangeSet blk0{ 1 /*count*/, 1 /*size*/, std::vector<size_t>{ 0, 1 } /*position*/ }; + std::vector<uint8_t> block0_buffer(BLOCKSIZE); - if (ReadBlocks(blk0, block0_buffer, fd) == -1) { - ErrorAbort(state, kFreadFailure, "failed to read %s: %s", arg_filename->data.c_str(), - strerror(errno)); - return StringValue(""); - } + if (ReadBlocks(blk0, block0_buffer, fd) == -1) { + ErrorAbort(state, kFreadFailure, "failed to read %s: %s", arg_filename->data.c_str(), + strerror(errno)); + return StringValue(""); + } - // https://ext4.wiki.kernel.org/index.php/Ext4_Disk_Layout - // Super block starts from block 0, offset 0x400 - // 0x2C: len32 Mount time - // 0x30: len32 Write time - // 0x34: len16 Number of mounts since the last fsck - // 0x38: len16 Magic signature 0xEF53 + // https://ext4.wiki.kernel.org/index.php/Ext4_Disk_Layout + // Super block starts from block 0, offset 0x400 + // 0x2C: len32 Mount time + // 0x30: len32 Write time + // 0x34: len16 Number of mounts since the last fsck + // 0x38: len16 Magic signature 0xEF53 - time_t mount_time = *reinterpret_cast<uint32_t*>(&block0_buffer[0x400+0x2C]); - uint16_t mount_count = *reinterpret_cast<uint16_t*>(&block0_buffer[0x400+0x34]); + time_t mount_time = *reinterpret_cast<uint32_t*>(&block0_buffer[0x400 + 0x2C]); + uint16_t mount_count = *reinterpret_cast<uint16_t*>(&block0_buffer[0x400 + 0x34]); - if (mount_count > 0) { - uiPrintf(state, "Device was remounted R/W %d times\n", mount_count); - uiPrintf(state, "Last remount happened on %s", ctime(&mount_time)); - } + if (mount_count > 0) { + uiPrintf(state, "Device was remounted R/W %" PRIu16 " times", mount_count); + uiPrintf(state, "Last remount happened on %s", ctime(&mount_time)); + } - return StringValue("t"); + return StringValue("t"); } - Value* BlockImageRecoverFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) { - if (argv.size() != 2) { - ErrorAbort(state, kArgsParsingFailure, "block_image_recover expects 2 arguments, got %zu", - argv.size()); - return StringValue(""); - } + if (argv.size() != 2) { + ErrorAbort(state, kArgsParsingFailure, "block_image_recover expects 2 arguments, got %zu", + argv.size()); + return StringValue(""); + } - std::vector<std::unique_ptr<Value>> args; - if (!ReadValueArgs(state, argv, &args)) { - return nullptr; - } + std::vector<std::unique_ptr<Value>> args; + if (!ReadValueArgs(state, argv, &args)) { + return nullptr; + } - const Value* filename = args[0].get(); - const Value* ranges = args[1].get(); + const std::unique_ptr<Value>& filename = args[0]; + const std::unique_ptr<Value>& ranges = args[1]; - if (filename->type != VAL_STRING) { - ErrorAbort(state, kArgsParsingFailure, "filename argument to %s must be string", name); - return StringValue(""); - } - if (ranges->type != VAL_STRING) { - ErrorAbort(state, kArgsParsingFailure, "ranges argument to %s must be string", name); - return StringValue(""); - } + if (filename->type != VAL_STRING) { + ErrorAbort(state, kArgsParsingFailure, "filename argument to %s must be string", name); + return StringValue(""); + } + if (ranges->type != VAL_STRING) { + ErrorAbort(state, kArgsParsingFailure, "ranges argument to %s must be string", name); + return StringValue(""); + } - // Output notice to log when recover is attempted - LOG(INFO) << filename->data << " image corrupted, attempting to recover..."; + // Output notice to log when recover is attempted + LOG(INFO) << filename->data << " image corrupted, attempting to recover..."; - // When opened with O_RDWR, libfec rewrites corrupted blocks when they are read - fec::io fh(filename->data.c_str(), O_RDWR); + // When opened with O_RDWR, libfec rewrites corrupted blocks when they are read + fec::io fh(filename->data, O_RDWR); - if (!fh) { - ErrorAbort(state, kLibfecFailure, "fec_open \"%s\" failed: %s", filename->data.c_str(), - strerror(errno)); - return StringValue(""); - } + if (!fh) { + ErrorAbort(state, kLibfecFailure, "fec_open \"%s\" failed: %s", filename->data.c_str(), + strerror(errno)); + return StringValue(""); + } - if (!fh.has_ecc() || !fh.has_verity()) { - ErrorAbort(state, kLibfecFailure, "unable to use metadata to correct errors"); - return StringValue(""); - } + if (!fh.has_ecc() || !fh.has_verity()) { + ErrorAbort(state, kLibfecFailure, "unable to use metadata to correct errors"); + return StringValue(""); + } - fec_status status; + fec_status status; - if (!fh.get_status(status)) { - ErrorAbort(state, kLibfecFailure, "failed to read FEC status"); - return StringValue(""); - } + if (!fh.get_status(status)) { + ErrorAbort(state, kLibfecFailure, "failed to read FEC status"); + return StringValue(""); + } - RangeSet rs = parse_range(ranges->data); + RangeSet rs = RangeSet::Parse(ranges->data); - uint8_t buffer[BLOCKSIZE]; + uint8_t buffer[BLOCKSIZE]; - for (size_t i = 0; i < rs.count; ++i) { - for (size_t j = rs.pos[i * 2]; j < rs.pos[i * 2 + 1]; ++j) { - // Stay within the data area, libfec validates and corrects metadata - if (status.data_size <= (uint64_t)j * BLOCKSIZE) { - continue; - } + for (size_t i = 0; i < rs.count; ++i) { + for (size_t j = rs.pos[i * 2]; j < rs.pos[i * 2 + 1]; ++j) { + // Stay within the data area, libfec validates and corrects metadata + if (status.data_size <= (uint64_t)j * BLOCKSIZE) { + continue; + } - if (fh.pread(buffer, BLOCKSIZE, (off64_t)j * BLOCKSIZE) != BLOCKSIZE) { - ErrorAbort(state, kLibfecFailure, "failed to recover %s (block %zu): %s", - filename->data.c_str(), j, strerror(errno)); - return StringValue(""); - } + if (fh.pread(buffer, BLOCKSIZE, (off64_t)j * BLOCKSIZE) != BLOCKSIZE) { + ErrorAbort(state, kLibfecFailure, "failed to recover %s (block %zu): %s", + filename->data.c_str(), j, strerror(errno)); + return StringValue(""); + } - // If we want to be able to recover from a situation where rewriting a corrected - // block doesn't guarantee the same data will be returned when re-read later, we - // can save a copy of corrected blocks to /cache. Note: - // - // 1. Maximum space required from /cache is the same as the maximum number of - // corrupted blocks we can correct. For RS(255, 253) and a 2 GiB partition, - // this would be ~16 MiB, for example. - // - // 2. To find out if this block was corrupted, call fec_get_status after each - // read and check if the errors field value has increased. - } + // If we want to be able to recover from a situation where rewriting a corrected + // block doesn't guarantee the same data will be returned when re-read later, we + // can save a copy of corrected blocks to /cache. Note: + // + // 1. Maximum space required from /cache is the same as the maximum number of + // corrupted blocks we can correct. For RS(255, 253) and a 2 GiB partition, + // this would be ~16 MiB, for example. + // + // 2. To find out if this block was corrupted, call fec_get_status after each + // read and check if the errors field value has increased. } - LOG(INFO) << "..." << filename->data << " image recovered successfully."; - return StringValue("t"); + } + LOG(INFO) << "..." << filename->data << " image recovered successfully."; + return StringValue("t"); } void RegisterBlockImageFunctions() { - RegisterFunction("block_image_verify", BlockImageVerifyFn); - RegisterFunction("block_image_update", BlockImageUpdateFn); - RegisterFunction("block_image_recover", BlockImageRecoverFn); - RegisterFunction("check_first_block", CheckFirstBlockFn); - RegisterFunction("range_sha1", RangeSha1Fn); + RegisterFunction("block_image_verify", BlockImageVerifyFn); + RegisterFunction("block_image_update", BlockImageUpdateFn); + RegisterFunction("block_image_recover", BlockImageRecoverFn); + RegisterFunction("check_first_block", CheckFirstBlockFn); + RegisterFunction("range_sha1", RangeSha1Fn); } diff --git a/updater/include/updater/rangeset.h b/updater/include/updater/rangeset.h new file mode 100644 index 000000000..afaa82dcd --- /dev/null +++ b/updater/include/updater/rangeset.h @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <stddef.h> + +#include <string> +#include <vector> + +#include <android-base/logging.h> +#include <android-base/parseint.h> +#include <android-base/strings.h> + +struct RangeSet { + size_t count; // Limit is INT_MAX. + size_t size; // The number of blocks in the RangeSet. + std::vector<size_t> pos; // Actual limit is INT_MAX. + + static RangeSet Parse(const std::string& range_text) { + std::vector<std::string> pieces = android::base::Split(range_text, ","); + CHECK_GE(pieces.size(), static_cast<size_t>(3)) << "Invalid range text: " << range_text; + + size_t num; + CHECK(android::base::ParseUint(pieces[0], &num, static_cast<size_t>(INT_MAX))) + << "Failed to parse the number of tokens: " << range_text; + + CHECK_NE(num, static_cast<size_t>(0)) << "Invalid number of tokens: " << range_text; + CHECK_EQ(num % 2, static_cast<size_t>(0)) << "Number of tokens must be even: " << range_text; + CHECK_EQ(num, pieces.size() - 1) << "Mismatching number of tokens: " << range_text; + + std::vector<size_t> pairs(num); + size_t size = 0; + for (size_t i = 0; i < num; i += 2) { + CHECK(android::base::ParseUint(pieces[i + 1], &pairs[i], static_cast<size_t>(INT_MAX))); + CHECK(android::base::ParseUint(pieces[i + 2], &pairs[i + 1], static_cast<size_t>(INT_MAX))); + CHECK_LT(pairs[i], pairs[i + 1]) + << "Empty or negative range: " << pairs[i] << ", " << pairs[i + 1]; + + size_t sz = pairs[i + 1] - pairs[i]; + CHECK_LE(size, SIZE_MAX - sz) << "RangeSet size overflow"; + size += sz; + } + + return RangeSet{ num / 2, size, std::move(pairs) }; + } + + // Get the block number for the i-th (starting from 0) block in the RangeSet. + size_t GetBlockNumber(size_t idx) const { + CHECK_LT(idx, size) << "Index " << idx << " is greater than RangeSet size " << size; + for (size_t i = 0; i < pos.size(); i += 2) { + if (idx < pos[i + 1] - pos[i]) { + return pos[i] + idx; + } + idx -= (pos[i + 1] - pos[i]); + } + CHECK(false); + return 0; // Unreachable, but to make compiler happy. + } + + // RangeSet has half-closed half-open bounds. For example, "3,5" contains blocks 3 and 4. So "3,5" + // and "5,7" are not overlapped. + bool Overlaps(const RangeSet& other) const { + for (size_t i = 0; i < count; ++i) { + size_t start = pos[i * 2]; + size_t end = pos[i * 2 + 1]; + for (size_t j = 0; j < other.count; ++j) { + size_t other_start = other.pos[j * 2]; + size_t other_end = other.pos[j * 2 + 1]; + // [start, end) vs [other_start, other_end) + if (!(other_start >= end || start >= other_end)) { + return true; + } + } + } + return false; + } + + bool operator==(const RangeSet& other) const { + return (count == other.count && size == other.size && pos == other.pos); + } +}; diff --git a/updater/install.cpp b/updater/install.cpp index f91f3fc9f..857d7f1e0 100644 --- a/updater/install.cpp +++ b/updater/install.cpp @@ -181,8 +181,8 @@ Value* MountFn(const char* name, State* state, const std::vector<std::unique_ptr if (mount(location.c_str(), mount_point.c_str(), fs_type.c_str(), MS_NOATIME | MS_NODEV | MS_NODIRATIME, mount_options.c_str()) < 0) { - uiPrintf(state, "%s: failed to mount %s at %s: %s\n", name, location.c_str(), - mount_point.c_str(), strerror(errno)); + uiPrintf(state, "%s: Failed to mount %s at %s: %s", name, location.c_str(), mount_point.c_str(), + strerror(errno)); return StringValue(""); } @@ -231,12 +231,12 @@ Value* UnmountFn(const char* name, State* state, const std::vector<std::unique_p scan_mounted_volumes(); MountedVolume* vol = find_mounted_volume_by_mount_point(mount_point.c_str()); if (vol == nullptr) { - uiPrintf(state, "unmount of %s failed; no such volume\n", mount_point.c_str()); + uiPrintf(state, "Failed to unmount %s: No such volume", mount_point.c_str()); return nullptr; } else { int ret = unmount_mounted_volume(vol); if (ret != 0) { - uiPrintf(state, "unmount of %s failed (%d): %s\n", mount_point.c_str(), ret, strerror(errno)); + uiPrintf(state, "Failed to unmount %s: %s", mount_point.c_str(), strerror(errno)); } } @@ -699,15 +699,15 @@ Value* ApplyPatchCheckFn(const char* name, State* state, const std::vector<std:: return StringValue(result == 0 ? "t" : ""); } -// This is the updater side handler for ui_print() in edify script. Contents -// will be sent over to the recovery side for on-screen display. +// This is the updater side handler for ui_print() in edify script. Contents will be sent over to +// the recovery side for on-screen display. Value* UIPrintFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) { std::vector<std::string> args; if (!ReadArgs(state, argv, &args)) { - return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse the argument(s)", name); + return ErrorAbort(state, kArgsParsingFailure, "%s(): Failed to parse the argument(s)", name); } - std::string buffer = android::base::Join(args, "") + "\n"; + std::string buffer = android::base::Join(args, ""); uiPrint(state, buffer); return StringValue(buffer); } |