From 1536db887f496b6c50522d62be22d9f6584eaf87 Mon Sep 17 00:00:00 2001 From: Tianjie Xu Date: Tue, 14 May 2019 10:54:43 -0700 Subject: Add UpdaterRuntime class This class adds a wrapper to the runtime dependent functions. Therefore, the behavior of update on device stays the same, while simulators can have their own implementations. Also change the caller side of the registered updater functions to call these runtime wrappers. Bug: 131911365 Test: unit tests pass, sideload an update on cuttlefish Change-Id: Ib3ab67132991d67fc132f27120e4152439d16ac5 --- edify/expr.cpp | 4 +- edify/include/edify/expr.h | 9 +- edify/include/edify/updater_interface.h | 47 +++++++ edify/include/edify/updater_runtime_interface.h | 69 ++++++++++ tests/unit/updater_test.cpp | 12 +- updater/Android.bp | 1 + updater/blockimg.cpp | 74 +++++++---- updater/include/updater/updater.h | 40 +++--- updater/include/updater/updater_runtime.h | 58 ++++++++ updater/install.cpp | 167 +++++++++--------------- updater/updater.cpp | 32 +++-- updater/updater_main.cpp | 5 +- updater/updater_runtime.cpp | 132 +++++++++++++++++++ 13 files changed, 486 insertions(+), 164 deletions(-) create mode 100644 edify/include/edify/updater_interface.h create mode 100644 edify/include/edify/updater_runtime_interface.h create mode 100644 updater/include/updater/updater_runtime.h create mode 100644 updater/updater_runtime.cpp 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 #include +#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 + +#include +#include + +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 +#include +#include + +// 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 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& args, bool is_vfork) const = 0; + + // Runs tune2fs with arguments |args|. + virtual int Tune2Fs(const std::vector& 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; static void expect(const char* expected, const std::string& expr_str, CauseCode cause_code, - Updater* updater = nullptr) { + Updater* updater) { std::unique_ptr 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 #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(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::vectordata.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 buffer(BLOCKSIZE); for (const auto& [begin, end] : rs) { if (!check_lseek(fd, static_cast(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::vectordata.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(&block0_buffer[0x400 + 0x34]); if (mount_count > 0) { - auto updater = static_cast(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(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 #include +#include #include #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 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 runtime_; + MemMapping mapped_package_; ZipArchiveHandle package_handle_{ nullptr }; std::string updater_script_; bool is_retry_{ false }; std::unique_ptr cmd_pipe_{ nullptr, fclose }; - struct selabel_handle* sehandle_{ nullptr }; std::string result_; + std::vector 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 +#include +#include +#include +#include + +#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 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& args, bool is_vfork) const override; + int Tune2Fs(const std::vector& 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 #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(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(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(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(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::vectorupdater->GetRuntime(); + if (!updater_runtime->IsMounted(mount_point)) { return StringValue(""); } @@ -347,42 +353,20 @@ Value* UnmountFn(const char* name, State* state, const std::vector(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& 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= mount_point= @@ -427,6 +411,7 @@ Value* FormatFn(const char* name, State* state, const std::vectorupdater->GetRuntime(); if (fs_type == "ext4") { std::vector mke2fs_args = { "/system/bin/mke2fs", "-t", "ext4", "-b", "4096", location @@ -435,12 +420,13 @@ Value* FormatFn(const char* name, State* state, const std::vectorRunProgram(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= 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(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(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::vectorupdater->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(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::vectorupdater->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::vectorupdater->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::vectorupdater->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::vectorupdater->GetRuntime(); + int status = updater_runtime->WipeBlockDevice(filename, len); + return StringValue(status == 0 ? "t" : ""); } Value* EnableRebootFn(const char* name, State* state, const std::vector>& argv) { @@ -856,7 +821,7 @@ Value* EnableRebootFn(const char* name, State* state, const std::vector(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::vectorupdater->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 #include +#include "updater/updater_runtime.h" + +Updater::Updater() : Updater(std::make_unique(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 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 lines = android::base::Split(message, "\n"); + std::vector 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(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 +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#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 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& 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& 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()); +} -- cgit v1.2.3