diff options
author | Tianjie Xu <xunchang@google.com> | 2019-05-21 19:07:30 +0200 |
---|---|---|
committer | Gerrit Code Review <noreply-gerritcodereview@google.com> | 2019-05-21 19:07:30 +0200 |
commit | e7b3c5698e0eb6ed30489807496e0626cb37060e (patch) | |
tree | 45ea6aa3e7a034f376969cbc45b5e0b9ab630e17 | |
parent | Merge "Add misc_writer." (diff) | |
parent | Add UpdaterRuntime class (diff) | |
download | android_bootable_recovery-e7b3c5698e0eb6ed30489807496e0626cb37060e.tar android_bootable_recovery-e7b3c5698e0eb6ed30489807496e0626cb37060e.tar.gz android_bootable_recovery-e7b3c5698e0eb6ed30489807496e0626cb37060e.tar.bz2 android_bootable_recovery-e7b3c5698e0eb6ed30489807496e0626cb37060e.tar.lz android_bootable_recovery-e7b3c5698e0eb6ed30489807496e0626cb37060e.tar.xz android_bootable_recovery-e7b3c5698e0eb6ed30489807496e0626cb37060e.tar.zst android_bootable_recovery-e7b3c5698e0eb6ed30489807496e0626cb37060e.zip |
-rw-r--r-- | edify/expr.cpp | 4 | ||||
-rw-r--r-- | edify/include/edify/expr.h | 9 | ||||
-rw-r--r-- | edify/include/edify/updater_interface.h | 47 | ||||
-rw-r--r-- | edify/include/edify/updater_runtime_interface.h | 69 | ||||
-rw-r--r-- | tests/unit/updater_test.cpp | 12 | ||||
-rw-r--r-- | updater/Android.bp | 1 | ||||
-rw-r--r-- | updater/blockimg.cpp | 74 | ||||
-rw-r--r-- | updater/include/updater/updater.h | 40 | ||||
-rw-r--r-- | updater/include/updater/updater_runtime.h | 58 | ||||
-rw-r--r-- | updater/install.cpp | 167 | ||||
-rw-r--r-- | updater/updater.cpp | 32 | ||||
-rw-r--r-- | updater/updater_main.cpp | 5 | ||||
-rw-r--r-- | updater/updater_runtime.cpp | 132 |
13 files changed, 486 insertions, 164 deletions
diff --git a/edify/expr.cpp b/edify/expr.cpp index c090eb28a..e5e0e240a 100644 --- a/edify/expr.cpp +++ b/edify/expr.cpp @@ -421,5 +421,5 @@ Value* ErrorAbort(State* state, CauseCode cause_code, const char* format, ...) { return nullptr; } -State::State(const std::string& script, void* cookie) - : script(script), cookie(cookie), error_code(kNoError), cause_code(kNoCause) {} +State::State(const std::string& script, UpdaterInterface* interface) + : script(script), updater(interface), error_code(kNoError), cause_code(kNoCause) {} diff --git a/edify/include/edify/expr.h b/edify/include/edify/expr.h index 5cbd5e15d..cd9c70120 100644 --- a/edify/include/edify/expr.h +++ b/edify/include/edify/expr.h @@ -23,19 +23,20 @@ #include <string> #include <vector> +#include "edify/updater_interface.h" + // Forward declaration to avoid including "otautil/error_code.h". enum ErrorCode : int; enum CauseCode : int; struct State { - State(const std::string& script, void* cookie); + State(const std::string& script, UpdaterInterface* cookie); // The source of the original script. const std::string& script; - // Optional pointer to app-specific data; the core of edify never - // uses this value. - void* cookie; + // A pointer to app-specific data; the libedify doesn't use this value. + UpdaterInterface* updater; // The error message (if any) returned if the evaluation aborts. // Should be empty initially, will be either empty or a string that diff --git a/edify/include/edify/updater_interface.h b/edify/include/edify/updater_interface.h new file mode 100644 index 000000000..a4d581eec --- /dev/null +++ b/edify/include/edify/updater_interface.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <stdint.h> + +#include <string> +#include <string_view> + +struct ZipArchive; +typedef ZipArchive* ZipArchiveHandle; + +class UpdaterRuntimeInterface; + +class UpdaterInterface { + public: + virtual ~UpdaterInterface() = default; + + // Writes the message to command pipe, adds a new line in the end. + virtual void WriteToCommandPipe(const std::string_view message, bool flush = false) const = 0; + + // Sends over the message to recovery to print it on the screen. + virtual void UiPrint(const std::string_view message) const = 0; + + // Given the name of the block device, returns |name| for updates on the device; or the file path + // to the fake block device for simulations. + virtual std::string FindBlockDeviceName(const std::string_view name) const = 0; + + virtual UpdaterRuntimeInterface* GetRuntime() const = 0; + virtual ZipArchiveHandle GetPackageHandle() const = 0; + virtual std::string GetResult() const = 0; + virtual uint8_t* GetMappedPackageAddress() const = 0; +}; diff --git a/edify/include/edify/updater_runtime_interface.h b/edify/include/edify/updater_runtime_interface.h new file mode 100644 index 000000000..15ccd832d --- /dev/null +++ b/edify/include/edify/updater_runtime_interface.h @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <string> +#include <string_view> +#include <vector> + +// This class serves as the base to updater runtime. It wraps the runtime dependent functions; and +// updates on device and host simulations can have different implementations. e.g. block devices +// during host simulation merely a temporary file. With this class, the caller side in registered +// updater's functions will stay the same for both update and simulation. +class UpdaterRuntimeInterface { + public: + virtual ~UpdaterRuntimeInterface() = default; + + // Returns true if it's a runtime instance for simulation. + virtual bool IsSimulator() const = 0; + + // Returns the value of system property |key|. If the property doesn't exist, returns + // |default_value|. + virtual std::string GetProperty(const std::string_view key, + const std::string_view default_value) const = 0; + + // Given the name of the block device, returns |name| for updates on the device; or the file path + // to the fake block device for simulations. + virtual std::string FindBlockDeviceName(const std::string_view name) const = 0; + + // Mounts the |location| on |mount_point|. Returns 0 on success. + virtual int Mount(const std::string_view location, const std::string_view mount_point, + const std::string_view fs_type, const std::string_view mount_options) = 0; + + // Returns true if |mount_point| is mounted. + virtual bool IsMounted(const std::string_view mount_point) const = 0; + + // Unmounts the |mount_point|. Returns a pair of results with the first value indicating + // if the |mount_point| is mounted, and the second value indicating the result of umount(2). + virtual std::pair<bool, int> Unmount(const std::string_view mount_point) = 0; + + // Reads |filename| and puts its value to |content|. + virtual bool ReadFileToString(const std::string_view filename, std::string* content) const = 0; + + // Updates the content of |filename| with |content|. + virtual bool WriteStringToFile(const std::string_view content, + const std::string_view filename) const = 0; + + // Wipes the first |len| bytes of block device in |filename|. + virtual int WipeBlockDevice(const std::string_view filename, size_t len) const = 0; + + // Starts a child process and runs the program with |args|. Uses vfork(2) if |is_vfork| is true. + virtual int RunProgram(const std::vector<std::string>& args, bool is_vfork) const = 0; + + // Runs tune2fs with arguments |args|. + virtual int Tune2Fs(const std::vector<std::string>& args) const = 0; +};
\ No newline at end of file diff --git a/tests/unit/updater_test.cpp b/tests/unit/updater_test.cpp index 4a8d1e6ff..81229f50a 100644 --- a/tests/unit/updater_test.cpp +++ b/tests/unit/updater_test.cpp @@ -52,13 +52,14 @@ #include "updater/blockimg.h" #include "updater/install.h" #include "updater/updater.h" +#include "updater/updater_runtime.h" using namespace std::string_literals; using PackageEntries = std::unordered_map<std::string, std::string>; static void expect(const char* expected, const std::string& expr_str, CauseCode cause_code, - Updater* updater = nullptr) { + Updater* updater) { std::unique_ptr<Expr> e; int error_count = 0; ASSERT_EQ(0, ParseString(expr_str, &e, &error_count)); @@ -83,6 +84,11 @@ static void expect(const char* expected, const std::string& expr_str, CauseCode ASSERT_EQ(cause_code, state.cause_code); } +static void expect(const char* expected, const std::string& expr_str, CauseCode cause_code) { + Updater updater; + expect(expected, expr_str, cause_code, &updater); +} + static void BuildUpdatePackage(const PackageEntries& entries, int fd) { FILE* zip_file_ptr = fdopen(fd, "wb"); ZipWriter zip_writer(zip_file_ptr); @@ -168,9 +174,9 @@ class UpdaterTestBase { // Set up the handler, command_pipe, patch offset & length. TemporaryFile temp_pipe; - ASSERT_TRUE(updater_.Init(temp_pipe.release(), zip_file.path, false, nullptr)); + ASSERT_TRUE(updater_.Init(temp_pipe.release(), zip_file.path, false)); ASSERT_TRUE(updater_.RunUpdate()); - ASSERT_EQ(result, updater_.result()); + ASSERT_EQ(result, updater_.GetResult()); // Parse the cause code written to the command pipe. int received_cause_code = kNoCause; diff --git a/updater/Android.bp b/updater/Android.bp index daf7e3277..72f8bc9b3 100644 --- a/updater/Android.bp +++ b/updater/Android.bp @@ -71,6 +71,7 @@ cc_library_static { "dynamic_partitions.cpp", "install.cpp", "updater.cpp", + "updater_runtime.cpp", ], include_dirs: [ diff --git a/updater/blockimg.cpp b/updater/blockimg.cpp index 3b2b2c02d..55218b023 100644 --- a/updater/blockimg.cpp +++ b/updater/blockimg.cpp @@ -54,6 +54,7 @@ #include <ziparchive/zip_archive.h> #include "edify/expr.h" +#include "edify/updater_interface.h" #include "otautil/dirutil.h" #include "otautil/error_code.h" #include "otautil/paths.h" @@ -61,7 +62,6 @@ #include "otautil/rangeset.h" #include "private/commands.h" #include "updater/install.h" -#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 @@ -1669,8 +1669,15 @@ static Value* PerformBlockImageUpdate(const char* name, State* state, return StringValue(""); } - auto updater = static_cast<Updater*>(state->cookie); - ZipArchiveHandle za = updater->package_handle(); + auto updater = state->updater; + auto block_device_path = updater->FindBlockDeviceName(blockdev_filename->data); + if (block_device_path.empty()) { + LOG(ERROR) << "Block device path for " << blockdev_filename->data << " not found. " << name + << " failed."; + return StringValue(""); + } + + ZipArchiveHandle za = updater->GetPackageHandle(); if (za == nullptr) { return StringValue(""); } @@ -1690,15 +1697,15 @@ static Value* PerformBlockImageUpdate(const char* name, State* state, return StringValue(""); } - params.fd.reset(TEMP_FAILURE_RETRY(open(blockdev_filename->data.c_str(), O_RDWR))); + params.fd.reset(TEMP_FAILURE_RETRY(open(block_device_path.c_str(), O_RDWR))); if (params.fd == -1) { failure_type = errno == EIO ? kEioFailure : kFileOpenFailure; - PLOG(ERROR) << "open \"" << blockdev_filename->data << "\" failed"; + PLOG(ERROR) << "open \"" << block_device_path << "\" failed"; return StringValue(""); } uint8_t digest[SHA_DIGEST_LENGTH]; - if (!Sha1DevicePath(blockdev_filename->data, digest)) { + if (!Sha1DevicePath(block_device_path, digest)) { return StringValue(""); } params.stashbase = print_sha1(digest); @@ -1711,8 +1718,7 @@ static Value* PerformBlockImageUpdate(const char* name, State* state, struct stat sb; int result = stat(updated_marker.c_str(), &sb); if (result == 0) { - LOG(INFO) << "Skipping already updated partition " << blockdev_filename->data - << " based on marker"; + LOG(INFO) << "Skipping already updated partition " << block_device_path << " based on marker"; return StringValue("t"); } } else { @@ -1910,7 +1916,7 @@ pbiudone: LOG(INFO) << "stashed " << params.stashed << " blocks"; LOG(INFO) << "max alloc needed was " << params.buffer.size(); - const char* partition = strrchr(blockdev_filename->data.c_str(), '/'); + const char* partition = strrchr(block_device_path.c_str(), '/'); if (partition != nullptr && *(partition + 1) != 0) { updater->WriteToCommandPipe( android::base::StringPrintf("log bytes_written_%s: %" PRIu64, partition + 1, @@ -2078,10 +2084,17 @@ Value* RangeSha1Fn(const char* name, State* state, const std::vector<std::unique return StringValue(""); } - android::base::unique_fd fd(open(blockdev_filename->data.c_str(), O_RDWR)); + auto block_device_path = state->updater->FindBlockDeviceName(blockdev_filename->data); + if (block_device_path.empty()) { + LOG(ERROR) << "Block device path for " << blockdev_filename->data << " not found. " << name + << " failed."; + return StringValue(""); + } + + android::base::unique_fd fd(open(block_device_path.c_str(), O_RDWR)); if (fd == -1) { CauseCode cause_code = errno == EIO ? kEioFailure : kFileOpenFailure; - ErrorAbort(state, cause_code, "open \"%s\" failed: %s", blockdev_filename->data.c_str(), + ErrorAbort(state, cause_code, "open \"%s\" failed: %s", block_device_path.c_str(), strerror(errno)); return StringValue(""); } @@ -2095,7 +2108,7 @@ Value* RangeSha1Fn(const char* name, State* state, const std::vector<std::unique std::vector<uint8_t> buffer(BLOCKSIZE); for (const auto& [begin, end] : rs) { if (!check_lseek(fd, static_cast<off64_t>(begin) * BLOCKSIZE, SEEK_SET)) { - ErrorAbort(state, kLseekFailure, "failed to seek %s: %s", blockdev_filename->data.c_str(), + ErrorAbort(state, kLseekFailure, "failed to seek %s: %s", block_device_path.c_str(), strerror(errno)); return StringValue(""); } @@ -2103,7 +2116,7 @@ Value* RangeSha1Fn(const char* name, State* state, const std::vector<std::unique for (size_t j = begin; j < end; ++j) { if (!android::base::ReadFully(fd, buffer.data(), BLOCKSIZE)) { CauseCode cause_code = errno == EIO ? kEioFailure : kFreadFailure; - ErrorAbort(state, cause_code, "failed to read %s: %s", blockdev_filename->data.c_str(), + ErrorAbort(state, cause_code, "failed to read %s: %s", block_device_path.c_str(), strerror(errno)); return StringValue(""); } @@ -2142,10 +2155,17 @@ Value* CheckFirstBlockFn(const char* name, State* state, return StringValue(""); } - android::base::unique_fd fd(open(arg_filename->data.c_str(), O_RDONLY)); + auto block_device_path = state->updater->FindBlockDeviceName(arg_filename->data); + if (block_device_path.empty()) { + LOG(ERROR) << "Block device path for " << arg_filename->data << " not found. " << name + << " failed."; + return StringValue(""); + } + + android::base::unique_fd fd(open(block_device_path.c_str(), O_RDONLY)); if (fd == -1) { CauseCode cause_code = errno == EIO ? kEioFailure : kFileOpenFailure; - ErrorAbort(state, cause_code, "open \"%s\" failed: %s", arg_filename->data.c_str(), + ErrorAbort(state, cause_code, "open \"%s\" failed: %s", block_device_path.c_str(), strerror(errno)); return StringValue(""); } @@ -2155,7 +2175,7 @@ Value* CheckFirstBlockFn(const char* name, State* state, if (ReadBlocks(blk0, &block0_buffer, fd) == -1) { CauseCode cause_code = errno == EIO ? kEioFailure : kFreadFailure; - ErrorAbort(state, cause_code, "failed to read %s: %s", arg_filename->data.c_str(), + ErrorAbort(state, cause_code, "failed to read %s: %s", block_device_path.c_str(), strerror(errno)); return StringValue(""); } @@ -2171,10 +2191,9 @@ Value* CheckFirstBlockFn(const char* name, State* state, uint16_t mount_count = *reinterpret_cast<uint16_t*>(&block0_buffer[0x400 + 0x34]); if (mount_count > 0) { - auto updater = static_cast<Updater*>(state->cookie); - updater->UiPrint( + state->updater->UiPrint( android::base::StringPrintf("Device was remounted R/W %" PRIu16 " times", mount_count)); - updater->UiPrint( + state->updater->UiPrint( android::base::StringPrintf("Last remount happened on %s", ctime(&mount_time))); } @@ -2211,14 +2230,21 @@ Value* BlockImageRecoverFn(const char* name, State* state, return StringValue(""); } + auto block_device_path = state->updater->FindBlockDeviceName(filename->data); + if (block_device_path.empty()) { + LOG(ERROR) << "Block device path for " << filename->data << " not found. " << name + << " failed."; + return StringValue(""); + } + // Output notice to log when recover is attempted - LOG(INFO) << filename->data << " image corrupted, attempting to recover..."; + LOG(INFO) << block_device_path << " 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(block_device_path, O_RDWR); if (!fh) { - ErrorAbort(state, kLibfecFailure, "fec_open \"%s\" failed: %s", filename->data.c_str(), + ErrorAbort(state, kLibfecFailure, "fec_open \"%s\" failed: %s", block_device_path.c_str(), strerror(errno)); return StringValue(""); } @@ -2244,7 +2270,7 @@ Value* BlockImageRecoverFn(const char* name, State* state, if (fh.pread(buffer, BLOCKSIZE, static_cast<off64_t>(j) * BLOCKSIZE) != BLOCKSIZE) { ErrorAbort(state, kLibfecFailure, "failed to recover %s (block %zu): %s", - filename->data.c_str(), j, strerror(errno)); + block_device_path.c_str(), j, strerror(errno)); return StringValue(""); } @@ -2260,7 +2286,7 @@ Value* BlockImageRecoverFn(const char* name, State* state, // read and check if the errors field value has increased. } } - LOG(INFO) << "..." << filename->data << " image recovered successfully."; + LOG(INFO) << "..." << block_device_path << " image recovered successfully."; return StringValue("t"); } diff --git a/updater/include/updater/updater.h b/updater/include/updater/updater.h index d5468292b..7bbecbc57 100644 --- a/updater/include/updater/updater.h +++ b/updater/include/updater/updater.h @@ -21,45 +21,53 @@ #include <memory> #include <string> +#include <string_view> #include <ziparchive/zip_archive.h> #include "edify/expr.h" +#include "edify/updater_interface.h" #include "otautil/error_code.h" #include "otautil/sysutil.h" -struct selabel_handle; +class UpdaterRuntime; -class Updater { +class Updater : public UpdaterInterface { public: - ~Updater(); + explicit Updater(std::unique_ptr<UpdaterRuntimeInterface> run_time) + : runtime_(std::move(run_time)) {} + + Updater(); + + ~Updater() override; // Memory-maps the OTA package and opens it as a zip file. Also sets up the command pipe and - // selabel handle. TODO(xunchang) implement a run time environment class and move sehandle there. - bool Init(int fd, const std::string& package_filename, bool is_retry, - struct selabel_handle* sehandle); + // UpdaterRuntime. + bool Init(int fd, const std::string_view package_filename, bool is_retry); // Parses and evaluates the updater-script in the OTA package. Reports the error code if the // evaluation fails. bool RunUpdate(); // Writes the message to command pipe, adds a new line in the end. - void WriteToCommandPipe(const std::string& message, bool flush = false) const; + void WriteToCommandPipe(const std::string_view message, bool flush = false) const override; // Sends over the message to recovery to print it on the screen. - void UiPrint(const std::string& message) const; + void UiPrint(const std::string_view message) const override; - ZipArchiveHandle package_handle() const { - return package_handle_; + std::string FindBlockDeviceName(const std::string_view name) const override; + + UpdaterRuntimeInterface* GetRuntime() const override { + return runtime_.get(); } - struct selabel_handle* sehandle() const { - return sehandle_; + ZipArchiveHandle GetPackageHandle() const override { + return package_handle_; } - std::string result() const { + std::string GetResult() const override { return result_; } - uint8_t* GetMappedPackageAddress() const { + uint8_t* GetMappedPackageAddress() const override { return mapped_package_.addr; } @@ -76,13 +84,15 @@ class Updater { // Parses the error code embedded in state->errmsg; and reports the error code and cause code. void ParseAndReportErrorCode(State* state); + std::unique_ptr<UpdaterRuntimeInterface> runtime_; + MemMapping mapped_package_; ZipArchiveHandle package_handle_{ nullptr }; std::string updater_script_; bool is_retry_{ false }; std::unique_ptr<FILE, decltype(&fclose)> cmd_pipe_{ nullptr, fclose }; - struct selabel_handle* sehandle_{ nullptr }; std::string result_; + std::vector<std::string> skipped_functions_; }; diff --git a/updater/include/updater/updater_runtime.h b/updater/include/updater/updater_runtime.h new file mode 100644 index 000000000..6cd0ffb48 --- /dev/null +++ b/updater/include/updater/updater_runtime.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <memory> +#include <string> +#include <string_view> +#include <utility> +#include <vector> + +#include "edify/updater_runtime_interface.h" + +struct selabel_handle; +struct Partition; + +class UpdaterRuntime : public UpdaterRuntimeInterface { + public: + explicit UpdaterRuntime(struct selabel_handle* sehandle) : sehandle_(sehandle) {} + ~UpdaterRuntime() override = default; + + bool IsSimulator() const override { + return false; + } + + std::string GetProperty(const std::string_view key, + const std::string_view default_value) const override; + + std::string FindBlockDeviceName(const std::string_view name) const override; + + int Mount(const std::string_view location, const std::string_view mount_point, + const std::string_view fs_type, const std::string_view mount_options) override; + bool IsMounted(const std::string_view mount_point) const override; + std::pair<bool, int> Unmount(const std::string_view mount_point) override; + + bool ReadFileToString(const std::string_view filename, std::string* content) const override; + bool WriteStringToFile(const std::string_view content, + const std::string_view filename) const override; + + int WipeBlockDevice(const std::string_view filename, size_t len) const override; + int RunProgram(const std::vector<std::string>& args, bool is_vfork) const override; + int Tune2Fs(const std::vector<std::string>& args) const override; + + struct selabel_handle* sehandle_{ nullptr }; +}; diff --git a/updater/install.cpp b/updater/install.cpp index b4d88403c..6b15eaa3a 100644 --- a/updater/install.cpp +++ b/updater/install.cpp @@ -57,12 +57,25 @@ #include <ziparchive/zip_archive.h> #include "edify/expr.h" +#include "edify/updater_interface.h" +#include "edify/updater_runtime_interface.h" #include "otautil/dirutil.h" #include "otautil/error_code.h" #include "otautil/mounts.h" #include "otautil/print_sha1.h" #include "otautil/sysutil.h" -#include "updater/updater.h" + +static bool UpdateBlockDeviceNameForPartition(UpdaterInterface* updater, Partition* partition) { + CHECK(updater); + std::string name = updater->FindBlockDeviceName(partition->name); + if (name.empty()) { + LOG(ERROR) << "Failed to find the block device " << partition->name; + return false; + } + + partition->name = std::move(name); + return true; +} // 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. @@ -73,7 +86,7 @@ Value* UIPrintFn(const char* name, State* state, const std::vector<std::unique_p } std::string buffer = android::base::Join(args, ""); - static_cast<Updater*>(state->cookie)->UiPrint(buffer); + state->updater->UiPrint(buffer); return StringValue(buffer); } @@ -99,7 +112,7 @@ Value* PackageExtractFileFn(const char* name, State* state, const std::string& zip_path = args[0]; const std::string& dest_path = args[1]; - ZipArchiveHandle za = static_cast<Updater*>(state->cookie)->package_handle(); + ZipArchiveHandle za = state->updater->GetPackageHandle(); ZipEntry entry; if (FindEntry(za, zip_path, &entry) != 0) { LOG(ERROR) << name << ": no " << zip_path << " in package"; @@ -142,7 +155,7 @@ Value* PackageExtractFileFn(const char* name, State* state, } const std::string& zip_path = args[0]; - ZipArchiveHandle za = static_cast<Updater*>(state->cookie)->package_handle(); + ZipArchiveHandle za = state->updater->GetPackageHandle(); ZipEntry entry; if (FindEntry(za, zip_path, &entry) != 0) { return ErrorAbort(state, kPackageExtractFileFailure, "%s(): no %s in package", name, @@ -197,6 +210,11 @@ Value* PatchPartitionCheckFn(const char* name, State* state, args[1].c_str(), err.c_str()); } + if (!UpdateBlockDeviceNameForPartition(state->updater, &source) || + !UpdateBlockDeviceNameForPartition(state->updater, &target)) { + return StringValue(""); + } + bool result = PatchPartitionCheck(target, source); return StringValue(result ? "t" : ""); } @@ -238,6 +256,11 @@ Value* PatchPartitionFn(const char* name, State* state, return ErrorAbort(state, kArgsParsingFailure, "%s(): Invalid patch arg", name); } + if (!UpdateBlockDeviceNameForPartition(state->updater, &source) || + !UpdateBlockDeviceNameForPartition(state->updater, &target)) { + return StringValue(""); + } + bool result = PatchPartition(target, source, *values[0], nullptr); return StringValue(result ? "t" : ""); } @@ -281,24 +304,8 @@ Value* MountFn(const char* name, State* state, const std::vector<std::unique_ptr name); } - auto updater = static_cast<Updater*>(state->cookie); - { - char* secontext = nullptr; - if (updater->sehandle()) { - selabel_lookup(updater->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) { + auto updater = state->updater; + if (updater->GetRuntime()->Mount(location, mount_point, fs_type, mount_options) != 0) { updater->UiPrint(android::base::StringPrintf("%s: Failed to mount %s at %s: %s", name, location.c_str(), mount_point.c_str(), strerror(errno))); @@ -324,9 +331,8 @@ Value* IsMountedFn(const char* name, State* state, const std::vector<std::unique "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) { + auto updater_runtime = state->updater->GetRuntime(); + if (!updater_runtime->IsMounted(mount_point)) { return StringValue(""); } @@ -347,42 +353,20 @@ Value* UnmountFn(const char* name, State* state, const std::vector<std::unique_p "mount_point argument to unmount() can't be empty"); } - auto updater = static_cast<Updater*>(state->cookie); - scan_mounted_volumes(); - MountedVolume* vol = find_mounted_volume_by_mount_point(mount_point.c_str()); - if (vol == nullptr) { + auto updater = state->updater; + auto [mounted, result] = updater->GetRuntime()->Unmount(mount_point); + if (!mounted) { updater->UiPrint( android::base::StringPrintf("Failed to unmount %s: No such volume", mount_point.c_str())); return nullptr; - } else { - int ret = unmount_mounted_volume(vol); - if (ret != 0) { - updater->UiPrint(android::base::StringPrintf("Failed to unmount %s: %s", mount_point.c_str(), - strerror(errno))); - } + } else if (result != 0) { + updater->UiPrint(android::base::StringPrintf("Failed to unmount %s: %s", mount_point.c_str(), + strerror(errno))); } return StringValue(mount_point); } -static int exec_cmd(const std::vector<std::string>& args) { - CHECK(!args.empty()); - auto argv = StringVectorToNullTerminatedArray(args); - - pid_t child; - if ((child = vfork()) == 0) { - execv(argv[0], argv.data()); - _exit(EXIT_FAILURE); - } - - int status; - waitpid(child, &status, 0); - if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { - LOG(ERROR) << args[0] << " failed with status " << WEXITSTATUS(status); - } - return WEXITSTATUS(status); -} - // format(fs_type, partition_type, location, fs_size, mount_point) // // fs_type="ext4" partition_type="EMMC" location=device fs_size=<bytes> mount_point=<location> @@ -427,6 +411,7 @@ Value* FormatFn(const char* name, State* state, const std::vector<std::unique_pt fs_size.c_str()); } + auto updater_runtime = state->updater->GetRuntime(); if (fs_type == "ext4") { std::vector<std::string> mke2fs_args = { "/system/bin/mke2fs", "-t", "ext4", "-b", "4096", location @@ -435,12 +420,13 @@ Value* FormatFn(const char* name, State* state, const std::vector<std::unique_pt mke2fs_args.push_back(std::to_string(size / 4096LL)); } - if (auto status = exec_cmd(mke2fs_args); status != 0) { + if (auto status = updater_runtime->RunProgram(mke2fs_args, true); status != 0) { LOG(ERROR) << name << ": mke2fs failed (" << status << ") on " << location; return StringValue(""); } - if (auto status = exec_cmd({ "/system/bin/e2fsdroid", "-e", "-a", mount_point, location }); + if (auto status = updater_runtime->RunProgram( + { "/system/bin/e2fsdroid", "-e", "-a", mount_point, location }, true); status != 0) { LOG(ERROR) << name << ": e2fsdroid failed (" << status << ") on " << location; return StringValue(""); @@ -459,12 +445,13 @@ Value* FormatFn(const char* name, State* state, const std::vector<std::unique_pt if (size >= 512) { f2fs_args.push_back(std::to_string(size / 512)); } - if (auto status = exec_cmd(f2fs_args); status != 0) { + if (auto status = updater_runtime->RunProgram(f2fs_args, true); status != 0) { LOG(ERROR) << name << ": make_f2fs failed (" << status << ") on " << location; return StringValue(""); } - if (auto status = exec_cmd({ "/system/bin/sload_f2fs", "-t", mount_point, location }); + if (auto status = updater_runtime->RunProgram( + { "/system/bin/sload_f2fs", "-t", mount_point, location }, true); status != 0) { LOG(ERROR) << name << ": sload_f2fs failed (" << status << ") on " << location; return StringValue(""); @@ -503,8 +490,7 @@ Value* ShowProgressFn(const char* name, State* state, sec_str.c_str()); } - auto updater = static_cast<Updater*>(state->cookie); - updater->WriteToCommandPipe(android::base::StringPrintf("progress %f %d", frac, sec)); + state->updater->WriteToCommandPipe(android::base::StringPrintf("progress %f %d", frac, sec)); return StringValue(frac_str); } @@ -527,8 +513,7 @@ Value* SetProgressFn(const char* name, State* state, frac_str.c_str()); } - auto updater = static_cast<Updater*>(state->cookie); - updater->WriteToCommandPipe(android::base::StringPrintf("set_progress %f", frac)); + state->updater->WriteToCommandPipe(android::base::StringPrintf("set_progress %f", frac)); return StringValue(frac_str); } @@ -541,7 +526,9 @@ Value* GetPropFn(const char* name, State* state, const std::vector<std::unique_p if (!Evaluate(state, argv[0], &key)) { return nullptr; } - std::string value = android::base::GetProperty(key, ""); + + auto updater_runtime = state->updater->GetRuntime(); + std::string value = updater_runtime->GetProperty(key, ""); return StringValue(value); } @@ -566,7 +553,8 @@ Value* FileGetPropFn(const char* name, State* state, const std::string& key = args[1]; std::string buffer; - if (!android::base::ReadFileToString(filename, &buffer)) { + auto updater_runtime = state->updater->GetRuntime(); + if (!updater_runtime->ReadFileToString(filename, &buffer)) { ErrorAbort(state, kFreadFailure, "%s: failed to read %s", name, filename.c_str()); return nullptr; } @@ -628,7 +616,7 @@ Value* WipeCacheFn(const char* name, State* state, const std::vector<std::unique argv.size()); } - static_cast<Updater*>(state->cookie)->WriteToCommandPipe("wipe_cache"); + state->updater->WriteToCommandPipe("wipe_cache"); return StringValue("t"); } @@ -642,26 +630,8 @@ Value* RunProgramFn(const char* name, State* state, const std::vector<std::uniqu return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse the argument(s)", name); } - auto exec_args = StringVectorToNullTerminatedArray(args); - LOG(INFO) << "about to run program [" << exec_args[0] << "] with " << argv.size() << " args"; - - pid_t child = fork(); - if (child == 0) { - execv(exec_args[0], exec_args.data()); - PLOG(ERROR) << "run_program: execv failed"; - _exit(EXIT_FAILURE); - } - - 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); - } - + auto updater_runtime = state->updater->GetRuntime(); + auto status = updater_runtime->RunProgram(args, false); return StringValue(std::to_string(status)); } @@ -679,7 +649,8 @@ Value* ReadFileFn(const char* name, State* state, const std::vector<std::unique_ const std::string& filename = args[0]; std::string contents; - if (android::base::ReadFileToString(filename, &contents)) { + auto updater_runtime = state->updater->GetRuntime(); + if (updater_runtime->ReadFileToString(filename, &contents)) { return new Value(Value::Type::STRING, std::move(contents)); } @@ -708,12 +679,12 @@ Value* WriteValueFn(const char* name, State* state, const std::vector<std::uniqu } const std::string& value = args[0]; - if (!android::base::WriteStringToFile(value, filename)) { + auto updater_runtime = state->updater->GetRuntime(); + if (!updater_runtime->WriteStringToFile(value, filename)) { PLOG(ERROR) << name << ": Failed to write to \"" << filename << "\""; return StringValue(""); - } else { - return StringValue("t"); } + return StringValue("t"); } // Immediately reboot the device. Recovery is not finished normally, @@ -839,16 +810,10 @@ Value* WipeBlockDeviceFn(const char* name, State* state, const std::vector<std:: if (!android::base::ParseUint(len_str.c_str(), &len)) { return nullptr; } - android::base::unique_fd fd(open(filename.c_str(), O_WRONLY)); - if (fd == -1) { - PLOG(ERROR) << "Failed to open " << filename; - return StringValue(""); - } - // 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" : ""); + auto updater_runtime = state->updater->GetRuntime(); + int status = updater_runtime->WipeBlockDevice(filename, len); + return StringValue(status == 0 ? "t" : ""); } Value* EnableRebootFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) { @@ -856,7 +821,7 @@ Value* EnableRebootFn(const char* name, State* state, const std::vector<std::uni return ErrorAbort(state, kArgsParsingFailure, "%s() expects no args, got %zu", name, argv.size()); } - static_cast<Updater*>(state->cookie)->WriteToCommandPipe("enable_reboot"); + state->updater->WriteToCommandPipe("enable_reboot"); return StringValue("t"); } @@ -872,10 +837,8 @@ Value* Tune2FsFn(const char* name, State* state, const std::vector<std::unique_p // tune2fs expects the program name as its first arg. args.insert(args.begin(), "tune2fs"); - auto tune2fs_args = StringVectorToNullTerminatedArray(args); - - // tune2fs changes the filesystem parameters on an ext2 filesystem; it returns 0 on success. - if (auto result = tune2fs_main(tune2fs_args.size() - 1, tune2fs_args.data()); result != 0) { + auto updater_runtime = state->updater->GetRuntime(); + if (auto result = updater_runtime->Tune2Fs(args); result != 0) { return ErrorAbort(state, kTune2FsFailure, "%s() returned error code %d", name, result); } return StringValue("t"); diff --git a/updater/updater.cpp b/updater/updater.cpp index e0679fb0c..dbfa2f426 100644 --- a/updater/updater.cpp +++ b/updater/updater.cpp @@ -24,14 +24,17 @@ #include <android-base/logging.h> #include <android-base/strings.h> +#include "updater/updater_runtime.h" + +Updater::Updater() : Updater(std::make_unique<UpdaterRuntime>(nullptr)) {} + Updater::~Updater() { if (package_handle_) { CloseArchive(package_handle_); } } -bool Updater::Init(int fd, const std::string& package_filename, bool is_retry, - struct selabel_handle* sehandle) { +bool Updater::Init(int fd, const std::string_view package_filename, bool is_retry) { // Set up the pipe for sending commands back to the parent process. cmd_pipe_.reset(fdopen(fd, "wb")); if (!cmd_pipe_) { @@ -41,12 +44,12 @@ bool Updater::Init(int fd, const std::string& package_filename, bool is_retry, setlinebuf(cmd_pipe_.get()); - if (!mapped_package_.MapFile(package_filename)) { + if (!mapped_package_.MapFile(std::string(package_filename))) { LOG(ERROR) << "failed to map package " << package_filename; return false; } if (int open_err = OpenArchiveFromMemory(mapped_package_.addr, mapped_package_.length, - package_filename.c_str(), &package_handle_); + std::string(package_filename).c_str(), &package_handle_); open_err != 0) { LOG(ERROR) << "failed to open package " << package_filename << ": " << ErrorCodeString(open_err); @@ -58,14 +61,12 @@ bool Updater::Init(int fd, const std::string& package_filename, bool is_retry, is_retry_ = is_retry; - sehandle_ = sehandle; - if (!sehandle_) { - fprintf(cmd_pipe_.get(), "ui_print Warning: No file_contexts\n"); - } return true; } bool Updater::RunUpdate() { + CHECK(runtime_); + // Parse the script. std::unique_ptr<Expr> root; int error_count = 0; @@ -86,6 +87,9 @@ bool Updater::RunUpdate() { if (result_.empty() && state.cause_code != kNoCause) { fprintf(cmd_pipe_.get(), "log cause: %d\n", state.cause_code); } + for (const auto& func : skipped_functions_) { + LOG(WARNING) << "Skipped executing function " << func; + } return true; } @@ -93,17 +97,17 @@ bool Updater::RunUpdate() { return false; } -void Updater::WriteToCommandPipe(const std::string& message, bool flush) const { - fprintf(cmd_pipe_.get(), "%s\n", message.c_str()); +void Updater::WriteToCommandPipe(const std::string_view message, bool flush) const { + fprintf(cmd_pipe_.get(), "%s\n", std::string(message).c_str()); if (flush) { fflush(cmd_pipe_.get()); } } -void Updater::UiPrint(const std::string& message) const { +void Updater::UiPrint(const std::string_view message) const { // "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(message, "\n"); + std::vector<std::string> lines = android::base::Split(std::string(message), "\n"); for (const auto& line : lines) { if (!line.empty()) { fprintf(cmd_pipe_.get(), "ui_print %s\n", line.c_str()); @@ -116,6 +120,10 @@ void Updater::UiPrint(const std::string& message) const { LOG(INFO) << message; } +std::string Updater::FindBlockDeviceName(const std::string_view name) const { + return runtime_->FindBlockDeviceName(name); +} + void Updater::ParseAndReportErrorCode(State* state) { CHECK(state); if (state->errmsg.empty()) { diff --git a/updater/updater_main.cpp b/updater/updater_main.cpp index dd22c137d..055a8ac76 100644 --- a/updater/updater_main.cpp +++ b/updater/updater_main.cpp @@ -31,6 +31,7 @@ #include "updater/dynamic_partitions.h" #include "updater/install.h" #include "updater/updater.h" +#include "updater/updater_runtime.h" // Generated by the makefile, this function defines the // RegisterDeviceExtensions() function, which calls all the @@ -95,8 +96,8 @@ int main(int argc, char** argv) { auto sehandle = selinux_android_file_context_handle(); selinux_android_set_sehandle(sehandle); - Updater updater; - if (!updater.Init(fd, package_name, is_retry, sehandle)) { + Updater updater(std::make_unique<UpdaterRuntime>(sehandle)); + if (!updater.Init(fd, package_name, is_retry)) { return 1; } diff --git a/updater/updater_runtime.cpp b/updater/updater_runtime.cpp new file mode 100644 index 000000000..761f99975 --- /dev/null +++ b/updater/updater_runtime.cpp @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "updater/updater_runtime.h" + +#include <string.h> +#include <sys/mount.h> +#include <sys/stat.h> +#include <sys/wait.h> +#include <unistd.h> + +#include <android-base/file.h> +#include <android-base/logging.h> +#include <android-base/properties.h> +#include <android-base/strings.h> +#include <android-base/unique_fd.h> +#include <ext4_utils/wipe.h> +#include <selinux/label.h> +#include <tune2fs.h> + +#include "otautil/mounts.h" +#include "otautil/sysutil.h" + +std::string UpdaterRuntime::GetProperty(const std::string_view key, + const std::string_view default_value) const { + return android::base::GetProperty(std::string(key), std::string(default_value)); +} + +std::string UpdaterRuntime::FindBlockDeviceName(const std::string_view name) const { + return std::string(name); +} + +int UpdaterRuntime::Mount(const std::string_view location, const std::string_view mount_point, + const std::string_view fs_type, const std::string_view mount_options) { + std::string mount_point_string(mount_point); + char* secontext = nullptr; + if (sehandle_) { + selabel_lookup(sehandle_, &secontext, mount_point_string.c_str(), 0755); + setfscreatecon(secontext); + } + + mkdir(mount_point_string.c_str(), 0755); + + if (secontext) { + freecon(secontext); + setfscreatecon(nullptr); + } + + return mount(std::string(location).c_str(), mount_point_string.c_str(), + std::string(fs_type).c_str(), MS_NOATIME | MS_NODEV | MS_NODIRATIME, + std::string(mount_options).c_str()); +} + +bool UpdaterRuntime::IsMounted(const std::string_view mount_point) const { + scan_mounted_volumes(); + MountedVolume* vol = find_mounted_volume_by_mount_point(std::string(mount_point).c_str()); + return vol != nullptr; +} + +std::pair<bool, int> UpdaterRuntime::Unmount(const std::string_view mount_point) { + scan_mounted_volumes(); + MountedVolume* vol = find_mounted_volume_by_mount_point(std::string(mount_point).c_str()); + if (vol == nullptr) { + return { false, -1 }; + } + + int ret = unmount_mounted_volume(vol); + return { true, ret }; +} + +bool UpdaterRuntime::ReadFileToString(const std::string_view filename, std::string* content) const { + return android::base::ReadFileToString(std::string(filename), content); +} + +bool UpdaterRuntime::WriteStringToFile(const std::string_view content, + const std::string_view filename) const { + return android::base::WriteStringToFile(std::string(content), std::string(filename)); +} + +int UpdaterRuntime::WipeBlockDevice(const std::string_view filename, size_t len) const { + android::base::unique_fd fd(open(std::string(filename).c_str(), O_WRONLY)); + if (fd == -1) { + PLOG(ERROR) << "Failed to open " << filename; + return false; + } + // The wipe_block_device function in ext4_utils returns 0 on success and 1 for failure. + return wipe_block_device(fd, len); +} + +int UpdaterRuntime::RunProgram(const std::vector<std::string>& args, bool is_vfork) const { + CHECK(!args.empty()); + auto argv = StringVectorToNullTerminatedArray(args); + LOG(INFO) << "about to run program [" << args[0] << "] with " << argv.size() << " args"; + + pid_t child = is_vfork ? vfork() : fork(); + if (child == 0) { + execv(argv[0], argv.data()); + PLOG(ERROR) << "run_program: execv failed"; + _exit(EXIT_FAILURE); + } + + 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 status; +} + +int UpdaterRuntime::Tune2Fs(const std::vector<std::string>& args) const { + auto tune2fs_args = StringVectorToNullTerminatedArray(args); + // tune2fs changes the filesystem parameters on an ext2 filesystem; it returns 0 on success. + return tune2fs_main(tune2fs_args.size() - 1, tune2fs_args.data()); +} |