summaryrefslogtreecommitdiffstats
path: root/updater
diff options
context:
space:
mode:
authorEthan Yonker <dees_troy@teamw.in>2018-08-24 18:17:36 +0200
committerEthan Yonker <dees_troy@teamw.in>2018-08-24 18:17:39 +0200
commit58f2132bc3954fc704787d477500a209eedb8e29 (patch)
treeeb0f79aacd68724b0c0c091018384ef924380f47 /updater
parentRemove remaining pieces of supersu (diff)
parentSnap for 4745538 from 723056a83f8c8b15af02d9c302862dbb2304ea8c to pi-release (diff)
downloadandroid_bootable_recovery-58f2132bc3954fc704787d477500a209eedb8e29.tar
android_bootable_recovery-58f2132bc3954fc704787d477500a209eedb8e29.tar.gz
android_bootable_recovery-58f2132bc3954fc704787d477500a209eedb8e29.tar.bz2
android_bootable_recovery-58f2132bc3954fc704787d477500a209eedb8e29.tar.lz
android_bootable_recovery-58f2132bc3954fc704787d477500a209eedb8e29.tar.xz
android_bootable_recovery-58f2132bc3954fc704787d477500a209eedb8e29.tar.zst
android_bootable_recovery-58f2132bc3954fc704787d477500a209eedb8e29.zip
Diffstat (limited to 'updater')
-rw-r--r--updater/Android.mk4
-rw-r--r--updater/blockimg.cpp295
-rw-r--r--updater/include/updater/blockimg.h2
-rw-r--r--updater/include/updater/rangeset.h164
-rw-r--r--updater/install.cpp468
-rw-r--r--updater/updater.cpp4
6 files changed, 531 insertions, 406 deletions
diff --git a/updater/Android.mk b/updater/Android.mk
index cef6b963f..3d2591391 100644
--- a/updater/Android.mk
+++ b/updater/Android.mk
@@ -71,7 +71,7 @@ LOCAL_C_INCLUDES := \
external/e2fsprogs/misc
LOCAL_CFLAGS := \
- -Wno-unused-parameter \
+ -Wall \
-Werror
LOCAL_EXPORT_C_INCLUDE_DIRS := \
@@ -96,7 +96,7 @@ LOCAL_C_INCLUDES := \
$(LOCAL_PATH)/include
LOCAL_CFLAGS := \
- -Wno-unused-parameter \
+ -Wall \
-Werror
LOCAL_STATIC_LIBRARIES := \
diff --git a/updater/blockimg.cpp b/updater/blockimg.cpp
index a0b9ad233..e93196b27 100644
--- a/updater/blockimg.cpp
+++ b/updater/blockimg.cpp
@@ -34,11 +34,13 @@
#include <fec/io.h>
#include <functional>
+#include <limits>
#include <memory>
#include <string>
#include <unordered_map>
#include <vector>
+#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/parseint.h>
#include <android-base/strings.h>
@@ -50,11 +52,12 @@
#include <ziparchive/zip_archive.h>
#include "edify/expr.h"
-#include "error_code.h"
-#include "ota_io.h"
-#include "print_sha1.h"
+#include "otafault/ota_io.h"
+#include "otautil/cache_location.h"
+#include "otautil/error_code.h"
+#include "otautil/print_sha1.h"
+#include "otautil/rangeset.h"
#include "updater/install.h"
-#include "updater/rangeset.h"
#include "updater/updater.h"
// Set this to 0 to interpret 'erase' transfers to mean do a
@@ -63,7 +66,6 @@
#define DEBUG_ERASE 0
static constexpr size_t BLOCKSIZE = 4096;
-static constexpr const char* STASH_DIRECTORY_BASE = "/cache/recovery";
static constexpr mode_t STASH_DIRECTORY_MODE = 0700;
static constexpr mode_t STASH_FILE_MODE = 0600;
@@ -71,6 +73,93 @@ 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();
+ if (unlink(last_command_file.c_str()) == -1 && errno != ENOENT) {
+ PLOG(ERROR) << "Failed to unlink: " << last_command_file;
+ }
+}
+
+// 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();
+ android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(last_command_file.c_str(), O_RDONLY)));
+ if (fd == -1) {
+ if (errno != ENOENT) {
+ PLOG(ERROR) << "Failed to open " << last_command_file;
+ return false;
+ }
+
+ LOG(INFO) << last_command_file << " doesn't exist.";
+ return false;
+ }
+
+ // Now that the last_command file exists, parse the last command index of previous update.
+ std::string content;
+ if (!android::base::ReadFdToString(fd.get(), &content)) {
+ LOG(ERROR) << "Failed to read: " << last_command_file;
+ return false;
+ }
+
+ std::vector<std::string> lines = android::base::Split(android::base::Trim(content), "\n");
+ if (lines.size() != 2) {
+ LOG(ERROR) << "Unexpected line counts in last command file: " << content;
+ return false;
+ }
+
+ if (!android::base::ParseInt(lines[0], last_command_index)) {
+ LOG(ERROR) << "Failed to parse integer in: " << lines[0];
+ return false;
+ }
+
+ 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();
+ 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(
+ TEMP_FAILURE_RETRY(open(last_command_tmp.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0660)));
+ if (wfd == -1 || !android::base::WriteStringToFd(content, wfd)) {
+ PLOG(ERROR) << "Failed to update last command";
+ return false;
+ }
+
+ if (fsync(wfd) == -1) {
+ PLOG(ERROR) << "Failed to fsync " << last_command_tmp;
+ return false;
+ }
+
+ if (chown(last_command_tmp.c_str(), AID_SYSTEM, AID_SYSTEM) == -1) {
+ PLOG(ERROR) << "Failed to change owner for " << last_command_tmp;
+ return false;
+ }
+
+ if (rename(last_command_tmp.c_str(), last_command_file.c_str()) == -1) {
+ PLOG(ERROR) << "Failed to rename" << last_command_tmp;
+ 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;
+ 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) {
@@ -281,6 +370,11 @@ static bool receive_new_data(const uint8_t* data, size_t size, void* cookie) {
// Wait for nti->writer to be non-null, indicating some of this data is wanted.
pthread_mutex_lock(&nti->mu);
while (nti->writer == nullptr) {
+ // End the new data receiver if we encounter an error when performing block image update.
+ if (!nti->receiver_available) {
+ pthread_mutex_unlock(&nti->mu);
+ return false;
+ }
pthread_cond_wait(&nti->cv, &nti->mu);
}
pthread_mutex_unlock(&nti->mu);
@@ -316,6 +410,11 @@ static bool receive_brotli_new_data(const uint8_t* data, size_t size, void* cook
// Wait for nti->writer to be non-null, indicating some of this data is wanted.
pthread_mutex_lock(&nti->mu);
while (nti->writer == nullptr) {
+ // End the receiver if we encounter an error when performing block image update.
+ if (!nti->receiver_available) {
+ pthread_mutex_unlock(&nti->mu);
+ return false;
+ }
pthread_cond_wait(&nti->cv, &nti->mu);
}
pthread_mutex_unlock(&nti->mu);
@@ -429,6 +528,7 @@ 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 freestash;
@@ -445,6 +545,7 @@ struct CommandParameters {
pthread_t thread;
std::vector<uint8_t> buffer;
uint8_t* patch_start;
+ bool target_verified; // The target blocks have expected contents already.
};
// Print the hash in hex for corrupted source blocks (excluding the stashed blocks which is
@@ -482,6 +583,10 @@ static void PrintHashForCorruptedSourceBlocks(const CommandParameters& params,
}
RangeSet src = RangeSet::Parse(params.tokens[pos++]);
+ if (!src) {
+ LOG(ERROR) << "Failed to parse range in " << params.cmdline;
+ return;
+ }
RangeSet locs;
// If there's no stashed blocks, content in the buffer is consecutive and has the same
@@ -572,7 +677,7 @@ static std::string GetStashFileName(const std::string& base, const std::string&
return "";
}
- std::string fn(STASH_DIRECTORY_BASE);
+ std::string fn(CacheLocation::location().stash_directory_base());
fn += "/" + base + "/" + id + postfix;
return fn;
@@ -803,7 +908,7 @@ static int CreateStash(State* state, size_t maxblocks, const std::string& blockd
size_t max_stash_size = maxblocks * BLOCKSIZE;
if (res == -1 && errno != ENOENT) {
- ErrorAbort(state, kStashCreationFailure, "stat \"%s\" failed: %s\n", dirname.c_str(),
+ ErrorAbort(state, kStashCreationFailure, "stat \"%s\" failed: %s", dirname.c_str(),
strerror(errno));
return -1;
} else if (res != 0) {
@@ -811,19 +916,19 @@ static int CreateStash(State* state, size_t maxblocks, const std::string& blockd
res = mkdir(dirname.c_str(), STASH_DIRECTORY_MODE);
if (res != 0) {
- ErrorAbort(state, kStashCreationFailure, "mkdir \"%s\" failed: %s\n", dirname.c_str(),
+ ErrorAbort(state, kStashCreationFailure, "mkdir \"%s\" failed: %s", dirname.c_str(),
strerror(errno));
return -1;
}
if (chown(dirname.c_str(), AID_SYSTEM, AID_SYSTEM) != 0) { // system user
- ErrorAbort(state, kStashCreationFailure, "chown \"%s\" failed: %s\n", dirname.c_str(),
+ ErrorAbort(state, kStashCreationFailure, "chown \"%s\" failed: %s", dirname.c_str(),
strerror(errno));
return -1;
}
if (CacheSizeCheck(max_stash_size) != 0) {
- ErrorAbort(state, kStashCreationFailure, "not enough space for stash (%zu needed)\n",
+ ErrorAbort(state, kStashCreationFailure, "not enough space for stash (%zu needed)",
max_stash_size);
return -1;
}
@@ -855,7 +960,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) {
- ErrorAbort(state, kStashCreationFailure, "not enough space for stash (%zu more needed)\n",
+ ErrorAbort(state, kStashCreationFailure, "not enough space for stash (%zu more needed)",
needed);
return -1;
}
@@ -926,6 +1031,7 @@ static int LoadSourceBlocks(CommandParameters& params, const RangeSet& tgt, size
params.cpos++;
} else {
RangeSet src = RangeSet::Parse(params.tokens[params.cpos++]);
+ CHECK(static_cast<bool>(src));
*overlap = src.Overlaps(tgt);
if (ReadBlocks(src, params.buffer, params.fd) == -1) {
@@ -938,6 +1044,7 @@ static int LoadSourceBlocks(CommandParameters& params, const RangeSet& tgt, size
}
RangeSet locs = RangeSet::Parse(params.tokens[params.cpos++]);
+ CHECK(static_cast<bool>(locs));
MoveRange(params.buffer, locs, params.buffer);
}
@@ -960,6 +1067,7 @@ static int LoadSourceBlocks(CommandParameters& params, const RangeSet& tgt, size
}
RangeSet locs = RangeSet::Parse(tokens[1]);
+ CHECK(static_cast<bool>(locs));
MoveRange(params.buffer, locs, stash);
}
@@ -1024,6 +1132,7 @@ static int LoadSrcTgtVersion3(CommandParameters& params, RangeSet& tgt, size_t*
// <tgt_range>
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) {
@@ -1054,6 +1163,10 @@ 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) {
@@ -1094,8 +1207,11 @@ static int PerformCommandMove(CommandParameters& params) {
if (status == 0) {
params.foundwrites = true;
- } else if (params.foundwrites) {
- LOG(WARNING) << "warning: commands executed out of order [" << params.cmdname << "]";
+ } else {
+ params.target_verified = true;
+ if (params.foundwrites) {
+ LOG(WARNING) << "warning: commands executed out of order [" << params.cmdname << "]";
+ }
}
if (params.canwrite) {
@@ -1136,6 +1252,7 @@ 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) {
@@ -1158,8 +1275,15 @@ static int PerformCommandStash(CommandParameters& params) {
}
LOG(INFO) << "stashing " << blocks << " blocks to " << id;
- params.stashed += blocks;
- return WriteStash(params.stashbase, id, blocks, params.buffer, false, nullptr);
+ 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;
}
static int PerformCommandFree(CommandParameters& params) {
@@ -1186,6 +1310,7 @@ static int PerformCommandZero(CommandParameters& params) {
}
RangeSet tgt = RangeSet::Parse(params.tokens[params.cpos++]);
+ CHECK(static_cast<bool>(tgt));
LOG(INFO) << " zeroing " << tgt.blocks() << " blocks";
@@ -1228,6 +1353,7 @@ static int PerformCommandNew(CommandParameters& params) {
}
RangeSet tgt = RangeSet::Parse(params.tokens[params.cpos++]);
+ CHECK(static_cast<bool>(tgt));
if (params.canwrite) {
LOG(INFO) << " writing " << tgt.blocks() << " blocks of new data";
@@ -1285,8 +1411,11 @@ static int PerformCommandDiff(CommandParameters& params) {
if (status == 0) {
params.foundwrites = true;
- } else if (params.foundwrites) {
- LOG(WARNING) << "warning: commands executed out of order [" << params.cmdname << "]";
+ } else {
+ params.target_verified = true;
+ if (params.foundwrites) {
+ LOG(WARNING) << "warning: commands executed out of order [" << params.cmdname << "]";
+ }
}
if (params.canwrite) {
@@ -1297,7 +1426,7 @@ static int PerformCommandDiff(CommandParameters& params) {
RangeSinkWriter writer(params.fd, tgt);
if (params.cmdname[0] == 'i') { // imgdiff
- if (ApplyImagePatch(params.buffer.data(), blocks * BLOCKSIZE, &patch_value,
+ if (ApplyImagePatch(params.buffer.data(), blocks * BLOCKSIZE, patch_value,
std::bind(&RangeSinkWriter::Write, &writer, std::placeholders::_1,
std::placeholders::_2),
nullptr, nullptr) != 0) {
@@ -1306,7 +1435,7 @@ static int PerformCommandDiff(CommandParameters& params) {
return -1;
}
} else {
- if (ApplyBSDiffPatch(params.buffer.data(), blocks * BLOCKSIZE, &patch_value, 0,
+ if (ApplyBSDiffPatch(params.buffer.data(), blocks * BLOCKSIZE, patch_value, 0,
std::bind(&RangeSinkWriter::Write, &writer, std::placeholders::_1,
std::placeholders::_2),
nullptr) != 0) {
@@ -1358,6 +1487,7 @@ static int PerformCommandErase(CommandParameters& params) {
}
RangeSet tgt = RangeSet::Parse(params.tokens[params.cpos++]);
+ CHECK(static_cast<bool>(tgt));
if (params.canwrite) {
LOG(INFO) << " erasing " << tgt.blocks() << " blocks";
@@ -1495,7 +1625,7 @@ static Value* PerformBlockImageUpdate(const char* name, State* state,
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]\n",
+ ErrorAbort(state, kArgsParsingFailure, "too few lines in the transfer list [%zd]",
lines.size());
return StringValue("");
}
@@ -1511,7 +1641,7 @@ static Value* PerformBlockImageUpdate(const char* name, State* state,
// Second line in transfer list is the total number of blocks we expect to write.
size_t total_blocks;
if (!android::base::ParseUint(lines[1], &total_blocks)) {
- ErrorAbort(state, kArgsParsingFailure, "unexpected block count [%s]\n", lines[1].c_str());
+ ErrorAbort(state, kArgsParsingFailure, "unexpected block count [%s]", lines[1].c_str());
return StringValue("");
}
@@ -1521,7 +1651,7 @@ static Value* PerformBlockImageUpdate(const char* name, State* state,
size_t start = 2;
if (lines.size() < 4) {
- ErrorAbort(state, kArgsParsingFailure, "too few lines in the transfer list [%zu]\n",
+ ErrorAbort(state, kArgsParsingFailure, "too few lines in the transfer list [%zu]",
lines.size());
return StringValue("");
}
@@ -1532,7 +1662,7 @@ static Value* PerformBlockImageUpdate(const char* name, State* state,
// Fourth line is the maximum number of blocks that will be stashed simultaneously
size_t stash_max_blocks;
if (!android::base::ParseUint(lines[3], &stash_max_blocks)) {
- ErrorAbort(state, kArgsParsingFailure, "unexpected maximum stash blocks [%s]\n",
+ ErrorAbort(state, kArgsParsingFailure, "unexpected maximum stash blocks [%s]",
lines[3].c_str());
return StringValue("");
}
@@ -1544,6 +1674,23 @@ static Value* PerformBlockImageUpdate(const char* name, State* state,
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.
+ // 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
+ // to attempt to execute them again. Therefore, we will delete the last_command_file so that
+ // the update will resume from the start of the transfer list.
+ // 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;
+ 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
@@ -1559,14 +1706,20 @@ static Value* PerformBlockImageUpdate(const char* name, State* state,
int rc = -1;
// Subsequent lines are all individual transfer commands
- for (auto it = lines.cbegin() + start; it != lines.cend(); it++) {
- const std::string& line(*it);
+ for (size_t i = start; i < lines.size(); i++) {
+ const std::string& line = lines[i];
if (line.empty()) continue;
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.target_verified = false;
if (cmd_map.find(params.cmdname) == cmd_map.end()) {
LOG(ERROR) << "unexpected command [" << params.cmdname << "]";
@@ -1575,11 +1728,40 @@ static Value* PerformBlockImageUpdate(const char* name, State* state,
const Command* cmd = cmd_map[params.cmdname];
- if (cmd->f != nullptr && cmd->f(params) == -1) {
+ // Skip the command if we explicitly set the corresponding function pointer to nullptr, e.g.
+ // "erase" during block_image_verify.
+ if (cmd->f == 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
+ << ", last executed command for previous update: " << saved_last_command_index;
+ continue;
+ }
+
+ if (cmd->f(params) == -1) {
LOG(ERROR) << "failed to execute command [" << line << "]";
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) {
+ // 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") &&
+ !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;
+ DeleteLastCommandFile();
+ }
+ }
if (params.canwrite) {
if (ota_fsync(params.fd) == -1) {
failure_type = kFsyncFailure;
@@ -1591,29 +1773,45 @@ static Value* PerformBlockImageUpdate(const char* name, State* state,
}
}
+ rc = 0;
+
+pbiudone:
if (params.canwrite) {
- pthread_join(params.thread, nullptr);
+ pthread_mutex_lock(&params.nti.mu);
+ if (params.nti.receiver_available) {
+ LOG(WARNING) << "new data receiver is still available after executing all commands.";
+ }
+ params.nti.receiver_available = false;
+ pthread_cond_broadcast(&params.nti.cv);
+ pthread_mutex_unlock(&params.nti.mu);
+ int ret = pthread_join(params.thread, nullptr);
+ if (ret != 0) {
+ LOG(WARNING) << "pthread join returned with " << strerror(ret);
+ }
- LOG(INFO) << "wrote " << params.written << " blocks; expected " << total_blocks;
- LOG(INFO) << "stashed " << params.stashed << " blocks";
- LOG(INFO) << "max alloc needed was " << params.buffer.size();
+ if (rc == 0) {
+ LOG(INFO) << "wrote " << params.written << " blocks; expected " << total_blocks;
+ LOG(INFO) << "stashed " << params.stashed << " blocks";
+ LOG(INFO) << "max alloc needed was " << params.buffer.size();
- 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);
- fflush(cmd_pipe);
+ 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);
+ 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();
}
- // Delete stash only after successfully completing the update, as it may contain blocks needed
- // to complete the update later.
- DeleteStash(params.stashbase);
- } else {
+
+ pthread_mutex_destroy(&params.nti.mu);
+ pthread_cond_destroy(&params.nti.cv);
+ } else if (rc == 0) {
LOG(INFO) << "verified partition contents; update may be resumed";
}
- rc = 0;
-
-pbiudone:
if (ota_fsync(params.fd) == -1) {
failure_type = kFsyncFailure;
PLOG(ERROR) << "fsync failed";
@@ -1624,6 +1822,11 @@ pbiudone:
BrotliDecoderDestroyInstance(params.nti.brotli_decoder_state);
}
+ // Delete the last command file if the update cannot be resumed.
+ if (params.isunresumable) {
+ DeleteLastCommandFile();
+ }
+
// Only delete the stash if the update cannot be resumed, or it's a verification run and we
// created the stash.
if (params.isunresumable || (!params.canwrite && params.createdstash)) {
@@ -1748,6 +1951,7 @@ Value* RangeSha1Fn(const char* name, State* state, const std::vector<std::unique
}
RangeSet rs = RangeSet::Parse(ranges->data);
+ CHECK(static_cast<bool>(rs));
SHA_CTX ctx;
SHA1_Init(&ctx);
@@ -1859,6 +2063,11 @@ Value* BlockImageRecoverFn(const char* name, State* state,
ErrorAbort(state, kArgsParsingFailure, "ranges argument to %s must be string", name);
return StringValue("");
}
+ RangeSet rs = RangeSet::Parse(ranges->data);
+ if (!rs) {
+ ErrorAbort(state, kArgsParsingFailure, "failed to parse ranges: %s", ranges->data.c_str());
+ return StringValue("");
+ }
// Output notice to log when recover is attempted
LOG(INFO) << filename->data << " image corrupted, attempting to recover...";
@@ -1884,7 +2093,7 @@ Value* BlockImageRecoverFn(const char* name, State* state,
}
uint8_t buffer[BLOCKSIZE];
- for (const auto& range : RangeSet::Parse(ranges->data)) {
+ for (const auto& range : rs) {
for (size_t j = range.first; j < range.second; ++j) {
// Stay within the data area, libfec validates and corrects metadata
if (status.data_size <= static_cast<uint64_t>(j) * BLOCKSIZE) {
diff --git a/updater/include/updater/blockimg.h b/updater/include/updater/blockimg.h
index 2f4ad3c04..71733b303 100644
--- a/updater/include/updater/blockimg.h
+++ b/updater/include/updater/blockimg.h
@@ -17,6 +17,8 @@
#ifndef _UPDATER_BLOCKIMG_H_
#define _UPDATER_BLOCKIMG_H_
+#include <string>
+
void RegisterBlockImageFunctions();
#endif
diff --git a/updater/include/updater/rangeset.h b/updater/include/updater/rangeset.h
deleted file mode 100644
index fad038043..000000000
--- a/updater/include/updater/rangeset.h
+++ /dev/null
@@ -1,164 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <stddef.h>
-
-#include <string>
-#include <utility>
-#include <vector>
-
-#include <android-base/logging.h>
-#include <android-base/parseint.h>
-#include <android-base/strings.h>
-
-using Range = std::pair<size_t, size_t>;
-
-class RangeSet {
- public:
- RangeSet() : blocks_(0) {}
-
- explicit RangeSet(std::vector<Range>&& pairs) {
- CHECK_NE(pairs.size(), static_cast<size_t>(0)) << "Invalid number of tokens";
-
- // Sanity check the input.
- size_t result = 0;
- for (const auto& range : pairs) {
- CHECK_LT(range.first, range.second)
- << "Empty or negative range: " << range.first << ", " << range.second;
- size_t sz = range.second - range.first;
- CHECK_LE(result, SIZE_MAX - sz) << "RangeSet size overflow";
- result += sz;
- }
-
- ranges_ = pairs;
- blocks_ = result;
- }
-
- static RangeSet Parse(const std::string& range_text) {
- std::vector<std::string> pieces = android::base::Split(range_text, ",");
- CHECK_GE(pieces.size(), static_cast<size_t>(3)) << "Invalid range text: " << range_text;
-
- size_t num;
- CHECK(android::base::ParseUint(pieces[0], &num, static_cast<size_t>(INT_MAX)))
- << "Failed to parse the number of tokens: " << range_text;
-
- CHECK_NE(num, static_cast<size_t>(0)) << "Invalid number of tokens: " << range_text;
- CHECK_EQ(num % 2, static_cast<size_t>(0)) << "Number of tokens must be even: " << range_text;
- CHECK_EQ(num, pieces.size() - 1) << "Mismatching number of tokens: " << range_text;
-
- std::vector<Range> pairs;
- for (size_t i = 0; i < num; i += 2) {
- size_t first;
- CHECK(android::base::ParseUint(pieces[i + 1], &first, static_cast<size_t>(INT_MAX)));
- size_t second;
- CHECK(android::base::ParseUint(pieces[i + 2], &second, static_cast<size_t>(INT_MAX)));
-
- pairs.emplace_back(first, second);
- }
-
- return RangeSet(std::move(pairs));
- }
-
- // Get the block number for the i-th (starting from 0) block in the RangeSet.
- size_t GetBlockNumber(size_t idx) const {
- CHECK_LT(idx, blocks_) << "Out of bound index " << idx << " (total blocks: " << blocks_ << ")";
-
- for (const auto& range : ranges_) {
- if (idx < range.second - range.first) {
- return range.first + idx;
- }
- idx -= (range.second - range.first);
- }
-
- CHECK(false) << "Failed to find block number for index " << idx;
- return 0; // Unreachable, but to make compiler happy.
- }
-
- // RangeSet has half-closed half-open bounds. For example, "3,5" contains blocks 3 and 4. So "3,5"
- // and "5,7" are not overlapped.
- bool Overlaps(const RangeSet& other) const {
- for (const auto& range : ranges_) {
- size_t start = range.first;
- size_t end = range.second;
- for (const auto& other_range : other.ranges_) {
- size_t other_start = other_range.first;
- size_t other_end = other_range.second;
- // [start, end) vs [other_start, other_end)
- if (!(other_start >= end || start >= other_end)) {
- return true;
- }
- }
- }
- return false;
- }
-
- // size() gives the number of Range's in this RangeSet.
- size_t size() const {
- return ranges_.size();
- }
-
- // blocks() gives the number of all blocks in this RangeSet.
- size_t blocks() const {
- return blocks_;
- }
-
- // We provide const iterators only.
- std::vector<Range>::const_iterator cbegin() const {
- return ranges_.cbegin();
- }
-
- std::vector<Range>::const_iterator cend() const {
- return ranges_.cend();
- }
-
- // Need to provide begin()/end() since range-based loop expects begin()/end().
- std::vector<Range>::const_iterator begin() const {
- return ranges_.cbegin();
- }
-
- std::vector<Range>::const_iterator end() const {
- return ranges_.cend();
- }
-
- // Reverse const iterators for MoveRange().
- std::vector<Range>::const_reverse_iterator crbegin() const {
- return ranges_.crbegin();
- }
-
- std::vector<Range>::const_reverse_iterator crend() const {
- return ranges_.crend();
- }
-
- const Range& operator[](size_t i) const {
- return ranges_[i];
- }
-
- bool operator==(const RangeSet& other) const {
- // The orders of Range's matter. "4,1,5,8,10" != "4,8,10,1,5".
- return (ranges_ == other.ranges_);
- }
-
- bool operator!=(const RangeSet& other) const {
- return ranges_ != other.ranges_;
- }
-
- private:
- // Actual limit for each value and the total number are both INT_MAX.
- std::vector<Range> ranges_;
- size_t blocks_;
-};
diff --git a/updater/install.cpp b/updater/install.cpp
index a8ee4bf39..741d97014 100644
--- a/updater/install.cpp
+++ b/updater/install.cpp
@@ -49,17 +49,15 @@
#include <applypatch/applypatch.h>
#include <bootloader_message/bootloader_message.h>
#include <cutils/android_reboot.h>
-#include <ext4_utils/make_ext4fs.h>
#include <ext4_utils/wipe.h>
#include <openssl/sha.h>
#include <selinux/label.h>
#include <selinux/selinux.h>
+#include <tune2fs.h>
#include <ziparchive/zip_archive.h>
#include "edify/expr.h"
-#include "error_code.h"
#include "mounts.h"
-#include "ota_io.h"
#include "applypatch/applypatch.h"
#include "flashutils/flashutils.h"
@@ -73,10 +71,11 @@
#include "wipe.h"
#endif
-#include "otautil/DirUtil.h"
#include "otautil/ZipUtil.h"
-#include "print_sha1.h"
-#include "tune2fs.h"
+#include "otafault/ota_io.h"
+#include "otautil/DirUtil.h"
+#include "otautil/error_code.h"
+#include "otautil/print_sha1.h"
#include "updater/updater.h"
// Send over the buffer to recovery though the command pipe.
@@ -109,32 +108,242 @@ void uiPrintf(State* _Nonnull state, const char* _Nonnull format, ...) {
uiPrint(state, error_msg);
}
-static bool is_dir(const std::string& dirpath) {
- struct stat st;
- return stat(dirpath.c_str(), &st) == 0 && S_ISDIR(st.st_mode);
+// This is the updater side handler for ui_print() in edify script. Contents will be sent over to
+// the recovery side for on-screen display.
+Value* UIPrintFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) {
+ std::vector<std::string> args;
+ if (!ReadArgs(state, argv, &args)) {
+ return ErrorAbort(state, kArgsParsingFailure, "%s(): Failed to parse the argument(s)", name);
+ }
+
+ std::string buffer = android::base::Join(args, "");
+ uiPrint(state, buffer);
+ return StringValue(buffer);
}
-// Create all parent directories of name, if necessary.
-static bool make_parents(const std::string& name) {
- size_t prev_end = 0;
- while (prev_end < name.size()) {
- size_t next_end = name.find('/', prev_end + 1);
- if (next_end == std::string::npos) {
- break;
+// package_extract_file(package_file[, dest_file])
+// Extracts a single package_file from the update package and writes it to dest_file,
+// overwriting existing files if necessary. Without the dest_file argument, returns the
+// contents of the package file as a binary blob.
+Value* PackageExtractFileFn(const char* name, State* state,
+ const std::vector<std::unique_ptr<Expr>>& argv) {
+ if (argv.size() < 1 || argv.size() > 2) {
+ return ErrorAbort(state, kArgsParsingFailure, "%s() expects 1 or 2 args, got %zu", name,
+ argv.size());
+ }
+
+ if (argv.size() == 2) {
+ // The two-argument version extracts to a file.
+
+ std::vector<std::string> args;
+ if (!ReadArgs(state, argv, &args)) {
+ return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse %zu args", name,
+ argv.size());
}
- std::string dir_path = name.substr(0, next_end);
- if (!is_dir(dir_path)) {
- int result = mkdir(dir_path.c_str(), 0700);
- if (result != 0) {
- PLOG(ERROR) << "failed to mkdir " << dir_path << " when make parents for " << name;
- return false;
- }
+ const std::string& zip_path = args[0];
+ const std::string& dest_path = args[1];
- LOG(INFO) << "created [" << dir_path << "]";
+ ZipArchiveHandle za = static_cast<UpdaterInfo*>(state->cookie)->package_zip;
+ ZipString zip_string_path(zip_path.c_str());
+ ZipEntry entry;
+ if (FindEntry(za, zip_string_path, &entry) != 0) {
+ LOG(ERROR) << name << ": no " << zip_path << " in package";
+ return StringValue("");
+ }
+
+ unique_fd fd(TEMP_FAILURE_RETRY(
+ ota_open(dest_path.c_str(), O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR)));
+ if (fd == -1) {
+ PLOG(ERROR) << name << ": can't open " << dest_path << " for write";
+ return StringValue("");
+ }
+
+ bool success = true;
+ int32_t ret = ExtractEntryToFile(za, &entry, fd);
+ if (ret != 0) {
+ LOG(ERROR) << name << ": Failed to extract entry \"" << zip_path << "\" ("
+ << entry.uncompressed_length << " bytes) to \"" << dest_path
+ << "\": " << ErrorCodeString(ret);
+ success = false;
+ }
+ if (ota_fsync(fd) == -1) {
+ PLOG(ERROR) << "fsync of \"" << dest_path << "\" failed";
+ success = false;
+ }
+ if (ota_close(fd) == -1) {
+ PLOG(ERROR) << "close of \"" << dest_path << "\" failed";
+ success = false;
+ }
+
+ return StringValue(success ? "t" : "");
+ } else {
+ // The one-argument version returns the contents of the file as the result.
+
+ std::vector<std::string> args;
+ if (!ReadArgs(state, argv, &args)) {
+ return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse %zu args", name,
+ argv.size());
}
- prev_end = next_end;
+ const std::string& zip_path = args[0];
+
+ ZipArchiveHandle za = static_cast<UpdaterInfo*>(state->cookie)->package_zip;
+ ZipString zip_string_path(zip_path.c_str());
+ ZipEntry entry;
+ if (FindEntry(za, zip_string_path, &entry) != 0) {
+ return ErrorAbort(state, kPackageExtractFileFailure, "%s(): no %s in package", name,
+ zip_path.c_str());
+ }
+
+ std::string buffer;
+ buffer.resize(entry.uncompressed_length);
+
+ int32_t ret =
+ ExtractToMemory(za, &entry, reinterpret_cast<uint8_t*>(&buffer[0]), buffer.size());
+ if (ret != 0) {
+ return ErrorAbort(state, kPackageExtractFileFailure,
+ "%s: Failed to extract entry \"%s\" (%zu bytes) to memory: %s", name,
+ zip_path.c_str(), buffer.size(), ErrorCodeString(ret));
+ }
+
+ return new Value(VAL_BLOB, buffer);
+ }
+}
+
+// apply_patch(src_file, tgt_file, tgt_sha1, tgt_size, patch1_sha1, patch1_blob, [...])
+// Applies a binary patch to the src_file to produce the tgt_file. If the desired target is the
+// same as the source, pass "-" for tgt_file. tgt_sha1 and tgt_size are the expected final SHA1
+// hash and size of the target file. The remaining arguments must come in pairs: a SHA1 hash (a
+// 40-character hex string) and a blob. The blob is the patch to be applied when the source
+// file's current contents have the given SHA1.
+//
+// The patching is done in a safe manner that guarantees the target file either has the desired
+// SHA1 hash and size, or it is untouched -- it will not be left in an unrecoverable intermediate
+// state. If the process is interrupted during patching, the target file may be in an intermediate
+// state; a copy exists in the cache partition so restarting the update can successfully update
+// the file.
+Value* ApplyPatchFn(const char* name, State* state,
+ const std::vector<std::unique_ptr<Expr>>& argv) {
+ if (argv.size() < 6 || (argv.size() % 2) == 1) {
+ return ErrorAbort(state, kArgsParsingFailure,
+ "%s(): expected at least 6 args and an "
+ "even number, got %zu",
+ name, argv.size());
+ }
+
+ std::vector<std::string> args;
+ if (!ReadArgs(state, argv, &args, 0, 4)) {
+ return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse the argument(s)", name);
+ }
+ const std::string& source_filename = args[0];
+ const std::string& target_filename = args[1];
+ const std::string& target_sha1 = args[2];
+ const std::string& target_size_str = args[3];
+
+ size_t target_size;
+ if (!android::base::ParseUint(target_size_str.c_str(), &target_size)) {
+ return ErrorAbort(state, kArgsParsingFailure, "%s(): can't parse \"%s\" as byte count", name,
+ target_size_str.c_str());
+ }
+
+ int patchcount = (argv.size() - 4) / 2;
+ std::vector<std::unique_ptr<Value>> arg_values;
+ if (!ReadValueArgs(state, argv, &arg_values, 4, argv.size() - 4)) {
+ return nullptr;
+ }
+
+ for (int i = 0; i < patchcount; ++i) {
+ if (arg_values[i * 2]->type != VAL_STRING) {
+ return ErrorAbort(state, kArgsParsingFailure, "%s(): sha-1 #%d is not string", name, i * 2);
+ }
+ if (arg_values[i * 2 + 1]->type != VAL_BLOB) {
+ return ErrorAbort(state, kArgsParsingFailure, "%s(): patch #%d is not blob", name, i * 2 + 1);
+ }
+ }
+
+ std::vector<std::string> patch_sha_str;
+ std::vector<std::unique_ptr<Value>> patches;
+ for (int i = 0; i < patchcount; ++i) {
+ patch_sha_str.push_back(arg_values[i * 2]->data);
+ patches.push_back(std::move(arg_values[i * 2 + 1]));
+ }
+
+ int result = applypatch(source_filename.c_str(), target_filename.c_str(), target_sha1.c_str(),
+ target_size, patch_sha_str, patches, nullptr);
+
+ return StringValue(result == 0 ? "t" : "");
+}
+
+// apply_patch_check(filename, [sha1, ...])
+// Returns true if the contents of filename or the temporary copy in the cache partition (if
+// present) have a SHA-1 checksum equal to one of the given sha1 values. sha1 values are
+// specified as 40 hex digits. This function differs from sha1_check(read_file(filename),
+// sha1 [, ...]) in that it knows to check the cache partition copy, so apply_patch_check() will
+// succeed even if the file was corrupted by an interrupted apply_patch() update.
+Value* ApplyPatchCheckFn(const char* name, State* state,
+ const std::vector<std::unique_ptr<Expr>>& argv) {
+ if (argv.size() < 1) {
+ return ErrorAbort(state, kArgsParsingFailure, "%s(): expected at least 1 arg, got %zu", name,
+ argv.size());
+ }
+
+ std::vector<std::string> args;
+ if (!ReadArgs(state, argv, &args, 0, 1)) {
+ return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse the argument(s)", name);
+ }
+ const std::string& filename = args[0];
+
+ std::vector<std::string> sha1s;
+ if (argv.size() > 1 && !ReadArgs(state, argv, &sha1s, 1, argv.size() - 1)) {
+ return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse the argument(s)", name);
}
- return true;
+ int result = applypatch_check(filename.c_str(), sha1s);
+
+ return StringValue(result == 0 ? "t" : "");
+}
+
+// sha1_check(data)
+// to return the sha1 of the data (given in the format returned by
+// read_file).
+//
+// sha1_check(data, sha1_hex, [sha1_hex, ...])
+// returns the sha1 of the file if it matches any of the hex
+// strings passed, or "" if it does not equal any of them.
+//
+Value* Sha1CheckFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) {
+ if (argv.size() < 1) {
+ return ErrorAbort(state, kArgsParsingFailure, "%s() expects at least 1 arg", name);
+ }
+
+ std::vector<std::unique_ptr<Value>> args;
+ if (!ReadValueArgs(state, argv, &args)) {
+ return nullptr;
+ }
+
+ if (args[0]->type == VAL_INVALID) {
+ return StringValue("");
+ }
+ uint8_t digest[SHA_DIGEST_LENGTH];
+ SHA1(reinterpret_cast<const uint8_t*>(args[0]->data.c_str()), args[0]->data.size(), digest);
+
+ if (argv.size() == 1) {
+ return StringValue(print_sha1(digest));
+ }
+
+ for (size_t i = 1; i < argv.size(); ++i) {
+ uint8_t arg_digest[SHA_DIGEST_LENGTH];
+ if (args[i]->type != VAL_STRING) {
+ LOG(ERROR) << name << "(): arg " << i << " is not a string; skipping";
+ } else if (ParseSha1(args[i]->data.c_str(), arg_digest) != 0) {
+ // Warn about bad args and skip them.
+ LOG(ERROR) << name << "(): error parsing \"" << args[i]->data << "\" as sha-1; skipping";
+ } else if (memcmp(digest, arg_digest, SHA_DIGEST_LENGTH) == 0) {
+ // Found a match.
+ return args[i].release();
+ }
+ }
+
+ // Didn't match any of the hex strings; return false.
+ return StringValue("");
}
// mount(fs_type, partition_type, location, mount_point)
@@ -311,7 +520,7 @@ Value* FormatFn(const char* name, State* state, const std::vector<std::unique_pt
int64_t size;
if (!android::base::ParseInt(fs_size, &size)) {
- return ErrorAbort(state, kArgsParsingFailure, "%s: failed to parse int in %s\n", name,
+ return ErrorAbort(state, kArgsParsingFailure, "%s: failed to parse int in %s", name,
fs_size.c_str());
}
@@ -326,14 +535,8 @@ Value* FormatFn(const char* name, State* state, const std::vector<std::unique_pt
int status = exec_cmd(mke2fs_argv[0], const_cast<char**>(mke2fs_argv));
if (status != 0) {
- LOG(WARNING) << name << ": mke2fs failed (" << status << ") on " << location
- << ", falling back to make_ext4fs";
- status = make_ext4fs(location.c_str(), size, mount_point.c_str(), sehandle);
- if (status != 0) {
- LOG(ERROR) << name << ": make_ext4fs failed (" << status << ") on " << location;
- return StringValue("");
- }
- return StringValue(location);
+ LOG(ERROR) << name << ": mke2fs failed (" << status << ") on " << location;
+ return StringValue("");
}
const char* e2fsdroid_argv[] = { "/sbin/e2fsdroid_static", "-e", "-a", mount_point.c_str(),
@@ -352,15 +555,30 @@ Value* FormatFn(const char* name, State* state, const std::vector<std::unique_pt
std::string num_sectors = std::to_string(size / 512);
const char* f2fs_path = "/sbin/mkfs.f2fs";
- const char* f2fs_argv[] = {
- "mkfs.f2fs", "-t", "-d1", location.c_str(), (size < 512) ? nullptr : num_sectors.c_str(),
- nullptr
- };
+ const char* f2fs_argv[] = { "mkfs.f2fs",
+ "-d1",
+ "-f",
+ "-O", "encrypt",
+ "-O", "quota",
+ "-O", "verity",
+ "-w", "512",
+ location.c_str(),
+ (size < 512) ? nullptr : num_sectors.c_str(),
+ nullptr };
int status = exec_cmd(f2fs_path, const_cast<char**>(f2fs_argv));
if (status != 0) {
LOG(ERROR) << name << ": mkfs.f2fs failed (" << status << ") on " << location;
return StringValue("");
}
+
+ const char* sload_argv[] = { "/sbin/sload.f2fs", "-t", mount_point.c_str(), location.c_str(),
+ nullptr };
+ status = exec_cmd(sload_argv[0], const_cast<char**>(sload_argv));
+ if (status != 0) {
+ LOG(ERROR) << name << ": sload.f2fs failed (" << status << ") on " << location;
+ return StringValue("");
+ }
+
return StringValue(location);
} else {
LOG(ERROR) << name << ": unsupported fs_type \"" << fs_type << "\" partition_type \""
@@ -447,12 +665,12 @@ Value* ShowProgressFn(const char* name, State* state,
double frac;
if (!android::base::ParseDouble(frac_str.c_str(), &frac)) {
- return ErrorAbort(state, kArgsParsingFailure, "%s: failed to parse double in %s\n", name,
+ return ErrorAbort(state, kArgsParsingFailure, "%s: failed to parse double in %s", name,
frac_str.c_str());
}
int sec;
if (!android::base::ParseInt(sec_str.c_str(), &sec)) {
- return ErrorAbort(state, kArgsParsingFailure, "%s: failed to parse int in %s\n", name,
+ return ErrorAbort(state, kArgsParsingFailure, "%s: failed to parse int in %s", name,
sec_str.c_str());
}
@@ -462,7 +680,8 @@ Value* ShowProgressFn(const char* name, State* state,
return StringValue(frac_str);
}
-Value* SetProgressFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) {
+Value* SetProgressFn(const char* name, State* state,
+ const std::vector<std::unique_ptr<Expr>>& argv) {
if (argv.size() != 1) {
return ErrorAbort(state, kArgsParsingFailure, "%s() expects 1 arg, got %zu", name, argv.size());
}
@@ -475,7 +694,7 @@ Value* SetProgressFn(const char* name, State* state, const std::vector<std::uniq
double frac;
if (!android::base::ParseDouble(frac_str.c_str(), &frac)) {
- return ErrorAbort(state, kArgsParsingFailure, "%s: failed to parse double in %s\n", name,
+ return ErrorAbort(state, kArgsParsingFailure, "%s: failed to parse double in %s", name,
frac_str.c_str());
}
@@ -902,7 +1121,8 @@ Value* GetPropFn(const char* name, State* state, const std::vector<std::unique_p
// interprets 'file' as a getprop-style file (key=value pairs, one
// per line. # comment lines, blank lines, lines without '=' ignored),
// and returns the value for 'key' (or "" if it isn't defined).
-Value* FileGetPropFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) {
+Value* FileGetPropFn(const char* name, State* state,
+ const std::vector<std::unique_ptr<Expr>>& argv) {
if (argv.size() != 2) {
return ErrorAbort(state, kArgsParsingFailure, "%s() expects 2 args, got %zu", name,
argv.size());
@@ -968,7 +1188,8 @@ Value* FileGetPropFn(const char* name, State* state, const std::vector<std::uniq
}
// apply_patch_space(bytes)
-Value* ApplyPatchSpaceFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) {
+Value* ApplyPatchSpaceFn(const char* name, State* state,
+ const std::vector<std::unique_ptr<Expr>>& argv) {
if (argv.size() != 1) {
return ErrorAbort(state, kArgsParsingFailure, "%s() expects 1 args, got %zu", name,
argv.size());
@@ -981,115 +1202,15 @@ Value* ApplyPatchSpaceFn(const char* name, State* state, const std::vector<std::
size_t bytes;
if (!android::base::ParseUint(bytes_str.c_str(), &bytes)) {
- return ErrorAbort(state, kArgsParsingFailure, "%s(): can't parse \"%s\" as byte count\n\n",
- name, bytes_str.c_str());
- }
-
- return StringValue(CacheSizeCheck(bytes) ? "" : "t");
-}
-
-// apply_patch(src_file, tgt_file, tgt_sha1, tgt_size, patch1_sha1, patch1_blob, [...])
-// Applies a binary patch to the src_file to produce the tgt_file. If the desired target is the
-// same as the source, pass "-" for tgt_file. tgt_sha1 and tgt_size are the expected final SHA1
-// hash and size of the target file. The remaining arguments must come in pairs: a SHA1 hash (a
-// 40-character hex string) and a blob. The blob is the patch to be applied when the source
-// file's current contents have the given SHA1.
-//
-// The patching is done in a safe manner that guarantees the target file either has the desired
-// SHA1 hash and size, or it is untouched -- it will not be left in an unrecoverable intermediate
-// state. If the process is interrupted during patching, the target file may be in an intermediate
-// state; a copy exists in the cache partition so restarting the update can successfully update
-// the file.
-Value* ApplyPatchFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) {
- if (argv.size() < 6 || (argv.size() % 2) == 1) {
- return ErrorAbort(state, kArgsParsingFailure, "%s(): expected at least 6 args and an "
- "even number, got %zu", name, argv.size());
- }
-
- std::vector<std::string> args;
- if (!ReadArgs(state, argv, &args, 0, 4)) {
- return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse the argument(s)", name);
- }
- const std::string& source_filename = args[0];
- const std::string& target_filename = args[1];
- const std::string& target_sha1 = args[2];
- const std::string& target_size_str = args[3];
-
- size_t target_size;
- if (!android::base::ParseUint(target_size_str.c_str(), &target_size)) {
- return ErrorAbort(state, kArgsParsingFailure, "%s(): can't parse \"%s\" as byte count",
- name, target_size_str.c_str());
- }
-
- int patchcount = (argv.size()-4) / 2;
- std::vector<std::unique_ptr<Value>> arg_values;
- if (!ReadValueArgs(state, argv, &arg_values, 4, argv.size() - 4)) {
- return nullptr;
- }
-
- for (int i = 0; i < patchcount; ++i) {
- if (arg_values[i * 2]->type != VAL_STRING) {
- return ErrorAbort(state, kArgsParsingFailure, "%s(): sha-1 #%d is not string", name,
- i * 2);
- }
- if (arg_values[i * 2 + 1]->type != VAL_BLOB) {
- return ErrorAbort(state, kArgsParsingFailure, "%s(): patch #%d is not blob", name,
- i * 2 + 1);
- }
- }
-
- std::vector<std::string> patch_sha_str;
- std::vector<std::unique_ptr<Value>> patches;
- for (int i = 0; i < patchcount; ++i) {
- patch_sha_str.push_back(arg_values[i * 2]->data);
- patches.push_back(std::move(arg_values[i * 2 + 1]));
- }
-
- int result = applypatch(source_filename.c_str(), target_filename.c_str(),
- target_sha1.c_str(), target_size,
- patch_sha_str, patches, nullptr);
-
- return StringValue(result == 0 ? "t" : "");
-}
-
-// apply_patch_check(filename, [sha1, ...])
-// Returns true if the contents of filename or the temporary copy in the cache partition (if
-// present) have a SHA-1 checksum equal to one of the given sha1 values. sha1 values are
-// specified as 40 hex digits. This function differs from sha1_check(read_file(filename),
-// sha1 [, ...]) in that it knows to check the cache partition copy, so apply_patch_check() will
-// succeed even if the file was corrupted by an interrupted apply_patch() update.
-Value* ApplyPatchCheckFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) {
- if (argv.size() < 1) {
- return ErrorAbort(state, kArgsParsingFailure, "%s(): expected at least 1 arg, got %zu", name,
- argv.size());
- }
-
- std::vector<std::string> args;
- if (!ReadArgs(state, argv, &args, 0, 1)) {
- return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse the argument(s)", name);
- }
- const std::string& filename = args[0];
-
- std::vector<std::string> sha1s;
- if (argv.size() > 1 && !ReadArgs(state, argv, &sha1s, 1, argv.size() - 1)) {
- return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse the argument(s)", name);
+ return ErrorAbort(state, kArgsParsingFailure, "%s(): can't parse \"%s\" as byte count", name,
+ bytes_str.c_str());
}
- int result = applypatch_check(filename.c_str(), sha1s);
- return StringValue(result == 0 ? "t" : "");
-}
-
-// This is the updater side handler for ui_print() in edify script. Contents will be sent over to
-// the recovery side for on-screen display.
-Value* UIPrintFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) {
- std::vector<std::string> args;
- if (!ReadArgs(state, argv, &args)) {
- return ErrorAbort(state, kArgsParsingFailure, "%s(): Failed to parse the argument(s)", name);
+ // Skip the cache size check if the update is a retry.
+ if (state->is_retry || CacheSizeCheck(bytes) == 0) {
+ return StringValue("t");
}
-
- std::string buffer = android::base::Join(args, "");
- uiPrint(state, buffer);
- return StringValue(buffer);
+ return StringValue("");
}
Value* WipeCacheFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) {
@@ -1139,51 +1260,6 @@ Value* RunProgramFn(const char* name, State* state, const std::vector<std::uniqu
return StringValue(std::to_string(status));
}
-// sha1_check(data)
-// to return the sha1 of the data (given in the format returned by
-// read_file).
-//
-// sha1_check(data, sha1_hex, [sha1_hex, ...])
-// returns the sha1 of the file if it matches any of the hex
-// strings passed, or "" if it does not equal any of them.
-//
-Value* Sha1CheckFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) {
- if (argv.size() < 1) {
- return ErrorAbort(state, kArgsParsingFailure, "%s() expects at least 1 arg", name);
- }
-
- std::vector<std::unique_ptr<Value>> args;
- if (!ReadValueArgs(state, argv, &args)) {
- return nullptr;
- }
-
- if (args[0]->type == VAL_INVALID) {
- return StringValue("");
- }
- uint8_t digest[SHA_DIGEST_LENGTH];
- SHA1(reinterpret_cast<const uint8_t*>(args[0]->data.c_str()), args[0]->data.size(), digest);
-
- if (argv.size() == 1) {
- return StringValue(print_sha1(digest));
- }
-
- for (size_t i = 1; i < argv.size(); ++i) {
- uint8_t arg_digest[SHA_DIGEST_LENGTH];
- if (args[i]->type != VAL_STRING) {
- LOG(ERROR) << name << "(): arg " << i << " is not a string; skipping";
- } else if (ParseSha1(args[i]->data.c_str(), arg_digest) != 0) {
- // Warn about bad args and skip them.
- LOG(ERROR) << name << "(): error parsing \"" << args[i]->data << "\" as sha-1; skipping";
- } else if (memcmp(digest, arg_digest, SHA_DIGEST_LENGTH) == 0) {
- // Found a match.
- return args[i].release();
- }
- }
-
- // Didn't match any of the hex strings; return false.
- return StringValue("");
-}
-
// Read a local file and return its contents (the Value* returned
// is actually a FileContents*).
Value* ReadFileFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) {
diff --git a/updater/updater.cpp b/updater/updater.cpp
index 25bd541b0..bd3b6feb3 100644
--- a/updater/updater.cpp
+++ b/updater/updater.cpp
@@ -31,10 +31,12 @@
#include <selinux/selinux.h>
#include <ziparchive/zip_archive.h>
-#include "config.h"
#include "edify/expr.h"
+#include "otafault/config.h"
#include "otautil/DirUtil.h"
#include "otautil/SysUtil.h"
+#include "otautil/cache_location.h"
+#include "otautil/error_code.h"
#include "updater/blockimg.h"
#include "updater/install.h"