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
This commit is contained in:
Tianjie Xu 2019-05-14 10:54:43 -07:00
parent e7a0262c14
commit 1536db887f
13 changed files with 486 additions and 164 deletions

View file

@ -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) {}

View file

@ -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

View file

@ -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;
};

View file

@ -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;
};

View file

@ -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;

View file

@ -71,6 +71,7 @@ cc_library_static {
"dynamic_partitions.cpp",
"install.cpp",
"updater.cpp",
"updater_runtime.cpp",
],
include_dirs: [

View file

@ -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");
}

View file

@ -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 {
std::string FindBlockDeviceName(const std::string_view name) const override;
UpdaterRuntimeInterface* GetRuntime() const override {
return runtime_.get();
}
ZipArchiveHandle GetPackageHandle() const override {
return package_handle_;
}
struct selabel_handle* sehandle() const {
return sehandle_;
}
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_;
};

View file

@ -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 };
};

View file

@ -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");

View file

@ -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()) {

View file

@ -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;
}

132
updater/updater_runtime.cpp Normal file
View file

@ -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());
}