diff options
Diffstat (limited to 'updater')
-rw-r--r-- | updater/Android.mk | 115 | ||||
-rw-r--r-- | updater/blockimg.cpp | 757 | ||||
-rw-r--r-- | updater/include/updater/blockimg.h (renamed from updater/blockimg.h) | 0 | ||||
-rw-r--r-- | updater/include/updater/install.h (renamed from updater/install.h) | 7 | ||||
-rw-r--r-- | updater/include/updater/updater.h (renamed from updater/updater.h) | 8 | ||||
-rw-r--r-- | updater/install.cpp | 2555 | ||||
-rw-r--r-- | updater/updater.cpp | 325 |
7 files changed, 1764 insertions, 2003 deletions
diff --git a/updater/Android.mk b/updater/Android.mk index d7aa613e9..d8a84c248 100644 --- a/updater/Android.mk +++ b/updater/Android.mk @@ -14,52 +14,89 @@ LOCAL_PATH := $(call my-dir) -updater_src_files := \ - install.cpp \ - blockimg.cpp \ - updater.cpp - -# -# Build a statically-linked binary to include in OTA packages -# +tune2fs_static_libraries := \ + libext2_com_err \ + libext2_blkid \ + libext2_quota \ + libext2_uuid_static \ + libext2_e2p \ + libext2fs + +updater_common_static_libraries := \ + libapplypatch \ + libedify \ + libziparchive \ + libotautil \ + libbootloader_message \ + libutils \ + libmounts \ + libotafault \ + libext4_utils \ + libfec \ + libfec_rs \ + libfs_mgr \ + liblog \ + libselinux \ + libsparse \ + libsquashfs_utils \ + libbz \ + libz \ + libbase \ + libcrypto \ + libcrypto_utils \ + libcutils \ + libtune2fs \ + $(tune2fs_static_libraries) + +# libupdater (static library) +# =============================== include $(CLEAR_VARS) -# Build only in eng, so we don't end up with a copy of this in /system -# on user builds. (TODO: find a better way to build device binaries -# needed only for OTA packages.) -LOCAL_MODULE_TAGS := eng +LOCAL_MODULE := libupdater -LOCAL_CLANG := true +LOCAL_SRC_FILES := \ + install.cpp \ + blockimg.cpp -LOCAL_SRC_FILES := $(updater_src_files) +LOCAL_C_INCLUDES := \ + $(LOCAL_PATH)/.. \ + $(LOCAL_PATH)/include \ + external/e2fsprogs/misc -LOCAL_STATIC_LIBRARIES += libfec libfec_rs libext4_utils_static libsquashfs_utils libcrypto_static +LOCAL_CFLAGS := \ + -Wno-unused-parameter \ + -Werror -ifeq ($(TARGET_USERIMAGES_USE_EXT4), true) -LOCAL_CFLAGS += -DUSE_EXT4 -LOCAL_CFLAGS += -Wno-unused-parameter -LOCAL_C_INCLUDES += system/extras/ext4_utils -LOCAL_STATIC_LIBRARIES += \ - libsparse_static \ - libz -endif +LOCAL_EXPORT_C_INCLUDE_DIRS := \ + $(LOCAL_PATH)/include -LOCAL_STATIC_LIBRARIES += $(TARGET_RECOVERY_UPDATER_LIBS) $(TARGET_RECOVERY_UPDATER_EXTRA_LIBS) -LOCAL_STATIC_LIBRARIES += libapplypatch libbase libotafault libedify libmtdutils libminzip libz -LOCAL_STATIC_LIBRARIES += libbz -LOCAL_STATIC_LIBRARIES += libcutils liblog libc -LOCAL_STATIC_LIBRARIES += libselinux -tune2fs_static_libraries := \ - libext2_com_err \ - libext2_blkid \ - libext2_quota \ - libext2_uuid_static \ - libext2_e2p \ - libext2fs -LOCAL_STATIC_LIBRARIES += libtune2fs $(tune2fs_static_libraries) +LOCAL_STATIC_LIBRARIES := \ + $(updater_common_static_libraries) + +include $(BUILD_STATIC_LIBRARY) -LOCAL_C_INCLUDES += external/e2fsprogs/misc -LOCAL_C_INCLUDES += $(LOCAL_PATH)/.. +# updater (static executable) +# =============================== +include $(CLEAR_VARS) + +LOCAL_MODULE := updater + +LOCAL_SRC_FILES := \ + updater.cpp + +LOCAL_C_INCLUDES := \ + $(LOCAL_PATH)/.. \ + $(LOCAL_PATH)/include + +LOCAL_CFLAGS := \ + -Wno-unused-parameter \ + -Werror + +LOCAL_STATIC_LIBRARIES := \ + libupdater \ + $(TARGET_RECOVERY_UPDATER_LIBS) \ + $(TARGET_RECOVERY_UPDATER_EXTRA_LIBS) \ + $(updater_common_static_libraries) # Each library in TARGET_RECOVERY_UPDATER_LIBS should have a function # named "Register_<libname>()". Here we emit a little C function that @@ -100,8 +137,6 @@ LOCAL_C_INCLUDES += $(dir $(inc)) inc := inc_dep_file := -LOCAL_MODULE := updater - LOCAL_FORCE_STATIC_EXECUTABLE := true include $(BUILD_EXECUTABLE) diff --git a/updater/blockimg.cpp b/updater/blockimg.cpp index a80180a9a..6755d78cb 100644 --- a/updater/blockimg.cpp +++ b/updater/blockimg.cpp @@ -18,7 +18,6 @@ #include <errno.h> #include <dirent.h> #include <fcntl.h> -#include <inttypes.h> #include <linux/fs.h> #include <pthread.h> #include <stdarg.h> @@ -33,114 +32,113 @@ #include <unistd.h> #include <fec/io.h> -#include <map> #include <memory> #include <string> +#include <unordered_map> #include <vector> +#include <android-base/logging.h> #include <android-base/parseint.h> #include <android-base/strings.h> +#include <android-base/unique_fd.h> +#include <applypatch/applypatch.h> +#include <openssl/sha.h> +#include <ziparchive/zip_archive.h> -#include "applypatch/applypatch.h" #include "edify/expr.h" #include "error_code.h" -#include "install.h" -#include "openssl/sha.h" -#include "minzip/Hash.h" +#include "updater/install.h" #include "ota_io.h" #include "print_sha1.h" -#include "unique_fd.h" -#include "updater.h" - -#define BLOCKSIZE 4096 +#include "updater/updater.h" // Set this to 0 to interpret 'erase' transfers to mean do a // BLKDISCARD ioctl (the normal behavior). Set to 1 to interpret // erase to mean fill the region with zeroes. #define DEBUG_ERASE 0 -#define STASH_DIRECTORY_BASE "/cache/recovery" -#define STASH_DIRECTORY_MODE 0700 -#define STASH_FILE_MODE 0600 +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; struct RangeSet { - size_t count; // Limit is INT_MAX. - size_t size; - std::vector<size_t> pos; // Actual limit is INT_MAX. + size_t count; // Limit is INT_MAX. + size_t size; + std::vector<size_t> pos; // Actual limit is INT_MAX. }; static CauseCode failure_type = kNoCause; static bool is_retry = false; -static std::map<std::string, RangeSet> stash_map; - -static void parse_range(const std::string& range_text, RangeSet& rs) { +static std::unordered_map<std::string, RangeSet> stash_map; - std::vector<std::string> pieces = android::base::Split(range_text, ","); - if (pieces.size() < 3) { - goto err; - } +static RangeSet parse_range(const std::string& range_text) { + RangeSet rs; - size_t num; - if (!android::base::ParseUint(pieces[0].c_str(), &num, static_cast<size_t>(INT_MAX))) { - goto err; - } + std::vector<std::string> pieces = android::base::Split(range_text, ","); + if (pieces.size() < 3) { + goto err; + } - if (num == 0 || num % 2) { - goto err; // must be even - } else if (num != pieces.size() - 1) { - goto err; - } + size_t num; + if (!android::base::ParseUint(pieces[0], &num, static_cast<size_t>(INT_MAX))) { + goto err; + } - rs.pos.resize(num); - rs.count = num / 2; - rs.size = 0; + if (num == 0 || num % 2) { + goto err; // must be even + } else if (num != pieces.size() - 1) { + goto err; + } - for (size_t i = 0; i < num; i += 2) { - if (!android::base::ParseUint(pieces[i+1].c_str(), &rs.pos[i], - static_cast<size_t>(INT_MAX))) { - goto err; - } + rs.pos.resize(num); + rs.count = num / 2; + rs.size = 0; - if (!android::base::ParseUint(pieces[i+2].c_str(), &rs.pos[i+1], - static_cast<size_t>(INT_MAX))) { - goto err; - } + for (size_t i = 0; i < num; i += 2) { + if (!android::base::ParseUint(pieces[i + 1], &rs.pos[i], static_cast<size_t>(INT_MAX))) { + goto err; + } - if (rs.pos[i] >= rs.pos[i+1]) { - goto err; // empty or negative range - } + if (!android::base::ParseUint(pieces[i + 2], &rs.pos[i + 1], static_cast<size_t>(INT_MAX))) { + goto err; + } - size_t sz = rs.pos[i+1] - rs.pos[i]; - if (rs.size > SIZE_MAX - sz) { - goto err; // overflow - } + if (rs.pos[i] >= rs.pos[i + 1]) { + goto err; // empty or negative range + } - rs.size += sz; + size_t sz = rs.pos[i + 1] - rs.pos[i]; + if (rs.size > SIZE_MAX - sz) { + goto err; // overflow } - return; + rs.size += sz; + } + + return rs; err: - fprintf(stderr, "failed to parse range '%s'\n", range_text.c_str()); - exit(1); + LOG(ERROR) << "failed to parse range '" << range_text << "'"; + exit(1); } static bool range_overlaps(const RangeSet& r1, const RangeSet& r2) { - for (size_t i = 0; i < r1.count; ++i) { - size_t r1_0 = r1.pos[i * 2]; - size_t r1_1 = r1.pos[i * 2 + 1]; + for (size_t i = 0; i < r1.count; ++i) { + size_t r1_0 = r1.pos[i * 2]; + size_t r1_1 = r1.pos[i * 2 + 1]; - for (size_t j = 0; j < r2.count; ++j) { - size_t r2_0 = r2.pos[j * 2]; - size_t r2_1 = r2.pos[j * 2 + 1]; + for (size_t j = 0; j < r2.count; ++j) { + size_t r2_0 = r2.pos[j * 2]; + size_t r2_1 = r2.pos[j * 2 + 1]; - if (!(r2_0 >= r1_1 || r1_0 >= r2_1)) { - return true; - } - } + if (!(r2_0 >= r1_1 || r1_0 >= r2_1)) { + return true; + } } + } - return false; + return false; } static int read_all(int fd, uint8_t* data, size_t size) { @@ -149,7 +147,11 @@ static int read_all(int fd, uint8_t* data, size_t size) { ssize_t r = TEMP_FAILURE_RETRY(ota_read(fd, data+so_far, size-so_far)); if (r == -1) { failure_type = kFreadFailure; - fprintf(stderr, "read failed: %s\n", strerror(errno)); + PLOG(ERROR) << "read failed"; + return -1; + } else if (r == 0) { + failure_type = kFreadFailure; + LOG(ERROR) << "read reached unexpected EOF."; return -1; } so_far += r; @@ -167,7 +169,7 @@ static int write_all(int fd, const uint8_t* data, size_t size) { ssize_t w = TEMP_FAILURE_RETRY(ota_write(fd, data+written, size-written)); if (w == -1) { failure_type = kFwriteFailure; - fprintf(stderr, "write failed: %s\n", strerror(errno)); + PLOG(ERROR) << "write failed"; return -1; } written += w; @@ -189,7 +191,7 @@ static bool discard_blocks(int fd, off64_t offset, uint64_t size) { uint64_t args[2] = {static_cast<uint64_t>(offset), size}; int status = ioctl(fd, BLKDISCARD, &args); if (status == -1) { - fprintf(stderr, "BLKDISCARD ioctl failed: %s\n", strerror(errno)); + PLOG(ERROR) << "BLKDISCARD ioctl failed"; return false; } return true; @@ -199,7 +201,7 @@ static bool check_lseek(int fd, off64_t offset, int whence) { off64_t rc = TEMP_FAILURE_RETRY(lseek64(fd, offset, whence)); if (rc == -1) { failure_type = kLseekFailure; - fprintf(stderr, "lseek64 failed: %s\n", strerror(errno)); + PLOG(ERROR) << "lseek64 failed"; return false; } return true; @@ -213,7 +215,7 @@ static void allocate(size_t size, std::vector<uint8_t>& buffer) { } struct RangeSinkState { - RangeSinkState(RangeSet& rs) : tgt(rs) { }; + explicit RangeSinkState(RangeSet& rs) : tgt(rs) { }; int fd; const RangeSet& tgt; @@ -225,7 +227,7 @@ static ssize_t RangeSinkWrite(const uint8_t* data, ssize_t size, void* token) { RangeSinkState* rss = reinterpret_cast<RangeSinkState*>(token); if (rss->p_remain == 0) { - fprintf(stderr, "range sink write overrun"); + LOG(ERROR) << "range sink write overrun"; return 0; } @@ -296,8 +298,8 @@ static ssize_t RangeSinkWrite(const uint8_t* data, ssize_t size, void* token) { // rss and signals the condition again. struct NewThreadInfo { - ZipArchive* za; - const ZipEntry* entry; + ZipArchiveHandle za; + ZipEntry entry; RangeSinkState* rss; @@ -305,7 +307,7 @@ struct NewThreadInfo { pthread_cond_t cv; }; -static bool receive_new_data(const unsigned char* data, int size, void* cookie) { +static bool receive_new_data(const uint8_t* data, size_t size, void* cookie) { NewThreadInfo* nti = reinterpret_cast<NewThreadInfo*>(cookie); while (size > 0) { @@ -338,7 +340,7 @@ static bool receive_new_data(const unsigned char* data, int size, void* cookie) static void* unzip_new_data(void* cookie) { NewThreadInfo* nti = (NewThreadInfo*) cookie; - mzProcessZipEntryContents(nti->za, nti->entry, receive_new_data, nti); + ProcessZipEntryContents(nti->za, &nti->entry, receive_new_data, nti); return nullptr; } @@ -398,7 +400,7 @@ struct CommandParameters { std::string stashbase; bool canwrite; int createdstash; - int fd; + android::base::unique_fd fd; bool foundwrites; bool isunresumable; int version; @@ -422,16 +424,15 @@ static int LoadSrcTgtVersion1(CommandParameters& params, RangeSet& tgt, size_t& std::vector<uint8_t>& buffer, int fd) { if (params.cpos + 1 >= params.tokens.size()) { - fprintf(stderr, "invalid parameters\n"); + LOG(ERROR) << "invalid parameters"; return -1; } // <src_range> - RangeSet src; - parse_range(params.tokens[params.cpos++], src); + RangeSet src = parse_range(params.tokens[params.cpos++]); // <tgt_range> - parse_range(params.tokens[params.cpos++], tgt); + tgt = parse_range(params.tokens[params.cpos++]); allocate(src.size * BLOCKSIZE, buffer); int rc = ReadBlocks(src, buffer, fd); @@ -451,8 +452,8 @@ static int VerifyBlocks(const std::string& expected, const std::vector<uint8_t>& if (hexdigest != expected) { if (printerror) { - fprintf(stderr, "failed to verify blocks (expected %s, read %s)\n", - expected.c_str(), hexdigest.c_str()); + LOG(ERROR) << "failed to verify blocks (expected " << expected << ", read " + << hexdigest << ")"; } return -1; } @@ -488,7 +489,7 @@ static void EnumerateStash(const std::string& dirname, StashCallback callback, v if (directory == nullptr) { if (errno != ENOENT) { - fprintf(stderr, "opendir \"%s\" failed: %s\n", dirname.c_str(), strerror(errno)); + PLOG(ERROR) << "opendir \"" << dirname << "\" failed"; } return; } @@ -505,18 +506,18 @@ static void EnumerateStash(const std::string& dirname, StashCallback callback, v } static void UpdateFileSize(const std::string& fn, void* data) { - if (fn.empty() || !data) { - return; - } + if (fn.empty() || !data) { + return; + } - struct stat sb; - if (stat(fn.c_str(), &sb) == -1) { - fprintf(stderr, "stat \"%s\" failed: %s\n", fn.c_str(), strerror(errno)); - return; - } + struct stat sb; + if (stat(fn.c_str(), &sb) == -1) { + PLOG(ERROR) << "stat \"" << fn << "\" failed"; + return; + } - int* size = reinterpret_cast<int*>(data); - *size += sb.st_size; + size_t* size = static_cast<size_t*>(data); + *size += sb.st_size; } // Deletes the stash directory and all files in it. Assumes that it only @@ -525,10 +526,10 @@ static void UpdateFileSize(const std::string& fn, void* data) { static void DeleteFile(const std::string& fn, void* /* data */) { if (!fn.empty()) { - fprintf(stderr, "deleting %s\n", fn.c_str()); + LOG(INFO) << "deleting " << fn; if (unlink(fn.c_str()) == -1 && errno != ENOENT) { - fprintf(stderr, "unlink \"%s\" failed: %s\n", fn.c_str(), strerror(errno)); + PLOG(ERROR) << "unlink \"" << fn << "\" failed"; } } } @@ -544,14 +545,14 @@ static void DeleteStash(const std::string& base) { return; } - fprintf(stderr, "deleting stash %s\n", base.c_str()); + LOG(INFO) << "deleting stash " << base; std::string dirname = GetStashFileName(base, "", ""); EnumerateStash(dirname, DeleteFile, nullptr); if (rmdir(dirname.c_str()) == -1) { if (errno != ENOENT && errno != ENOTDIR) { - fprintf(stderr, "rmdir \"%s\" failed: %s\n", dirname.c_str(), strerror(errno)); + PLOG(ERROR) << "rmdir \"" << dirname << "\" failed"; } } } @@ -567,11 +568,11 @@ static int LoadStash(CommandParameters& params, const std::string& base, const s allocate(src.size * BLOCKSIZE, buffer); if (ReadBlocks(src, buffer, params.fd) == -1) { - fprintf(stderr, "failed to read source blocks in stash map.\n"); + LOG(ERROR) << "failed to read source blocks in stash map."; return -1; } if (VerifyBlocks(id, buffer, src.size, true) != 0) { - fprintf(stderr, "failed to verify loaded source blocks in stash map.\n"); + LOG(ERROR) << "failed to verify loaded source blocks in stash map."; return -1; } return 0; @@ -595,24 +596,21 @@ static int LoadStash(CommandParameters& params, const std::string& base, const s if (res == -1) { if (errno != ENOENT || printnoent) { - fprintf(stderr, "stat \"%s\" failed: %s\n", fn.c_str(), strerror(errno)); + PLOG(ERROR) << "stat \"" << fn << "\" failed"; } return -1; } - fprintf(stderr, " loading %s\n", fn.c_str()); + LOG(INFO) << " loading " << fn; if ((sb.st_size % BLOCKSIZE) != 0) { - fprintf(stderr, "%s size %" PRId64 " not multiple of block size %d", - fn.c_str(), static_cast<int64_t>(sb.st_size), BLOCKSIZE); + LOG(ERROR) << fn << " size " << sb.st_size << " not multiple of block size " << BLOCKSIZE; return -1; } - int fd = TEMP_FAILURE_RETRY(open(fn.c_str(), O_RDONLY)); - unique_fd fd_holder(fd); - + android::base::unique_fd fd(TEMP_FAILURE_RETRY(ota_open(fn.c_str(), O_RDONLY))); if (fd == -1) { - fprintf(stderr, "open \"%s\" failed: %s\n", fn.c_str(), strerror(errno)); + PLOG(ERROR) << "open \"" << fn << "\" failed"; return -1; } @@ -625,7 +623,7 @@ static int LoadStash(CommandParameters& params, const std::string& base, const s *blocks = sb.st_size / BLOCKSIZE; if (verify && VerifyBlocks(id, buffer, *blocks, true) != 0) { - fprintf(stderr, "unexpected contents in %s\n", fn.c_str()); + LOG(ERROR) << "unexpected contents in " << fn; DeleteFile(fn, nullptr); return -1; } @@ -640,7 +638,7 @@ static int WriteStash(const std::string& base, const std::string& id, int blocks } if (checkspace && CacheSizeCheck(blocks * BLOCKSIZE) != 0) { - fprintf(stderr, "not enough space to write stash\n"); + LOG(ERROR) << "not enough space to write stash"; return -1; } @@ -655,7 +653,7 @@ static int WriteStash(const std::string& base, const std::string& id, int blocks // 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) - fprintf(stderr, " skipping %d existing blocks in %s\n", blocks, cn.c_str()); + LOG(INFO) << " skipping " << blocks << " existing blocks in " << cn; *exists = true; return 0; } @@ -663,13 +661,12 @@ static int WriteStash(const std::string& base, const std::string& id, int blocks *exists = false; } - fprintf(stderr, " writing %d blocks to %s\n", blocks, cn.c_str()); - - int fd = TEMP_FAILURE_RETRY(open(fn.c_str(), O_WRONLY | O_CREAT | O_TRUNC, STASH_FILE_MODE)); - unique_fd fd_holder(fd); + LOG(INFO) << " writing " << blocks << " blocks to " << cn; + 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) { - fprintf(stderr, "failed to create \"%s\": %s\n", fn.c_str(), strerror(errno)); + PLOG(ERROR) << "failed to create \"" << fn << "\""; return -1; } @@ -679,29 +676,27 @@ static int WriteStash(const std::string& base, const std::string& id, int blocks if (ota_fsync(fd) == -1) { failure_type = kFsyncFailure; - fprintf(stderr, "fsync \"%s\" failed: %s\n", fn.c_str(), strerror(errno)); + PLOG(ERROR) << "fsync \"" << fn << "\" failed"; return -1; } if (rename(fn.c_str(), cn.c_str()) == -1) { - fprintf(stderr, "rename(\"%s\", \"%s\") failed: %s\n", fn.c_str(), cn.c_str(), - strerror(errno)); + PLOG(ERROR) << "rename(\"" << fn << "\", \"" << cn << "\") failed"; return -1; } std::string dname = GetStashFileName(base, "", ""); - int dfd = TEMP_FAILURE_RETRY(open(dname.c_str(), O_RDONLY | O_DIRECTORY)); - unique_fd dfd_holder(dfd); - + android::base::unique_fd dfd(TEMP_FAILURE_RETRY(ota_open(dname.c_str(), + O_RDONLY | O_DIRECTORY))); if (dfd == -1) { failure_type = kFileOpenFailure; - fprintf(stderr, "failed to open \"%s\" failed: %s\n", dname.c_str(), strerror(errno)); + PLOG(ERROR) << "failed to open \"" << dname << "\" failed"; return -1; } if (ota_fsync(dfd) == -1) { failure_type = kFsyncFailure; - fprintf(stderr, "fsync \"%s\" failed: %s\n", dname.c_str(), strerror(errno)); + PLOG(ERROR) << "fsync \"" << dname << "\" failed"; return -1; } @@ -712,63 +707,67 @@ static int WriteStash(const std::string& base, const std::string& id, int blocks // 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, int maxblocks, const char* blockdev, std::string& base) { - if (blockdev == nullptr) { - 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), strlen(blockdev), digest); - base = print_sha1(digest); - - std::string dirname = GetStashFileName(base, "", ""); - struct stat sb; - int res = stat(dirname.c_str(), &sb); - - if (res == -1 && errno != ENOENT) { - ErrorAbort(state, kStashCreationFailure, "stat \"%s\" failed: %s\n", - dirname.c_str(), strerror(errno)); - return -1; - } else if (res != 0) { - fprintf(stderr, "creating stash %s\n", dirname.c_str()); - res = mkdir(dirname.c_str(), STASH_DIRECTORY_MODE); - - if (res != 0) { - ErrorAbort(state, kStashCreationFailure, "mkdir \"%s\" failed: %s\n", - dirname.c_str(), strerror(errno)); - return -1; - } +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); + + 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\n", dirname.c_str(), + strerror(errno)); + return -1; + } else if (res != 0) { + LOG(INFO) << "creating stash " << dirname; + res = mkdir(dirname.c_str(), STASH_DIRECTORY_MODE); - if (CacheSizeCheck(maxblocks * BLOCKSIZE) != 0) { - ErrorAbort(state, kStashCreationFailure, "not enough space for stash\n"); - return -1; - } + if (res != 0) { + ErrorAbort(state, kStashCreationFailure, "mkdir \"%s\" failed: %s\n", dirname.c_str(), + strerror(errno)); + return -1; + } - return 1; // Created directory + if (CacheSizeCheck(max_stash_size) != 0) { + ErrorAbort(state, kStashCreationFailure, "not enough space for stash (%zu needed)\n", + max_stash_size); + return -1; } - fprintf(stderr, "using existing stash %s\n", dirname.c_str()); + return 1; // Created directory + } - // If the directory already exists, calculate the space already allocated to - // stash files and check if there's enough for all required blocks. Delete any - // partially completed stash files first. + LOG(INFO) << "using existing stash " << dirname; - EnumerateStash(dirname, DeletePartial, nullptr); - int size = 0; - EnumerateStash(dirname, UpdateFileSize, &size); + // If the directory already exists, calculate the space already allocated to + // stash files and check if there's enough for all required blocks. Delete any + // partially completed stash files first. - size = maxblocks * BLOCKSIZE - size; + EnumerateStash(dirname, DeletePartial, nullptr); + size_t existing = 0; + EnumerateStash(dirname, UpdateFileSize, &existing); - if (size > 0 && CacheSizeCheck(size) != 0) { - ErrorAbort(state, kStashCreationFailure, "not enough space for stash (%d more needed)\n", - size); - return -1; + 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", + needed); + return -1; } + } - return 0; // Using existing directory + return 0; // Using existing directory } static int SaveStash(CommandParameters& params, const std::string& base, @@ -776,7 +775,7 @@ static int SaveStash(CommandParameters& params, const std::string& base, // <stash_id> <src_range> if (params.cpos + 1 >= params.tokens.size()) { - fprintf(stderr, "missing id and/or src range fields in stash command\n"); + LOG(ERROR) << "missing id and/or src range fields in stash command"; return -1; } const std::string& id = params.tokens[params.cpos++]; @@ -789,8 +788,7 @@ static int SaveStash(CommandParameters& params, const std::string& base, return 0; } - RangeSet src; - parse_range(params.tokens[params.cpos++], src); + RangeSet src = parse_range(params.tokens[params.cpos++]); allocate(src.size * BLOCKSIZE, buffer); if (ReadBlocks(src, buffer, fd) == -1) { @@ -803,7 +801,7 @@ static int SaveStash(CommandParameters& params, const std::string& base, // data later, this is an unrecoverable error. However, the command // that uses the data may have already completed previously, so the // possible failure will occur during source block verification. - fprintf(stderr, "failed to load source blocks for stash %s\n", id.c_str()); + LOG(ERROR) << "failed to load source blocks for stash " << id; return 0; } @@ -813,7 +811,7 @@ static int SaveStash(CommandParameters& params, const std::string& base, return 0; } - fprintf(stderr, "stashing %zu blocks to %s\n", blocks, id.c_str()); + LOG(INFO) << "stashing " << blocks << " blocks to " << id; params.stashed += blocks; return WriteStash(base, id, blocks, buffer, false, nullptr); } @@ -869,17 +867,17 @@ static int LoadSrcTgtVersion2(CommandParameters& params, RangeSet& tgt, size_t& // At least it needs to provide three parameters: <tgt_range>, // <src_block_count> and "-"/<src_range>. if (params.cpos + 2 >= params.tokens.size()) { - fprintf(stderr, "invalid parameters\n"); + LOG(ERROR) << "invalid parameters"; return -1; } // <tgt_range> - parse_range(params.tokens[params.cpos++], tgt); + tgt = parse_range(params.tokens[params.cpos++]); // <src_block_count> const std::string& token = params.tokens[params.cpos++]; if (!android::base::ParseUint(token.c_str(), &src_blocks)) { - fprintf(stderr, "invalid src_block_count \"%s\"\n", token.c_str()); + LOG(ERROR) << "invalid src_block_count \"" << token << "\""; return -1; } @@ -890,8 +888,7 @@ static int LoadSrcTgtVersion2(CommandParameters& params, RangeSet& tgt, size_t& // no source ranges, only stashes params.cpos++; } else { - RangeSet src; - parse_range(params.tokens[params.cpos++], src); + RangeSet src = parse_range(params.tokens[params.cpos++]); int res = ReadBlocks(src, buffer, fd); if (overlap) { @@ -907,8 +904,7 @@ static int LoadSrcTgtVersion2(CommandParameters& params, RangeSet& tgt, size_t& return 0; } - RangeSet locs; - parse_range(params.tokens[params.cpos++], locs); + RangeSet locs = parse_range(params.tokens[params.cpos++]); MoveRange(buffer, locs, buffer); } @@ -919,7 +915,7 @@ static int LoadSrcTgtVersion2(CommandParameters& params, RangeSet& tgt, size_t& // stashed data should go. std::vector<std::string> tokens = android::base::Split(params.tokens[params.cpos++], ":"); if (tokens.size() != 2) { - fprintf(stderr, "invalid parameter\n"); + LOG(ERROR) << "invalid parameter"; return -1; } @@ -929,12 +925,11 @@ static int LoadSrcTgtVersion2(CommandParameters& params, RangeSet& tgt, size_t& if (res == -1) { // These source blocks will fail verification if used later, but we // will let the caller decide if this is a fatal failure - fprintf(stderr, "failed to load stash %s\n", tokens[0].c_str()); + LOG(ERROR) << "failed to load stash " << tokens[0]; continue; } - RangeSet locs; - parse_range(tokens[1], locs); + RangeSet locs = parse_range(tokens[1]); MoveRange(buffer, locs, stash); } @@ -963,7 +958,7 @@ static int LoadSrcTgtVersion3(CommandParameters& params, RangeSet& tgt, size_t& bool onehash, bool& overlap) { if (params.cpos >= params.tokens.size()) { - fprintf(stderr, "missing source hash\n"); + LOG(ERROR) << "missing source hash"; return -1; } @@ -974,14 +969,14 @@ static int LoadSrcTgtVersion3(CommandParameters& params, RangeSet& tgt, size_t& tgthash = srchash; } else { if (params.cpos >= params.tokens.size()) { - fprintf(stderr, "missing target hash\n"); + LOG(ERROR) << "missing target hash"; return -1; } tgthash = params.tokens[params.cpos++]; } - if (LoadSrcTgtVersion2(params, tgt, src_blocks, params.buffer, params.fd, params.stashbase, - &overlap) == -1) { + if (LoadSrcTgtVersion2(params, tgt, src_blocks, params.buffer, params.fd, + params.stashbase, &overlap) == -1) { return -1; } @@ -1001,13 +996,12 @@ static int LoadSrcTgtVersion3(CommandParameters& params, RangeSet& tgt, size_t& // resume from possible write errors. In verify mode, we can skip stashing // because the source blocks won't be overwritten. if (overlap && params.canwrite) { - fprintf(stderr, "stashing %zu overlapping blocks to %s\n", src_blocks, - srchash.c_str()); + LOG(INFO) << "stashing " << src_blocks << " overlapping blocks to " << srchash; bool stash_exists = false; if (WriteStash(params.stashbase, srchash, src_blocks, params.buffer, true, &stash_exists) != 0) { - fprintf(stderr, "failed to stash overlapping source blocks\n"); + LOG(ERROR) << "failed to stash overlapping source blocks"; return -1; } @@ -1031,7 +1025,7 @@ static int LoadSrcTgtVersion3(CommandParameters& params, RangeSet& tgt, size_t& } // Valid source data not available, update cannot be resumed - fprintf(stderr, "partition has unexpected contents\n"); + LOG(ERROR) << "partition has unexpected contents"; params.isunresumable = true; return -1; @@ -1053,25 +1047,25 @@ static int PerformCommandMove(CommandParameters& params) { } if (status == -1) { - fprintf(stderr, "failed to read blocks for move\n"); + LOG(ERROR) << "failed to read blocks for move"; return -1; } if (status == 0) { params.foundwrites = true; } else if (params.foundwrites) { - fprintf(stderr, "warning: commands executed out of order [%s]\n", params.cmdname); + LOG(WARNING) << "warning: commands executed out of order [" << params.cmdname << "]"; } if (params.canwrite) { if (status == 0) { - fprintf(stderr, " moving %zu blocks\n", blocks); + LOG(INFO) << " moving " << blocks << " blocks"; if (WriteBlocks(tgt, params.buffer, params.fd) == -1) { return -1; } } else { - fprintf(stderr, "skipping %zu already moved blocks\n", blocks); + LOG(INFO) << "skipping " << blocks << " already moved blocks"; } } @@ -1094,7 +1088,7 @@ static int PerformCommandStash(CommandParameters& params) { static int PerformCommandFree(CommandParameters& params) { // <stash_id> if (params.cpos >= params.tokens.size()) { - fprintf(stderr, "missing stash id in free command\n"); + LOG(ERROR) << "missing stash id in free command"; return -1; } @@ -1115,14 +1109,13 @@ static int PerformCommandFree(CommandParameters& params) { static int PerformCommandZero(CommandParameters& params) { if (params.cpos >= params.tokens.size()) { - fprintf(stderr, "missing target blocks for zero\n"); + LOG(ERROR) << "missing target blocks for zero"; return -1; } - RangeSet tgt; - parse_range(params.tokens[params.cpos++], tgt); + RangeSet tgt = parse_range(params.tokens[params.cpos++]); - fprintf(stderr, " zeroing %zu blocks\n", tgt.size); + LOG(INFO) << " zeroing " << tgt.size << " blocks"; allocate(BLOCKSIZE, params.buffer); memset(params.buffer.data(), 0, BLOCKSIZE); @@ -1159,15 +1152,14 @@ static int PerformCommandZero(CommandParameters& params) { static int PerformCommandNew(CommandParameters& params) { if (params.cpos >= params.tokens.size()) { - fprintf(stderr, "missing target blocks for new\n"); + LOG(ERROR) << "missing target blocks for new"; return -1; } - RangeSet tgt; - parse_range(params.tokens[params.cpos++], tgt); + RangeSet tgt = parse_range(params.tokens[params.cpos++]); if (params.canwrite) { - fprintf(stderr, " writing %zu blocks of new data\n", tgt.size); + LOG(INFO) << " writing " << tgt.size << " blocks of new data"; RangeSinkState rss(tgt); rss.fd = params.fd; @@ -1203,19 +1195,19 @@ static int PerformCommandDiff(CommandParameters& params) { // <offset> <length> if (params.cpos + 1 >= params.tokens.size()) { - fprintf(stderr, "missing patch offset or length for %s\n", params.cmdname); + LOG(ERROR) << "missing patch offset or length for " << params.cmdname; return -1; } size_t offset; if (!android::base::ParseUint(params.tokens[params.cpos++].c_str(), &offset)) { - fprintf(stderr, "invalid patch offset\n"); + LOG(ERROR) << "invalid patch offset"; return -1; } size_t len; if (!android::base::ParseUint(params.tokens[params.cpos++].c_str(), &len)) { - fprintf(stderr, "invalid patch offset\n"); + LOG(ERROR) << "invalid patch len"; return -1; } @@ -1233,24 +1225,22 @@ static int PerformCommandDiff(CommandParameters& params) { } if (status == -1) { - fprintf(stderr, "failed to read blocks for diff\n"); + LOG(ERROR) << "failed to read blocks for diff"; return -1; } if (status == 0) { params.foundwrites = true; } else if (params.foundwrites) { - fprintf(stderr, "warning: commands executed out of order [%s]\n", params.cmdname); + LOG(WARNING) << "warning: commands executed out of order [" << params.cmdname << "]"; } if (params.canwrite) { if (status == 0) { - fprintf(stderr, "patching %zu blocks to %zu\n", blocks, tgt.size); + LOG(INFO) << "patching " << blocks << " blocks to " << tgt.size; - Value patch_value; - patch_value.type = VAL_BLOB; - patch_value.size = len; - patch_value.data = (char*) (params.patch_start + offset); + Value patch_value(VAL_BLOB, + std::string(reinterpret_cast<const char*>(params.patch_start + offset), len)); RangeSinkState rss(tgt); rss.fd = params.fd; @@ -1269,24 +1259,24 @@ static int PerformCommandDiff(CommandParameters& params) { if (params.cmdname[0] == 'i') { // imgdiff if (ApplyImagePatch(params.buffer.data(), blocks * BLOCKSIZE, &patch_value, &RangeSinkWrite, &rss, nullptr, nullptr) != 0) { - fprintf(stderr, "Failed to apply image patch.\n"); + LOG(ERROR) << "Failed to apply image patch."; return -1; } } else { if (ApplyBSDiffPatch(params.buffer.data(), blocks * BLOCKSIZE, &patch_value, 0, &RangeSinkWrite, &rss, nullptr) != 0) { - fprintf(stderr, "Failed to apply bsdiff patch.\n"); + LOG(ERROR) << "Failed to apply bsdiff patch."; return -1; } } // We expect the output of the patcher to fill the tgt ranges exactly. if (rss.p_block != tgt.count || rss.p_remain != 0) { - fprintf(stderr, "range sink underrun?\n"); + LOG(ERROR) << "range sink underrun?"; } } else { - fprintf(stderr, "skipping %zu blocks already patched to %zu [%s]\n", - blocks, tgt.size, params.cmdline); + LOG(INFO) << "skipping " << blocks << " blocks already patched to " << tgt.size + << " [" << params.cmdline << "]"; } } @@ -1307,25 +1297,24 @@ static int PerformCommandErase(CommandParameters& params) { struct stat sb; if (fstat(params.fd, &sb) == -1) { - fprintf(stderr, "failed to fstat device to erase: %s\n", strerror(errno)); + PLOG(ERROR) << "failed to fstat device to erase"; return -1; } if (!S_ISBLK(sb.st_mode)) { - fprintf(stderr, "not a block device; skipping erase\n"); + LOG(ERROR) << "not a block device; skipping erase"; return -1; } if (params.cpos >= params.tokens.size()) { - fprintf(stderr, "missing target blocks for erase\n"); + LOG(ERROR) << "missing target blocks for erase"; return -1; } - RangeSet tgt; - parse_range(params.tokens[params.cpos++], tgt); + RangeSet tgt = parse_range(params.tokens[params.cpos++]); if (params.canwrite) { - fprintf(stderr, " erasing %zu blocks\n", tgt.size); + LOG(INFO) << " erasing " << tgt.size << " blocks"; for (size_t i = 0; i < tgt.count; ++i) { uint64_t blocks[2]; @@ -1335,7 +1324,7 @@ static int PerformCommandErase(CommandParameters& params) { blocks[1] = (tgt.pos[i * 2 + 1] - tgt.pos[i * 2]) * (uint64_t) BLOCKSIZE; if (ioctl(params.fd, BLKDISCARD, &blocks) == -1) { - fprintf(stderr, "BLKDISCARD ioctl failed: %s\n", strerror(errno)); + PLOG(ERROR) << "BLKDISCARD ioctl failed"; return -1; } } @@ -1352,28 +1341,6 @@ struct Command { CommandFunction f; }; -// CompareCommands and CompareCommandNames are for the hash table - -static int CompareCommands(const void* c1, const void* c2) { - return strcmp(((const Command*) c1)->name, ((const Command*) c2)->name); -} - -static int CompareCommandNames(const void* c1, const void* c2) { - return strcmp(((const Command*) c1)->name, (const char*) c2); -} - -// HashString is used to hash command names for the hash table - -static unsigned int HashString(const char *s) { - unsigned int hash = 0; - if (s) { - while (*s) { - hash = hash * 33 + *s++; - } - } - return hash; -} - // args: // - block device (or file) to modify in-place // - transfer list (blob) @@ -1382,82 +1349,75 @@ static unsigned int HashString(const char *s) { static Value* PerformBlockImageUpdate(const char* name, State* state, int /* argc */, Expr* argv[], const Command* commands, size_t cmdcount, bool dryrun) { - CommandParameters params; - memset(¶ms, 0, sizeof(params)); + CommandParameters params = {}; params.canwrite = !dryrun; - fprintf(stderr, "performing %s\n", dryrun ? "verification" : "update"); + LOG(INFO) << "performing " << (dryrun ? "verification" : "update"); if (state->is_retry) { is_retry = true; - fprintf(stderr, "This update is a retry.\n"); + LOG(INFO) << "This update is a retry."; } - Value* blockdev_filename = nullptr; - Value* transfer_list_value = nullptr; - Value* new_data_fn = nullptr; - Value* patch_data_fn = nullptr; - if (ReadValueArgs(state, argv, 4, &blockdev_filename, &transfer_list_value, - &new_data_fn, &patch_data_fn) < 0) { - return StringValue(strdup("")); + std::vector<std::unique_ptr<Value>> args; + if (!ReadValueArgs(state, 4, argv, &args)) { + return nullptr; } - std::unique_ptr<Value, decltype(&FreeValue)> blockdev_filename_holder(blockdev_filename, - FreeValue); - std::unique_ptr<Value, decltype(&FreeValue)> transfer_list_value_holder(transfer_list_value, - FreeValue); - std::unique_ptr<Value, decltype(&FreeValue)> new_data_fn_holder(new_data_fn, FreeValue); - std::unique_ptr<Value, decltype(&FreeValue)> patch_data_fn_holder(patch_data_fn, FreeValue); + + const Value* blockdev_filename = args[0].get(); + const Value* transfer_list_value = args[1].get(); + const Value* new_data_fn = args[2].get(); + const Value* patch_data_fn = args[3].get(); if (blockdev_filename->type != VAL_STRING) { ErrorAbort(state, kArgsParsingFailure, "blockdev_filename argument to %s must be string", name); - return StringValue(strdup("")); + return StringValue(""); } if (transfer_list_value->type != VAL_BLOB) { ErrorAbort(state, kArgsParsingFailure, "transfer_list argument to %s must be blob", name); - return StringValue(strdup("")); + return StringValue(""); } if (new_data_fn->type != VAL_STRING) { ErrorAbort(state, kArgsParsingFailure, "new_data_fn argument to %s must be string", name); - return StringValue(strdup("")); + return StringValue(""); } if (patch_data_fn->type != VAL_STRING) { ErrorAbort(state, kArgsParsingFailure, "patch_data_fn argument to %s must be string", name); - return StringValue(strdup("")); + return StringValue(""); } - UpdaterInfo* ui = reinterpret_cast<UpdaterInfo*>(state->cookie); - + UpdaterInfo* ui = static_cast<UpdaterInfo*>(state->cookie); if (ui == nullptr) { - return StringValue(strdup("")); + return StringValue(""); } FILE* cmd_pipe = ui->cmd_pipe; - ZipArchive* za = ui->package_zip; + ZipArchiveHandle za = ui->package_zip; if (cmd_pipe == nullptr || za == nullptr) { - return StringValue(strdup("")); + return StringValue(""); } - const ZipEntry* patch_entry = mzFindZipEntry(za, patch_data_fn->data); - if (patch_entry == nullptr) { - fprintf(stderr, "%s(): no file \"%s\" in package", name, patch_data_fn->data); - return StringValue(strdup("")); + ZipString path_data(patch_data_fn->data.c_str()); + ZipEntry patch_entry; + if (FindEntry(za, path_data, &patch_entry) != 0) { + LOG(ERROR) << name << "(): no file \"" << patch_data_fn->data << "\" in package"; + return StringValue(""); } - params.patch_start = ui->package_zip_addr + mzGetZipEntryOffset(patch_entry); - const ZipEntry* new_entry = mzFindZipEntry(za, new_data_fn->data); - if (new_entry == nullptr) { - fprintf(stderr, "%s(): no file \"%s\" in package", name, new_data_fn->data); - return StringValue(strdup("")); + params.patch_start = ui->package_zip_addr + patch_entry.offset; + ZipString new_data(new_data_fn->data.c_str()); + ZipEntry new_entry; + if (FindEntry(za, new_data, &new_entry) != 0) { + LOG(ERROR) << name << "(): no file \"" << new_data_fn->data << "\" in package"; + return StringValue(""); } - params.fd = TEMP_FAILURE_RETRY(open(blockdev_filename->data, O_RDWR)); - unique_fd fd_holder(params.fd); - + params.fd.reset(TEMP_FAILURE_RETRY(ota_open(blockdev_filename->data.c_str(), O_RDWR))); if (params.fd == -1) { - fprintf(stderr, "open \"%s\" failed: %s\n", blockdev_filename->data, strerror(errno)); - return StringValue(strdup("")); + PLOG(ERROR) << "open \"" << blockdev_filename->data << "\" failed"; + return StringValue(""); } if (params.canwrite) { @@ -1472,62 +1432,59 @@ static Value* PerformBlockImageUpdate(const char* name, State* state, int /* arg int error = pthread_create(¶ms.thread, &attr, unzip_new_data, ¶ms.nti); if (error != 0) { - fprintf(stderr, "pthread_create failed: %s\n", strerror(error)); - return StringValue(strdup("")); + PLOG(ERROR) << "pthread_create failed"; + return StringValue(""); } } - // Copy all the lines in transfer_list_value into std::string for - // processing. - const std::string transfer_list(transfer_list_value->data, transfer_list_value->size); - std::vector<std::string> lines = android::base::Split(transfer_list, "\n"); + 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", lines.size()); - return StringValue(strdup("")); + return StringValue(""); } // First line in transfer list is the version number - if (!android::base::ParseInt(lines[0].c_str(), ¶ms.version, 1, 4)) { - fprintf(stderr, "unexpected transfer list version [%s]\n", lines[0].c_str()); - return StringValue(strdup("")); + if (!android::base::ParseInt(lines[0], ¶ms.version, 1, 4)) { + LOG(ERROR) << "unexpected transfer list version [" << lines[0] << "]"; + return StringValue(""); } - fprintf(stderr, "blockimg version is %d\n", params.version); + LOG(INFO) << "blockimg version is " << params.version; // Second line in transfer list is the total number of blocks we expect to write - int total_blocks; - if (!android::base::ParseInt(lines[1].c_str(), &total_blocks, 0)) { + size_t total_blocks; + if (!android::base::ParseUint(lines[1], &total_blocks)) { ErrorAbort(state, kArgsParsingFailure, "unexpected block count [%s]\n", lines[1].c_str()); - return StringValue(strdup("")); + return StringValue(""); } if (total_blocks == 0) { - return StringValue(strdup("t")); + return StringValue("t"); } size_t start = 2; if (params.version >= 2) { if (lines.size() < 4) { - ErrorAbort(state, kArgsParsingFailure, "too few lines in the transfer list [%zu]\n", - lines.size()); - return StringValue(strdup("")); + ErrorAbort(state, kArgsParsingFailure, "too few lines in the transfer list [%zu]\n", + lines.size()); + return StringValue(""); } // Third line is how many stash entries are needed simultaneously - fprintf(stderr, "maximum stash entries %s\n", lines[2].c_str()); + LOG(INFO) << "maximum stash entries " << lines[2]; // Fourth line is the maximum number of blocks that will be stashed simultaneously - int stash_max_blocks; - if (!android::base::ParseInt(lines[3].c_str(), &stash_max_blocks, 0)) { + size_t stash_max_blocks; + if (!android::base::ParseUint(lines[3], &stash_max_blocks)) { ErrorAbort(state, kArgsParsingFailure, "unexpected maximum stash blocks [%s]\n", lines[3].c_str()); - return StringValue(strdup("")); + return StringValue(""); } int res = CreateStash(state, stash_max_blocks, blockdev_filename->data, params.stashbase); if (res == -1) { - return StringValue(strdup("")); + return StringValue(""); } params.createdstash = res; @@ -1535,51 +1492,49 @@ static Value* PerformBlockImageUpdate(const char* name, State* state, int /* arg start += 2; } - // Build a hash table of the available commands - HashTable* cmdht = mzHashTableCreate(cmdcount, nullptr); - std::unique_ptr<HashTable, decltype(&mzHashTableFree)> cmdht_holder(cmdht, mzHashTableFree); - + // Build a map of the available commands + std::unordered_map<std::string, const Command*> cmd_map; for (size_t i = 0; i < cmdcount; ++i) { - unsigned int cmdhash = HashString(commands[i].name); - mzHashTableLookup(cmdht, cmdhash, (void*) &commands[i], CompareCommands, true); + 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]; } int rc = -1; // Subsequent lines are all individual transfer commands for (auto it = lines.cbegin() + start; it != lines.cend(); it++) { - const std::string& line_str(*it); - if (line_str.empty()) { - continue; - } + const std::string& line(*it); + if (line.empty()) continue; - params.tokens = android::base::Split(line_str, " "); + params.tokens = android::base::Split(line, " "); params.cpos = 0; params.cmdname = params.tokens[params.cpos++].c_str(); - params.cmdline = line_str.c_str(); + params.cmdline = line.c_str(); - unsigned int cmdhash = HashString(params.cmdname); - const Command* cmd = reinterpret_cast<const Command*>(mzHashTableLookup(cmdht, cmdhash, - const_cast<char*>(params.cmdname), CompareCommandNames, - false)); - - if (cmd == nullptr) { - fprintf(stderr, "unexpected command [%s]\n", params.cmdname); + if (cmd_map.find(params.cmdname) == cmd_map.end()) { + LOG(ERROR) << "unexpected command [" << params.cmdname << "]"; goto pbiudone; } + const Command* cmd = cmd_map[params.cmdname]; + if (cmd->f != nullptr && cmd->f(params) == -1) { - fprintf(stderr, "failed to execute command [%s]\n", line_str.c_str()); + LOG(ERROR) << "failed to execute command [" << line << "]"; goto pbiudone; } if (params.canwrite) { if (ota_fsync(params.fd) == -1) { failure_type = kFsyncFailure; - fprintf(stderr, "fsync failed: %s\n", strerror(errno)); + PLOG(ERROR) << "fsync failed"; goto pbiudone; } - fprintf(cmd_pipe, "set_progress %.4f\n", (double) params.written / total_blocks); + fprintf(cmd_pipe, "set_progress %.4f\n", + static_cast<double>(params.written) / total_blocks); fflush(cmd_pipe); } } @@ -1587,12 +1542,12 @@ static Value* PerformBlockImageUpdate(const char* name, State* state, int /* arg if (params.canwrite) { pthread_join(params.thread, nullptr); - fprintf(stderr, "wrote %zu blocks; expected %d\n", params.written, total_blocks); - fprintf(stderr, "stashed %zu blocks\n", params.stashed); - fprintf(stderr, "max alloc needed was %zu\n", params.buffer.size()); + 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, '/'); - if (partition != nullptr && *(partition+1) != 0) { + 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, @@ -1603,7 +1558,7 @@ static Value* PerformBlockImageUpdate(const char* name, State* state, int /* arg // may contain blocks needed to complete the update later. DeleteStash(params.stashbase); } else { - fprintf(stderr, "verified partition contents; update may be resumed\n"); + LOG(INFO) << "verified partition contents; update may be resumed"; } rc = 0; @@ -1611,9 +1566,9 @@ static Value* PerformBlockImageUpdate(const char* name, State* state, int /* arg pbiudone: if (ota_fsync(params.fd) == -1) { failure_type = kFsyncFailure; - fprintf(stderr, "fsync failed: %s\n", strerror(errno)); + PLOG(ERROR) << "fsync failed"; } - // params.fd will be automatically closed because of the fd_holder above. + // params.fd will be automatically closed because it's a unique_fd. // Only delete the stash if the update cannot be resumed, or it's // a verification run and we created the stash. @@ -1625,7 +1580,7 @@ pbiudone: state->cause_code = failure_type; } - return StringValue(rc == 0 ? strdup("t") : strdup("")); + return StringValue(rc == 0 ? "t" : ""); } // The transfer list is a text file containing commands to @@ -1719,36 +1674,32 @@ Value* BlockImageUpdateFn(const char* name, State* state, int argc, Expr* argv[] } Value* RangeSha1Fn(const char* name, State* state, int /* argc */, Expr* argv[]) { - Value* blockdev_filename; - Value* ranges; - - if (ReadValueArgs(state, argv, 2, &blockdev_filename, &ranges) < 0) { - return StringValue(strdup("")); + std::vector<std::unique_ptr<Value>> args; + if (!ReadValueArgs(state, 2, argv, &args)) { + return nullptr; } - std::unique_ptr<Value, decltype(&FreeValue)> ranges_holder(ranges, FreeValue); - std::unique_ptr<Value, decltype(&FreeValue)> blockdev_filename_holder(blockdev_filename, - FreeValue); + + const Value* blockdev_filename = args[0].get(); + const Value* ranges = args[1].get(); if (blockdev_filename->type != VAL_STRING) { ErrorAbort(state, kArgsParsingFailure, "blockdev_filename argument to %s must be string", name); - return StringValue(strdup("")); + return StringValue(""); } if (ranges->type != VAL_STRING) { ErrorAbort(state, kArgsParsingFailure, "ranges argument to %s must be string", name); - return StringValue(strdup("")); + return StringValue(""); } - int fd = open(blockdev_filename->data, O_RDWR); - unique_fd fd_holder(fd); - if (fd < 0) { - ErrorAbort(state, kFileOpenFailure, "open \"%s\" failed: %s", blockdev_filename->data, - strerror(errno)); - return StringValue(strdup("")); + android::base::unique_fd fd(ota_open(blockdev_filename->data.c_str(), O_RDWR)); + if (fd == -1) { + ErrorAbort(state, kFileOpenFailure, "open \"%s\" failed: %s", + blockdev_filename->data.c_str(), strerror(errno)); + return StringValue(""); } - RangeSet rs; - parse_range(ranges->data, rs); + RangeSet rs = parse_range(ranges->data); SHA_CTX ctx; SHA1_Init(&ctx); @@ -1756,16 +1707,16 @@ Value* RangeSha1Fn(const char* name, State* state, int /* argc */, Expr* argv[]) std::vector<uint8_t> buffer(BLOCKSIZE); for (size_t i = 0; i < rs.count; ++i) { if (!check_lseek(fd, (off64_t)rs.pos[i*2] * BLOCKSIZE, SEEK_SET)) { - ErrorAbort(state, kLseekFailure, "failed to seek %s: %s", blockdev_filename->data, - strerror(errno)); - return StringValue(strdup("")); + ErrorAbort(state, kLseekFailure, "failed to seek %s: %s", + blockdev_filename->data.c_str(), strerror(errno)); + return StringValue(""); } for (size_t j = rs.pos[i*2]; j < rs.pos[i*2+1]; ++j) { if (read_all(fd, buffer, BLOCKSIZE) == -1) { - ErrorAbort(state, kFreadFailure, "failed to read %s: %s", blockdev_filename->data, - strerror(errno)); - return StringValue(strdup("")); + ErrorAbort(state, kFreadFailure, "failed to read %s: %s", + blockdev_filename->data.c_str(), strerror(errno)); + return StringValue(""); } SHA1_Update(&ctx, buffer.data(), BLOCKSIZE); @@ -1774,7 +1725,7 @@ Value* RangeSha1Fn(const char* name, State* state, int /* argc */, Expr* argv[]) uint8_t digest[SHA_DIGEST_LENGTH]; SHA1_Final(digest, &ctx); - return StringValue(strdup(print_sha1(digest).c_str())); + return StringValue(print_sha1(digest)); } // This function checks if a device has been remounted R/W prior to an incremental @@ -1783,33 +1734,32 @@ Value* RangeSha1Fn(const char* name, State* state, int /* argc */, Expr* argv[]) // if executes successfully and an empty string otherwise. Value* CheckFirstBlockFn(const char* name, State* state, int argc, Expr* argv[]) { - Value* arg_filename; - - if (ReadValueArgs(state, argv, 1, &arg_filename) < 0) { + std::vector<std::unique_ptr<Value>> args; + if (!ReadValueArgs(state, 1, argv, &args)) { return nullptr; } - std::unique_ptr<Value, decltype(&FreeValue)> filename(arg_filename, FreeValue); - if (filename->type != VAL_STRING) { + const Value* arg_filename = args[0].get(); + + if (arg_filename->type != VAL_STRING) { ErrorAbort(state, kArgsParsingFailure, "filename argument to %s must be string", name); - return StringValue(strdup("")); + return StringValue(""); } - int fd = open(arg_filename->data, O_RDONLY); - unique_fd fd_holder(fd); + android::base::unique_fd fd(ota_open(arg_filename->data.c_str(), O_RDONLY)); if (fd == -1) { - ErrorAbort(state, kFileOpenFailure, "open \"%s\" failed: %s", arg_filename->data, + ErrorAbort(state, kFileOpenFailure, "open \"%s\" failed: %s", arg_filename->data.c_str(), strerror(errno)); - return StringValue(strdup("")); + return StringValue(""); } RangeSet blk0 {1 /*count*/, 1/*size*/, std::vector<size_t> {0, 1}/*position*/}; std::vector<uint8_t> block0_buffer(BLOCKSIZE); if (ReadBlocks(blk0, block0_buffer, fd) == -1) { - ErrorAbort(state, kFreadFailure, "failed to read %s: %s", arg_filename->data, + ErrorAbort(state, kFreadFailure, "failed to read %s: %s", arg_filename->data.c_str(), strerror(errno)); - return StringValue(strdup("")); + return StringValue(""); } // https://ext4.wiki.kernel.org/index.php/Ext4_Disk_Layout @@ -1827,56 +1777,53 @@ Value* CheckFirstBlockFn(const char* name, State* state, int argc, Expr* argv[]) uiPrintf(state, "Last remount happened on %s", ctime(&mount_time)); } - return StringValue(strdup("t")); + return StringValue("t"); } Value* BlockImageRecoverFn(const char* name, State* state, int argc, Expr* argv[]) { - Value* arg_filename; - Value* arg_ranges; - - if (ReadValueArgs(state, argv, 2, &arg_filename, &arg_ranges) < 0) { - return NULL; + std::vector<std::unique_ptr<Value>> args; + if (!ReadValueArgs(state, 2, argv, &args)) { + return nullptr; } - std::unique_ptr<Value, decltype(&FreeValue)> filename(arg_filename, FreeValue); - std::unique_ptr<Value, decltype(&FreeValue)> ranges(arg_ranges, FreeValue); + const Value* filename = args[0].get(); + const Value* ranges = args[1].get(); if (filename->type != VAL_STRING) { ErrorAbort(state, kArgsParsingFailure, "filename argument to %s must be string", name); - return StringValue(strdup("")); + return StringValue(""); } if (ranges->type != VAL_STRING) { ErrorAbort(state, kArgsParsingFailure, "ranges argument to %s must be string", name); - return StringValue(strdup("")); + return StringValue(""); } // Output notice to log when recover is attempted - fprintf(stderr, "%s image corrupted, attempting to recover...\n", filename->data); + LOG(INFO) << filename->data << " image corrupted, attempting to recover..."; // When opened with O_RDWR, libfec rewrites corrupted blocks when they are read - fec::io fh(filename->data, O_RDWR); + fec::io fh(filename->data.c_str(), O_RDWR); if (!fh) { - ErrorAbort(state, kLibfecFailure, "fec_open \"%s\" failed: %s", filename->data, + ErrorAbort(state, kLibfecFailure, "fec_open \"%s\" failed: %s", filename->data.c_str(), strerror(errno)); - return StringValue(strdup("")); + return StringValue(""); } if (!fh.has_ecc() || !fh.has_verity()) { ErrorAbort(state, kLibfecFailure, "unable to use metadata to correct errors"); - return StringValue(strdup("")); + return StringValue(""); } fec_status status; if (!fh.get_status(status)) { ErrorAbort(state, kLibfecFailure, "failed to read FEC status"); - return StringValue(strdup("")); + return StringValue(""); } - RangeSet rs; - parse_range(ranges->data, rs); + RangeSet rs = parse_range(ranges->data); uint8_t buffer[BLOCKSIZE]; @@ -1889,8 +1836,8 @@ Value* BlockImageRecoverFn(const char* name, State* state, int argc, Expr* argv[ if (fh.pread(buffer, BLOCKSIZE, (off64_t)j * BLOCKSIZE) != BLOCKSIZE) { ErrorAbort(state, kLibfecFailure, "failed to recover %s (block %zu): %s", - filename->data, j, strerror(errno)); - return StringValue(strdup("")); + filename->data.c_str(), j, strerror(errno)); + return StringValue(""); } // If we want to be able to recover from a situation where rewriting a corrected @@ -1905,8 +1852,8 @@ Value* BlockImageRecoverFn(const char* name, State* state, int argc, Expr* argv[ // read and check if the errors field value has increased. } } - fprintf(stderr, "...%s image recovered successfully.\n", filename->data); - return StringValue(strdup("t")); + LOG(INFO) << "..." << filename->data << " image recovered successfully."; + return StringValue("t"); } void RegisterBlockImageFunctions() { diff --git a/updater/blockimg.h b/updater/include/updater/blockimg.h index 2f4ad3c04..2f4ad3c04 100644 --- a/updater/blockimg.h +++ b/updater/include/updater/blockimg.h diff --git a/updater/install.h b/updater/include/updater/install.h index 70e343404..8d6ca4728 100644 --- a/updater/install.h +++ b/updater/include/updater/install.h @@ -17,11 +17,12 @@ #ifndef _UPDATER_INSTALL_H_ #define _UPDATER_INSTALL_H_ +struct State; + void RegisterInstallFunctions(); // uiPrintf function prints msg to screen as well as logs -void uiPrintf(State* state, const char* format, ...); - -static int make_parents(char* name); +void uiPrintf(State* _Nonnull state, const char* _Nonnull format, ...) + __attribute__((__format__(printf, 2, 3))); #endif diff --git a/updater/updater.h b/updater/include/updater/updater.h index d1dfdd05e..f4a2fe874 100644 --- a/updater/updater.h +++ b/updater/include/updater/updater.h @@ -18,20 +18,18 @@ #define _UPDATER_UPDATER_H_ #include <stdio.h> -#include "minzip/Zip.h" - -#include <selinux/selinux.h> -#include <selinux/label.h> +#include <ziparchive/zip_archive.h> typedef struct { FILE* cmd_pipe; - ZipArchive* package_zip; + ZipArchiveHandle package_zip; int version; uint8_t* package_zip_addr; size_t package_zip_len; } UpdaterInfo; +struct selabel_handle; extern struct selabel_handle *sehandle; #endif diff --git a/updater/install.cpp b/updater/install.cpp index 005f9f97d..643145447 100644 --- a/updater/install.cpp +++ b/updater/install.cpp @@ -14,880 +14,782 @@ * limitations under the License. */ +#include "updater/install.h" + #include <ctype.h> #include <errno.h> +#include <fcntl.h> +#include <ftw.h> +#include <inttypes.h> #include <stdarg.h> #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <sys/capability.h> #include <sys/mount.h> #include <sys/stat.h> #include <sys/types.h> #include <sys/wait.h> -#include <unistd.h> -#include <fcntl.h> -#include <time.h> -#include <selinux/selinux.h> -#include <ftw.h> -#include <sys/capability.h> #include <sys/xattr.h> -#include <linux/xattr.h> -#include <inttypes.h> +#include <time.h> +#include <unistd.h> +#include <utime.h> #include <memory> +#include <string> #include <vector> +#include <android-base/file.h> +#include <android-base/logging.h> +#include <android-base/parsedouble.h> #include <android-base/parseint.h> -#include <android-base/strings.h> +#include <android-base/properties.h> #include <android-base/stringprintf.h> +#include <android-base/strings.h> +#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 <ziparchive/zip_archive.h> -#include "bootloader.h" -#include "applypatch/applypatch.h" -#include "cutils/android_reboot.h" -#include "cutils/misc.h" -#include "cutils/properties.h" #include "edify/expr.h" #include "error_code.h" -#include "minzip/DirUtil.h" -#include "mtdutils/mounts.h" -#include "mtdutils/mtdutils.h" -#include "openssl/sha.h" +#include "mounts.h" #include "ota_io.h" -#include "updater.h" -#include "install.h" +#include "otautil/DirUtil.h" +#include "otautil/ZipUtil.h" +#include "print_sha1.h" #include "tune2fs.h" - -#ifdef USE_EXT4 -#include "make_ext4fs.h" -#include "wipe.h" -#endif +#include "updater/updater.h" // Send over the buffer to recovery though the command pipe. static void uiPrint(State* state, const std::string& buffer) { - UpdaterInfo* ui = reinterpret_cast<UpdaterInfo*>(state->cookie); - - // "line1\nline2\n" will be split into 3 tokens: "line1", "line2" and "". - // So skip sending empty strings to UI. - std::vector<std::string> lines = android::base::Split(buffer, "\n"); - for (auto& line: lines) { - if (!line.empty()) { - fprintf(ui->cmd_pipe, "ui_print %s\n", line.c_str()); - fprintf(ui->cmd_pipe, "ui_print\n"); - } - } - - // On the updater side, we need to dump the contents to stderr (which has - // been redirected to the log file). Because the recovery will only print - // the contents to screen when processing pipe command ui_print. - fprintf(stderr, "%s", buffer.c_str()); + UpdaterInfo* ui = static_cast<UpdaterInfo*>(state->cookie); + + // "line1\nline2\n" will be split into 3 tokens: "line1", "line2" and "". + // So skip sending empty strings to UI. + std::vector<std::string> lines = android::base::Split(buffer, "\n"); + for (auto& line : lines) { + if (!line.empty()) { + fprintf(ui->cmd_pipe, "ui_print %s\n", line.c_str()); + fprintf(ui->cmd_pipe, "ui_print\n"); + } + } + + // On the updater side, we need to dump the contents to stderr (which has + // been redirected to the log file). Because the recovery will only print + // the contents to screen when processing pipe command ui_print. + LOG(INFO) << buffer; } -__attribute__((__format__(printf, 2, 3))) __nonnull((2)) -void uiPrintf(State* state, const char* format, ...) { - std::string error_msg; +void uiPrintf(State* _Nonnull state, const char* _Nonnull format, ...) { + std::string error_msg; - va_list ap; - va_start(ap, format); - android::base::StringAppendV(&error_msg, format, ap); - va_end(ap); + va_list ap; + va_start(ap, format); + android::base::StringAppendV(&error_msg, format, ap); + va_end(ap); - uiPrint(state, error_msg); + uiPrint(state, error_msg); } -// Take a sha-1 digest and return it as a newly-allocated hex string. -char* PrintSha1(const uint8_t* digest) { - char* buffer = reinterpret_cast<char*>(malloc(SHA_DIGEST_LENGTH*2 + 1)); - const char* alphabet = "0123456789abcdef"; - size_t i; - for (i = 0; i < SHA_DIGEST_LENGTH; ++i) { - buffer[i*2] = alphabet[(digest[i] >> 4) & 0xf]; - buffer[i*2+1] = alphabet[digest[i] & 0xf]; - } - buffer[i*2] = '\0'; - return buffer; +static bool is_dir(const std::string& dirpath) { + struct stat st; + return stat(dirpath.c_str(), &st) == 0 && S_ISDIR(st.st_mode); +} + +// 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; + } + 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; + } + + LOG(INFO) << "created [" << dir_path << "]"; + } + prev_end = next_end; + } + return true; } // mount(fs_type, partition_type, location, mount_point) +// mount(fs_type, partition_type, location, mount_point, mount_options) // -// fs_type="yaffs2" partition_type="MTD" location=partition // fs_type="ext4" partition_type="EMMC" location=device Value* MountFn(const char* name, State* state, int argc, Expr* argv[]) { - char* result = NULL; - if (argc != 4 && argc != 5) { - return ErrorAbort(state, kArgsParsingFailure, "%s() expects 4-5 args, got %d", name, argc); - } - char* fs_type; - char* partition_type; - char* location; - char* mount_point; - char* mount_options; - bool has_mount_options; - if (argc == 5) { - has_mount_options = true; - if (ReadArgs(state, argv, 5, &fs_type, &partition_type, - &location, &mount_point, &mount_options) < 0) { - return NULL; - } - } else { - has_mount_options = false; - if (ReadArgs(state, argv, 4, &fs_type, &partition_type, - &location, &mount_point) < 0) { - return NULL; - } - } - - if (strlen(fs_type) == 0) { - ErrorAbort(state, kArgsParsingFailure, "fs_type argument to %s() can't be empty", name); - goto done; - } - if (strlen(partition_type) == 0) { - ErrorAbort(state, kArgsParsingFailure, "partition_type argument to %s() can't be empty", - name); - goto done; - } - if (strlen(location) == 0) { - ErrorAbort(state, kArgsParsingFailure, "location argument to %s() can't be empty", name); - goto done; - } - if (strlen(mount_point) == 0) { - ErrorAbort(state, kArgsParsingFailure, "mount_point argument to %s() can't be empty", - name); - goto done; - } - - { - char *secontext = NULL; - - if (sehandle) { - selabel_lookup(sehandle, &secontext, mount_point, 0755); - setfscreatecon(secontext); - } - - mkdir(mount_point, 0755); - - if (secontext) { - freecon(secontext); - setfscreatecon(NULL); - } - } - - if (strcmp(partition_type, "MTD") == 0) { - mtd_scan_partitions(); - const MtdPartition* mtd; - mtd = mtd_find_partition_by_name(location); - if (mtd == NULL) { - uiPrintf(state, "%s: no mtd partition named \"%s\"\n", - name, location); - result = strdup(""); - goto done; - } - if (mtd_mount_partition(mtd, mount_point, fs_type, 0 /* rw */) != 0) { - uiPrintf(state, "mtd mount of %s failed: %s\n", - location, strerror(errno)); - result = strdup(""); - goto done; - } - result = mount_point; - } else { - if (mount(location, mount_point, fs_type, - MS_NOATIME | MS_NODEV | MS_NODIRATIME, - has_mount_options ? mount_options : "") < 0) { - uiPrintf(state, "%s: failed to mount %s at %s: %s\n", - name, location, mount_point, strerror(errno)); - result = strdup(""); - } else { - result = mount_point; - } - } - -done: - free(fs_type); - free(partition_type); - free(location); - if (result != mount_point) free(mount_point); - if (has_mount_options) free(mount_options); - return StringValue(result); + if (argc != 4 && argc != 5) { + return ErrorAbort(state, kArgsParsingFailure, "%s() expects 4-5 args, got %d", name, argc); + } + + std::vector<std::string> args; + if (!ReadArgs(state, argc, argv, &args)) { + return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse the argument(s)", name); + } + const std::string& fs_type = args[0]; + const std::string& partition_type = args[1]; + const std::string& location = args[2]; + const std::string& mount_point = args[3]; + std::string mount_options; + + if (argc == 5) { + mount_options = args[4]; + } + + if (fs_type.empty()) { + return ErrorAbort(state, kArgsParsingFailure, "fs_type argument to %s() can't be empty", name); + } + if (partition_type.empty()) { + return ErrorAbort(state, kArgsParsingFailure, "partition_type argument to %s() can't be empty", + name); + } + if (location.empty()) { + return ErrorAbort(state, kArgsParsingFailure, "location argument to %s() can't be empty", name); + } + if (mount_point.empty()) { + return ErrorAbort(state, kArgsParsingFailure, "mount_point argument to %s() can't be empty", + name); + } + + { + char* secontext = nullptr; + + if (sehandle) { + selabel_lookup(sehandle, &secontext, mount_point.c_str(), 0755); + setfscreatecon(secontext); + } + + mkdir(mount_point.c_str(), 0755); + + if (secontext) { + freecon(secontext); + setfscreatecon(nullptr); + } + } + + if (mount(location.c_str(), mount_point.c_str(), fs_type.c_str(), + MS_NOATIME | MS_NODEV | MS_NODIRATIME, mount_options.c_str()) < 0) { + uiPrintf(state, "%s: failed to mount %s at %s: %s\n", name, location.c_str(), + mount_point.c_str(), strerror(errno)); + return StringValue(""); + } + + return StringValue(mount_point); } // is_mounted(mount_point) Value* IsMountedFn(const char* name, State* state, int argc, Expr* argv[]) { - char* result = NULL; - if (argc != 1) { - return ErrorAbort(state, kArgsParsingFailure, "%s() expects 1 arg, got %d", name, argc); - } - char* mount_point; - if (ReadArgs(state, argv, 1, &mount_point) < 0) { - return NULL; - } - if (strlen(mount_point) == 0) { - ErrorAbort(state, kArgsParsingFailure, "mount_point argument to unmount() can't be empty"); - goto done; - } - - scan_mounted_volumes(); - { - const MountedVolume* vol = find_mounted_volume_by_mount_point(mount_point); - if (vol == NULL) { - result = strdup(""); - } else { - result = mount_point; - } - } - -done: - if (result != mount_point) free(mount_point); - return StringValue(result); + if (argc != 1) { + return ErrorAbort(state, kArgsParsingFailure, "%s() expects 1 arg, got %d", name, argc); + } + + std::vector<std::string> args; + if (!ReadArgs(state, argc, argv, &args)) { + return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse the argument(s)", name); + } + const std::string& mount_point = args[0]; + if (mount_point.empty()) { + return ErrorAbort(state, kArgsParsingFailure, + "mount_point argument to unmount() can't be empty"); + } + + scan_mounted_volumes(); + MountedVolume* vol = find_mounted_volume_by_mount_point(mount_point.c_str()); + if (vol == nullptr) { + return StringValue(""); + } + + return StringValue(mount_point); } - Value* UnmountFn(const char* name, State* state, int argc, Expr* argv[]) { - char* result = NULL; - if (argc != 1) { - return ErrorAbort(state, kArgsParsingFailure, "%s() expects 1 arg, got %d", name, argc); - } - char* mount_point; - if (ReadArgs(state, argv, 1, &mount_point) < 0) { - return NULL; - } - if (strlen(mount_point) == 0) { - ErrorAbort(state, kArgsParsingFailure, "mount_point argument to unmount() can't be empty"); - goto done; - } - - scan_mounted_volumes(); - { - const MountedVolume* vol = find_mounted_volume_by_mount_point(mount_point); - if (vol == NULL) { - uiPrintf(state, "unmount of %s failed; no such volume\n", mount_point); - result = strdup(""); - } else { - int ret = unmount_mounted_volume(vol); - if (ret != 0) { - uiPrintf(state, "unmount of %s failed (%d): %s\n", - mount_point, ret, strerror(errno)); - } - result = mount_point; - } - } - -done: - if (result != mount_point) free(mount_point); - return StringValue(result); + if (argc != 1) { + return ErrorAbort(state, kArgsParsingFailure, "%s() expects 1 arg, got %d", name, argc); + } + std::vector<std::string> args; + if (!ReadArgs(state, argc, argv, &args)) { + return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse the argument(s)", name); + } + const std::string& mount_point = args[0]; + if (mount_point.empty()) { + return ErrorAbort(state, kArgsParsingFailure, + "mount_point argument to unmount() can't be empty"); + } + + scan_mounted_volumes(); + MountedVolume* vol = find_mounted_volume_by_mount_point(mount_point.c_str()); + if (vol == nullptr) { + uiPrintf(state, "unmount of %s failed; no such volume\n", mount_point.c_str()); + return nullptr; + } else { + int ret = unmount_mounted_volume(vol); + if (ret != 0) { + uiPrintf(state, "unmount of %s failed (%d): %s\n", mount_point.c_str(), ret, strerror(errno)); + } + } + + return StringValue(mount_point); } static int exec_cmd(const char* path, char* const argv[]) { - int status; - pid_t child; - if ((child = vfork()) == 0) { - execv(path, argv); - _exit(-1); - } - waitpid(child, &status, 0); - if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { - printf("%s failed with status %d\n", path, WEXITSTATUS(status)); - } - return WEXITSTATUS(status); + pid_t child; + if ((child = vfork()) == 0) { + execv(path, argv); + _exit(-1); + } + + int status; + waitpid(child, &status, 0); + if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { + LOG(ERROR) << path << " failed with status " << WEXITSTATUS(status); + } + return WEXITSTATUS(status); } - // format(fs_type, partition_type, location, fs_size, mount_point) // -// fs_type="yaffs2" partition_type="MTD" location=partition fs_size=<bytes> mount_point=<location> -// fs_type="ext4" partition_type="EMMC" location=device fs_size=<bytes> mount_point=<location> -// fs_type="f2fs" partition_type="EMMC" location=device fs_size=<bytes> mount_point=<location> +// fs_type="ext4" partition_type="EMMC" location=device fs_size=<bytes> mount_point=<location> +// fs_type="f2fs" partition_type="EMMC" location=device fs_size=<bytes> mount_point=<location> // if fs_size == 0, then make fs uses the entire partition. // if fs_size > 0, that is the size to use // if fs_size < 0, then reserve that many bytes at the end of the partition (not for "f2fs") Value* FormatFn(const char* name, State* state, int argc, Expr* argv[]) { - char* result = NULL; - if (argc != 5) { - return ErrorAbort(state, kArgsParsingFailure, "%s() expects 5 args, got %d", name, argc); - } - char* fs_type; - char* partition_type; - char* location; - char* fs_size; - char* mount_point; - - if (ReadArgs(state, argv, 5, &fs_type, &partition_type, &location, &fs_size, &mount_point) < 0) { - return NULL; - } - - if (strlen(fs_type) == 0) { - ErrorAbort(state, kArgsParsingFailure, "fs_type argument to %s() can't be empty", name); - goto done; - } - if (strlen(partition_type) == 0) { - ErrorAbort(state, kArgsParsingFailure, "partition_type argument to %s() can't be empty", - name); - goto done; - } - if (strlen(location) == 0) { - ErrorAbort(state, kArgsParsingFailure, "location argument to %s() can't be empty", name); - goto done; - } - - if (strlen(mount_point) == 0) { - ErrorAbort(state, kArgsParsingFailure, "mount_point argument to %s() can't be empty", - name); - goto done; - } - - if (strcmp(partition_type, "MTD") == 0) { - mtd_scan_partitions(); - const MtdPartition* mtd = mtd_find_partition_by_name(location); - if (mtd == NULL) { - printf("%s: no mtd partition named \"%s\"", - name, location); - result = strdup(""); - goto done; - } - MtdWriteContext* ctx = mtd_write_partition(mtd); - if (ctx == NULL) { - printf("%s: can't write \"%s\"", name, location); - result = strdup(""); - goto done; - } - if (mtd_erase_blocks(ctx, -1) == -1) { - mtd_write_close(ctx); - printf("%s: failed to erase \"%s\"", name, location); - result = strdup(""); - goto done; - } - if (mtd_write_close(ctx) != 0) { - printf("%s: failed to close \"%s\"", name, location); - result = strdup(""); - goto done; - } - result = location; -#ifdef USE_EXT4 - } else if (strcmp(fs_type, "ext4") == 0) { - int status = make_ext4fs(location, atoll(fs_size), mount_point, sehandle); - if (status != 0) { - printf("%s: make_ext4fs failed (%d) on %s", - name, status, location); - result = strdup(""); - goto done; - } - result = location; - } else if (strcmp(fs_type, "f2fs") == 0) { - char *num_sectors; - if (asprintf(&num_sectors, "%lld", atoll(fs_size) / 512) <= 0) { - printf("format_volume: failed to create %s command for %s\n", fs_type, location); - result = strdup(""); - goto done; - } - const char *f2fs_path = "/sbin/mkfs.f2fs"; - const char* const f2fs_argv[] = {"mkfs.f2fs", "-t", "-d1", location, num_sectors, NULL}; - int status = exec_cmd(f2fs_path, (char* const*)f2fs_argv); - free(num_sectors); - if (status != 0) { - printf("%s: mkfs.f2fs failed (%d) on %s", - name, status, location); - result = strdup(""); - goto done; - } - result = location; -#endif - } else { - printf("%s: unsupported fs_type \"%s\" partition_type \"%s\"", - name, fs_type, partition_type); - } - -done: - free(fs_type); - free(partition_type); - if (result != location) free(location); - return StringValue(result); + if (argc != 5) { + return ErrorAbort(state, kArgsParsingFailure, "%s() expects 5 args, got %d", name, argc); + } + + std::vector<std::string> args; + if (!ReadArgs(state, argc, argv, &args)) { + return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse the argument(s)", name); + } + const std::string& fs_type = args[0]; + const std::string& partition_type = args[1]; + const std::string& location = args[2]; + const std::string& fs_size = args[3]; + const std::string& mount_point = args[4]; + + if (fs_type.empty()) { + return ErrorAbort(state, kArgsParsingFailure, "fs_type argument to %s() can't be empty", name); + } + if (partition_type.empty()) { + return ErrorAbort(state, kArgsParsingFailure, "partition_type argument to %s() can't be empty", + name); + } + if (location.empty()) { + return ErrorAbort(state, kArgsParsingFailure, "location argument to %s() can't be empty", name); + } + if (mount_point.empty()) { + return ErrorAbort(state, kArgsParsingFailure, "mount_point argument to %s() can't be empty", + name); + } + + int64_t size; + if (!android::base::ParseInt(fs_size, &size)) { + return ErrorAbort(state, kArgsParsingFailure, "%s: failed to parse int in %s\n", name, + fs_size.c_str()); + } + + if (fs_type == "ext4") { + int 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); + } else if (fs_type == "f2fs") { + if (size < 0) { + LOG(ERROR) << name << ": fs_size can't be negative for f2fs: " << fs_size; + return StringValue(""); + } + std::string num_sectors = std::to_string(size / 512); + + const char* f2fs_path = "/sbin/mkfs.f2fs"; + const char* const f2fs_argv[] = { "mkfs.f2fs", "-t", "-d1", location.c_str(), + num_sectors.c_str(), nullptr }; + int status = exec_cmd(f2fs_path, const_cast<char* const*>(f2fs_argv)); + if (status != 0) { + LOG(ERROR) << name << ": mkfs.f2fs failed (" << status << ") on " << location; + return StringValue(""); + } + return StringValue(location); + } else { + LOG(ERROR) << name << ": unsupported fs_type \"" << fs_type << "\" partition_type \"" + << partition_type << "\""; + } + + return nullptr; } +// rename(src_name, dst_name) +// Renames src_name to dst_name. It automatically creates the necessary directories for dst_name. +// Example: rename("system/app/Hangouts/Hangouts.apk", "system/priv-app/Hangouts/Hangouts.apk") Value* RenameFn(const char* name, State* state, int argc, Expr* argv[]) { - char* result = NULL; - if (argc != 2) { - return ErrorAbort(state, kArgsParsingFailure, "%s() expects 2 args, got %d", name, argc); - } - - char* src_name; - char* dst_name; - - if (ReadArgs(state, argv, 2, &src_name, &dst_name) < 0) { - return NULL; - } - if (strlen(src_name) == 0) { - ErrorAbort(state, kArgsParsingFailure, "src_name argument to %s() can't be empty", name); - goto done; - } - if (strlen(dst_name) == 0) { - ErrorAbort(state, kArgsParsingFailure, "dst_name argument to %s() can't be empty", name); - goto done; - } - if (make_parents(dst_name) != 0) { - ErrorAbort(state, kFileRenameFailure, "Creating parent of %s failed, error %s", - dst_name, strerror(errno)); - } else if (access(dst_name, F_OK) == 0 && access(src_name, F_OK) != 0) { - // File was already moved - result = dst_name; - } else if (rename(src_name, dst_name) != 0) { - ErrorAbort(state, kFileRenameFailure, "Rename of %s to %s failed, error %s", - src_name, dst_name, strerror(errno)); - } else { - result = dst_name; - } - -done: - free(src_name); - if (result != dst_name) free(dst_name); - return StringValue(result); + if (argc != 2) { + return ErrorAbort(state, kArgsParsingFailure, "%s() expects 2 args, got %d", name, argc); + } + + std::vector<std::string> args; + if (!ReadArgs(state, argc, argv, &args)) { + return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse the argument(s)", name); + } + const std::string& src_name = args[0]; + const std::string& dst_name = args[1]; + + if (src_name.empty()) { + return ErrorAbort(state, kArgsParsingFailure, "src_name argument to %s() can't be empty", name); + } + if (dst_name.empty()) { + return ErrorAbort(state, kArgsParsingFailure, "dst_name argument to %s() can't be empty", name); + } + if (!make_parents(dst_name)) { + return ErrorAbort(state, kFileRenameFailure, "Creating parent of %s failed, error %s", + dst_name.c_str(), strerror(errno)); + } else if (access(dst_name.c_str(), F_OK) == 0 && access(src_name.c_str(), F_OK) != 0) { + // File was already moved + return StringValue(dst_name); + } else if (rename(src_name.c_str(), dst_name.c_str()) != 0) { + return ErrorAbort(state, kFileRenameFailure, "Rename of %s to %s failed, error %s", + src_name.c_str(), dst_name.c_str(), strerror(errno)); + } + + return StringValue(dst_name); } +// delete([filename, ...]) +// Deletes all the filenames listed. Returns the number of files successfully deleted. +// +// delete_recursive([dirname, ...]) +// Recursively deletes dirnames and all their contents. Returns the number of directories +// successfully deleted. Value* DeleteFn(const char* name, State* state, int argc, Expr* argv[]) { - char** paths = reinterpret_cast<char**>(malloc(argc * sizeof(char*))); - for (int i = 0; i < argc; ++i) { - paths[i] = Evaluate(state, argv[i]); - if (paths[i] == NULL) { - for (int j = 0; j < i; ++j) { - free(paths[j]); - } - free(paths); - return NULL; - } + std::vector<std::string> paths(argc); + for (int i = 0; i < argc; ++i) { + if (!Evaluate(state, argv[i], &paths[i])) { + return nullptr; } + } - bool recursive = (strcmp(name, "delete_recursive") == 0); + bool recursive = (strcmp(name, "delete_recursive") == 0); - int success = 0; - for (int i = 0; i < argc; ++i) { - if ((recursive ? dirUnlinkHierarchy(paths[i]) : unlink(paths[i])) == 0) - ++success; - free(paths[i]); + int success = 0; + for (int i = 0; i < argc; ++i) { + if ((recursive ? dirUnlinkHierarchy(paths[i].c_str()) : unlink(paths[i].c_str())) == 0) { + ++success; } - free(paths); + } - char buffer[10]; - sprintf(buffer, "%d", success); - return StringValue(strdup(buffer)); + return StringValue(std::to_string(success)); } Value* ShowProgressFn(const char* name, State* state, int argc, Expr* argv[]) { - if (argc != 2) { - return ErrorAbort(state, kArgsParsingFailure, "%s() expects 2 args, got %d", name, argc); - } - char* frac_str; - char* sec_str; - if (ReadArgs(state, argv, 2, &frac_str, &sec_str) < 0) { - return NULL; - } - - double frac = strtod(frac_str, NULL); - int sec; - android::base::ParseInt(sec_str, &sec); - - UpdaterInfo* ui = (UpdaterInfo*)(state->cookie); - fprintf(ui->cmd_pipe, "progress %f %d\n", frac, sec); - - free(sec_str); - return StringValue(frac_str); + if (argc != 2) { + return ErrorAbort(state, kArgsParsingFailure, "%s() expects 2 args, got %d", name, argc); + } + + std::vector<std::string> args; + if (!ReadArgs(state, argc, argv, &args)) { + return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse the argument(s)", name); + } + const std::string& frac_str = args[0]; + const std::string& sec_str = args[1]; + + double frac; + if (!android::base::ParseDouble(frac_str.c_str(), &frac)) { + return ErrorAbort(state, kArgsParsingFailure, "%s: failed to parse double in %s\n", 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, + sec_str.c_str()); + } + + UpdaterInfo* ui = static_cast<UpdaterInfo*>(state->cookie); + fprintf(ui->cmd_pipe, "progress %f %d\n", frac, sec); + + return StringValue(frac_str); } Value* SetProgressFn(const char* name, State* state, int argc, Expr* argv[]) { - if (argc != 1) { - return ErrorAbort(state, kArgsParsingFailure, "%s() expects 1 arg, got %d", name, argc); - } - char* frac_str; - if (ReadArgs(state, argv, 1, &frac_str) < 0) { - return NULL; - } - - double frac = strtod(frac_str, NULL); - - UpdaterInfo* ui = (UpdaterInfo*)(state->cookie); - fprintf(ui->cmd_pipe, "set_progress %f\n", frac); - - return StringValue(frac_str); -} - -// package_extract_dir(package_path, destination_path) -Value* PackageExtractDirFn(const char* name, State* state, - int argc, Expr* argv[]) { - if (argc != 2) { - return ErrorAbort(state, kArgsParsingFailure, "%s() expects 2 args, got %d", name, argc); - } - char* zip_path; - char* dest_path; - if (ReadArgs(state, argv, 2, &zip_path, &dest_path) < 0) return NULL; - - ZipArchive* za = ((UpdaterInfo*)(state->cookie))->package_zip; - - // To create a consistent system image, never use the clock for timestamps. - struct utimbuf timestamp = { 1217592000, 1217592000 }; // 8/1/2008 default - - bool success = mzExtractRecursive(za, zip_path, dest_path, - ×tamp, - NULL, NULL, sehandle); - free(zip_path); - free(dest_path); - return StringValue(strdup(success ? "t" : "")); + if (argc != 1) { + return ErrorAbort(state, kArgsParsingFailure, "%s() expects 1 arg, got %d", name, argc); + } + + std::vector<std::string> args; + if (!ReadArgs(state, 1, argv, &args)) { + return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse the argument(s)", name); + } + const std::string& frac_str = args[0]; + + double frac; + if (!android::base::ParseDouble(frac_str.c_str(), &frac)) { + return ErrorAbort(state, kArgsParsingFailure, "%s: failed to parse double in %s\n", name, + frac_str.c_str()); + } + + UpdaterInfo* ui = static_cast<UpdaterInfo*>(state->cookie); + fprintf(ui->cmd_pipe, "set_progress %f\n", frac); + + return StringValue(frac_str); } +// package_extract_dir(package_dir, dest_dir) +// Extracts all files from the package underneath package_dir and writes them to the +// corresponding tree beneath dest_dir. Any existing files are overwritten. +// Example: package_extract_dir("system", "/system") +// +// Note: package_dir needs to be a relative path; dest_dir needs to be an absolute path. +Value* PackageExtractDirFn(const char* name, State* state, int argc, Expr* argv[]) { + if (argc != 2) { + return ErrorAbort(state, kArgsParsingFailure, "%s() expects 2 args, got %d", name, argc); + } -// package_extract_file(package_path, destination_path) -// or -// package_extract_file(package_path) -// to return the entire contents of the file as the result of this -// function (the char* returned is actually a FileContents*). -Value* PackageExtractFileFn(const char* name, State* state, - int argc, Expr* argv[]) { - if (argc < 1 || argc > 2) { - return ErrorAbort(state, kArgsParsingFailure, "%s() expects 1 or 2 args, got %d", - name, argc); - } - bool success = false; - - if (argc == 2) { - // The two-argument version extracts to a file. - - ZipArchive* za = ((UpdaterInfo*)(state->cookie))->package_zip; + std::vector<std::string> args; + if (!ReadArgs(state, 2, argv, &args)) { + return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse the argument(s)", name); + } + const std::string& zip_path = args[0]; + const std::string& dest_path = args[1]; - char* zip_path; - char* dest_path; - if (ReadArgs(state, argv, 2, &zip_path, &dest_path) < 0) return NULL; + ZipArchiveHandle za = static_cast<UpdaterInfo*>(state->cookie)->package_zip; - const ZipEntry* entry = mzFindZipEntry(za, zip_path); - if (entry == NULL) { - printf("%s: no %s in package\n", name, zip_path); - goto done2; - } + // To create a consistent system image, never use the clock for timestamps. + constexpr struct utimbuf timestamp = { 1217592000, 1217592000 }; // 8/1/2008 default - { - int fd = TEMP_FAILURE_RETRY(ota_open(dest_path, O_WRONLY | O_CREAT | O_TRUNC | O_SYNC, - S_IRUSR | S_IWUSR)); - if (fd == -1) { - printf("%s: can't open %s for write: %s\n", name, dest_path, strerror(errno)); - goto done2; - } - success = mzExtractZipEntryToFile(za, entry, fd); - if (ota_fsync(fd) == -1) { - printf("fsync of \"%s\" failed: %s\n", dest_path, strerror(errno)); - success = false; - } - if (ota_close(fd) == -1) { - printf("close of \"%s\" failed: %s\n", dest_path, strerror(errno)); - success = false; - } - } + bool success = ExtractPackageRecursive(za, zip_path, dest_path, ×tamp, sehandle); - done2: - free(zip_path); - free(dest_path); - return StringValue(strdup(success ? "t" : "")); - } else { - // The one-argument version returns the contents of the file - // as the result. - - char* zip_path; - if (ReadArgs(state, argv, 1, &zip_path) < 0) return NULL; - - Value* v = reinterpret_cast<Value*>(malloc(sizeof(Value))); - v->type = VAL_BLOB; - v->size = -1; - v->data = NULL; - - ZipArchive* za = ((UpdaterInfo*)(state->cookie))->package_zip; - const ZipEntry* entry = mzFindZipEntry(za, zip_path); - if (entry == NULL) { - printf("%s: no %s in package\n", name, zip_path); - goto done1; - } + return StringValue(success ? "t" : ""); +} - v->size = mzGetZipEntryUncompLen(entry); - v->data = reinterpret_cast<char*>(malloc(v->size)); - if (v->data == NULL) { - printf("%s: failed to allocate %ld bytes for %s\n", - name, (long)v->size, zip_path); - goto done1; - } +// 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, int argc, Expr* argv[]) { + if (argc < 1 || argc > 2) { + return ErrorAbort(state, kArgsParsingFailure, "%s() expects 1 or 2 args, got %d", name, argc); + } - success = mzExtractZipEntryToBuffer(za, entry, - (unsigned char *)v->data); + if (argc == 2) { + // The two-argument version extracts to a file. - done1: - free(zip_path); - if (!success) { - free(v->data); - v->data = NULL; - v->size = -1; - } - return v; + std::vector<std::string> args; + if (!ReadArgs(state, 2, argv, &args)) { + return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse %d args", name, argc); } -} + const std::string& zip_path = args[0]; + const std::string& dest_path = args[1]; -// Create all parent directories of name, if necessary. -static int make_parents(char* name) { - char* p; - for (p = name + (strlen(name)-1); p > name; --p) { - if (*p != '/') continue; - *p = '\0'; - if (make_parents(name) < 0) return -1; - int result = mkdir(name, 0700); - if (result == 0) printf("created [%s]\n", name); - *p = '/'; - if (result == 0 || errno == EEXIST) { - // successfully created or already existed; we're done - return 0; - } else { - printf("failed to mkdir %s: %s\n", name, strerror(errno)); - return -1; - } + 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(""); } - return 0; -} -// symlink target src1 src2 ... -// unlinks any previously existing src1, src2, etc before creating symlinks. -Value* SymlinkFn(const char* name, State* state, int argc, Expr* argv[]) { - if (argc == 0) { - return ErrorAbort(state, kArgsParsingFailure, "%s() expects 1+ args, got %d", name, argc); - } - char* target; - target = Evaluate(state, argv[0]); - if (target == NULL) return NULL; - - char** srcs = ReadVarArgs(state, argc-1, argv+1); - if (srcs == NULL) { - free(target); - return NULL; + 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(""); } - int bad = 0; - int i; - for (i = 0; i < argc-1; ++i) { - if (unlink(srcs[i]) < 0) { - if (errno != ENOENT) { - printf("%s: failed to remove %s: %s\n", - name, srcs[i], strerror(errno)); - ++bad; - } - } - if (make_parents(srcs[i])) { - printf("%s: failed to symlink %s to %s: making parents failed\n", - name, srcs[i], target); - ++bad; - } - if (symlink(target, srcs[i]) < 0) { - printf("%s: failed to symlink %s to %s: %s\n", - name, srcs[i], target, strerror(errno)); - ++bad; - } - free(srcs[i]); + 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; } - free(srcs); - if (bad) { - return ErrorAbort(state, kSymlinkFailure, "%s: some symlinks failed", name); + 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(strdup("")); -} -struct perm_parsed_args { - bool has_uid; - uid_t uid; - bool has_gid; - gid_t gid; - bool has_mode; - mode_t mode; - bool has_fmode; - mode_t fmode; - bool has_dmode; - mode_t dmode; - bool has_selabel; - char* selabel; - bool has_capabilities; - uint64_t capabilities; -}; + return StringValue(success ? "t" : ""); + } else { + // The one-argument version returns the contents of the file as the result. -static struct perm_parsed_args ParsePermArgs(State * state, int argc, char** args) { - int i; - struct perm_parsed_args parsed; - int bad = 0; - static int max_warnings = 20; - - memset(&parsed, 0, sizeof(parsed)); - - for (i = 1; i < argc; i += 2) { - if (strcmp("uid", args[i]) == 0) { - int64_t uid; - if (sscanf(args[i+1], "%" SCNd64, &uid) == 1) { - parsed.uid = uid; - parsed.has_uid = true; - } else { - uiPrintf(state, "ParsePermArgs: invalid UID \"%s\"\n", args[i + 1]); - bad++; - } - continue; - } - if (strcmp("gid", args[i]) == 0) { - int64_t gid; - if (sscanf(args[i+1], "%" SCNd64, &gid) == 1) { - parsed.gid = gid; - parsed.has_gid = true; - } else { - uiPrintf(state, "ParsePermArgs: invalid GID \"%s\"\n", args[i + 1]); - bad++; - } - continue; - } - if (strcmp("mode", args[i]) == 0) { - int32_t mode; - if (sscanf(args[i+1], "%" SCNi32, &mode) == 1) { - parsed.mode = mode; - parsed.has_mode = true; - } else { - uiPrintf(state, "ParsePermArgs: invalid mode \"%s\"\n", args[i + 1]); - bad++; - } - continue; - } - if (strcmp("dmode", args[i]) == 0) { - int32_t mode; - if (sscanf(args[i+1], "%" SCNi32, &mode) == 1) { - parsed.dmode = mode; - parsed.has_dmode = true; - } else { - uiPrintf(state, "ParsePermArgs: invalid dmode \"%s\"\n", args[i + 1]); - bad++; - } - continue; - } - if (strcmp("fmode", args[i]) == 0) { - int32_t mode; - if (sscanf(args[i+1], "%" SCNi32, &mode) == 1) { - parsed.fmode = mode; - parsed.has_fmode = true; - } else { - uiPrintf(state, "ParsePermArgs: invalid fmode \"%s\"\n", args[i + 1]); - bad++; - } - continue; - } - if (strcmp("capabilities", args[i]) == 0) { - int64_t capabilities; - if (sscanf(args[i+1], "%" SCNi64, &capabilities) == 1) { - parsed.capabilities = capabilities; - parsed.has_capabilities = true; - } else { - uiPrintf(state, "ParsePermArgs: invalid capabilities \"%s\"\n", args[i + 1]); - bad++; - } - continue; - } - if (strcmp("selabel", args[i]) == 0) { - if (args[i+1][0] != '\0') { - parsed.selabel = args[i+1]; - parsed.has_selabel = true; - } else { - uiPrintf(state, "ParsePermArgs: invalid selabel \"%s\"\n", args[i + 1]); - bad++; - } - continue; - } - if (max_warnings != 0) { - printf("ParsedPermArgs: unknown key \"%s\", ignoring\n", args[i]); - max_warnings--; - if (max_warnings == 0) { - printf("ParsedPermArgs: suppressing further warnings\n"); - } - } + std::vector<std::string> args; + if (!ReadArgs(state, 1, argv, &args)) { + return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse %d args", name, argc); } - return parsed; -} + const std::string& zip_path = args[0]; -static int ApplyParsedPerms( - State * state, - const char* filename, - const struct stat *statptr, - struct perm_parsed_args parsed) -{ - int bad = 0; - - if (parsed.has_selabel) { - if (lsetfilecon(filename, parsed.selabel) != 0) { - uiPrintf(state, "ApplyParsedPerms: lsetfilecon of %s to %s failed: %s\n", - filename, parsed.selabel, strerror(errno)); - bad++; - } + 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()); } - /* ignore symlinks */ - if (S_ISLNK(statptr->st_mode)) { - return bad; - } + std::string buffer; + buffer.resize(entry.uncompressed_length); - if (parsed.has_uid) { - if (chown(filename, parsed.uid, -1) < 0) { - uiPrintf(state, "ApplyParsedPerms: chown of %s to %d failed: %s\n", - filename, parsed.uid, strerror(errno)); - bad++; - } + 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)); } - if (parsed.has_gid) { - if (chown(filename, -1, parsed.gid) < 0) { - uiPrintf(state, "ApplyParsedPerms: chgrp of %s to %d failed: %s\n", - filename, parsed.gid, strerror(errno)); - bad++; - } - } + return new Value(VAL_BLOB, buffer); + } +} - if (parsed.has_mode) { - if (chmod(filename, parsed.mode) < 0) { - uiPrintf(state, "ApplyParsedPerms: chmod of %s to %d failed: %s\n", - filename, parsed.mode, strerror(errno)); - bad++; - } - } +// symlink(target, [src1, src2, ...]) +// Creates all sources as symlinks to target. It unlinks any previously existing src1, src2, etc +// before creating symlinks. +Value* SymlinkFn(const char* name, State* state, int argc, Expr* argv[]) { + if (argc == 0) { + return ErrorAbort(state, kArgsParsingFailure, "%s() expects 1+ args, got %d", name, argc); + } + std::string target; + if (!Evaluate(state, argv[0], &target)) { + return nullptr; + } + + std::vector<std::string> srcs; + if (!ReadArgs(state, argc - 1, argv + 1, &srcs)) { + return ErrorAbort(state, kArgsParsingFailure, "%s(): Failed to parse the argument(s)", name); + } + + size_t bad = 0; + for (const auto& src : srcs) { + if (unlink(src.c_str()) == -1 && errno != ENOENT) { + PLOG(ERROR) << name << ": failed to remove " << src; + ++bad; + } else if (!make_parents(src)) { + LOG(ERROR) << name << ": failed to symlink " << src << " to " << target + << ": making parents failed"; + ++bad; + } else if (symlink(target.c_str(), src.c_str()) == -1) { + PLOG(ERROR) << name << ": failed to symlink " << src << " to " << target; + ++bad; + } + } + if (bad != 0) { + return ErrorAbort(state, kSymlinkFailure, "%s: Failed to create %zu symlink(s)", name, bad); + } + return StringValue("t"); +} - if (parsed.has_dmode && S_ISDIR(statptr->st_mode)) { - if (chmod(filename, parsed.dmode) < 0) { - uiPrintf(state, "ApplyParsedPerms: chmod of %s to %d failed: %s\n", - filename, parsed.dmode, strerror(errno)); - bad++; - } - } +struct perm_parsed_args { + bool has_uid; + uid_t uid; + bool has_gid; + gid_t gid; + bool has_mode; + mode_t mode; + bool has_fmode; + mode_t fmode; + bool has_dmode; + mode_t dmode; + bool has_selabel; + const char* selabel; + bool has_capabilities; + uint64_t capabilities; +}; - if (parsed.has_fmode && S_ISREG(statptr->st_mode)) { - if (chmod(filename, parsed.fmode) < 0) { - uiPrintf(state, "ApplyParsedPerms: chmod of %s to %d failed: %s\n", - filename, parsed.fmode, strerror(errno)); - bad++; - } - } +static struct perm_parsed_args ParsePermArgs(State * state, int argc, + const std::vector<std::string>& args) { + struct perm_parsed_args parsed; + int bad = 0; + static int max_warnings = 20; + + memset(&parsed, 0, sizeof(parsed)); + + for (int i = 1; i < argc; i += 2) { + if (args[i] == "uid") { + int64_t uid; + if (sscanf(args[i + 1].c_str(), "%" SCNd64, &uid) == 1) { + parsed.uid = uid; + parsed.has_uid = true; + } else { + uiPrintf(state, "ParsePermArgs: invalid UID \"%s\"\n", args[i + 1].c_str()); + bad++; + } + continue; + } + if (args[i] == "gid") { + int64_t gid; + if (sscanf(args[i + 1].c_str(), "%" SCNd64, &gid) == 1) { + parsed.gid = gid; + parsed.has_gid = true; + } else { + uiPrintf(state, "ParsePermArgs: invalid GID \"%s\"\n", args[i + 1].c_str()); + bad++; + } + continue; + } + if (args[i] == "mode") { + int32_t mode; + if (sscanf(args[i + 1].c_str(), "%" SCNi32, &mode) == 1) { + parsed.mode = mode; + parsed.has_mode = true; + } else { + uiPrintf(state, "ParsePermArgs: invalid mode \"%s\"\n", args[i + 1].c_str()); + bad++; + } + continue; + } + if (args[i] == "dmode") { + int32_t mode; + if (sscanf(args[i + 1].c_str(), "%" SCNi32, &mode) == 1) { + parsed.dmode = mode; + parsed.has_dmode = true; + } else { + uiPrintf(state, "ParsePermArgs: invalid dmode \"%s\"\n", args[i + 1].c_str()); + bad++; + } + continue; + } + if (args[i] == "fmode") { + int32_t mode; + if (sscanf(args[i + 1].c_str(), "%" SCNi32, &mode) == 1) { + parsed.fmode = mode; + parsed.has_fmode = true; + } else { + uiPrintf(state, "ParsePermArgs: invalid fmode \"%s\"\n", args[i + 1].c_str()); + bad++; + } + continue; + } + if (args[i] == "capabilities") { + int64_t capabilities; + if (sscanf(args[i + 1].c_str(), "%" SCNi64, &capabilities) == 1) { + parsed.capabilities = capabilities; + parsed.has_capabilities = true; + } else { + uiPrintf(state, "ParsePermArgs: invalid capabilities \"%s\"\n", args[i + 1].c_str()); + bad++; + } + continue; + } + if (args[i] == "selabel") { + if (!args[i + 1].empty()) { + parsed.selabel = args[i + 1].c_str(); + parsed.has_selabel = true; + } else { + uiPrintf(state, "ParsePermArgs: invalid selabel \"%s\"\n", args[i + 1].c_str()); + bad++; + } + continue; + } + if (max_warnings != 0) { + printf("ParsedPermArgs: unknown key \"%s\", ignoring\n", args[i].c_str()); + max_warnings--; + if (max_warnings == 0) { + LOG(INFO) << "ParsedPermArgs: suppressing further warnings"; + } + } + } + return parsed; +} - if (parsed.has_capabilities && S_ISREG(statptr->st_mode)) { - if (parsed.capabilities == 0) { - if ((removexattr(filename, XATTR_NAME_CAPS) == -1) && (errno != ENODATA)) { - // Report failure unless it's ENODATA (attribute not set) - uiPrintf(state, "ApplyParsedPerms: removexattr of %s to %" PRIx64 " failed: %s\n", - filename, parsed.capabilities, strerror(errno)); - bad++; - } - } else { - struct vfs_cap_data cap_data; - memset(&cap_data, 0, sizeof(cap_data)); - cap_data.magic_etc = VFS_CAP_REVISION | VFS_CAP_FLAGS_EFFECTIVE; - cap_data.data[0].permitted = (uint32_t) (parsed.capabilities & 0xffffffff); - cap_data.data[0].inheritable = 0; - cap_data.data[1].permitted = (uint32_t) (parsed.capabilities >> 32); - cap_data.data[1].inheritable = 0; - if (setxattr(filename, XATTR_NAME_CAPS, &cap_data, sizeof(cap_data), 0) < 0) { - uiPrintf(state, "ApplyParsedPerms: setcap of %s to %" PRIx64 " failed: %s\n", - filename, parsed.capabilities, strerror(errno)); - bad++; - } - } +static int ApplyParsedPerms(State* state, const char* filename, const struct stat* statptr, + struct perm_parsed_args parsed) { + int bad = 0; + + if (parsed.has_selabel) { + if (lsetfilecon(filename, parsed.selabel) != 0) { + uiPrintf(state, "ApplyParsedPerms: lsetfilecon of %s to %s failed: %s\n", filename, + parsed.selabel, strerror(errno)); + bad++; } + } + /* ignore symlinks */ + if (S_ISLNK(statptr->st_mode)) { return bad; + } + + if (parsed.has_uid) { + if (chown(filename, parsed.uid, -1) < 0) { + uiPrintf(state, "ApplyParsedPerms: chown of %s to %d failed: %s\n", filename, parsed.uid, + strerror(errno)); + bad++; + } + } + + if (parsed.has_gid) { + if (chown(filename, -1, parsed.gid) < 0) { + uiPrintf(state, "ApplyParsedPerms: chgrp of %s to %d failed: %s\n", filename, parsed.gid, + strerror(errno)); + bad++; + } + } + + if (parsed.has_mode) { + if (chmod(filename, parsed.mode) < 0) { + uiPrintf(state, "ApplyParsedPerms: chmod of %s to %d failed: %s\n", filename, parsed.mode, + strerror(errno)); + bad++; + } + } + + if (parsed.has_dmode && S_ISDIR(statptr->st_mode)) { + if (chmod(filename, parsed.dmode) < 0) { + uiPrintf(state, "ApplyParsedPerms: chmod of %s to %d failed: %s\n", filename, parsed.dmode, + strerror(errno)); + bad++; + } + } + + if (parsed.has_fmode && S_ISREG(statptr->st_mode)) { + if (chmod(filename, parsed.fmode) < 0) { + uiPrintf(state, "ApplyParsedPerms: chmod of %s to %d failed: %s\n", filename, parsed.fmode, + strerror(errno)); + bad++; + } + } + + if (parsed.has_capabilities && S_ISREG(statptr->st_mode)) { + if (parsed.capabilities == 0) { + if ((removexattr(filename, XATTR_NAME_CAPS) == -1) && (errno != ENODATA)) { + // Report failure unless it's ENODATA (attribute not set) + uiPrintf(state, "ApplyParsedPerms: removexattr of %s to %" PRIx64 " failed: %s\n", filename, + parsed.capabilities, strerror(errno)); + bad++; + } + } else { + struct vfs_cap_data cap_data; + memset(&cap_data, 0, sizeof(cap_data)); + cap_data.magic_etc = VFS_CAP_REVISION | VFS_CAP_FLAGS_EFFECTIVE; + cap_data.data[0].permitted = (uint32_t)(parsed.capabilities & 0xffffffff); + cap_data.data[0].inheritable = 0; + cap_data.data[1].permitted = (uint32_t)(parsed.capabilities >> 32); + cap_data.data[1].inheritable = 0; + if (setxattr(filename, XATTR_NAME_CAPS, &cap_data, sizeof(cap_data), 0) < 0) { + uiPrintf(state, "ApplyParsedPerms: setcap of %s to %" PRIx64 " failed: %s\n", filename, + parsed.capabilities, strerror(errno)); + bad++; + } + } + } + + return bad; } // nftw doesn't allow us to pass along context, so we need to use @@ -895,454 +797,296 @@ static int ApplyParsedPerms( static struct perm_parsed_args recursive_parsed_args; static State* recursive_state; -static int do_SetMetadataRecursive(const char* filename, const struct stat *statptr, - int fileflags, struct FTW *pfwt) { - return ApplyParsedPerms(recursive_state, filename, statptr, recursive_parsed_args); +static int do_SetMetadataRecursive(const char* filename, const struct stat* statptr, int fileflags, + struct FTW* pfwt) { + return ApplyParsedPerms(recursive_state, filename, statptr, recursive_parsed_args); } static Value* SetMetadataFn(const char* name, State* state, int argc, Expr* argv[]) { - int bad = 0; - struct stat sb; - Value* result = NULL; - - bool recursive = (strcmp(name, "set_metadata_recursive") == 0); - - if ((argc % 2) != 1) { - return ErrorAbort(state, kArgsParsingFailure, - "%s() expects an odd number of arguments, got %d", name, argc); - } - - char** args = ReadVarArgs(state, argc, argv); - if (args == NULL) return NULL; - - if (lstat(args[0], &sb) == -1) { - result = ErrorAbort(state, kSetMetadataFailure, "%s: Error on lstat of \"%s\": %s", - name, args[0], strerror(errno)); - goto done; - } - - { - struct perm_parsed_args parsed = ParsePermArgs(state, argc, args); - - if (recursive) { - recursive_parsed_args = parsed; - recursive_state = state; - bad += nftw(args[0], do_SetMetadataRecursive, 30, FTW_CHDIR | FTW_DEPTH | FTW_PHYS); - memset(&recursive_parsed_args, 0, sizeof(recursive_parsed_args)); - recursive_state = NULL; - } else { - bad += ApplyParsedPerms(state, args[0], &sb, parsed); - } - } - -done: - for (int i = 0; i < argc; ++i) { - free(args[i]); - } - free(args); - - if (result != NULL) { - return result; - } - - if (bad > 0) { - return ErrorAbort(state, kSetMetadataFailure, "%s: some changes failed", name); - } - - return StringValue(strdup("")); + if ((argc % 2) != 1) { + return ErrorAbort(state, kArgsParsingFailure, "%s() expects an odd number of arguments, got %d", + name, argc); + } + + std::vector<std::string> args; + if (!ReadArgs(state, argc, argv, &args)) { + return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse the argument(s)", name); + } + + struct stat sb; + if (lstat(args[0].c_str(), &sb) == -1) { + return ErrorAbort(state, kSetMetadataFailure, "%s: Error on lstat of \"%s\": %s", name, + args[0].c_str(), strerror(errno)); + } + + struct perm_parsed_args parsed = ParsePermArgs(state, argc, args); + int bad = 0; + bool recursive = (strcmp(name, "set_metadata_recursive") == 0); + + if (recursive) { + recursive_parsed_args = parsed; + recursive_state = state; + bad += nftw(args[0].c_str(), do_SetMetadataRecursive, 30, FTW_CHDIR | FTW_DEPTH | FTW_PHYS); + memset(&recursive_parsed_args, 0, sizeof(recursive_parsed_args)); + recursive_state = NULL; + } else { + bad += ApplyParsedPerms(state, args[0].c_str(), &sb, parsed); + } + + if (bad > 0) { + return ErrorAbort(state, kSetMetadataFailure, "%s: some changes failed", name); + } + + return StringValue(""); } Value* GetPropFn(const char* name, State* state, int argc, Expr* argv[]) { - if (argc != 1) { - return ErrorAbort(state, kArgsParsingFailure, "%s() expects 1 arg, got %d", name, argc); - } - char* key = Evaluate(state, argv[0]); - if (key == NULL) return NULL; - - char value[PROPERTY_VALUE_MAX]; - property_get(key, value, ""); - free(key); - - return StringValue(strdup(value)); + if (argc != 1) { + return ErrorAbort(state, kArgsParsingFailure, "%s() expects 1 arg, got %d", name, argc); + } + std::string key; + if (!Evaluate(state, argv[0], &key)) { + return nullptr; + } + std::string value = android::base::GetProperty(key, ""); + + return StringValue(value); } - // file_getprop(file, key) // // interprets 'file' as a getprop-style file (key=value pairs, one -// per line. # comment lines,blank lines, lines without '=' ignored), +// 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, int argc, Expr* argv[]) { - char* result = NULL; - char* buffer = NULL; - char* filename; - char* key; - if (ReadArgs(state, argv, 2, &filename, &key) < 0) { - return NULL; - } - - struct stat st; - if (stat(filename, &st) < 0) { - ErrorAbort(state, kFileGetPropFailure, "%s: failed to stat \"%s\": %s", name, filename, - strerror(errno)); - goto done; - } - -#define MAX_FILE_GETPROP_SIZE 65536 - - if (st.st_size > MAX_FILE_GETPROP_SIZE) { - ErrorAbort(state, kFileGetPropFailure, "%s too large for %s (max %d)", filename, name, - MAX_FILE_GETPROP_SIZE); - goto done; - } - - buffer = reinterpret_cast<char*>(malloc(st.st_size+1)); - if (buffer == NULL) { - ErrorAbort(state, kFileGetPropFailure, "%s: failed to alloc %lld bytes", name, - (long long)st.st_size+1); - goto done; - } - - FILE* f; - f = fopen(filename, "rb"); - if (f == NULL) { - ErrorAbort(state, kFileOpenFailure, "%s: failed to open %s: %s", name, filename, - strerror(errno)); - goto done; - } - - if (ota_fread(buffer, 1, st.st_size, f) != static_cast<size_t>(st.st_size)) { - ErrorAbort(state, kFreadFailure, "%s: failed to read %lld bytes from %s", - name, (long long)st.st_size+1, filename); - fclose(f); - goto done; - } - buffer[st.st_size] = '\0'; - - fclose(f); - - char* line; - line = strtok(buffer, "\n"); - do { - // skip whitespace at start of line - while (*line && isspace(*line)) ++line; - - // comment or blank line: skip to next line - if (*line == '\0' || *line == '#') continue; - - char* equal = strchr(line, '='); - if (equal == NULL) { - continue; - } - - // trim whitespace between key and '=' - char* key_end = equal-1; - while (key_end > line && isspace(*key_end)) --key_end; - key_end[1] = '\0'; - - // not the key we're looking for - if (strcmp(key, line) != 0) continue; - - // skip whitespace after the '=' to the start of the value - char* val_start = equal+1; - while(*val_start && isspace(*val_start)) ++val_start; - - // trim trailing whitespace - char* val_end = val_start + strlen(val_start)-1; - while (val_end > val_start && isspace(*val_end)) --val_end; - val_end[1] = '\0'; - - result = strdup(val_start); - break; - - } while ((line = strtok(NULL, "\n"))); - - if (result == NULL) result = strdup(""); - - done: - free(filename); - free(key); - free(buffer); - return StringValue(result); -} - -// write_raw_image(filename_or_blob, partition) -Value* WriteRawImageFn(const char* name, State* state, int argc, Expr* argv[]) { - char* result = NULL; - - Value* partition_value; - Value* contents; - if (ReadValueArgs(state, argv, 2, &contents, &partition_value) < 0) { - return NULL; - } - - char* partition = NULL; - if (partition_value->type != VAL_STRING) { - ErrorAbort(state, kArgsParsingFailure, "partition argument to %s must be string", name); - goto done; - } - partition = partition_value->data; - if (strlen(partition) == 0) { - ErrorAbort(state, kArgsParsingFailure, "partition argument to %s can't be empty", name); - goto done; - } - if (contents->type == VAL_STRING && strlen((char*) contents->data) == 0) { - ErrorAbort(state, kArgsParsingFailure, "file argument to %s can't be empty", name); - goto done; - } - - mtd_scan_partitions(); - const MtdPartition* mtd; - mtd = mtd_find_partition_by_name(partition); - if (mtd == NULL) { - printf("%s: no mtd partition named \"%s\"\n", name, partition); - result = strdup(""); - goto done; - } - - MtdWriteContext* ctx; - ctx = mtd_write_partition(mtd); - if (ctx == NULL) { - printf("%s: can't write mtd partition \"%s\"\n", - name, partition); - result = strdup(""); - goto done; - } - - bool success; - - if (contents->type == VAL_STRING) { - // we're given a filename as the contents - char* filename = contents->data; - FILE* f = ota_fopen(filename, "rb"); - if (f == NULL) { - printf("%s: can't open %s: %s\n", name, filename, strerror(errno)); - result = strdup(""); - goto done; - } - - success = true; - char* buffer = reinterpret_cast<char*>(malloc(BUFSIZ)); - int read; - while (success && (read = ota_fread(buffer, 1, BUFSIZ, f)) > 0) { - int wrote = mtd_write_data(ctx, buffer, read); - success = success && (wrote == read); - } - free(buffer); - ota_fclose(f); - } else { - // we're given a blob as the contents - ssize_t wrote = mtd_write_data(ctx, contents->data, contents->size); - success = (wrote == contents->size); - } - if (!success) { - printf("mtd_write_data to %s failed: %s\n", - partition, strerror(errno)); - } - - if (mtd_erase_blocks(ctx, -1) == -1) { - printf("%s: error erasing blocks of %s\n", name, partition); - } - if (mtd_write_close(ctx) != 0) { - printf("%s: error closing write of %s\n", name, partition); - } - - printf("%s %s partition\n", - success ? "wrote" : "failed to write", partition); - - result = success ? partition : strdup(""); - -done: - if (result != partition) FreeValue(partition_value); - FreeValue(contents); - return StringValue(result); + if (argc != 2) { + return ErrorAbort(state, kArgsParsingFailure, "%s() expects 2 args, got %d", name, argc); + } + + std::vector<std::string> args; + if (!ReadArgs(state, 2, argv, &args)) { + return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse the argument(s)", name); + } + const std::string& filename = args[0]; + const std::string& key = args[1]; + + struct stat st; + if (stat(filename.c_str(), &st) < 0) { + return ErrorAbort(state, kFileGetPropFailure, "%s: failed to stat \"%s\": %s", name, + filename.c_str(), strerror(errno)); + } + + constexpr off_t MAX_FILE_GETPROP_SIZE = 65536; + if (st.st_size > MAX_FILE_GETPROP_SIZE) { + return ErrorAbort(state, kFileGetPropFailure, "%s too large for %s (max %lld)", + filename.c_str(), name, static_cast<long long>(MAX_FILE_GETPROP_SIZE)); + } + + std::string buffer(st.st_size, '\0'); + unique_file f(ota_fopen(filename.c_str(), "rb")); + if (f == nullptr) { + return ErrorAbort(state, kFileOpenFailure, "%s: failed to open %s: %s", name, filename.c_str(), + strerror(errno)); + } + + if (ota_fread(&buffer[0], 1, st.st_size, f.get()) != static_cast<size_t>(st.st_size)) { + ErrorAbort(state, kFreadFailure, "%s: failed to read %zu bytes from %s", name, + static_cast<size_t>(st.st_size), filename.c_str()); + return nullptr; + } + + ota_fclose(f); + + std::vector<std::string> lines = android::base::Split(buffer, "\n"); + for (size_t i = 0; i < lines.size(); i++) { + std::string line = android::base::Trim(lines[i]); + + // comment or blank line: skip to next line + if (line.empty() || line[0] == '#') { + continue; + } + size_t equal_pos = line.find('='); + if (equal_pos == std::string::npos) { + continue; + } + + // trim whitespace between key and '=' + std::string str = android::base::Trim(line.substr(0, equal_pos)); + + // not the key we're looking for + if (key != str) continue; + + return StringValue(android::base::Trim(line.substr(equal_pos + 1))); + } + + return StringValue(""); } // apply_patch_space(bytes) -Value* ApplyPatchSpaceFn(const char* name, State* state, - int argc, Expr* argv[]) { - char* bytes_str; - if (ReadArgs(state, argv, 1, &bytes_str) < 0) { - return NULL; - } - - size_t bytes; - if (!android::base::ParseUint(bytes_str, &bytes)) { - ErrorAbort(state, kArgsParsingFailure, "%s(): can't parse \"%s\" as byte count\n\n", - name, bytes_str); - free(bytes_str); - return nullptr; - } - - return StringValue(strdup(CacheSizeCheck(bytes) ? "" : "t")); +Value* ApplyPatchSpaceFn(const char* name, State* state, int argc, Expr* argv[]) { + std::vector<std::string> args; + if (!ReadArgs(state, 1, argv, &args)) { + return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse the argument(s)", name); + } + const std::string& bytes_str = args[0]; + + 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(file, size, init_sha1, tgt_sha1, patch) - +// 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, int argc, Expr* argv[]) { if (argc < 6 || (argc % 2) == 1) { return ErrorAbort(state, kArgsParsingFailure, "%s(): expected at least 6 args and an " - "even number, got %d", name, argc); + "even number, got %d", name, argc); } - char* source_filename; - char* target_filename; - char* target_sha1; - char* target_size_str; - if (ReadArgs(state, argv, 4, &source_filename, &target_filename, - &target_sha1, &target_size_str) < 0) { - return NULL; + std::vector<std::string> args; + if (!ReadArgs(state, 4, argv, &args)) { + 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, &target_size)) { - ErrorAbort(state, kArgsParsingFailure, "%s(): can't parse \"%s\" as byte count", - name, target_size_str); - free(source_filename); - free(target_filename); - free(target_sha1); - free(target_size_str); - return nullptr; + 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 = (argc-4) / 2; - std::unique_ptr<Value*, decltype(&free)> arg_values(ReadValueVarArgs(state, argc-4, argv+4), - free); - if (!arg_values) { + std::vector<std::unique_ptr<Value>> arg_values; + if (!ReadValueArgs(state, argc-4, argv+4, &arg_values)) { return nullptr; } - std::vector<std::unique_ptr<Value, decltype(&FreeValue)>> patch_shas; - std::vector<std::unique_ptr<Value, decltype(&FreeValue)>> patches; - // Protect values by unique_ptrs first to get rid of memory leak. - for (int i = 0; i < patchcount * 2; i += 2) { - patch_shas.emplace_back(arg_values.get()[i], FreeValue); - patches.emplace_back(arg_values.get()[i+1], FreeValue); - } for (int i = 0; i < patchcount; ++i) { - if (patch_shas[i]->type != VAL_STRING) { - ErrorAbort(state, kArgsParsingFailure, "%s(): sha-1 #%d is not string", name, i); - return nullptr; + if (arg_values[i * 2]->type != VAL_STRING) { + return ErrorAbort(state, kArgsParsingFailure, "%s(): sha-1 #%d is not string", name, + i * 2); } - if (patches[i]->type != VAL_BLOB) { - ErrorAbort(state, kArgsParsingFailure, "%s(): patch #%d is not blob", name, i); - return nullptr; + 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<char*> patch_sha_str; - std::vector<Value*> patch_ptrs; + 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(patch_shas[i]->data); - patch_ptrs.push_back(patches[i].get()); + 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, target_filename, - target_sha1, target_size, - patchcount, patch_sha_str.data(), patch_ptrs.data(), NULL); + int result = applypatch(source_filename.c_str(), target_filename.c_str(), + target_sha1.c_str(), target_size, + patch_sha_str, patches, nullptr); - return StringValue(strdup(result == 0 ? "t" : "")); + return StringValue(result == 0 ? "t" : ""); } -// apply_patch_check(file, [sha1_1, ...]) -Value* ApplyPatchCheckFn(const char* name, State* state, - int argc, Expr* argv[]) { - if (argc < 1) { - return ErrorAbort(state, kArgsParsingFailure, "%s(): expected at least 1 arg, got %d", - name, argc); - } - - char* filename; - if (ReadArgs(state, argv, 1, &filename) < 0) { - return NULL; - } - - int patchcount = argc-1; - char** sha1s = ReadVarArgs(state, argc-1, argv+1); - - int result = applypatch_check(filename, patchcount, sha1s); - - int i; - for (i = 0; i < patchcount; ++i) { - free(sha1s[i]); - } - free(sha1s); - - return StringValue(strdup(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, int argc, Expr* argv[]) { + if (argc < 1) { + return ErrorAbort(state, kArgsParsingFailure, "%s(): expected at least 1 arg, got %d", name, + argc); + } + + std::vector<std::string> args; + if (!ReadArgs(state, 1, argv, &args)) { + return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse the argument(s)", name); + } + const std::string& filename = args[0]; + + std::vector<std::string> sha1s; + if (!ReadArgs(state, argc - 1, argv + 1, &sha1s)) { + return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse the argument(s)", name); + } + 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, int argc, Expr* argv[]) { - char** args = ReadVarArgs(state, argc, argv); - if (args == NULL) { - return NULL; - } - - std::string buffer; - for (int i = 0; i < argc; ++i) { - buffer += args[i]; - free(args[i]); - } - free(args); - - buffer += "\n"; - uiPrint(state, buffer); - return StringValue(strdup(buffer.c_str())); + std::vector<std::string> args; + if (!ReadArgs(state, argc, argv, &args)) { + return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse the argument(s)", name); + } + + std::string buffer = android::base::Join(args, "") + "\n"; + uiPrint(state, buffer); + return StringValue(buffer); } Value* WipeCacheFn(const char* name, State* state, int argc, Expr* argv[]) { - if (argc != 0) { - return ErrorAbort(state, kArgsParsingFailure, "%s() expects no args, got %d", name, argc); - } - fprintf(((UpdaterInfo*)(state->cookie))->cmd_pipe, "wipe_cache\n"); - return StringValue(strdup("t")); + if (argc != 0) { + return ErrorAbort(state, kArgsParsingFailure, "%s() expects no args, got %d", name, argc); + } + fprintf(static_cast<UpdaterInfo*>(state->cookie)->cmd_pipe, "wipe_cache\n"); + return StringValue("t"); } Value* RunProgramFn(const char* name, State* state, int argc, Expr* argv[]) { - if (argc < 1) { - return ErrorAbort(state, kArgsParsingFailure, "%s() expects at least 1 arg", name); - } - char** args = ReadVarArgs(state, argc, argv); - if (args == NULL) { - return NULL; - } - - char** args2 = reinterpret_cast<char**>(malloc(sizeof(char*) * (argc+1))); - memcpy(args2, args, sizeof(char*) * argc); - args2[argc] = NULL; - - printf("about to run program [%s] with %d args\n", args2[0], argc); - - pid_t child = fork(); - if (child == 0) { - execv(args2[0], args2); - printf("run_program: execv failed: %s\n", strerror(errno)); - _exit(1); - } - int status; - waitpid(child, &status, 0); - if (WIFEXITED(status)) { - if (WEXITSTATUS(status) != 0) { - printf("run_program: child exited with status %d\n", - WEXITSTATUS(status)); - } - } else if (WIFSIGNALED(status)) { - printf("run_program: child terminated by signal %d\n", - WTERMSIG(status)); - } - - int i; - for (i = 0; i < argc; ++i) { - free(args[i]); - } - free(args); - free(args2); - - char buffer[20]; - sprintf(buffer, "%d", status); - - return StringValue(strdup(buffer)); + if (argc < 1) { + return ErrorAbort(state, kArgsParsingFailure, "%s() expects at least 1 arg", name); + } + + std::vector<std::string> args; + if (!ReadArgs(state, argc, argv, &args)) { + return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse the argument(s)", name); + } + + char* args2[argc + 1]; + for (int i = 0; i < argc; i++) { + args2[i] = &args[i][0]; + } + args2[argc] = nullptr; + + LOG(INFO) << "about to run program [" << args2[0] << "] with " << argc << " args"; + + pid_t child = fork(); + if (child == 0) { + execv(args2[0], args2); + PLOG(ERROR) << "run_program: execv failed"; + _exit(1); + } + + int status; + waitpid(child, &status, 0); + if (WIFEXITED(status)) { + if (WEXITSTATUS(status) != 0) { + LOG(ERROR) << "run_program: child exited with status " << WEXITSTATUS(status); + } + } else if (WIFSIGNALED(status)) { + LOG(ERROR) << "run_program: child terminated by signal " << WTERMSIG(status); + } + + return StringValue(std::to_string(status)); } // sha1_check(data) @@ -1354,78 +1098,90 @@ Value* RunProgramFn(const char* name, State* state, int argc, Expr* argv[]) { // strings passed, or "" if it does not equal any of them. // Value* Sha1CheckFn(const char* name, State* state, int argc, Expr* argv[]) { - if (argc < 1) { - return ErrorAbort(state, kArgsParsingFailure, "%s() expects at least 1 arg", name); - } - - std::unique_ptr<Value*, decltype(&free)> arg_values(ReadValueVarArgs(state, argc, argv), free); - if (arg_values == nullptr) { - return nullptr; - } - std::vector<std::unique_ptr<Value, decltype(&FreeValue)>> args; - for (int i = 0; i < argc; ++i) { - args.emplace_back(arg_values.get()[i], FreeValue); - } - - if (args[0]->size < 0) { - return StringValue(strdup("")); - } - uint8_t digest[SHA_DIGEST_LENGTH]; - SHA1(reinterpret_cast<uint8_t*>(args[0]->data), args[0]->size, digest); - - if (argc == 1) { - return StringValue(PrintSha1(digest)); - } - - int i; + if (argc < 1) { + return ErrorAbort(state, kArgsParsingFailure, "%s() expects at least 1 arg", name); + } + + std::vector<std::unique_ptr<Value>> args; + if (!ReadValueArgs(state, argc, 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 (argc == 1) { + return StringValue(print_sha1(digest)); + } + + for (int i = 1; i < argc; ++i) { uint8_t arg_digest[SHA_DIGEST_LENGTH]; - for (i = 1; i < argc; ++i) { - if (args[i]->type != VAL_STRING) { - printf("%s(): arg %d is not a string; skipping", - name, i); - } else if (ParseSha1(args[i]->data, arg_digest) != 0) { - // Warn about bad args and skip them. - printf("%s(): error parsing \"%s\" as sha-1; skipping", - name, args[i]->data); - } else if (memcmp(digest, arg_digest, SHA_DIGEST_LENGTH) == 0) { - break; - } - } - if (i >= argc) { - // Didn't match any of the hex strings; return false. - return StringValue(strdup("")); - } - // Found a match. - return args[i].release(); + 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, int argc, Expr* argv[]) { - if (argc != 1) { - return ErrorAbort(state, kArgsParsingFailure, "%s() expects 1 arg, got %d", name, argc); - } - char* filename; - if (ReadArgs(state, argv, 1, &filename) < 0) return NULL; + if (argc != 1) { + return ErrorAbort(state, kArgsParsingFailure, "%s() expects 1 arg, got %d", name, argc); + } - Value* v = static_cast<Value*>(malloc(sizeof(Value))); - if (v == nullptr) { - return nullptr; - } + std::vector<std::string> args; + if (!ReadArgs(state, 1, argv, &args)) { + return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse the argument(s)", name); + } + const std::string& filename = args[0]; + + Value* v = new Value(VAL_INVALID, ""); + + FileContents fc; + if (LoadFileContents(filename.c_str(), &fc) == 0) { v->type = VAL_BLOB; - v->size = -1; - v->data = nullptr; - - FileContents fc; - if (LoadFileContents(filename, &fc) == 0) { - v->data = static_cast<char*>(malloc(fc.data.size())); - if (v->data != nullptr) { - memcpy(v->data, fc.data.data(), fc.data.size()); - v->size = fc.data.size(); - } - } - free(filename); - return v; + v->data = std::string(fc.data.begin(), fc.data.end()); + } + return v; +} + +// write_value(value, filename) +// Writes 'value' to 'filename'. +// Example: write_value("960000", "/sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq") +Value* WriteValueFn(const char* name, State* state, int argc, Expr* argv[]) { + if (argc != 2) { + return ErrorAbort(state, kArgsParsingFailure, "%s() expects 2 args, got %d", name, argc); + } + + std::vector<std::string> args; + if (!ReadArgs(state, 2, argv, &args)) { + return ErrorAbort(state, kArgsParsingFailure, "%s(): Failed to parse the argument(s)", name); + } + + const std::string& filename = args[1]; + if (filename.empty()) { + return ErrorAbort(state, kArgsParsingFailure, "%s(): Filename cannot be empty", name); + } + + const std::string& value = args[0]; + if (!android::base::WriteStringToFile(value, filename)) { + PLOG(ERROR) << name << ": Failed to write to \"" << filename << "\""; + return StringValue(""); + } else { + return StringValue("t"); + } } // Immediately reboot the device. Recovery is not finished normally, @@ -1438,35 +1194,35 @@ Value* ReadFileFn(const char* name, State* state, int argc, Expr* argv[]) { // partition, or "" (empty string) to boot from the regular boot // partition. Value* RebootNowFn(const char* name, State* state, int argc, Expr* argv[]) { - if (argc != 2) { - return ErrorAbort(state, kArgsParsingFailure, "%s() expects 2 args, got %d", name, argc); - } - - char* filename; - char* property; - if (ReadArgs(state, argv, 2, &filename, &property) < 0) return NULL; - - char buffer[80]; - - // zero out the 'command' field of the bootloader message. - memset(buffer, 0, sizeof(((struct bootloader_message*)0)->command)); - FILE* f = fopen(filename, "r+b"); - fseek(f, offsetof(struct bootloader_message, command), SEEK_SET); - ota_fwrite(buffer, sizeof(((struct bootloader_message*)0)->command), 1, f); - fclose(f); - free(filename); - - strcpy(buffer, "reboot,"); - if (property != NULL) { - strncat(buffer, property, sizeof(buffer)-10); - } - - property_set(ANDROID_RB_PROPERTY, buffer); - - sleep(5); - free(property); - ErrorAbort(state, kRebootFailure, "%s() failed to reboot", name); - return NULL; + if (argc != 2) { + return ErrorAbort(state, kArgsParsingFailure, "%s() expects 2 args, got %d", name, argc); + } + + std::vector<std::string> args; + if (!ReadArgs(state, 2, argv, &args)) { + return ErrorAbort(state, kArgsParsingFailure, "%s(): Failed to parse the argument(s)", name); + } + const std::string& filename = args[0]; + const std::string& property = args[1]; + + // Zero out the 'command' field of the bootloader message. Leave the rest intact. + bootloader_message boot; + std::string err; + if (!read_bootloader_message_from(&boot, filename, &err)) { + LOG(ERROR) << name << "(): Failed to read from \"" << filename << "\": " << err; + return StringValue(""); + } + memset(boot.command, 0, sizeof(boot.command)); + if (!write_bootloader_message_to(boot, filename, &err)) { + LOG(ERROR) << name << "(): Failed to write to \"" << filename << "\": " << err; + return StringValue(""); + } + + const std::string reboot_cmd = "reboot," + property; + android::base::SetProperty(ANDROID_RB_PROPERTY, reboot_cmd); + + sleep(5); + return ErrorAbort(state, kRebootFailure, "%s() failed to reboot", name); } // Store a string value somewhere that future invocations of recovery @@ -1480,164 +1236,171 @@ Value* RebootNowFn(const char* name, State* state, int argc, Expr* argv[]) { // second argument is the string to store; it should not exceed 31 // bytes. Value* SetStageFn(const char* name, State* state, int argc, Expr* argv[]) { - if (argc != 2) { - return ErrorAbort(state, kArgsParsingFailure, "%s() expects 2 args, got %d", name, argc); - } - - char* filename; - char* stagestr; - if (ReadArgs(state, argv, 2, &filename, &stagestr) < 0) return NULL; - - // Store this value in the misc partition, immediately after the - // bootloader message that the main recovery uses to save its - // arguments in case of the device restarting midway through - // package installation. - FILE* f = fopen(filename, "r+b"); - fseek(f, offsetof(struct bootloader_message, stage), SEEK_SET); - int to_write = strlen(stagestr)+1; - int max_size = sizeof(((struct bootloader_message*)0)->stage); - if (to_write > max_size) { - to_write = max_size; - stagestr[max_size-1] = 0; - } - ota_fwrite(stagestr, to_write, 1, f); - fclose(f); - - free(stagestr); - return StringValue(filename); + if (argc != 2) { + return ErrorAbort(state, kArgsParsingFailure, "%s() expects 2 args, got %d", name, argc); + } + + std::vector<std::string> args; + if (!ReadArgs(state, 2, argv, &args)) { + return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse the argument(s)", name); + } + const std::string& filename = args[0]; + const std::string& stagestr = args[1]; + + // Store this value in the misc partition, immediately after the + // bootloader message that the main recovery uses to save its + // arguments in case of the device restarting midway through + // package installation. + bootloader_message boot; + std::string err; + if (!read_bootloader_message_from(&boot, filename, &err)) { + LOG(ERROR) << name << "(): Failed to read from \"" << filename << "\": " << err; + return StringValue(""); + } + strlcpy(boot.stage, stagestr.c_str(), sizeof(boot.stage)); + if (!write_bootloader_message_to(boot, filename, &err)) { + LOG(ERROR) << name << "(): Failed to write to \"" << filename << "\": " << err; + return StringValue(""); + } + + return StringValue(filename); } // Return the value most recently saved with SetStageFn. The argument // is the block device for the misc partition. Value* GetStageFn(const char* name, State* state, int argc, Expr* argv[]) { - if (argc != 1) { - return ErrorAbort(state, kArgsParsingFailure, "%s() expects 1 arg, got %d", name, argc); - } - - char* filename; - if (ReadArgs(state, argv, 1, &filename) < 0) return NULL; - - char buffer[sizeof(((struct bootloader_message*)0)->stage)]; - FILE* f = fopen(filename, "rb"); - fseek(f, offsetof(struct bootloader_message, stage), SEEK_SET); - ota_fread(buffer, sizeof(buffer), 1, f); - fclose(f); - buffer[sizeof(buffer)-1] = '\0'; - - return StringValue(strdup(buffer)); + if (argc != 1) { + return ErrorAbort(state, kArgsParsingFailure, "%s() expects 1 arg, got %d", name, argc); + } + + std::vector<std::string> args; + if (!ReadArgs(state, 1, argv, &args)) { + return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse the argument(s)", name); + } + const std::string& filename = args[0]; + + bootloader_message boot; + std::string err; + if (!read_bootloader_message_from(&boot, filename, &err)) { + LOG(ERROR) << name << "(): Failed to read from \"" << filename << "\": " << err; + return StringValue(""); + } + + return StringValue(boot.stage); } Value* WipeBlockDeviceFn(const char* name, State* state, int argc, Expr* argv[]) { - if (argc != 2) { - return ErrorAbort(state, kArgsParsingFailure, "%s() expects 2 args, got %d", name, argc); - } - - char* filename; - char* len_str; - if (ReadArgs(state, argv, 2, &filename, &len_str) < 0) return NULL; - - size_t len; - android::base::ParseUint(len_str, &len); - int fd = ota_open(filename, O_WRONLY, 0644); - int success = wipe_block_device(fd, len); - - free(filename); - free(len_str); - - ota_close(fd); - - return StringValue(strdup(success ? "t" : "")); + if (argc != 2) { + return ErrorAbort(state, kArgsParsingFailure, "%s() expects 2 args, got %d", name, argc); + } + + std::vector<std::string> args; + if (!ReadArgs(state, 2, argv, &args)) { + return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse the argument(s)", name); + } + const std::string& filename = args[0]; + const std::string& len_str = args[1]; + + size_t len; + if (!android::base::ParseUint(len_str.c_str(), &len)) { + return nullptr; + } + unique_fd fd(ota_open(filename.c_str(), O_WRONLY, 0644)); + // The wipe_block_device function in ext4_utils returns 0 on success and 1 + // for failure. + int status = wipe_block_device(fd, len); + return StringValue((status == 0) ? "t" : ""); } Value* EnableRebootFn(const char* name, State* state, int argc, Expr* argv[]) { - if (argc != 0) { - return ErrorAbort(state, kArgsParsingFailure, "%s() expects no args, got %d", name, argc); - } - UpdaterInfo* ui = (UpdaterInfo*)(state->cookie); - fprintf(ui->cmd_pipe, "enable_reboot\n"); - return StringValue(strdup("t")); + if (argc != 0) { + return ErrorAbort(state, kArgsParsingFailure, "%s() expects no args, got %d", name, argc); + } + UpdaterInfo* ui = static_cast<UpdaterInfo*>(state->cookie); + fprintf(ui->cmd_pipe, "enable_reboot\n"); + return StringValue("t"); } Value* Tune2FsFn(const char* name, State* state, int argc, Expr* argv[]) { - if (argc == 0) { - return ErrorAbort(state, kArgsParsingFailure, "%s() expects args, got %d", name, argc); - } - - char** args = ReadVarArgs(state, argc, argv); - if (args == NULL) { - return ErrorAbort(state, kArgsParsingFailure, "%s() could not read args", name); - } - - char** args2 = reinterpret_cast<char**>(malloc(sizeof(char*) * (argc+1))); - // Tune2fs expects the program name as its args[0] - args2[0] = strdup(name); - for (int i = 0; i < argc; ++i) { - args2[i + 1] = args[i]; - } - int result = tune2fs_main(argc + 1, args2); - for (int i = 0; i < argc; ++i) { - free(args[i]); - } - free(args); - - free(args2[0]); - free(args2); - if (result != 0) { - return ErrorAbort(state, kTune2FsFailure, "%s() returned error code %d", - name, result); - } - return StringValue(strdup("t")); + if (argc == 0) { + return ErrorAbort(state, kArgsParsingFailure, "%s() expects args, got %d", name, argc); + } + + std::vector<std::string> args; + if (!ReadArgs(state, argc, argv, &args)) { + return ErrorAbort(state, kArgsParsingFailure, "%s() could not read args", name); + } + + char* args2[argc + 1]; + // Tune2fs expects the program name as its args[0] + args2[0] = const_cast<char*>(name); + if (args2[0] == nullptr) { + return nullptr; + } + for (int i = 0; i < argc; ++i) { + args2[i + 1] = &args[i][0]; + } + + // tune2fs changes the file system parameters on an ext2 file system; it + // returns 0 on success. + int result = tune2fs_main(argc + 1, args2); + if (result != 0) { + return ErrorAbort(state, kTune2FsFailure, "%s() returned error code %d", name, result); + } + return StringValue("t"); } void RegisterInstallFunctions() { - RegisterFunction("mount", MountFn); - RegisterFunction("is_mounted", IsMountedFn); - RegisterFunction("unmount", UnmountFn); - RegisterFunction("format", FormatFn); - RegisterFunction("show_progress", ShowProgressFn); - RegisterFunction("set_progress", SetProgressFn); - RegisterFunction("delete", DeleteFn); - RegisterFunction("delete_recursive", DeleteFn); - RegisterFunction("package_extract_dir", PackageExtractDirFn); - RegisterFunction("package_extract_file", PackageExtractFileFn); - RegisterFunction("symlink", SymlinkFn); - - // Usage: - // set_metadata("filename", "key1", "value1", "key2", "value2", ...) - // Example: - // set_metadata("/system/bin/netcfg", "uid", 0, "gid", 3003, "mode", 02750, "selabel", "u:object_r:system_file:s0", "capabilities", 0x0); - RegisterFunction("set_metadata", SetMetadataFn); - - // Usage: - // set_metadata_recursive("dirname", "key1", "value1", "key2", "value2", ...) - // Example: - // set_metadata_recursive("/system", "uid", 0, "gid", 0, "fmode", 0644, "dmode", 0755, "selabel", "u:object_r:system_file:s0", "capabilities", 0x0); - RegisterFunction("set_metadata_recursive", SetMetadataFn); - - RegisterFunction("getprop", GetPropFn); - RegisterFunction("file_getprop", FileGetPropFn); - RegisterFunction("write_raw_image", WriteRawImageFn); - - RegisterFunction("apply_patch", ApplyPatchFn); - RegisterFunction("apply_patch_check", ApplyPatchCheckFn); - RegisterFunction("apply_patch_space", ApplyPatchSpaceFn); - - RegisterFunction("wipe_block_device", WipeBlockDeviceFn); - - RegisterFunction("read_file", ReadFileFn); - RegisterFunction("sha1_check", Sha1CheckFn); - RegisterFunction("rename", RenameFn); - - RegisterFunction("wipe_cache", WipeCacheFn); - - RegisterFunction("ui_print", UIPrintFn); - - RegisterFunction("run_program", RunProgramFn); - - RegisterFunction("reboot_now", RebootNowFn); - RegisterFunction("get_stage", GetStageFn); - RegisterFunction("set_stage", SetStageFn); - - RegisterFunction("enable_reboot", EnableRebootFn); - RegisterFunction("tune2fs", Tune2FsFn); + RegisterFunction("mount", MountFn); + RegisterFunction("is_mounted", IsMountedFn); + RegisterFunction("unmount", UnmountFn); + RegisterFunction("format", FormatFn); + RegisterFunction("show_progress", ShowProgressFn); + RegisterFunction("set_progress", SetProgressFn); + RegisterFunction("delete", DeleteFn); + RegisterFunction("delete_recursive", DeleteFn); + RegisterFunction("package_extract_dir", PackageExtractDirFn); + RegisterFunction("package_extract_file", PackageExtractFileFn); + RegisterFunction("symlink", SymlinkFn); + + // Usage: + // set_metadata("filename", "key1", "value1", "key2", "value2", ...) + // Example: + // set_metadata("/system/bin/netcfg", "uid", 0, "gid", 3003, "mode", 02750, "selabel", + // "u:object_r:system_file:s0", "capabilities", 0x0); + RegisterFunction("set_metadata", SetMetadataFn); + + // Usage: + // set_metadata_recursive("dirname", "key1", "value1", "key2", "value2", ...) + // Example: + // set_metadata_recursive("/system", "uid", 0, "gid", 0, "fmode", 0644, "dmode", 0755, + // "selabel", "u:object_r:system_file:s0", "capabilities", 0x0); + RegisterFunction("set_metadata_recursive", SetMetadataFn); + + RegisterFunction("getprop", GetPropFn); + RegisterFunction("file_getprop", FileGetPropFn); + + RegisterFunction("apply_patch", ApplyPatchFn); + RegisterFunction("apply_patch_check", ApplyPatchCheckFn); + RegisterFunction("apply_patch_space", ApplyPatchSpaceFn); + + RegisterFunction("wipe_block_device", WipeBlockDeviceFn); + + RegisterFunction("read_file", ReadFileFn); + RegisterFunction("sha1_check", Sha1CheckFn); + RegisterFunction("rename", RenameFn); + RegisterFunction("write_value", WriteValueFn); + + RegisterFunction("wipe_cache", WipeCacheFn); + + RegisterFunction("ui_print", UIPrintFn); + + RegisterFunction("run_program", RunProgramFn); + + RegisterFunction("reboot_now", RebootNowFn); + RegisterFunction("get_stage", GetStageFn); + RegisterFunction("set_stage", SetStageFn); + + RegisterFunction("enable_reboot", EnableRebootFn); + RegisterFunction("tune2fs", Tune2FsFn); } diff --git a/updater/updater.cpp b/updater/updater.cpp index e956dd557..473066263 100644 --- a/updater/updater.cpp +++ b/updater/updater.cpp @@ -14,18 +14,27 @@ * limitations under the License. */ +#include "updater/updater.h" + #include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <string.h> -#include "edify/expr.h" -#include "updater.h" -#include "install.h" -#include "blockimg.h" -#include "minzip/Zip.h" -#include "minzip/SysUtil.h" +#include <string> + +#include <android-base/logging.h> +#include <android-base/strings.h> +#include <selinux/label.h> +#include <selinux/selinux.h> +#include <ziparchive/zip_archive.h> + #include "config.h" +#include "edify/expr.h" +#include "otautil/DirUtil.h" +#include "otautil/SysUtil.h" +#include "updater/blockimg.h" +#include "updater/install.h" // Generated by the makefile, this function defines the // RegisterDeviceExtensions() function, which calls all the @@ -34,170 +43,178 @@ // Where in the package we expect to find the edify script to execute. // (Note it's "updateR-script", not the older "update-script".) -#define SCRIPT_NAME "META-INF/com/google/android/updater-script" +static constexpr const char* SCRIPT_NAME = "META-INF/com/google/android/updater-script"; extern bool have_eio_error; struct selabel_handle *sehandle; -int main(int argc, char** argv) { - // Various things log information to stdout or stderr more or less - // at random (though we've tried to standardize on stdout). The - // log file makes more sense if buffering is turned off so things - // appear in the right order. - setbuf(stdout, NULL); - setbuf(stderr, NULL); - - if (argc != 4 && argc != 5) { - printf("unexpected number of arguments (%d)\n", argc); - return 1; - } - - char* version = argv[1]; - if ((version[0] != '1' && version[0] != '2' && version[0] != '3') || - version[1] != '\0') { - // We support version 1, 2, or 3. - printf("wrong updater binary API; expected 1, 2, or 3; " - "got %s\n", - argv[1]); - return 2; - } - - // Set up the pipe for sending commands back to the parent process. - - int fd = atoi(argv[2]); - FILE* cmd_pipe = fdopen(fd, "wb"); - setlinebuf(cmd_pipe); - - // Extract the script from the package. - - const char* package_filename = argv[3]; - MemMapping map; - if (sysMapFile(package_filename, &map) != 0) { - printf("failed to map package %s\n", argv[3]); - return 3; - } - ZipArchive za; - int err; - err = mzOpenZipArchive(map.addr, map.length, &za); - if (err != 0) { - printf("failed to open package %s: %s\n", - argv[3], strerror(err)); - return 3; - } - ota_io_init(&za); - - const ZipEntry* script_entry = mzFindZipEntry(&za, SCRIPT_NAME); - if (script_entry == NULL) { - printf("failed to find %s in %s\n", SCRIPT_NAME, package_filename); - return 4; - } - - char* script = reinterpret_cast<char*>(malloc(script_entry->uncompLen+1)); - if (!mzReadZipEntry(&za, script_entry, script, script_entry->uncompLen)) { - printf("failed to read script from package\n"); - return 5; - } - script[script_entry->uncompLen] = '\0'; - - // Configure edify's functions. - - RegisterBuiltins(); - RegisterInstallFunctions(); - RegisterBlockImageFunctions(); - RegisterDeviceExtensions(); - FinishRegistration(); - - // Parse the script. +static void UpdaterLogger(android::base::LogId /* id */, android::base::LogSeverity /* severity */, + const char* /* tag */, const char* /* file */, unsigned int /* line */, + const char* message) { + fprintf(stdout, "%s\n", message); +} - Expr* root; - int error_count = 0; - int error = parse_string(script, &root, &error_count); - if (error != 0 || error_count > 0) { - printf("%d parse errors\n", error_count); - return 6; +int main(int argc, char** argv) { + // Various things log information to stdout or stderr more or less + // at random (though we've tried to standardize on stdout). The + // log file makes more sense if buffering is turned off so things + // appear in the right order. + setbuf(stdout, nullptr); + setbuf(stderr, nullptr); + + // We don't have logcat yet under recovery. Update logs will always be written to stdout + // (which is redirected to recovery.log). + android::base::InitLogging(argv, &UpdaterLogger); + + if (argc != 4 && argc != 5) { + LOG(ERROR) << "unexpected number of arguments: " << argc; + return 1; + } + + char* version = argv[1]; + if ((version[0] != '1' && version[0] != '2' && version[0] != '3') || version[1] != '\0') { + // We support version 1, 2, or 3. + LOG(ERROR) << "wrong updater binary API; expected 1, 2, or 3; got " << argv[1]; + return 2; + } + + // Set up the pipe for sending commands back to the parent process. + + int fd = atoi(argv[2]); + FILE* cmd_pipe = fdopen(fd, "wb"); + setlinebuf(cmd_pipe); + + // Extract the script from the package. + + const char* package_filename = argv[3]; + MemMapping map; + if (sysMapFile(package_filename, &map) != 0) { + LOG(ERROR) << "failed to map package " << argv[3]; + return 3; + } + ZipArchiveHandle za; + int open_err = OpenArchiveFromMemory(map.addr, map.length, argv[3], &za); + if (open_err != 0) { + LOG(ERROR) << "failed to open package " << argv[3] << ": " << ErrorCodeString(open_err); + CloseArchive(za); + return 3; + } + + ZipString script_name(SCRIPT_NAME); + ZipEntry script_entry; + int find_err = FindEntry(za, script_name, &script_entry); + if (find_err != 0) { + LOG(ERROR) << "failed to find " << SCRIPT_NAME << " in " << package_filename << ": " + << ErrorCodeString(find_err); + CloseArchive(za); + return 4; + } + + std::string script; + script.resize(script_entry.uncompressed_length); + int extract_err = ExtractToMemory(za, &script_entry, reinterpret_cast<uint8_t*>(&script[0]), + script_entry.uncompressed_length); + if (extract_err != 0) { + LOG(ERROR) << "failed to read script from package: " << ErrorCodeString(extract_err); + CloseArchive(za); + return 5; + } + + // Configure edify's functions. + + RegisterBuiltins(); + RegisterInstallFunctions(); + RegisterBlockImageFunctions(); + RegisterDeviceExtensions(); + + // Parse the script. + + Expr* root; + int error_count = 0; + int error = parse_string(script.c_str(), &root, &error_count); + if (error != 0 || error_count > 0) { + LOG(ERROR) << error_count << " parse errors"; + CloseArchive(za); + return 6; + } + + struct selinux_opt seopts[] = { { SELABEL_OPT_PATH, "/file_contexts" } }; + + sehandle = selabel_open(SELABEL_CTX_FILE, seopts, 1); + + if (!sehandle) { + fprintf(cmd_pipe, "ui_print Warning: No file_contexts\n"); + } + + // Evaluate the parsed script. + + UpdaterInfo updater_info; + updater_info.cmd_pipe = cmd_pipe; + updater_info.package_zip = za; + updater_info.version = atoi(version); + updater_info.package_zip_addr = map.addr; + updater_info.package_zip_len = map.length; + + State state(script, &updater_info); + + if (argc == 5) { + if (strcmp(argv[4], "retry") == 0) { + state.is_retry = true; + } else { + printf("unexpected argument: %s", argv[4]); } + } + ota_io_init(za, state.is_retry); - struct selinux_opt seopts[] = { - { SELABEL_OPT_PATH, "/file_contexts" } - }; - - sehandle = selabel_open(SELABEL_CTX_FILE, seopts, 1); + std::string result; + bool status = Evaluate(&state, root, &result); - if (!sehandle) { - fprintf(cmd_pipe, "ui_print Warning: No file_contexts\n"); - } + if (have_eio_error) { + fprintf(cmd_pipe, "retry_update\n"); + } - // Evaluate the parsed script. - - UpdaterInfo updater_info; - updater_info.cmd_pipe = cmd_pipe; - updater_info.package_zip = &za; - updater_info.version = atoi(version); - updater_info.package_zip_addr = map.addr; - updater_info.package_zip_len = map.length; - - State state; - state.cookie = &updater_info; - state.script = script; - state.errmsg = NULL; - - if (argc == 5) { - if (strcmp(argv[4], "retry") == 0) { - state.is_retry = true; - } else { - printf("unexpected argument: %s", argv[4]); + if (!status) { + if (state.errmsg.empty()) { + LOG(ERROR) << "script aborted (no error message)"; + fprintf(cmd_pipe, "ui_print script aborted (no error message)\n"); + } else { + LOG(ERROR) << "script aborted: " << state.errmsg; + const std::vector<std::string> lines = android::base::Split(state.errmsg, "\n"); + for (const std::string& line : lines) { + // Parse the error code in abort message. + // Example: "E30: This package is for bullhead devices." + if (!line.empty() && line[0] == 'E') { + if (sscanf(line.c_str(), "E%u: ", &state.error_code) != 1) { + LOG(ERROR) << "Failed to parse error code: [" << line << "]"; + } } + fprintf(cmd_pipe, "ui_print %s\n", line.c_str()); + } + fprintf(cmd_pipe, "ui_print\n"); } - char* result = Evaluate(&state, root); - - if (have_eio_error) { - fprintf(cmd_pipe, "retry_update\n"); - } - - if (result == NULL) { - if (state.errmsg == NULL) { - printf("script aborted (no error message)\n"); - fprintf(cmd_pipe, "ui_print script aborted (no error message)\n"); - } else { - printf("script aborted: %s\n", state.errmsg); - char* line = strtok(state.errmsg, "\n"); - while (line) { - // Parse the error code in abort message. - // Example: "E30: This package is for bullhead devices." - if (*line == 'E') { - if (sscanf(line, "E%u: ", &state.error_code) != 1) { - printf("Failed to parse error code: [%s]\n", line); - } - } - fprintf(cmd_pipe, "ui_print %s\n", line); - line = strtok(NULL, "\n"); - } - fprintf(cmd_pipe, "ui_print\n"); - } - - if (state.error_code != kNoError) { - fprintf(cmd_pipe, "log error: %d\n", state.error_code); - // Cause code should provide additional information about the abort; - // report only when an error exists. - if (state.cause_code != kNoCause) { - fprintf(cmd_pipe, "log cause: %d\n", state.cause_code); - } - } - - free(state.errmsg); - return 7; - } else { - fprintf(cmd_pipe, "ui_print script succeeded: result was [%s]\n", result); - free(result); + if (state.error_code != kNoError) { + fprintf(cmd_pipe, "log error: %d\n", state.error_code); + // Cause code should provide additional information about the abort; + // report only when an error exists. + if (state.cause_code != kNoCause) { + fprintf(cmd_pipe, "log cause: %d\n", state.cause_code); + } } if (updater_info.package_zip) { - mzCloseZipArchive(updater_info.package_zip); + CloseArchive(updater_info.package_zip); } - sysReleaseMap(&map); - free(script); + return 7; + } else { + fprintf(cmd_pipe, "ui_print script succeeded: result was [%s]\n", result.c_str()); + } + + if (updater_info.package_zip) { + CloseArchive(updater_info.package_zip); + } + sysReleaseMap(&map); - return 0; + return 0; } |