summaryrefslogtreecommitdiffstats
path: root/updater/blockimg.cpp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--updater/blockimg.cpp856
1 files changed, 474 insertions, 382 deletions
diff --git a/updater/blockimg.cpp b/updater/blockimg.cpp
index e93196b27..47849a155 100644
--- a/updater/blockimg.cpp
+++ b/updater/blockimg.cpp
@@ -15,8 +15,8 @@
*/
#include <ctype.h>
-#include <errno.h>
#include <dirent.h>
+#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
#include <linux/fs.h>
@@ -25,13 +25,12 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
-#include <sys/ioctl.h>
#include <time.h>
#include <unistd.h>
-#include <fec/io.h>
#include <functional>
#include <limits>
@@ -47,16 +46,18 @@
#include <android-base/unique_fd.h>
#include <applypatch/applypatch.h>
#include <brotli/decode.h>
+#include <fec/io.h>
#include <openssl/sha.h>
#include <private/android_filesystem_config.h>
+#include <verity/hash_tree_builder.h>
#include <ziparchive/zip_archive.h>
#include "edify/expr.h"
-#include "otafault/ota_io.h"
-#include "otautil/cache_location.h"
#include "otautil/error_code.h"
+#include "otautil/paths.h"
#include "otautil/print_sha1.h"
#include "otautil/rangeset.h"
+#include "private/commands.h"
#include "updater/install.h"
#include "updater/updater.h"
@@ -74,7 +75,7 @@ static bool is_retry = false;
static std::unordered_map<std::string, RangeSet> stash_map;
static void DeleteLastCommandFile() {
- std::string last_command_file = CacheLocation::location().last_command_file();
+ const std::string& last_command_file = Paths::Get().last_command_file();
if (unlink(last_command_file.c_str()) == -1 && errno != ENOENT) {
PLOG(ERROR) << "Failed to unlink: " << last_command_file;
}
@@ -82,8 +83,8 @@ static void DeleteLastCommandFile() {
// Parse the last command index of the last update and save the result to |last_command_index|.
// Return true if we successfully read the index.
-static bool ParseLastCommandFile(int* last_command_index) {
- std::string last_command_file = CacheLocation::location().last_command_file();
+static bool ParseLastCommandFile(size_t* last_command_index) {
+ const std::string& last_command_file = Paths::Get().last_command_file();
android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(last_command_file.c_str(), O_RDONLY)));
if (fd == -1) {
if (errno != ENOENT) {
@@ -108,7 +109,7 @@ static bool ParseLastCommandFile(int* last_command_index) {
return false;
}
- if (!android::base::ParseInt(lines[0], last_command_index)) {
+ if (!android::base::ParseUint(lines[0], last_command_index)) {
LOG(ERROR) << "Failed to parse integer in: " << lines[0];
return false;
}
@@ -116,10 +117,24 @@ static bool ParseLastCommandFile(int* last_command_index) {
return true;
}
-// Update the last command index in the last_command_file if the current command writes to the
-// stash either explicitly or implicitly.
-static bool UpdateLastCommandIndex(int command_index, const std::string& command_string) {
- std::string last_command_file = CacheLocation::location().last_command_file();
+static bool FsyncDir(const std::string& dirname) {
+ android::base::unique_fd dfd(TEMP_FAILURE_RETRY(open(dirname.c_str(), O_RDONLY | O_DIRECTORY)));
+ if (dfd == -1) {
+ failure_type = errno == EIO ? kEioFailure : kFileOpenFailure;
+ PLOG(ERROR) << "Failed to open " << dirname;
+ return false;
+ }
+ if (fsync(dfd) == -1) {
+ failure_type = errno == EIO ? kEioFailure : kFsyncFailure;
+ PLOG(ERROR) << "Failed to fsync " << dirname;
+ return false;
+ }
+ return true;
+}
+
+// Update the last executed command index in the last_command_file.
+static bool UpdateLastCommandIndex(size_t command_index, const std::string& command_string) {
+ const std::string& last_command_file = Paths::Get().last_command_file();
std::string last_command_tmp = last_command_file + ".tmp";
std::string content = std::to_string(command_index) + "\n" + command_string;
android::base::unique_fd wfd(
@@ -144,61 +159,23 @@ static bool UpdateLastCommandIndex(int command_index, const std::string& command
return false;
}
- std::string last_command_dir = android::base::Dirname(last_command_file);
- android::base::unique_fd dfd(
- TEMP_FAILURE_RETRY(ota_open(last_command_dir.c_str(), O_RDONLY | O_DIRECTORY)));
- if (dfd == -1) {
- PLOG(ERROR) << "Failed to open " << last_command_dir;
- return false;
- }
-
- if (fsync(dfd) == -1) {
- PLOG(ERROR) << "Failed to fsync " << last_command_dir;
+ if (!FsyncDir(android::base::Dirname(last_command_file))) {
return false;
}
return true;
}
-static int read_all(int fd, uint8_t* data, size_t size) {
- size_t so_far = 0;
- while (so_far < size) {
- ssize_t r = TEMP_FAILURE_RETRY(ota_read(fd, data+so_far, size-so_far));
- if (r == -1) {
- failure_type = kFreadFailure;
- PLOG(ERROR) << "read failed";
- return -1;
- } else if (r == 0) {
- failure_type = kFreadFailure;
- LOG(ERROR) << "read reached unexpected EOF.";
- return -1;
- }
- so_far += r;
- }
- return 0;
-}
-
-static int read_all(int fd, std::vector<uint8_t>& buffer, size_t size) {
- return read_all(fd, buffer.data(), size);
-}
-
-static int write_all(int fd, const uint8_t* data, size_t size) {
- size_t written = 0;
- while (written < size) {
- ssize_t w = TEMP_FAILURE_RETRY(ota_write(fd, data+written, size-written));
- if (w == -1) {
- failure_type = kFwriteFailure;
- PLOG(ERROR) << "write failed";
- return -1;
- }
- written += w;
- }
-
- return 0;
-}
-
-static int write_all(int fd, const std::vector<uint8_t>& buffer, size_t size) {
- return write_all(fd, buffer.data(), size);
+static bool SetPartitionUpdatedMarker(const std::string& marker) {
+ if (!android::base::WriteStringToFile("", marker)) {
+ PLOG(ERROR) << "Failed to write to marker file " << marker;
+ return false;
+ }
+ if (!FsyncDir(android::base::Dirname(marker))) {
+ return false;
+ }
+ LOG(INFO) << "Wrote partition updated marker to " << marker;
+ return true;
}
static bool discard_blocks(int fd, off64_t offset, uint64_t size) {
@@ -225,11 +202,10 @@ static bool check_lseek(int fd, off64_t offset, int whence) {
return true;
}
-static void allocate(size_t size, std::vector<uint8_t>& buffer) {
- // if the buffer's big enough, reuse it.
- if (size <= buffer.size()) return;
-
- buffer.resize(size);
+static void allocate(size_t size, std::vector<uint8_t>* buffer) {
+ // If the buffer's big enough, reuse it.
+ if (size <= buffer->size()) return;
+ buffer->resize(size);
}
/**
@@ -274,7 +250,9 @@ class RangeSinkWriter {
write_now = current_range_left_;
}
- if (write_all(fd_, data, write_now) == -1) {
+ if (!android::base::WriteFully(fd_, data, write_now)) {
+ failure_type = errno == EIO ? kEioFailure : kFwriteFailure;
+ PLOG(ERROR) << "Failed to write " << write_now << " bytes of data";
break;
}
@@ -483,15 +461,17 @@ static void* unzip_new_data(void* cookie) {
return nullptr;
}
-static int ReadBlocks(const RangeSet& src, std::vector<uint8_t>& buffer, int fd) {
+static int ReadBlocks(const RangeSet& src, std::vector<uint8_t>* buffer, int fd) {
size_t p = 0;
- for (const auto& range : src) {
- if (!check_lseek(fd, static_cast<off64_t>(range.first) * BLOCKSIZE, SEEK_SET)) {
+ for (const auto& [begin, end] : src) {
+ if (!check_lseek(fd, static_cast<off64_t>(begin) * BLOCKSIZE, SEEK_SET)) {
return -1;
}
- size_t size = (range.second - range.first) * BLOCKSIZE;
- if (read_all(fd, buffer.data() + p, size) == -1) {
+ size_t size = (end - begin) * BLOCKSIZE;
+ if (!android::base::ReadFully(fd, buffer->data() + p, size)) {
+ failure_type = errno == EIO ? kEioFailure : kFreadFailure;
+ PLOG(ERROR) << "Failed to read " << size << " bytes of data";
return -1;
}
@@ -503,9 +483,9 @@ 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) {
size_t written = 0;
- for (const auto& range : tgt) {
- off64_t offset = static_cast<off64_t>(range.first) * BLOCKSIZE;
- size_t size = (range.second - range.first) * BLOCKSIZE;
+ for (const auto& [begin, end] : tgt) {
+ off64_t offset = static_cast<off64_t>(begin) * BLOCKSIZE;
+ size_t size = (end - begin) * BLOCKSIZE;
if (!discard_blocks(fd, offset, size)) {
return -1;
}
@@ -514,7 +494,9 @@ static int WriteBlocks(const RangeSet& tgt, const std::vector<uint8_t>& buffer,
return -1;
}
- if (write_all(fd, buffer.data() + written, size) == -1) {
+ if (!android::base::WriteFully(fd, buffer.data() + written, size)) {
+ failure_type = errno == EIO ? kEioFailure : kFwriteFailure;
+ PLOG(ERROR) << "Failed to write " << size << " bytes of data";
return -1;
}
@@ -528,9 +510,8 @@ static int WriteBlocks(const RangeSet& tgt, const std::vector<uint8_t>& buffer,
struct CommandParameters {
std::vector<std::string> tokens;
size_t cpos;
- int cmdindex;
- const char* cmdname;
- const char* cmdline;
+ std::string cmdname;
+ std::string cmdline;
std::string freestash;
std::string stashbase;
bool canwrite;
@@ -644,43 +625,43 @@ static void PrintHashForMissingStashedBlocks(const std::string& id, int fd) {
LOG(INFO) << "print hash in hex for source blocks in missing stash: " << id;
const RangeSet& src = stash_map[id];
std::vector<uint8_t> buffer(src.blocks() * BLOCKSIZE);
- if (ReadBlocks(src, buffer, fd) == -1) {
- LOG(ERROR) << "failed to read source blocks for stash: " << id;
- return;
+ if (ReadBlocks(src, &buffer, fd) == -1) {
+ LOG(ERROR) << "failed to read source blocks for stash: " << id;
+ return;
}
PrintHashForCorruptedStashedBlocks(id, buffer, src);
}
static int VerifyBlocks(const std::string& expected, const std::vector<uint8_t>& buffer,
- const size_t blocks, bool printerror) {
- uint8_t digest[SHA_DIGEST_LENGTH];
- const uint8_t* data = buffer.data();
+ const size_t blocks, bool printerror) {
+ uint8_t digest[SHA_DIGEST_LENGTH];
+ const uint8_t* data = buffer.data();
- SHA1(data, blocks * BLOCKSIZE, digest);
+ SHA1(data, blocks * BLOCKSIZE, digest);
- std::string hexdigest = print_sha1(digest);
+ std::string hexdigest = print_sha1(digest);
- if (hexdigest != expected) {
- if (printerror) {
- LOG(ERROR) << "failed to verify blocks (expected " << expected << ", read "
- << hexdigest << ")";
- }
- return -1;
+ if (hexdigest != expected) {
+ if (printerror) {
+ LOG(ERROR) << "failed to verify blocks (expected " << expected << ", read " << hexdigest
+ << ")";
}
+ return -1;
+ }
- return 0;
+ return 0;
}
static std::string GetStashFileName(const std::string& base, const std::string& id,
- const std::string& postfix) {
- if (base.empty()) {
- return "";
- }
-
- std::string fn(CacheLocation::location().stash_directory_base());
- fn += "/" + base + "/" + id + postfix;
-
- return fn;
+ const std::string& postfix) {
+ if (base.empty()) {
+ return "";
+ }
+ std::string filename = Paths::Get().stash_directory_base() + "/" + base;
+ if (id.empty() && postfix.empty()) {
+ return filename;
+ }
+ return filename + "/" + id + postfix;
}
// Does a best effort enumeration of stash files. Ignores possible non-file items in the stash
@@ -733,8 +714,8 @@ static void DeleteStash(const std::string& base) {
}
}
-static int LoadStash(CommandParameters& params, const std::string& id, bool verify, size_t* blocks,
- std::vector<uint8_t>& buffer, bool printnoent) {
+static int LoadStash(const CommandParameters& params, const std::string& id, bool verify,
+ std::vector<uint8_t>* buffer, bool printnoent) {
// In verify mode, if source range_set was saved for the given hash, check contents in the source
// blocks first. If the check fails, search for the stashed files on /cache as usual.
if (!params.canwrite) {
@@ -746,20 +727,17 @@ static int LoadStash(CommandParameters& params, const std::string& id, bool veri
LOG(ERROR) << "failed to read source blocks in stash map.";
return -1;
}
- if (VerifyBlocks(id, buffer, src.blocks(), true) != 0) {
+ if (VerifyBlocks(id, *buffer, src.blocks(), true) != 0) {
LOG(ERROR) << "failed to verify loaded source blocks in stash map.";
- PrintHashForCorruptedStashedBlocks(id, buffer, src);
+ if (!is_retry) {
+ PrintHashForCorruptedStashedBlocks(id, *buffer, src);
+ }
return -1;
}
return 0;
}
}
- size_t blockcount = 0;
- if (!blocks) {
- blocks = &blockcount;
- }
-
std::string fn = GetStashFileName(params.stashbase, id, "");
struct stat sb;
@@ -778,28 +756,30 @@ static int LoadStash(CommandParameters& params, const std::string& id, bool veri
return -1;
}
- android::base::unique_fd fd(TEMP_FAILURE_RETRY(ota_open(fn.c_str(), O_RDONLY)));
+ android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(fn.c_str(), O_RDONLY)));
if (fd == -1) {
+ failure_type = errno == EIO ? kEioFailure : kFileOpenFailure;
PLOG(ERROR) << "open \"" << fn << "\" failed";
return -1;
}
allocate(sb.st_size, buffer);
- if (read_all(fd, buffer, sb.st_size) == -1) {
+ if (!android::base::ReadFully(fd, buffer->data(), sb.st_size)) {
+ failure_type = errno == EIO ? kEioFailure : kFreadFailure;
+ PLOG(ERROR) << "Failed to read " << sb.st_size << " bytes of data";
return -1;
}
- *blocks = sb.st_size / BLOCKSIZE;
-
- if (verify && VerifyBlocks(id, buffer, *blocks, true) != 0) {
+ size_t blocks = sb.st_size / BLOCKSIZE;
+ if (verify && VerifyBlocks(id, *buffer, blocks, true) != 0) {
LOG(ERROR) << "unexpected contents in " << fn;
if (stash_map.find(id) == stash_map.end()) {
LOG(ERROR) << "failed to find source blocks number for stash " << id
<< " when executing command: " << params.cmdname;
} else {
const RangeSet& src = stash_map[id];
- PrintHashForCorruptedStashedBlocks(id, buffer, src);
+ PrintHashForCorruptedStashedBlocks(id, *buffer, src);
}
DeleteFile(fn);
return -1;
@@ -809,109 +789,90 @@ 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) {
- if (base.empty()) {
- return -1;
- }
-
- if (checkspace && CacheSizeCheck(blocks * BLOCKSIZE) != 0) {
- LOG(ERROR) << "not enough space to write stash";
- return -1;
- }
-
- std::string fn = GetStashFileName(base, id, ".partial");
- std::string cn = GetStashFileName(base, id, "");
+ const std::vector<uint8_t>& buffer, bool checkspace, bool* exists) {
+ if (base.empty()) {
+ return -1;
+ }
- if (exists) {
- struct stat sb;
- int res = stat(cn.c_str(), &sb);
+ if (checkspace && !CheckAndFreeSpaceOnCache(blocks * BLOCKSIZE)) {
+ LOG(ERROR) << "not enough space to write stash";
+ return -1;
+ }
- if (res == 0) {
- // The file already exists and since the name is the hash of the contents,
- // it's safe to assume the contents are identical (accidental hash collisions
- // are unlikely)
- LOG(INFO) << " skipping " << blocks << " existing blocks in " << cn;
- *exists = true;
- return 0;
- }
+ std::string fn = GetStashFileName(base, id, ".partial");
+ std::string cn = GetStashFileName(base, id, "");
- *exists = false;
+ if (exists) {
+ struct stat sb;
+ int res = stat(cn.c_str(), &sb);
+
+ if (res == 0) {
+ // The file already exists and since the name is the hash of the contents,
+ // it's safe to assume the contents are identical (accidental hash collisions
+ // are unlikely)
+ LOG(INFO) << " skipping " << blocks << " existing blocks in " << cn;
+ *exists = true;
+ return 0;
}
- LOG(INFO) << " writing " << blocks << " blocks to " << cn;
+ *exists = false;
+ }
- android::base::unique_fd fd(
- TEMP_FAILURE_RETRY(ota_open(fn.c_str(), O_WRONLY | O_CREAT | O_TRUNC, STASH_FILE_MODE)));
- if (fd == -1) {
- PLOG(ERROR) << "failed to create \"" << fn << "\"";
- return -1;
- }
+ LOG(INFO) << " writing " << blocks << " blocks to " << cn;
- if (fchown(fd, AID_SYSTEM, AID_SYSTEM) != 0) { // system user
- PLOG(ERROR) << "failed to chown \"" << fn << "\"";
- return -1;
- }
+ android::base::unique_fd fd(
+ TEMP_FAILURE_RETRY(open(fn.c_str(), O_WRONLY | O_CREAT | O_TRUNC, STASH_FILE_MODE)));
+ if (fd == -1) {
+ failure_type = errno == EIO ? kEioFailure : kFileOpenFailure;
+ PLOG(ERROR) << "failed to create \"" << fn << "\"";
+ return -1;
+ }
- if (write_all(fd, buffer, blocks * BLOCKSIZE) == -1) {
- return -1;
- }
+ if (fchown(fd, AID_SYSTEM, AID_SYSTEM) != 0) { // system user
+ PLOG(ERROR) << "failed to chown \"" << fn << "\"";
+ return -1;
+ }
- if (ota_fsync(fd) == -1) {
- failure_type = kFsyncFailure;
- PLOG(ERROR) << "fsync \"" << fn << "\" failed";
- return -1;
- }
+ if (!android::base::WriteFully(fd, buffer.data(), blocks * BLOCKSIZE)) {
+ failure_type = errno == EIO ? kEioFailure : kFwriteFailure;
+ PLOG(ERROR) << "Failed to write " << blocks * BLOCKSIZE << " bytes of data";
+ return -1;
+ }
- if (rename(fn.c_str(), cn.c_str()) == -1) {
- PLOG(ERROR) << "rename(\"" << fn << "\", \"" << cn << "\") failed";
- return -1;
- }
+ if (fsync(fd) == -1) {
+ failure_type = errno == EIO ? kEioFailure : kFsyncFailure;
+ PLOG(ERROR) << "fsync \"" << fn << "\" failed";
+ return -1;
+ }
- std::string dname = GetStashFileName(base, "", "");
- android::base::unique_fd dfd(TEMP_FAILURE_RETRY(ota_open(dname.c_str(),
- O_RDONLY | O_DIRECTORY)));
- if (dfd == -1) {
- failure_type = kFileOpenFailure;
- PLOG(ERROR) << "failed to open \"" << dname << "\" failed";
- return -1;
- }
+ if (rename(fn.c_str(), cn.c_str()) == -1) {
+ PLOG(ERROR) << "rename(\"" << fn << "\", \"" << cn << "\") failed";
+ return -1;
+ }
- if (ota_fsync(dfd) == -1) {
- failure_type = kFsyncFailure;
- PLOG(ERROR) << "fsync \"" << dname << "\" failed";
- return -1;
- }
+ std::string dname = GetStashFileName(base, "", "");
+ if (!FsyncDir(dname)) {
+ return -1;
+ }
- return 0;
+ return 0;
}
// Creates a directory for storing stash files and checks if the /cache partition
// hash enough space for the expected amount of blocks we need to store. Returns
// >0 if we created the directory, zero if it existed already, and <0 of failure.
-
-static int CreateStash(State* state, size_t maxblocks, const std::string& blockdev,
- std::string& base) {
- if (blockdev.empty()) {
- return -1;
- }
-
- // Stash directory should be different for each partition to avoid conflicts
- // when updating multiple partitions at the same time, so we use the hash of
- // the block device name as the base directory
- uint8_t digest[SHA_DIGEST_LENGTH];
- SHA1(reinterpret_cast<const uint8_t*>(blockdev.data()), blockdev.size(), digest);
- base = print_sha1(digest);
-
+static int CreateStash(State* state, size_t maxblocks, const std::string& base) {
std::string dirname = GetStashFileName(base, "", "");
struct stat sb;
int res = stat(dirname.c_str(), &sb);
- size_t max_stash_size = maxblocks * BLOCKSIZE;
-
if (res == -1 && errno != ENOENT) {
ErrorAbort(state, kStashCreationFailure, "stat \"%s\" failed: %s", dirname.c_str(),
strerror(errno));
return -1;
- } else if (res != 0) {
+ }
+
+ size_t max_stash_size = maxblocks * BLOCKSIZE;
+ if (res == -1) {
LOG(INFO) << "creating stash " << dirname;
res = mkdir(dirname.c_str(), STASH_DIRECTORY_MODE);
@@ -927,7 +888,7 @@ static int CreateStash(State* state, size_t maxblocks, const std::string& blockd
return -1;
}
- if (CacheSizeCheck(max_stash_size) != 0) {
+ if (!CheckAndFreeSpaceOnCache(max_stash_size)) {
ErrorAbort(state, kStashCreationFailure, "not enough space for stash (%zu needed)",
max_stash_size);
return -1;
@@ -959,7 +920,7 @@ static int CreateStash(State* state, size_t maxblocks, const std::string& blockd
if (max_stash_size > existing) {
size_t needed = max_stash_size - existing;
- if (CacheSizeCheck(needed) != 0) {
+ if (!CheckAndFreeSpaceOnCache(needed)) {
ErrorAbort(state, kStashCreationFailure, "not enough space for stash (%zu more needed)",
needed);
return -1;
@@ -1023,7 +984,7 @@ static int LoadSourceBlocks(CommandParameters& params, const RangeSet& tgt, size
return -1;
}
- allocate(*src_blocks * BLOCKSIZE, params.buffer);
+ allocate(*src_blocks * BLOCKSIZE, &params.buffer);
// "-" or <src_range> [<src_loc>]
if (params.tokens[params.cpos] == "-") {
@@ -1034,7 +995,7 @@ static int LoadSourceBlocks(CommandParameters& params, const RangeSet& tgt, size
CHECK(static_cast<bool>(src));
*overlap = src.Overlaps(tgt);
- if (ReadBlocks(src, params.buffer, params.fd) == -1) {
+ if (ReadBlocks(src, &params.buffer, params.fd) == -1) {
return -1;
}
@@ -1059,7 +1020,7 @@ static int LoadSourceBlocks(CommandParameters& params, const RangeSet& tgt, size
}
std::vector<uint8_t> stash;
- if (LoadStash(params, tokens[0], false, nullptr, stash, true) == -1) {
+ if (LoadStash(params, tokens[0], false, &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];
@@ -1100,10 +1061,9 @@ static int LoadSourceBlocks(CommandParameters& params, const RangeSet& tgt, size
*
* 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) {
+static int LoadSrcTgtVersion3(CommandParameters& params, RangeSet* tgt, size_t* src_blocks,
+ bool onehash) {
CHECK(src_blocks != nullptr);
- CHECK(overlap != nullptr);
if (params.cpos >= params.tokens.size()) {
LOG(ERROR) << "missing source hash";
@@ -1131,29 +1091,30 @@ static int LoadSrcTgtVersion3(CommandParameters& params, RangeSet& tgt, size_t*
}
// <tgt_range>
- tgt = RangeSet::Parse(params.tokens[params.cpos++]);
- CHECK(static_cast<bool>(tgt));
+ *tgt = RangeSet::Parse(params.tokens[params.cpos++]);
+ CHECK(static_cast<bool>(*tgt));
- std::vector<uint8_t> tgtbuffer(tgt.blocks() * BLOCKSIZE);
- if (ReadBlocks(tgt, tgtbuffer, params.fd) == -1) {
+ std::vector<uint8_t> tgtbuffer(tgt->blocks() * BLOCKSIZE);
+ if (ReadBlocks(*tgt, &tgtbuffer, params.fd) == -1) {
return -1;
}
// Return now if target blocks already have expected content.
- if (VerifyBlocks(tgthash, tgtbuffer, tgt.blocks(), false) == 0) {
+ if (VerifyBlocks(tgthash, tgtbuffer, tgt->blocks(), false) == 0) {
return 1;
}
// Load source blocks.
- if (LoadSourceBlocks(params, tgt, src_blocks, overlap) == -1) {
+ bool overlap = false;
+ if (LoadSourceBlocks(params, *tgt, src_blocks, &overlap) == -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) {
+ // 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;
@@ -1163,10 +1124,6 @@ static int LoadSrcTgtVersion3(CommandParameters& params, RangeSet& tgt, size_t*
return -1;
}
- if (!UpdateLastCommandIndex(params.cmdindex, params.cmdline)) {
- LOG(WARNING) << "Failed to update the last command file.";
- }
-
params.stashed += *src_blocks;
// Can be deleted when the write has completed.
if (!stash_exists) {
@@ -1178,7 +1135,7 @@ static int LoadSrcTgtVersion3(CommandParameters& params, RangeSet& tgt, size_t*
return 0;
}
- if (*overlap && LoadStash(params, srchash, true, nullptr, params.buffer, true) == 0) {
+ if (overlap && LoadStash(params, srchash, true, &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.
@@ -1196,9 +1153,8 @@ static int LoadSrcTgtVersion3(CommandParameters& params, RangeSet& tgt, size_t*
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);
if (status == -1) {
LOG(ERROR) << "failed to read blocks for move";
@@ -1244,8 +1200,7 @@ static int PerformCommandStash(CommandParameters& params) {
}
const std::string& id = params.tokens[params.cpos++];
- size_t blocks = 0;
- if (LoadStash(params, id, true, &blocks, params.buffer, false) == 0) {
+ if (LoadStash(params, id, true, &params.buffer, false) == 0) {
// Stash file already exists and has expected contents. Do not read from source again, as the
// source may have been already overwritten during a previous attempt.
return 0;
@@ -1254,11 +1209,11 @@ static int PerformCommandStash(CommandParameters& params) {
RangeSet src = RangeSet::Parse(params.tokens[params.cpos++]);
CHECK(static_cast<bool>(src));
- allocate(src.blocks() * BLOCKSIZE, params.buffer);
- if (ReadBlocks(src, params.buffer, params.fd) == -1) {
+ size_t blocks = src.blocks();
+ allocate(blocks * BLOCKSIZE, &params.buffer);
+ if (ReadBlocks(src, &params.buffer, params.fd) == -1) {
return -1;
}
- blocks = src.blocks();
stash_map[id] = src;
if (VerifyBlocks(id, params.buffer, blocks, true) != 0) {
@@ -1277,10 +1232,6 @@ static int PerformCommandStash(CommandParameters& params) {
LOG(INFO) << "stashing " << blocks << " blocks to " << id;
int result = WriteStash(params.stashbase, id, blocks, params.buffer, false, nullptr);
if (result == 0) {
- if (!UpdateLastCommandIndex(params.cmdindex, params.cmdline)) {
- LOG(WARNING) << "Failed to update the last command file.";
- }
-
params.stashed += blocks;
}
return result;
@@ -1314,13 +1265,13 @@ static int PerformCommandZero(CommandParameters& params) {
LOG(INFO) << " zeroing " << tgt.blocks() << " blocks";
- allocate(BLOCKSIZE, params.buffer);
+ allocate(BLOCKSIZE, &params.buffer);
memset(params.buffer.data(), 0, BLOCKSIZE);
if (params.canwrite) {
- for (const auto& range : tgt) {
- off64_t offset = static_cast<off64_t>(range.first) * BLOCKSIZE;
- size_t size = (range.second - range.first) * BLOCKSIZE;
+ for (const auto& [begin, end] : tgt) {
+ off64_t offset = static_cast<off64_t>(begin) * BLOCKSIZE;
+ size_t size = (end - begin) * BLOCKSIZE;
if (!discard_blocks(params.fd, offset, size)) {
return -1;
}
@@ -1329,8 +1280,10 @@ static int PerformCommandZero(CommandParameters& params) {
return -1;
}
- for (size_t j = range.first; j < range.second; ++j) {
- if (write_all(params.fd, params.buffer, BLOCKSIZE) == -1) {
+ for (size_t j = begin; j < end; ++j) {
+ if (!android::base::WriteFully(params.fd, params.buffer.data(), BLOCKSIZE)) {
+ failure_type = errno == EIO ? kEioFailure : kFwriteFailure;
+ PLOG(ERROR) << "Failed to write " << BLOCKSIZE << " bytes of data";
return -1;
}
}
@@ -1401,8 +1354,7 @@ static int PerformCommandDiff(CommandParameters& params) {
RangeSet tgt;
size_t blocks = 0;
- bool overlap = false;
- int status = LoadSrcTgtVersion3(params, tgt, &blocks, false, &overlap);
+ int status = LoadSrcTgtVersion3(params, &tgt, &blocks, false);
if (status == -1) {
LOG(ERROR) << "failed to read blocks for diff";
@@ -1422,14 +1374,15 @@ static int PerformCommandDiff(CommandParameters& params) {
if (status == 0) {
LOG(INFO) << "patching " << blocks << " blocks to " << tgt.blocks();
Value patch_value(
- VAL_BLOB, std::string(reinterpret_cast<const char*>(params.patch_start + offset), len));
+ Value::Type::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) {
+ nullptr) != 0) {
LOG(ERROR) << "Failed to apply image patch.";
failure_type = kPatchApplicationFailure;
return -1;
@@ -1437,8 +1390,7 @@ static int PerformCommandDiff(CommandParameters& params) {
} else {
if (ApplyBSDiffPatch(params.buffer.data(), blocks * BLOCKSIZE, patch_value, 0,
std::bind(&RangeSinkWriter::Write, &writer, std::placeholders::_1,
- std::placeholders::_2),
- nullptr) != 0) {
+ std::placeholders::_2)) != 0) {
LOG(ERROR) << "Failed to apply bsdiff patch.";
failure_type = kPatchApplicationFailure;
return -1;
@@ -1492,12 +1444,12 @@ static int PerformCommandErase(CommandParameters& params) {
if (params.canwrite) {
LOG(INFO) << " erasing " << tgt.blocks() << " blocks";
- for (const auto& range : tgt) {
+ for (const auto& [begin, end] : tgt) {
uint64_t blocks[2];
// offset in bytes
- blocks[0] = range.first * static_cast<uint64_t>(BLOCKSIZE);
+ blocks[0] = begin * static_cast<uint64_t>(BLOCKSIZE);
// length in bytes
- blocks[1] = (range.second - range.first) * static_cast<uint64_t>(BLOCKSIZE);
+ blocks[1] = (end - begin) * static_cast<uint64_t>(BLOCKSIZE);
if (ioctl(params.fd, BLKDISCARD, &blocks) == -1) {
PLOG(ERROR) << "BLKDISCARD ioctl failed";
@@ -1509,24 +1461,120 @@ static int PerformCommandErase(CommandParameters& params) {
return 0;
}
-// Definitions for transfer list command functions
-typedef int (*CommandFunction)(CommandParameters&);
+static int PerformCommandAbort(CommandParameters&) {
+ LOG(INFO) << "Aborting as instructed";
+ return -1;
+}
+
+// Computes the hash_tree bytes based on the parameters, checks if the root hash of the tree
+// matches the expected hash and writes the result to the specified range on the block_device.
+// Hash_tree computation arguments:
+// hash_tree_ranges
+// source_ranges
+// hash_algorithm
+// salt_hex
+// root_hash
+static int PerformCommandComputeHashTree(CommandParameters& params) {
+ if (params.cpos + 5 != params.tokens.size()) {
+ LOG(ERROR) << "Invaild arguments count in hash computation " << params.cmdline;
+ return -1;
+ }
-struct Command {
- const char* name;
- CommandFunction f;
-};
+ // Expects the hash_tree data to be contiguous.
+ RangeSet hash_tree_ranges = RangeSet::Parse(params.tokens[params.cpos++]);
+ if (!hash_tree_ranges || hash_tree_ranges.size() != 1) {
+ LOG(ERROR) << "Invalid hash tree ranges in " << params.cmdline;
+ return -1;
+ }
-// args:
-// - block device (or file) to modify in-place
-// - transfer list (blob)
-// - new data stream (filename within package.zip)
-// - patch stream (filename within package.zip, must be uncompressed)
+ RangeSet source_ranges = RangeSet::Parse(params.tokens[params.cpos++]);
+ if (!source_ranges) {
+ LOG(ERROR) << "Invalid source ranges in " << params.cmdline;
+ return -1;
+ }
+
+ auto hash_function = HashTreeBuilder::HashFunction(params.tokens[params.cpos++]);
+ if (hash_function == nullptr) {
+ LOG(ERROR) << "Invalid hash algorithm in " << params.cmdline;
+ return -1;
+ }
+
+ std::vector<unsigned char> salt;
+ std::string salt_hex = params.tokens[params.cpos++];
+ if (salt_hex.empty() || !HashTreeBuilder::ParseBytesArrayFromString(salt_hex, &salt)) {
+ LOG(ERROR) << "Failed to parse salt in " << params.cmdline;
+ return -1;
+ }
+
+ std::string expected_root_hash = params.tokens[params.cpos++];
+ if (expected_root_hash.empty()) {
+ LOG(ERROR) << "Invalid root hash in " << params.cmdline;
+ return -1;
+ }
+
+ // Starts the hash_tree computation.
+ HashTreeBuilder builder(BLOCKSIZE, hash_function);
+ if (!builder.Initialize(source_ranges.blocks() * BLOCKSIZE, salt)) {
+ LOG(ERROR) << "Failed to initialize hash tree computation, source " << source_ranges.ToString()
+ << ", salt " << salt_hex;
+ return -1;
+ }
+
+ // Iterates through every block in the source_ranges and updates the hash tree structure
+ // accordingly.
+ for (const auto& [begin, end] : source_ranges) {
+ uint8_t buffer[BLOCKSIZE];
+ if (!check_lseek(params.fd, static_cast<off64_t>(begin) * BLOCKSIZE, SEEK_SET)) {
+ PLOG(ERROR) << "Failed to seek to block: " << begin;
+ return -1;
+ }
+
+ for (size_t i = begin; i < end; i++) {
+ if (!android::base::ReadFully(params.fd, buffer, BLOCKSIZE)) {
+ failure_type = errno == EIO ? kEioFailure : kFreadFailure;
+ LOG(ERROR) << "Failed to read data in " << begin << ":" << end;
+ return -1;
+ }
+
+ if (!builder.Update(reinterpret_cast<unsigned char*>(buffer), BLOCKSIZE)) {
+ LOG(ERROR) << "Failed to update hash tree builder";
+ return -1;
+ }
+ }
+ }
+
+ if (!builder.BuildHashTree()) {
+ LOG(ERROR) << "Failed to build hash tree";
+ return -1;
+ }
+
+ std::string root_hash_hex = HashTreeBuilder::BytesArrayToString(builder.root_hash());
+ if (root_hash_hex != expected_root_hash) {
+ LOG(ERROR) << "Root hash of the verity hash tree doesn't match the expected value. Expected: "
+ << expected_root_hash << ", actual: " << root_hash_hex;
+ return -1;
+ }
+
+ uint64_t write_offset = static_cast<uint64_t>(hash_tree_ranges.GetBlockNumber(0)) * BLOCKSIZE;
+ if (params.canwrite && !builder.WriteHashTreeToFd(params.fd, write_offset)) {
+ LOG(ERROR) << "Failed to write hash tree to output";
+ return -1;
+ }
+
+ // TODO(xunchang) validates the written bytes
+
+ return 0;
+}
+
+using CommandFunction = std::function<int(CommandParameters&)>;
+
+using CommandMap = std::unordered_map<Command::Type, CommandFunction>;
static Value* PerformBlockImageUpdate(const char* name, State* state,
const std::vector<std::unique_ptr<Expr>>& argv,
- const Command* commands, size_t cmdcount, bool dryrun) {
+ const CommandMap& command_map, bool dryrun) {
CommandParameters params = {};
+ stash_map.clear();
params.canwrite = !dryrun;
LOG(INFO) << "performing " << (dryrun ? "verification" : "update");
@@ -1545,24 +1593,29 @@ static Value* PerformBlockImageUpdate(const char* name, State* state,
return nullptr;
}
+ // args:
+ // - block device (or file) to modify in-place
+ // - transfer list (blob)
+ // - new data stream (filename within package.zip)
+ // - patch stream (filename within package.zip, must be uncompressed)
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) {
+ if (blockdev_filename->type != Value::Type::STRING) {
ErrorAbort(state, kArgsParsingFailure, "blockdev_filename argument to %s must be string", name);
return StringValue("");
}
- if (transfer_list_value->type != VAL_BLOB) {
+ if (transfer_list_value->type != Value::Type::BLOB) {
ErrorAbort(state, kArgsParsingFailure, "transfer_list argument to %s must be blob", name);
return StringValue("");
}
- if (new_data_fn->type != VAL_STRING) {
+ if (new_data_fn->type != Value::Type::STRING) {
ErrorAbort(state, kArgsParsingFailure, "new_data_fn argument to %s must be string", name);
return StringValue("");
}
- if (patch_data_fn->type != VAL_STRING) {
+ if (patch_data_fn->type != Value::Type::STRING) {
ErrorAbort(state, kArgsParsingFailure, "patch_data_fn argument to %s must be string", name);
return StringValue("");
}
@@ -1594,38 +1647,46 @@ static Value* PerformBlockImageUpdate(const char* name, State* state,
return StringValue("");
}
- params.fd.reset(TEMP_FAILURE_RETRY(ota_open(blockdev_filename->data.c_str(), O_RDWR)));
+ params.fd.reset(TEMP_FAILURE_RETRY(open(blockdev_filename->data.c_str(), O_RDWR)));
if (params.fd == -1) {
+ failure_type = errno == EIO ? kEioFailure : kFileOpenFailure;
PLOG(ERROR) << "open \"" << blockdev_filename->data << "\" failed";
return StringValue("");
}
- if (params.canwrite) {
- params.nti.za = za;
- params.nti.entry = new_entry;
- params.nti.brotli_compressed = android::base::EndsWith(new_data_fn->data, ".br");
- if (params.nti.brotli_compressed) {
- // Initialize brotli decoder state.
- params.nti.brotli_decoder_state = BrotliDecoderCreateInstance(nullptr, nullptr, nullptr);
+ // Stash directory should be different for each partition to avoid conflicts when updating
+ // multiple partitions at the same time, so we use the hash of the block device name as the base
+ // directory.
+ uint8_t digest[SHA_DIGEST_LENGTH];
+ SHA1(reinterpret_cast<const uint8_t*>(blockdev_filename->data.data()),
+ blockdev_filename->data.size(), digest);
+ params.stashbase = print_sha1(digest);
+
+ // Possibly do return early on retry, by checking the marker. If the update on this partition has
+ // been finished (but interrupted at a later point), there could be leftover on /cache that would
+ // fail the no-op retry.
+ std::string updated_marker = GetStashFileName(params.stashbase + ".UPDATED", "", "");
+ if (is_retry) {
+ struct stat sb;
+ int result = stat(updated_marker.c_str(), &sb);
+ if (result == 0) {
+ LOG(INFO) << "Skipping already updated partition " << blockdev_filename->data
+ << " based on marker";
+ return StringValue("t");
}
- params.nti.receiver_available = true;
-
- pthread_mutex_init(&params.nti.mu, nullptr);
- pthread_cond_init(&params.nti.cv, nullptr);
- pthread_attr_t attr;
- pthread_attr_init(&attr);
- pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
-
- int error = pthread_create(&params.thread, &attr, unzip_new_data, &params.nti);
- if (error != 0) {
- PLOG(ERROR) << "pthread_create failed";
+ } else {
+ // Delete the obsolete marker if any.
+ std::string err;
+ if (!android::base::RemoveFileIfExists(updated_marker, &err)) {
+ LOG(ERROR) << "Failed to remove partition updated marker " << updated_marker << ": " << err;
return StringValue("");
}
}
+ static constexpr size_t kTransferListHeaderLines = 4;
std::vector<std::string> lines = android::base::Split(transfer_list_value->data, "\n");
- if (lines.size() < 2) {
- ErrorAbort(state, kArgsParsingFailure, "too few lines in the transfer list [%zd]",
+ if (lines.size() < kTransferListHeaderLines) {
+ ErrorAbort(state, kArgsParsingFailure, "too few lines in the transfer list [%zu]",
lines.size());
return StringValue("");
}
@@ -1649,13 +1710,6 @@ static Value* PerformBlockImageUpdate(const char* name, State* state,
return StringValue("t");
}
- size_t start = 2;
- if (lines.size() < 4) {
- ErrorAbort(state, kArgsParsingFailure, "too few lines in the transfer list [%zu]",
- lines.size());
- return StringValue("");
- }
-
// Third line is how many stash entries are needed simultaneously.
LOG(INFO) << "maximum stash entries " << lines[2];
@@ -1667,15 +1721,38 @@ static Value* PerformBlockImageUpdate(const char* name, State* state,
return StringValue("");
}
- int res = CreateStash(state, stash_max_blocks, blockdev_filename->data, params.stashbase);
+ int res = CreateStash(state, stash_max_blocks, params.stashbase);
if (res == -1) {
return StringValue("");
}
-
params.createdstash = res;
- // When performing an update, save the index and cmdline of the current command into
- // the last_command_file if this command writes to the stash either explicitly of implicitly.
+ // Set up the new data writer.
+ if (params.canwrite) {
+ params.nti.za = za;
+ params.nti.entry = new_entry;
+ params.nti.brotli_compressed = android::base::EndsWith(new_data_fn->data, ".br");
+ if (params.nti.brotli_compressed) {
+ // Initialize brotli decoder state.
+ params.nti.brotli_decoder_state = BrotliDecoderCreateInstance(nullptr, nullptr, nullptr);
+ }
+ params.nti.receiver_available = true;
+
+ pthread_mutex_init(&params.nti.mu, nullptr);
+ pthread_cond_init(&params.nti.cv, nullptr);
+ pthread_attr_t attr;
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
+
+ int error = pthread_create(&params.thread, &attr, unzip_new_data, &params.nti);
+ if (error != 0) {
+ LOG(ERROR) << "pthread_create failed: " << strerror(error);
+ return StringValue("");
+ }
+ }
+
+ // When performing an update, save the index and cmdline of the current command into the
+ // last_command_file.
// Upon resuming an update, read the saved index first; then
// 1. In verification mode, check if the 'move' or 'diff' commands before the saved index has
// the expected target blocks already. If not, these commands cannot be skipped and we need
@@ -1684,90 +1761,86 @@ static Value* PerformBlockImageUpdate(const char* name, State* state,
// 2. In update mode, skip all commands before the saved index. Therefore, we can avoid deleting
// stashes with duplicate id unintentionally (b/69858743); and also speed up the update.
// If an update succeeds or is unresumable, delete the last_command_file.
- int saved_last_command_index;
+ bool skip_executed_command = true;
+ size_t saved_last_command_index;
if (!ParseLastCommandFile(&saved_last_command_index)) {
DeleteLastCommandFile();
- // We failed to parse the last command, set it explicitly to -1.
- saved_last_command_index = -1;
- }
-
- start += 2;
-
- // Build a map of the available commands
- std::unordered_map<std::string, const Command*> cmd_map;
- for (size_t i = 0; i < cmdcount; ++i) {
- if (cmd_map.find(commands[i].name) != cmd_map.end()) {
- LOG(ERROR) << "Error: command [" << commands[i].name << "] already exists in the cmd map.";
- return StringValue(strdup(""));
- }
- cmd_map[commands[i].name] = &commands[i];
+ // We failed to parse the last command. Disallow skipping executed commands.
+ skip_executed_command = false;
}
int rc = -1;
// Subsequent lines are all individual transfer commands
- for (size_t i = start; i < lines.size(); i++) {
+ for (size_t i = kTransferListHeaderLines; i < lines.size(); i++) {
const std::string& line = lines[i];
if (line.empty()) continue;
+ size_t cmdindex = i - kTransferListHeaderLines;
params.tokens = android::base::Split(line, " ");
params.cpos = 0;
- if (i - start > std::numeric_limits<int>::max()) {
- params.cmdindex = -1;
- } else {
- params.cmdindex = i - start;
- }
- params.cmdname = params.tokens[params.cpos++].c_str();
- params.cmdline = line.c_str();
+ params.cmdname = params.tokens[params.cpos++];
+ params.cmdline = line;
params.target_verified = false;
- if (cmd_map.find(params.cmdname) == cmd_map.end()) {
+ Command::Type cmd_type = Command::ParseType(params.cmdname);
+ if (cmd_type == Command::Type::LAST) {
LOG(ERROR) << "unexpected command [" << params.cmdname << "]";
goto pbiudone;
}
- const Command* cmd = cmd_map[params.cmdname];
+ const CommandFunction& performer = command_map.at(cmd_type);
// Skip the command if we explicitly set the corresponding function pointer to nullptr, e.g.
// "erase" during block_image_verify.
- if (cmd->f == nullptr) {
+ if (performer == nullptr) {
LOG(DEBUG) << "skip executing command [" << line << "]";
continue;
}
- // Skip all commands before the saved last command index when resuming an update.
- if (params.canwrite && params.cmdindex != -1 && params.cmdindex <= saved_last_command_index) {
- LOG(INFO) << "Skipping already executed command: " << params.cmdindex
+ // Skip all commands before the saved last command index when resuming an update, except for
+ // "new" command. Because new commands read in the data sequentially.
+ if (params.canwrite && skip_executed_command && cmdindex <= saved_last_command_index &&
+ cmd_type != Command::Type::NEW) {
+ LOG(INFO) << "Skipping already executed command: " << cmdindex
<< ", last executed command for previous update: " << saved_last_command_index;
continue;
}
- if (cmd->f(params) == -1) {
+ if (performer(params) == -1) {
LOG(ERROR) << "failed to execute command [" << line << "]";
+ if (cmd_type == Command::Type::COMPUTE_HASH_TREE && failure_type == kNoCause) {
+ failure_type = kHashTreeComputationFailure;
+ }
goto pbiudone;
}
- // In verify mode, check if the commands before the saved last_command_index have been
- // executed correctly. If some target blocks have unexpected contents, delete the last command
- // file so that we will resume the update from the first command in the transfer list.
- if (!params.canwrite && saved_last_command_index != -1 && params.cmdindex != -1 &&
- params.cmdindex <= saved_last_command_index) {
+ // In verify mode, check if the commands before the saved last_command_index have been executed
+ // correctly. If some target blocks have unexpected contents, delete the last command file so
+ // that we will resume the update from the first command in the transfer list.
+ if (!params.canwrite && skip_executed_command && cmdindex <= saved_last_command_index) {
// TODO(xunchang) check that the cmdline of the saved index is correct.
- std::string cmdname = std::string(params.cmdname);
- if ((cmdname == "move" || cmdname == "bsdiff" || cmdname == "imgdiff") &&
+ if ((cmd_type == Command::Type::MOVE || cmd_type == Command::Type::BSDIFF ||
+ cmd_type == Command::Type::IMGDIFF) &&
!params.target_verified) {
LOG(WARNING) << "Previously executed command " << saved_last_command_index << ": "
<< params.cmdline << " doesn't produce expected target blocks.";
- saved_last_command_index = -1;
+ skip_executed_command = false;
DeleteLastCommandFile();
}
}
+
if (params.canwrite) {
- if (ota_fsync(params.fd) == -1) {
- failure_type = kFsyncFailure;
+ if (fsync(params.fd) == -1) {
+ failure_type = errno == EIO ? kEioFailure : kFsyncFailure;
PLOG(ERROR) << "fsync failed";
goto pbiudone;
}
+
+ if (!UpdateLastCommandIndex(cmdindex, params.cmdline)) {
+ LOG(WARNING) << "Failed to update the last command file.";
+ }
+
fprintf(cmd_pipe, "set_progress %.4f\n", static_cast<double>(params.written) / total_blocks);
fflush(cmd_pipe);
}
@@ -1804,6 +1877,13 @@ pbiudone:
// to complete the update later.
DeleteStash(params.stashbase);
DeleteLastCommandFile();
+
+ // Create a marker on /cache partition, which allows skipping the update on this partition on
+ // retry. The marker will be removed once booting into normal boot, or before starting next
+ // fresh install.
+ if (!SetPartitionUpdatedMarker(updated_marker)) {
+ LOG(WARNING) << "Failed to set updated marker; continuing";
+ }
}
pthread_mutex_destroy(&params.nti.mu);
@@ -1812,8 +1892,8 @@ pbiudone:
LOG(INFO) << "verified partition contents; update may be resumed";
}
- if (ota_fsync(params.fd) == -1) {
- failure_type = kFsyncFailure;
+ if (fsync(params.fd) == -1) {
+ failure_type = errno == EIO ? kEioFailure : kFsyncFailure;
PLOG(ERROR) << "fsync failed";
}
// params.fd will be automatically closed because it's a unique_fd.
@@ -1886,38 +1966,46 @@ pbiudone:
*/
Value* BlockImageVerifyFn(const char* name, State* state,
const std::vector<std::unique_ptr<Expr>>& argv) {
- // Commands which are not tested are set to nullptr to skip them completely
- const Command commands[] = {
- { "bsdiff", PerformCommandDiff },
- { "erase", nullptr },
- { "free", PerformCommandFree },
- { "imgdiff", PerformCommandDiff },
- { "move", PerformCommandMove },
- { "new", nullptr },
- { "stash", PerformCommandStash },
- { "zero", nullptr }
- };
-
- // Perform a dry run without writing to test if an update can proceed
- return PerformBlockImageUpdate(name, state, argv, commands,
- sizeof(commands) / sizeof(commands[0]), true);
+ // Commands which are not allowed are set to nullptr to skip them completely.
+ const CommandMap command_map{
+ // clang-format off
+ { Command::Type::ABORT, PerformCommandAbort },
+ { Command::Type::BSDIFF, PerformCommandDiff },
+ { Command::Type::COMPUTE_HASH_TREE, PerformCommandComputeHashTree },
+ { Command::Type::ERASE, nullptr },
+ { Command::Type::FREE, PerformCommandFree },
+ { Command::Type::IMGDIFF, PerformCommandDiff },
+ { Command::Type::MOVE, PerformCommandMove },
+ { Command::Type::NEW, nullptr },
+ { Command::Type::STASH, PerformCommandStash },
+ { Command::Type::ZERO, nullptr },
+ // clang-format on
+ };
+ CHECK_EQ(static_cast<size_t>(Command::Type::LAST), command_map.size());
+
+ // Perform a dry run without writing to test if an update can proceed.
+ return PerformBlockImageUpdate(name, state, argv, command_map, true);
}
Value* BlockImageUpdateFn(const char* name, State* state,
const std::vector<std::unique_ptr<Expr>>& argv) {
- const Command commands[] = {
- { "bsdiff", PerformCommandDiff },
- { "erase", PerformCommandErase },
- { "free", PerformCommandFree },
- { "imgdiff", PerformCommandDiff },
- { "move", PerformCommandMove },
- { "new", PerformCommandNew },
- { "stash", PerformCommandStash },
- { "zero", PerformCommandZero }
- };
-
- return PerformBlockImageUpdate(name, state, argv, commands,
- sizeof(commands) / sizeof(commands[0]), false);
+ const CommandMap command_map{
+ // clang-format off
+ { Command::Type::ABORT, PerformCommandAbort },
+ { Command::Type::BSDIFF, PerformCommandDiff },
+ { Command::Type::COMPUTE_HASH_TREE, PerformCommandComputeHashTree },
+ { Command::Type::ERASE, PerformCommandErase },
+ { Command::Type::FREE, PerformCommandFree },
+ { Command::Type::IMGDIFF, PerformCommandDiff },
+ { Command::Type::MOVE, PerformCommandMove },
+ { Command::Type::NEW, PerformCommandNew },
+ { Command::Type::STASH, PerformCommandStash },
+ { Command::Type::ZERO, PerformCommandZero },
+ // clang-format on
+ };
+ CHECK_EQ(static_cast<size_t>(Command::Type::LAST), command_map.size());
+
+ return PerformBlockImageUpdate(name, state, argv, command_map, false);
}
Value* RangeSha1Fn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) {
@@ -1934,18 +2022,19 @@ Value* RangeSha1Fn(const char* name, State* state, const std::vector<std::unique
const std::unique_ptr<Value>& blockdev_filename = args[0];
const std::unique_ptr<Value>& ranges = args[1];
- if (blockdev_filename->type != VAL_STRING) {
+ if (blockdev_filename->type != Value::Type::STRING) {
ErrorAbort(state, kArgsParsingFailure, "blockdev_filename argument to %s must be string", name);
return StringValue("");
}
- if (ranges->type != VAL_STRING) {
+ if (ranges->type != Value::Type::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));
+ android::base::unique_fd fd(open(blockdev_filename->data.c_str(), O_RDWR));
if (fd == -1) {
- ErrorAbort(state, kFileOpenFailure, "open \"%s\" failed: %s", blockdev_filename->data.c_str(),
+ CauseCode cause_code = errno == EIO ? kEioFailure : kFileOpenFailure;
+ ErrorAbort(state, cause_code, "open \"%s\" failed: %s", blockdev_filename->data.c_str(),
strerror(errno));
return StringValue("");
}
@@ -1957,16 +2046,17 @@ Value* RangeSha1Fn(const char* name, State* state, const std::vector<std::unique
SHA1_Init(&ctx);
std::vector<uint8_t> buffer(BLOCKSIZE);
- for (const auto& range : rs) {
- if (!check_lseek(fd, static_cast<off64_t>(range.first) * BLOCKSIZE, SEEK_SET)) {
+ for (const auto& [begin, end] : rs) {
+ if (!check_lseek(fd, static_cast<off64_t>(begin) * BLOCKSIZE, SEEK_SET)) {
ErrorAbort(state, kLseekFailure, "failed to seek %s: %s", blockdev_filename->data.c_str(),
strerror(errno));
return StringValue("");
}
- for (size_t j = range.first; j < range.second; ++j) {
- if (read_all(fd, buffer, BLOCKSIZE) == -1) {
- ErrorAbort(state, kFreadFailure, "failed to read %s: %s", blockdev_filename->data.c_str(),
+ for (size_t j = begin; j < end; ++j) {
+ if (!android::base::ReadFully(fd, buffer.data(), BLOCKSIZE)) {
+ CauseCode cause_code = errno == EIO ? kEioFailure : kFreadFailure;
+ ErrorAbort(state, cause_code, "failed to read %s: %s", blockdev_filename->data.c_str(),
strerror(errno));
return StringValue("");
}
@@ -2000,14 +2090,15 @@ Value* CheckFirstBlockFn(const char* name, State* state,
const std::unique_ptr<Value>& arg_filename = args[0];
- if (arg_filename->type != VAL_STRING) {
+ if (arg_filename->type != Value::Type::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));
+ android::base::unique_fd fd(open(arg_filename->data.c_str(), O_RDONLY));
if (fd == -1) {
- ErrorAbort(state, kFileOpenFailure, "open \"%s\" failed: %s", arg_filename->data.c_str(),
+ CauseCode cause_code = errno == EIO ? kEioFailure : kFileOpenFailure;
+ ErrorAbort(state, cause_code, "open \"%s\" failed: %s", arg_filename->data.c_str(),
strerror(errno));
return StringValue("");
}
@@ -2015,8 +2106,9 @@ Value* CheckFirstBlockFn(const char* name, State* state,
RangeSet blk0(std::vector<Range>{ Range{ 0, 1 } });
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(),
+ if (ReadBlocks(blk0, &block0_buffer, fd) == -1) {
+ CauseCode cause_code = errno == EIO ? kEioFailure : kFreadFailure;
+ ErrorAbort(state, cause_code, "failed to read %s: %s", arg_filename->data.c_str(),
strerror(errno));
return StringValue("");
}
@@ -2055,11 +2147,11 @@ Value* BlockImageRecoverFn(const char* name, State* state,
const std::unique_ptr<Value>& filename = args[0];
const std::unique_ptr<Value>& ranges = args[1];
- if (filename->type != VAL_STRING) {
+ if (filename->type != Value::Type::STRING) {
ErrorAbort(state, kArgsParsingFailure, "filename argument to %s must be string", name);
return StringValue("");
}
- if (ranges->type != VAL_STRING) {
+ if (ranges->type != Value::Type::STRING) {
ErrorAbort(state, kArgsParsingFailure, "ranges argument to %s must be string", name);
return StringValue("");
}
@@ -2093,8 +2185,8 @@ Value* BlockImageRecoverFn(const char* name, State* state,
}
uint8_t buffer[BLOCKSIZE];
- for (const auto& range : rs) {
- for (size_t j = range.first; j < range.second; ++j) {
+ for (const auto& [begin, end] : rs) {
+ for (size_t j = begin; j < end; ++j) {
// Stay within the data area, libfec validates and corrects metadata
if (status.data_size <= static_cast<uint64_t>(j) * BLOCKSIZE) {
continue;