Merge tag 'android-10.0.0_r25' into aosp10-4
Android 10.0.0 release 25
diff --git a/updater/blockimg.cpp b/updater/blockimg.cpp
index b44e1a2..9750a20 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,19 @@
#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/dirutil.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"
@@ -68,13 +70,14 @@
static constexpr size_t BLOCKSIZE = 4096;
static constexpr mode_t STASH_DIRECTORY_MODE = 0700;
static constexpr mode_t STASH_FILE_MODE = 0600;
+static constexpr mode_t MARKER_DIRECTORY_MODE = 0700;
static CauseCode failure_type = kNoCause;
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 +85,8 @@
// 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 +111,7 @@
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 +119,24 @@
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,71 +161,44 @@
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;
+bool SetUpdatedMarker(const std::string& marker) {
+ auto dirname = android::base::Dirname(marker);
+ auto res = mkdir(dirname.c_str(), MARKER_DIRECTORY_MODE);
+ if (res == -1 && errno != EEXIST) {
+ PLOG(ERROR) << "Failed to create directory for marker: " << dirname;
+ return false;
+ }
+
+ if (!android::base::WriteStringToFile("", marker)) {
+ PLOG(ERROR) << "Failed to write to marker file " << marker;
+ return false;
+ }
+ if (!FsyncDir(dirname)) {
+ return false;
+ }
+ LOG(INFO) << "Wrote updated marker to " << marker;
+ return true;
}
-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 discard_blocks(int fd, off64_t offset, uint64_t size) {
- // Don't discard blocks unless the update is a retry run.
- if (!is_retry) {
+static bool discard_blocks(int fd, off64_t offset, uint64_t size, bool force = false) {
+ // Don't discard blocks unless the update is a retry run or force == true
+ if (!is_retry && !force) {
return true;
}
uint64_t args[2] = { static_cast<uint64_t>(offset), size };
if (ioctl(fd, BLKDISCARD, &args) == -1) {
+ // On devices that does not support BLKDISCARD, ignore the error.
+ if (errno == EOPNOTSUPP) {
+ return true;
+ }
PLOG(ERROR) << "BLKDISCARD ioctl failed";
return false;
}
@@ -225,11 +215,10 @@
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 +263,9 @@
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 +474,17 @@
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 +496,9 @@
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 +507,9 @@
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 +523,8 @@
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 +638,43 @@
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 +727,8 @@
}
}
-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 +740,17 @@
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 +769,30 @@
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,111 +802,92 @@
}
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;
+ const std::vector<uint8_t>& buffer, bool checkspace, bool* exists) {
+ if (base.empty()) {
+ return -1;
+ }
+
+ if (checkspace && !CheckAndFreeSpaceOnCache(blocks * BLOCKSIZE)) {
+ LOG(ERROR) << "not enough space to write stash";
+ return -1;
+ }
+
+ std::string fn = GetStashFileName(base, id, ".partial");
+ std::string cn = GetStashFileName(base, id, "");
+
+ 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;
}
- if (checkspace && CacheSizeCheck(blocks * BLOCKSIZE) != 0) {
- LOG(ERROR) << "not enough space to write stash";
- return -1;
- }
+ *exists = false;
+ }
- std::string fn = GetStashFileName(base, id, ".partial");
- std::string cn = GetStashFileName(base, id, "");
+ LOG(INFO) << " writing " << blocks << " blocks to " << cn;
- if (exists) {
- struct stat sb;
- int res = stat(cn.c_str(), &sb);
+ 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 (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;
- }
+ if (fchown(fd, AID_SYSTEM, AID_SYSTEM) != 0) { // system user
+ PLOG(ERROR) << "failed to chown \"" << fn << "\"";
+ return -1;
+ }
- *exists = false;
- }
+ 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;
+ }
- LOG(INFO) << " writing " << blocks << " blocks to " << cn;
+ if (fsync(fd) == -1) {
+ failure_type = errno == EIO ? kEioFailure : kFsyncFailure;
+ PLOG(ERROR) << "fsync \"" << fn << "\" failed";
+ return -1;
+ }
- 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;
- }
+ if (rename(fn.c_str(), cn.c_str()) == -1) {
+ PLOG(ERROR) << "rename(\"" << fn << "\", \"" << cn << "\") failed";
+ return -1;
+ }
- if (fchown(fd, AID_SYSTEM, AID_SYSTEM) != 0) { // system user
- PLOG(ERROR) << "failed to chown \"" << fn << "\"";
- return -1;
- }
+ std::string dname = GetStashFileName(base, "", "");
+ if (!FsyncDir(dname)) {
+ return -1;
+ }
- if (write_all(fd, buffer, blocks * BLOCKSIZE) == -1) {
- return -1;
- }
-
- if (ota_fsync(fd) == -1) {
- failure_type = kFsyncFailure;
- PLOG(ERROR) << "fsync \"" << fn << "\" failed";
- return -1;
- }
-
- if (rename(fn.c_str(), cn.c_str()) == -1) {
- PLOG(ERROR) << "rename(\"" << fn << "\", \"" << cn << "\") 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 (ota_fsync(dfd) == -1) {
- failure_type = kFsyncFailure;
- PLOG(ERROR) << "fsync \"" << dname << "\" failed";
- 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);
+ res = mkdir_recursively(dirname, STASH_DIRECTORY_MODE, false, nullptr);
if (res != 0) {
ErrorAbort(state, kStashCreationFailure, "mkdir \"%s\" failed: %s", dirname.c_str(),
@@ -927,7 +901,7 @@
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 +933,7 @@
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 +997,7 @@
return -1;
}
- allocate(*src_blocks * BLOCKSIZE, params.buffer);
+ allocate(*src_blocks * BLOCKSIZE, ¶ms.buffer);
// "-" or <src_range> [<src_loc>]
if (params.tokens[params.cpos] == "-") {
@@ -1034,7 +1008,7 @@
CHECK(static_cast<bool>(src));
*overlap = src.Overlaps(tgt);
- if (ReadBlocks(src, params.buffer, params.fd) == -1) {
+ if (ReadBlocks(src, ¶ms.buffer, params.fd) == -1) {
return -1;
}
@@ -1059,7 +1033,7 @@
}
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 +1074,9 @@
*
* 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 +1104,30 @@
}
// <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 +1137,6 @@
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 +1148,7 @@
return 0;
}
- if (*overlap && LoadStash(params, srchash, true, nullptr, params.buffer, true) == 0) {
+ if (overlap && LoadStash(params, srchash, true, ¶ms.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 +1166,8 @@
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 +1213,7 @@
}
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, ¶ms.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 +1222,11 @@
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, ¶ms.buffer);
+ if (ReadBlocks(src, ¶ms.buffer, params.fd) == -1) {
return -1;
}
- blocks = src.blocks();
stash_map[id] = src;
if (VerifyBlocks(id, params.buffer, blocks, true) != 0) {
@@ -1277,10 +1245,6 @@
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 +1278,13 @@
LOG(INFO) << " zeroing " << tgt.blocks() << " blocks";
- allocate(BLOCKSIZE, params.buffer);
+ allocate(BLOCKSIZE, ¶ms.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 +1293,10 @@
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 +1367,7 @@
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 +1387,15 @@
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 +1403,7 @@
} 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;
@@ -1447,7 +1412,10 @@
// We expect the output of the patcher to fill the tgt ranges exactly.
if (!writer.Finished()) {
- LOG(ERROR) << "range sink underrun?";
+ LOG(ERROR) << "Failed to fully write target blocks (range sink underrun): Missing "
+ << writer.AvailableSpace() << " bytes";
+ failure_type = kPatchApplicationFailure;
+ return -1;
}
} else {
LOG(INFO) << "skipping " << blocks << " blocks already patched to " << tgt.blocks() << " ["
@@ -1492,43 +1460,176 @@
if (params.canwrite) {
LOG(INFO) << " erasing " << tgt.blocks() << " blocks";
- for (const auto& range : tgt) {
- uint64_t blocks[2];
- // offset in bytes
- blocks[0] = range.first * static_cast<uint64_t>(BLOCKSIZE);
- // length in bytes
- blocks[1] = (range.second - range.first) * static_cast<uint64_t>(BLOCKSIZE);
-
-#ifndef SUPPRESS_EMMC_WIPE
- if (ioctl(params.fd, BLKDISCARD, &blocks) == -1) {
- PLOG(ERROR) << "BLKDISCARD ioctl failed";
+ 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, true /* force */)) {
return -1;
}
-#endif
}
}
return 0;
}
-// Definitions for transfer list command functions
-typedef int (*CommandFunction)(CommandParameters&);
+static int PerformCommandAbort(CommandParameters&) {
+ LOG(INFO) << "Aborting as instructed";
+ return -1;
+}
-struct Command {
- const char* name;
- CommandFunction f;
-};
+// 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;
+ }
-// 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)
+ // 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;
+ }
+
+ 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(static_cast<int64_t>(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;
+ }
+
+<<<<<<< HEAD
+#ifndef SUPPRESS_EMMC_WIPE
+ if (ioctl(params.fd, BLKDISCARD, &blocks) == -1) {
+ PLOG(ERROR) << "BLKDISCARD ioctl failed";
+=======
+ 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";
+>>>>>>> android-10.0.0_r25
+ return -1;
+ }
+#endif
+ }
+ }
+
+ 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 bool Sha1DevicePath(const std::string& path, uint8_t digest[SHA_DIGEST_LENGTH]) {
+ auto device_name = android::base::Basename(path);
+ auto dm_target_name_path = "/sys/block/" + device_name + "/dm/name";
+
+ struct stat sb;
+ if (stat(dm_target_name_path.c_str(), &sb) == 0) {
+ // This is a device mapper target. Use partition name as part of the hash instead. Do not
+ // include extents as part of the hash, because the size of a partition may be shrunk after
+ // the patches are applied.
+ std::string dm_target_name;
+ if (!android::base::ReadFileToString(dm_target_name_path, &dm_target_name)) {
+ PLOG(ERROR) << "Cannot read " << dm_target_name_path;
+ return false;
+ }
+ SHA1(reinterpret_cast<const uint8_t*>(dm_target_name.data()), dm_target_name.size(), digest);
+ return true;
+ }
+
+ if (errno != ENOENT) {
+ // This is a device mapper target, but its name cannot be retrieved.
+ PLOG(ERROR) << "Cannot get dm target name for " << path;
+ return false;
+ }
+
+ // This doesn't appear to be a device mapper target, but if its name starts with dm-, something
+ // else might have gone wrong.
+ if (android::base::StartsWith(device_name, "dm-")) {
+ LOG(WARNING) << "Device " << path << " starts with dm- but is not mapped by device-mapper.";
+ }
+
+ // 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.
+ SHA1(reinterpret_cast<const uint8_t*>(path.data()), path.size(), digest);
+ return true;
+}
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");
@@ -1547,24 +1648,29 @@
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("");
}
@@ -1596,38 +1702,44 @@
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);
+ uint8_t digest[SHA_DIGEST_LENGTH];
+ if (!Sha1DevicePath(blockdev_filename->data, digest)) {
+ return StringValue("");
+ }
+ 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(¶ms.nti.mu, nullptr);
- pthread_cond_init(¶ms.nti.cv, nullptr);
- pthread_attr_t attr;
- pthread_attr_init(&attr);
- pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
-
- int error = pthread_create(¶ms.thread, &attr, unzip_new_data, ¶ms.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("");
}
@@ -1651,13 +1763,6 @@
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];
@@ -1669,15 +1774,38 @@
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(¶ms.nti.mu, nullptr);
+ pthread_cond_init(¶ms.nti.cv, nullptr);
+ pthread_attr_t attr;
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
+
+ int error = pthread_create(¶ms.thread, &attr, unzip_new_data, ¶ms.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
@@ -1686,90 +1814,86 @@
// 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);
}
@@ -1798,14 +1922,23 @@
const char* partition = strrchr(blockdev_filename->data.c_str(), '/');
if (partition != nullptr && *(partition + 1) != 0) {
- fprintf(cmd_pipe, "log bytes_written_%s: %zu\n", partition + 1, params.written * BLOCKSIZE);
- fprintf(cmd_pipe, "log bytes_stashed_%s: %zu\n", partition + 1, params.stashed * BLOCKSIZE);
+ fprintf(cmd_pipe, "log bytes_written_%s: %" PRIu64 "\n", partition + 1,
+ static_cast<uint64_t>(params.written) * BLOCKSIZE);
+ fprintf(cmd_pipe, "log bytes_stashed_%s: %" PRIu64 "\n", partition + 1,
+ static_cast<uint64_t>(params.stashed) * BLOCKSIZE);
fflush(cmd_pipe);
}
// Delete stash only after successfully completing the update, as it may contain blocks needed
// 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 (!SetUpdatedMarker(updated_marker)) {
+ LOG(WARNING) << "Failed to set updated marker; continuing";
+ }
}
pthread_mutex_destroy(¶ms.nti.mu);
@@ -1814,8 +1947,8 @@
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.
@@ -1888,38 +2021,46 @@
*/
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 }
- };
+ // 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, commands,
- sizeof(commands) / sizeof(commands[0]), true);
+ // 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 }
- };
+ 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, commands,
- sizeof(commands) / sizeof(commands[0]), false);
+ return PerformBlockImageUpdate(name, state, argv, command_map, false);
}
Value* RangeSha1Fn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) {
@@ -1936,18 +2077,19 @@
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("");
}
@@ -1959,16 +2101,17 @@
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("");
}
@@ -2002,14 +2145,15 @@
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("");
}
@@ -2017,8 +2161,9 @@
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("");
}
@@ -2057,11 +2202,11 @@
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("");
}
@@ -2095,8 +2240,8 @@
}
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;