Add SafetyNet logging for payload timestamp error.
am: 15c6d372ee -s ours
Change-Id: I9ef78b22ae2047ab59391b6521d6229be6204e50
diff --git a/Android.mk b/Android.mk
index 29591fa..3f44ecc 100644
--- a/Android.mk
+++ b/Android.mk
@@ -62,9 +62,8 @@
external/gtest/include \
system
ue_common_shared_libraries := \
- libbrillo \
- libbrillo-http \
libbrillo-stream \
+ libbrillo \
libchrome
ifeq ($(local_use_dbus),1)
@@ -146,14 +145,11 @@
$(ue_update_metadata_protos_exported_static_libraries)
ue_libpayload_consumer_exported_shared_libraries := \
libcrypto-host \
- libcurl-host \
- libssl-host \
$(ue_update_metadata_protos_exported_shared_libraries)
ue_libpayload_consumer_src_files := \
common/action_processor.cc \
common/boot_control_stub.cc \
- common/certificate_checker.cc \
common/clock.cc \
common/constants.cc \
common/cpu_limiter.cc \
@@ -161,8 +157,8 @@
common/hash_calculator.cc \
common/http_common.cc \
common/http_fetcher.cc \
+ common/file_fetcher.cc \
common/hwid_override.cc \
- common/libcurl_http_fetcher.cc \
common/multi_range_http_fetcher.cc \
common/platform_constants_android.cc \
common/prefs.cc \
@@ -258,7 +254,9 @@
libexpat \
libbrillo-policy \
libhardware \
+ libcurl \
libcutils \
+ libssl \
$(ue_libpayload_consumer_exported_shared_libraries) \
$(ue_update_metadata_protos_exported_shared_libraries)
ifeq ($(local_use_binder),1)
@@ -286,7 +284,8 @@
LOCAL_LDFLAGS := $(ue_common_ldflags)
LOCAL_C_INCLUDES := \
$(ue_common_c_includes) \
- $(ue_libupdate_engine_exported_c_includes)
+ $(ue_libupdate_engine_exported_c_includes) \
+ bootable/recovery
LOCAL_STATIC_LIBRARIES := \
libpayload_consumer \
update_metadata-protos \
@@ -303,6 +302,7 @@
$(ue_update_metadata_protos_exported_shared_libraries)
LOCAL_SRC_FILES := \
boot_control_android.cc \
+ certificate_checker.cc \
common_service.cc \
connection_manager.cc \
daemon.cc \
@@ -310,6 +310,7 @@
hardware_android.cc \
image_properties_android.cc \
libcros_proxy.cc \
+ libcurl_http_fetcher.cc \
metrics.cc \
metrics_utils.cc \
omaha_request_action.cc \
@@ -336,6 +337,7 @@
update_manager/state_factory.cc \
update_manager/update_manager.cc \
update_status_utils.cc \
+ utils_android.cc \
weave_service_factory.cc
ifeq ($(local_use_binder),1)
LOCAL_AIDL_INCLUDES += $(LOCAL_PATH)/binder_bindings
@@ -371,11 +373,14 @@
$(ue_libpayload_consumer_exported_static_libraries)
ue_libupdate_engine_android_exported_shared_libraries := \
$(ue_libpayload_consumer_exported_shared_libraries) \
+ libandroid \
libbinder \
libbinderwrapper \
libbrillo-binder \
libcutils \
+ libcurl \
libhardware \
+ libssl \
libutils
include $(CLEAR_VARS)
@@ -386,7 +391,9 @@
LOCAL_CFLAGS := $(ue_common_cflags)
LOCAL_CPPFLAGS := $(ue_common_cppflags)
LOCAL_LDFLAGS := $(ue_common_ldflags)
-LOCAL_C_INCLUDES := $(ue_common_c_includes)
+LOCAL_C_INCLUDES := \
+ $(ue_common_c_includes) \
+ bootable/recovery
#TODO(deymo): Remove external/cros/system_api/dbus once the strings are moved
# out of the DBus interface.
LOCAL_C_INCLUDES += \
@@ -402,12 +409,16 @@
binder_bindings/android/os/IUpdateEngineCallback.aidl \
binder_service_android.cc \
boot_control_android.cc \
+ certificate_checker.cc \
daemon.cc \
daemon_state_android.cc \
hardware_android.cc \
+ libcurl_http_fetcher.cc \
+ network_selector_android.cc \
proxy_resolver.cc \
update_attempter_android.cc \
- update_status_utils.cc
+ update_status_utils.cc \
+ utils_android.cc
include $(BUILD_STATIC_LIBRARY)
endif # !defined(BRILLO)
@@ -455,6 +466,74 @@
LOCAL_INIT_RC := update_engine.rc
include $(BUILD_EXECUTABLE)
+# update_engine_sideload (type: executable)
+# ========================================================
+# A static binary equivalent to update_engine daemon that installs an update
+# from a local file directly instead of running in the background.
+include $(CLEAR_VARS)
+LOCAL_MODULE := update_engine_sideload
+LOCAL_FORCE_STATIC_EXECUTABLE := true
+LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin
+LOCAL_MODULE_CLASS := EXECUTABLES
+LOCAL_REQUIRED_MODULES := \
+ bspatch_recovery
+LOCAL_CPP_EXTENSION := .cc
+LOCAL_CLANG := true
+LOCAL_CFLAGS := \
+ $(ue_common_cflags) \
+ -D_UE_SIDELOAD
+LOCAL_CPPFLAGS := $(ue_common_cppflags)
+LOCAL_LDFLAGS := $(ue_common_ldflags)
+LOCAL_C_INCLUDES := \
+ $(ue_common_c_includes) \
+ bootable/recovery
+#TODO(deymo): Remove external/cros/system_api/dbus once the strings are moved
+# out of the DBus interface.
+LOCAL_C_INCLUDES += \
+ external/cros/system_api/dbus
+LOCAL_SRC_FILES := \
+ boot_control_android.cc \
+ hardware_android.cc \
+ network_selector_stub.cc \
+ proxy_resolver.cc \
+ sideload_main.cc \
+ update_attempter_android.cc \
+ update_status_utils.cc \
+ utils_android.cc
+LOCAL_STATIC_LIBRARIES := \
+ libfs_mgr \
+ libpayload_consumer \
+ update_metadata-protos \
+ $(ue_libpayload_consumer_exported_static_libraries:-host=) \
+ $(ue_update_metadata_protos_exported_static_libraries)
+# We add the static versions of the shared libraries since we are forcing this
+# binary to be a static binary, so we also need to include all the static
+# library dependencies of these static libraries.
+LOCAL_STATIC_LIBRARIES += \
+ $(ue_common_shared_libraries) \
+ libcutils \
+ libcrypto_static \
+ $(ue_update_metadata_protos_exported_shared_libraries) \
+ libevent \
+ libmodpb64 \
+ liblog
+
+ifeq ($(strip $(PRODUCT_STATIC_BOOT_CONTROL_HAL)),)
+# No static boot_control HAL defined, so no sideload support. We use a fake
+# boot_control HAL to allow compiling update_engine_sideload for test purposes.
+ifeq ($(strip $(AB_OTA_UPDATER)),true)
+$(warning No PRODUCT_STATIC_BOOT_CONTROL_HAL configured but AB_OTA_UPDATER is \
+true, no update sideload support.)
+endif # AB_OTA_UPDATER == true
+LOCAL_SRC_FILES += \
+ boot_control_recovery_stub.cc
+else # PRODUCT_STATIC_BOOT_CONTROL_HAL != ""
+LOCAL_STATIC_LIBRARIES += \
+ $(PRODUCT_STATIC_BOOT_CONTROL_HAL)
+endif # PRODUCT_STATIC_BOOT_CONTROL_HAL != ""
+
+include $(BUILD_EXECUTABLE)
+
# libupdate_engine_client (type: shared_library)
# ========================================================
include $(CLEAR_VARS)
@@ -819,12 +898,13 @@
$(ue_libupdate_engine_exported_shared_libraries:-host=) \
$(ue_libpayload_generator_exported_shared_libraries:-host=)
LOCAL_SRC_FILES := \
+ certificate_checker_unittest.cc \
common/action_pipe_unittest.cc \
common/action_processor_unittest.cc \
common/action_unittest.cc \
- common/certificate_checker_unittest.cc \
common/cpu_limiter_unittest.cc \
common/fake_prefs.cc \
+ common/file_fetcher_unittest.cc \
common/hash_calculator_unittest.cc \
common/http_fetcher_unittest.cc \
common/hwid_override_unittest.cc \
diff --git a/boot_control_android.cc b/boot_control_android.cc
index e4d3c16..d096a1b 100644
--- a/boot_control_android.cc
+++ b/boot_control_android.cc
@@ -22,33 +22,19 @@
#include <base/strings/string_util.h>
#include <brillo/make_unique_ptr.h>
#include <brillo/message_loops/message_loop.h>
-#include <cutils/properties.h>
-#include <fs_mgr.h>
#include "update_engine/common/utils.h"
+#include "update_engine/utils_android.h"
using std::string;
-namespace {
-
-// Open the appropriate fstab file and fallback to /fstab.device if
-// that's what's being used.
-static struct fstab* OpenFSTab() {
- char propbuf[PROPERTY_VALUE_MAX];
- struct fstab* fstab;
-
- property_get("ro.hardware", propbuf, "");
- string fstab_name = string("/fstab.") + propbuf;
- fstab = fs_mgr_read_fstab(fstab_name.c_str());
- if (fstab != nullptr)
- return fstab;
-
- fstab = fs_mgr_read_fstab("/fstab.device");
- return fstab;
-}
-
-} // namespace
-
+#ifdef _UE_SIDELOAD
+// When called from update_engine_sideload, we don't attempt to dynamically load
+// the right boot_control HAL, instead we use the only HAL statically linked in
+// via the PRODUCT_STATIC_BOOT_CONTROL_HAL make variable and access the module
+// struct directly.
+extern const hw_module_t HAL_MODULE_INFO_SYM;
+#endif // _UE_SIDELOAD
namespace chromeos_update_engine {
@@ -69,7 +55,18 @@
const hw_module_t* hw_module;
int ret;
+#ifdef _UE_SIDELOAD
+ // For update_engine_sideload, we simulate the hw_get_module() by accessing it
+ // from the current process directly.
+ hw_module = &HAL_MODULE_INFO_SYM;
+ ret = 0;
+ if (!hw_module ||
+ strcmp(BOOT_CONTROL_HARDWARE_MODULE_ID, hw_module->id) != 0) {
+ ret = -EINVAL;
+ }
+#else // !_UE_SIDELOAD
ret = hw_get_module(BOOT_CONTROL_HARDWARE_MODULE_ID, &hw_module);
+#endif // _UE_SIDELOAD
if (ret != 0) {
LOG(ERROR) << "Error loading boot_control HAL implementation.";
return false;
@@ -97,9 +94,6 @@
bool BootControlAndroid::GetPartitionDevice(const string& partition_name,
Slot slot,
string* device) const {
- struct fstab* fstab;
- struct fstab_rec* record;
-
// We can't use fs_mgr to look up |partition_name| because fstab
// doesn't list every slot partition (it uses the slotselect option
// to mask the suffix).
@@ -119,20 +113,9 @@
// of misc and then finding an entry in /dev matching the sysfs
// entry.
- fstab = OpenFSTab();
- if (fstab == nullptr) {
- LOG(ERROR) << "Error opening fstab file.";
+ base::FilePath misc_device;
+ if (!utils::DeviceForMountPoint("/misc", &misc_device))
return false;
- }
- record = fs_mgr_get_entry_for_mount_point(fstab, "/misc");
- if (record == nullptr) {
- LOG(ERROR) << "Error finding /misc entry in fstab file.";
- fs_mgr_free_fstab(fstab);
- return false;
- }
-
- base::FilePath misc_device = base::FilePath(record->blk_device);
- fs_mgr_free_fstab(fstab);
if (!utils::IsSymlink(misc_device.value().c_str())) {
LOG(ERROR) << "Device file " << misc_device.value() << " for /misc "
diff --git a/boot_control_recovery_stub.cc b/boot_control_recovery_stub.cc
new file mode 100644
index 0000000..129c5d0
--- /dev/null
+++ b/boot_control_recovery_stub.cc
@@ -0,0 +1,21 @@
+//
+// Copyright (C) 2016 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 <hardware/hardware.h>
+
+hw_module_t HAL_MODULE_INFO_SYM = {
+ .id = "stub",
+};
diff --git a/common/certificate_checker.cc b/certificate_checker.cc
similarity index 98%
rename from common/certificate_checker.cc
rename to certificate_checker.cc
index 86df950..6e886e7 100644
--- a/common/certificate_checker.cc
+++ b/certificate_checker.cc
@@ -14,7 +14,7 @@
// limitations under the License.
//
-#include "update_engine/common/certificate_checker.h"
+#include "update_engine/certificate_checker.h"
#include <string>
diff --git a/common/certificate_checker.h b/certificate_checker.h
similarity index 97%
rename from common/certificate_checker.h
rename to certificate_checker.h
index c785192..5d0b5ba 100644
--- a/common/certificate_checker.h
+++ b/certificate_checker.h
@@ -14,8 +14,8 @@
// limitations under the License.
//
-#ifndef UPDATE_ENGINE_COMMON_CERTIFICATE_CHECKER_H_
-#define UPDATE_ENGINE_COMMON_CERTIFICATE_CHECKER_H_
+#ifndef UPDATE_ENGINE_CERTIFICATE_CHECKER_H_
+#define UPDATE_ENGINE_CERTIFICATE_CHECKER_H_
#include <curl/curl.h>
#include <openssl/ssl.h>
@@ -172,4 +172,4 @@
} // namespace chromeos_update_engine
-#endif // UPDATE_ENGINE_COMMON_CERTIFICATE_CHECKER_H_
+#endif // UPDATE_ENGINE_CERTIFICATE_CHECKER_H_
diff --git a/common/certificate_checker_unittest.cc b/certificate_checker_unittest.cc
similarity index 97%
rename from common/certificate_checker_unittest.cc
rename to certificate_checker_unittest.cc
index c30acc5..20efce9 100644
--- a/common/certificate_checker_unittest.cc
+++ b/certificate_checker_unittest.cc
@@ -14,7 +14,7 @@
// limitations under the License.
//
-#include "update_engine/common/certificate_checker.h"
+#include "update_engine/certificate_checker.h"
#include <string>
@@ -24,8 +24,8 @@
#include <gtest/gtest.h>
#include "update_engine/common/constants.h"
-#include "update_engine/common/mock_certificate_checker.h"
#include "update_engine/common/mock_prefs.h"
+#include "update_engine/mock_certificate_checker.h"
using ::testing::DoAll;
using ::testing::Return;
diff --git a/common/constants.cc b/common/constants.cc
index f138ce3..c3556f8 100644
--- a/common/constants.cc
+++ b/common/constants.cc
@@ -18,11 +18,6 @@
namespace chromeos_update_engine {
-const char kPowerwashMarkerFile[] =
- "/mnt/stateful_partition/factory_install_reset";
-
-const char kPowerwashCommand[] = "safe fast keepimg reason=update_engine\n";
-
const char kPowerwashSafePrefsSubDirectory[] = "update_engine/prefs";
const char kPrefsSubDirectory[] = "prefs";
@@ -96,5 +91,7 @@
const char kPayloadPropertyMetadataHash[] = "METADATA_HASH";
const char kPayloadPropertyAuthorization[] = "AUTHORIZATION";
const char kPayloadPropertyUserAgent[] = "USER_AGENT";
+const char kPayloadPropertyPowerwash[] = "POWERWASH";
+const char kPayloadPropertyNetworkId[] = "NETWORK_ID";
} // namespace chromeos_update_engine
diff --git a/common/constants.h b/common/constants.h
index f0d589d..ba2594a 100644
--- a/common/constants.h
+++ b/common/constants.h
@@ -19,13 +19,6 @@
namespace chromeos_update_engine {
-// The name of the marker file used to trigger powerwash when post-install
-// completes successfully so that the device is powerwashed on next reboot.
-extern const char kPowerwashMarkerFile[];
-
-// The contents of the powerwash marker file.
-extern const char kPowerwashCommand[];
-
// Directory for AU prefs that are preserved across powerwash.
extern const char kPowerwashSafePrefsSubDirectory[];
@@ -99,6 +92,8 @@
extern const char kPayloadPropertyMetadataHash[];
extern const char kPayloadPropertyAuthorization[];
extern const char kPayloadPropertyUserAgent[];
+extern const char kPayloadPropertyPowerwash[];
+extern const char kPayloadPropertyNetworkId[];
// A download source is any combination of protocol and server (that's of
// interest to us when looking at UMA metrics) using which we may download
diff --git a/common/fake_hardware.h b/common/fake_hardware.h
index 221a451..25324ae 100644
--- a/common/fake_hardware.h
+++ b/common/fake_hardware.h
@@ -62,6 +62,18 @@
int GetPowerwashCount() const override { return powerwash_count_; }
+ bool SchedulePowerwash() override {
+ powerwash_scheduled_ = true;
+ return true;
+ }
+
+ bool CancelPowerwash() override {
+ powerwash_scheduled_ = false;
+ return true;
+ }
+
+ bool IsPowerwashScheduled() { return powerwash_scheduled_; }
+
bool GetNonVolatileDirectory(base::FilePath* path) const override {
return false;
}
@@ -121,6 +133,7 @@
std::string firmware_version_;
std::string ec_version_;
int powerwash_count_;
+ bool powerwash_scheduled_{false};
int64_t build_timestamp_{0};
DISALLOW_COPY_AND_ASSIGN(FakeHardware);
diff --git a/common/file_fetcher.cc b/common/file_fetcher.cc
new file mode 100644
index 0000000..d0a109b
--- /dev/null
+++ b/common/file_fetcher.cc
@@ -0,0 +1,186 @@
+//
+// Copyright (C) 2016 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 "update_engine/common/file_fetcher.h"
+
+#include <algorithm>
+#include <string>
+
+#include <base/bind.h>
+#include <base/format_macros.h>
+#include <base/location.h>
+#include <base/logging.h>
+#include <base/strings/string_util.h>
+#include <base/strings/stringprintf.h>
+#include <brillo/streams/file_stream.h>
+
+#include "update_engine/common/hardware_interface.h"
+#include "update_engine/common/platform_constants.h"
+
+using std::string;
+
+namespace {
+
+size_t kReadBufferSize = 16 * 1024;
+
+} // namespace
+
+namespace chromeos_update_engine {
+
+// static
+bool FileFetcher::SupportedUrl(const string& url) {
+ // Note that we require the file path to start with a "/".
+ return base::StartsWith(
+ url, "file:///", base::CompareCase::INSENSITIVE_ASCII);
+}
+
+FileFetcher::~FileFetcher() {
+ LOG_IF(ERROR, transfer_in_progress_)
+ << "Destroying the fetcher while a transfer is in progress.";
+ CleanUp();
+}
+
+// Begins the transfer, which must not have already been started.
+void FileFetcher::BeginTransfer(const string& url) {
+ CHECK(!transfer_in_progress_);
+
+ if (!SupportedUrl(url)) {
+ LOG(ERROR) << "Unsupported file URL: " << url;
+ // No HTTP error code when the URL is not supported.
+ http_response_code_ = 0;
+ CleanUp();
+ if (delegate_)
+ delegate_->TransferComplete(this, false);
+ return;
+ }
+
+ string file_path = url.substr(strlen("file://"));
+ stream_ =
+ brillo::FileStream::Open(base::FilePath(file_path),
+ brillo::Stream::AccessMode::READ,
+ brillo::FileStream::Disposition::OPEN_EXISTING,
+ nullptr);
+
+ if (!stream_) {
+ LOG(ERROR) << "Couldn't open " << file_path;
+ http_response_code_ = kHttpResponseNotFound;
+ CleanUp();
+ if (delegate_)
+ delegate_->TransferComplete(this, false);
+ return;
+ }
+ http_response_code_ = kHttpResponseOk;
+
+ if (offset_)
+ stream_->SetPosition(offset_, nullptr);
+ bytes_copied_ = 0;
+ transfer_in_progress_ = true;
+ ScheduleRead();
+}
+
+void FileFetcher::TerminateTransfer() {
+ CleanUp();
+ if (delegate_) {
+ // Note that after the callback returns this object may be destroyed.
+ delegate_->TransferTerminated(this);
+ }
+}
+
+void FileFetcher::ScheduleRead() {
+ if (transfer_paused_ || ongoing_read_ || !transfer_in_progress_)
+ return;
+
+ buffer_.resize(kReadBufferSize);
+ size_t bytes_to_read = buffer_.size();
+ if (data_length_ >= 0) {
+ bytes_to_read = std::min(static_cast<uint64_t>(bytes_to_read),
+ data_length_ - bytes_copied_);
+ }
+
+ if (!bytes_to_read) {
+ OnReadDoneCallback(0);
+ return;
+ }
+
+ ongoing_read_ = stream_->ReadAsync(
+ buffer_.data(),
+ bytes_to_read,
+ base::Bind(&FileFetcher::OnReadDoneCallback, base::Unretained(this)),
+ base::Bind(&FileFetcher::OnReadErrorCallback, base::Unretained(this)),
+ nullptr);
+
+ if (!ongoing_read_) {
+ LOG(ERROR) << "Unable to schedule an asynchronous read from the stream.";
+ CleanUp();
+ if (delegate_)
+ delegate_->TransferComplete(this, false);
+ }
+}
+
+void FileFetcher::OnReadDoneCallback(size_t bytes_read) {
+ ongoing_read_ = false;
+ if (bytes_read == 0) {
+ CleanUp();
+ if (delegate_)
+ delegate_->TransferComplete(this, true);
+ } else {
+ bytes_copied_ += bytes_read;
+ if (delegate_)
+ delegate_->ReceivedBytes(this, buffer_.data(), bytes_read);
+ ScheduleRead();
+ }
+}
+
+void FileFetcher::OnReadErrorCallback(const brillo::Error* error) {
+ LOG(ERROR) << "Asynchronous read failed: " << error->GetMessage();
+ CleanUp();
+ if (delegate_)
+ delegate_->TransferComplete(this, false);
+}
+
+void FileFetcher::Pause() {
+ if (transfer_paused_) {
+ LOG(ERROR) << "Fetcher already paused.";
+ return;
+ }
+ transfer_paused_ = true;
+}
+
+void FileFetcher::Unpause() {
+ if (!transfer_paused_) {
+ LOG(ERROR) << "Resume attempted when fetcher not paused.";
+ return;
+ }
+ transfer_paused_ = false;
+ ScheduleRead();
+}
+
+void FileFetcher::CleanUp() {
+ if (stream_) {
+ stream_->CancelPendingAsyncOperations();
+ stream_->CloseBlocking(nullptr);
+ stream_.reset();
+ }
+ // Destroying the |stream_| releases the callback, so we don't have any
+ // ongoing read at this point.
+ ongoing_read_ = false;
+ buffer_ = brillo::Blob();
+
+ transfer_in_progress_ = false;
+ transfer_paused_ = false;
+}
+
+} // namespace chromeos_update_engine
diff --git a/common/file_fetcher.h b/common/file_fetcher.h
new file mode 100644
index 0000000..2368b1d
--- /dev/null
+++ b/common/file_fetcher.h
@@ -0,0 +1,119 @@
+//
+// Copyright (C) 2016 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.
+//
+
+#ifndef UPDATE_ENGINE_COMMON_FILE_FETCHER_H_
+#define UPDATE_ENGINE_COMMON_FILE_FETCHER_H_
+
+#include <memory>
+#include <string>
+#include <utility>
+
+#include <base/logging.h>
+#include <base/macros.h>
+#include <brillo/message_loops/message_loop.h>
+#include <brillo/streams/stream.h>
+
+#include "update_engine/common/http_fetcher.h"
+
+// This is a concrete implementation of HttpFetcher that reads files
+// asynchronously.
+
+namespace chromeos_update_engine {
+
+class FileFetcher : public HttpFetcher {
+ public:
+ // Returns whether the passed url is supported.
+ static bool SupportedUrl(const std::string& url);
+
+ FileFetcher() : HttpFetcher(nullptr) {}
+
+ // Cleans up all internal state. Does not notify delegate.
+ ~FileFetcher() override;
+
+ // HttpFetcher overrides.
+ void SetOffset(off_t offset) override { offset_ = offset; }
+ void SetLength(size_t length) override { data_length_ = length; }
+ void UnsetLength() override { SetLength(0); }
+
+ // Begins the transfer if it hasn't already begun.
+ void BeginTransfer(const std::string& url) override;
+
+ // If the transfer is in progress, aborts the transfer early. The transfer
+ // cannot be resumed.
+ void TerminateTransfer() override;
+
+ // Ignore all extra headers for files.
+ void SetHeader(const std::string& header_name,
+ const std::string& header_value) override {};
+
+ // Suspend the asynchronous file read.
+ void Pause() override;
+
+ // Resume the suspended file read.
+ void Unpause() override;
+
+ size_t GetBytesDownloaded() override {
+ return static_cast<size_t>(bytes_copied_);
+ }
+
+ // Ignore all the time limits for files.
+ void set_low_speed_limit(int low_speed_bps, int low_speed_sec) override {}
+ void set_connect_timeout(int connect_timeout_seconds) override {}
+ void set_max_retry_count(int max_retry_count) override {}
+
+ private:
+ // Cleans up the fetcher, resetting its status to a newly constructed one.
+ void CleanUp();
+
+ // Schedule a new asynchronous read if the stream is not paused and no other
+ // read is in process. This method can be called at any point.
+ void ScheduleRead();
+
+ // Called from the main loop when a single read from |stream_| succeeds or
+ // fails, calling OnReadDoneCallback() and OnReadErrorCallback() respectively.
+ void OnReadDoneCallback(size_t bytes_read);
+ void OnReadErrorCallback(const brillo::Error* error);
+
+ // Whether the transfer was started and didn't finish yet.
+ bool transfer_in_progress_{false};
+
+ // Whether the transfer is paused.
+ bool transfer_paused_{false};
+
+ // Whether there's an ongoing asynchronous read. When this value is true, the
+ // the |buffer_| is being used by the |stream_|.
+ bool ongoing_read_{false};
+
+ // Total number of bytes copied.
+ uint64_t bytes_copied_{0};
+
+ // The offset inside the file where the read should start.
+ uint64_t offset_{0};
+
+ // The length of the data or -1 if unknown (will read until EOF).
+ int64_t data_length_{-1};
+
+ brillo::StreamPtr stream_;
+
+ // The buffer used for reading from the stream.
+ brillo::Blob buffer_;
+
+ DISALLOW_COPY_AND_ASSIGN(FileFetcher);
+};
+
+} // namespace chromeos_update_engine
+
+#endif // UPDATE_ENGINE_COMMON_FILE_FETCHER_H_
diff --git a/common/file_fetcher_unittest.cc b/common/file_fetcher_unittest.cc
new file mode 100644
index 0000000..9c6b0ec
--- /dev/null
+++ b/common/file_fetcher_unittest.cc
@@ -0,0 +1,37 @@
+//
+// Copyright (C) 2016 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 "update_engine/common/file_fetcher.h"
+
+#include <string>
+
+#include <gtest/gtest.h>
+
+#include "update_engine/common/test_utils.h"
+
+namespace chromeos_update_engine {
+
+class FileFetcherUnitTest : public ::testing::Test {};
+
+TEST_F(FileFetcherUnitTest, SupporterUrlsTest) {
+ EXPECT_TRUE(FileFetcher::SupportedUrl("file:///path/to/somewhere.bin"));
+ EXPECT_TRUE(FileFetcher::SupportedUrl("FILE:///I/LIKE/TO/SHOUT"));
+
+ EXPECT_FALSE(FileFetcher::SupportedUrl("file://relative"));
+ EXPECT_FALSE(FileFetcher::SupportedUrl("http:///no_http_here"));
+}
+
+} // namespace chromeos_update_engine
diff --git a/common/hardware_interface.h b/common/hardware_interface.h
index c3688b1..d5f73f7 100644
--- a/common/hardware_interface.h
+++ b/common/hardware_interface.h
@@ -67,6 +67,13 @@
// recovery don't have this value set.
virtual int GetPowerwashCount() const = 0;
+ // Signals that a powerwash (stateful partition wipe) should be performed
+ // after reboot.
+ virtual bool SchedulePowerwash() = 0;
+
+ // Cancel the powerwash operation scheduled to be performed on next boot.
+ virtual bool CancelPowerwash() = 0;
+
// Store in |path| the path to a non-volatile directory (persisted across
// reboots) available for this daemon. In case of an error, such as no
// directory available, returns false.
diff --git a/common/hash_calculator_unittest.cc b/common/hash_calculator_unittest.cc
index 27dbc56..436e6a7 100644
--- a/common/hash_calculator_unittest.cc
+++ b/common/hash_calculator_unittest.cc
@@ -25,7 +25,6 @@
#include <brillo/secure_blob.h>
#include <gtest/gtest.h>
-#include "update_engine/common/libcurl_http_fetcher.h"
#include "update_engine/common/utils.h"
using std::string;
diff --git a/common/http_fetcher_unittest.cc b/common/http_fetcher_unittest.cc
index 0d4b5da..f99007f 100644
--- a/common/http_fetcher_unittest.cc
+++ b/common/http_fetcher_unittest.cc
@@ -42,12 +42,13 @@
#include <gtest/gtest.h>
#include "update_engine/common/fake_hardware.h"
+#include "update_engine/common/file_fetcher.h"
#include "update_engine/common/http_common.h"
-#include "update_engine/common/libcurl_http_fetcher.h"
#include "update_engine/common/mock_http_fetcher.h"
#include "update_engine/common/multi_range_http_fetcher.h"
#include "update_engine/common/test_utils.h"
#include "update_engine/common/utils.h"
+#include "update_engine/libcurl_http_fetcher.h"
#include "update_engine/mock_proxy_resolver.h"
#include "update_engine/proxy_resolver.h"
@@ -219,6 +220,7 @@
virtual bool IsMock() const = 0;
virtual bool IsMulti() const = 0;
+ virtual bool IsHttpSupported() const = 0;
virtual void IgnoreServerAborting(HttpServer* server) const {}
@@ -251,6 +253,7 @@
bool IsMock() const override { return true; }
bool IsMulti() const override { return false; }
+ bool IsHttpSupported() const override { return true; }
HttpServer* CreateServer() override {
return new NullHttpServer;
@@ -291,6 +294,7 @@
bool IsMock() const override { return false; }
bool IsMulti() const override { return false; }
+ bool IsHttpSupported() const override { return true; }
void IgnoreServerAborting(HttpServer* server) const override {
// Nothing to do.
@@ -326,6 +330,42 @@
bool IsMulti() const override { return true; }
};
+class FileFetcherTest : public AnyHttpFetcherTest {
+ public:
+ // Necessary to unhide the definition in the base class.
+ using AnyHttpFetcherTest::NewLargeFetcher;
+ HttpFetcher* NewLargeFetcher(ProxyResolver* /* proxy_resolver */) override {
+ return new FileFetcher();
+ }
+
+ // Necessary to unhide the definition in the base class.
+ using AnyHttpFetcherTest::NewSmallFetcher;
+ HttpFetcher* NewSmallFetcher(ProxyResolver* proxy_resolver) override {
+ return NewLargeFetcher(proxy_resolver);
+ }
+
+ string BigUrl(in_port_t port) const override {
+ return "file://" + temp_file_.path();
+ }
+ string SmallUrl(in_port_t port) const override {
+ test_utils::WriteFileString(temp_file_.path(), "small contents");
+ return "file://" + temp_file_.path();
+ }
+ string ErrorUrl(in_port_t port) const override {
+ return "file:///path/to/non-existing-file";
+ }
+
+ bool IsMock() const override { return false; }
+ bool IsMulti() const override { return false; }
+ bool IsHttpSupported() const override { return false; }
+
+ void IgnoreServerAborting(HttpServer* server) const override {}
+
+ HttpServer* CreateServer() override { return new NullHttpServer; }
+
+ private:
+ test_utils::ScopedTempFile temp_file_{"ue_file_fetcher.XXXXXX"};
+};
//
// Infrastructure for type tests of HTTP fetcher.
@@ -363,7 +403,9 @@
// Test case types list.
typedef ::testing::Types<LibcurlHttpFetcherTest,
MockHttpFetcherTest,
- MultiRangeHttpFetcherTest> HttpFetcherTestTypes;
+ MultiRangeHttpFetcherTest,
+ FileFetcherTest>
+ HttpFetcherTestTypes;
TYPED_TEST_CASE(HttpFetcherTest, HttpFetcherTestTypes);
@@ -394,6 +436,7 @@
void TransferTerminated(HttpFetcher* fetcher) override {
ADD_FAILURE();
times_transfer_terminated_called_++;
+ MessageLoop::current()->BreakLoop();
}
// Are we expecting an error response? (default: no)
@@ -477,7 +520,7 @@
}
TYPED_TEST(HttpFetcherTest, ExtraHeadersInRequestTest) {
- if (this->test_.IsMock())
+ if (this->test_.IsMock() || !this->test_.IsHttpSupported())
return;
HttpFetcherTestDelegate delegate;
@@ -498,7 +541,11 @@
int port = server.GetPort();
ASSERT_TRUE(server.started_);
- StartTransfer(fetcher.get(), LocalServerUrlForPath(port, "/echo-headers"));
+ this->loop_.PostTask(
+ FROM_HERE,
+ base::Bind(StartTransfer,
+ fetcher.get(),
+ LocalServerUrlForPath(port, "/echo-headers")));
this->loop_.Run();
EXPECT_NE(string::npos,
@@ -571,7 +618,7 @@
// This test will pause the fetcher while the download is not yet started
// because it is waiting for the proxy to be resolved.
TYPED_TEST(HttpFetcherTest, PauseWhileResolvingProxyTest) {
- if (this->test_.IsMock())
+ if (this->test_.IsMock() || !this->test_.IsHttpSupported())
return;
MockProxyResolver mock_resolver;
unique_ptr<HttpFetcher> fetcher(this->test_.NewLargeFetcher(&mock_resolver));
@@ -684,7 +731,7 @@
} // namespace
TYPED_TEST(HttpFetcherTest, FlakyTest) {
- if (this->test_.IsMock())
+ if (this->test_.IsMock() || !this->test_.IsHttpSupported())
return;
{
FlakyHttpFetcherTestDelegate delegate;
@@ -897,7 +944,7 @@
} // namespace
TYPED_TEST(HttpFetcherTest, SimpleRedirectTest) {
- if (this->test_.IsMock())
+ if (this->test_.IsMock() || !this->test_.IsHttpSupported())
return;
unique_ptr<HttpServer> server(this->test_.CreateServer());
@@ -912,7 +959,7 @@
}
TYPED_TEST(HttpFetcherTest, MaxRedirectTest) {
- if (this->test_.IsMock())
+ if (this->test_.IsMock() || !this->test_.IsHttpSupported())
return;
unique_ptr<HttpServer> server(this->test_.CreateServer());
@@ -928,7 +975,7 @@
}
TYPED_TEST(HttpFetcherTest, BeyondMaxRedirectTest) {
- if (this->test_.IsMock())
+ if (this->test_.IsMock() || !this->test_.IsHttpSupported())
return;
unique_ptr<HttpServer> server(this->test_.CreateServer());
diff --git a/common/prefs.cc b/common/prefs.cc
index a4b97d0..12d06c0 100644
--- a/common/prefs.cc
+++ b/common/prefs.cc
@@ -29,31 +29,12 @@
namespace chromeos_update_engine {
-bool Prefs::Init(const base::FilePath& prefs_dir) {
- prefs_dir_ = prefs_dir;
- return true;
+bool PrefsBase::GetString(const string& key, string* value) const {
+ return storage_->GetKey(key, value);
}
-bool Prefs::GetString(const string& key, string* value) const {
- base::FilePath filename;
- TEST_AND_RETURN_FALSE(GetFileNameForKey(key, &filename));
- if (!base::ReadFileToString(filename, value)) {
- LOG(INFO) << key << " not present in " << prefs_dir_.value();
- return false;
- }
- return true;
-}
-
-bool Prefs::SetString(const string& key, const string& value) {
- base::FilePath filename;
- TEST_AND_RETURN_FALSE(GetFileNameForKey(key, &filename));
- if (!base::DirectoryExists(filename.DirName())) {
- // Only attempt to create the directory if it doesn't exist to avoid calls
- // to parent directories where we might not have permission to write to.
- TEST_AND_RETURN_FALSE(base::CreateDirectory(filename.DirName()));
- }
- TEST_AND_RETURN_FALSE(base::WriteFile(filename, value.data(), value.size()) ==
- static_cast<int>(value.size()));
+bool PrefsBase::SetString(const string& key, const string& value) {
+ TEST_AND_RETURN_FALSE(storage_->SetKey(key, value));
const auto observers_for_key = observers_.find(key);
if (observers_for_key != observers_.end()) {
std::vector<ObserverInterface*> copy_observers(observers_for_key->second);
@@ -63,7 +44,7 @@
return true;
}
-bool Prefs::GetInt64(const string& key, int64_t* value) const {
+bool PrefsBase::GetInt64(const string& key, int64_t* value) const {
string str_value;
if (!GetString(key, &str_value))
return false;
@@ -72,11 +53,11 @@
return true;
}
-bool Prefs::SetInt64(const string& key, const int64_t value) {
+bool PrefsBase::SetInt64(const string& key, const int64_t value) {
return SetString(key, base::Int64ToString(value));
}
-bool Prefs::GetBoolean(const string& key, bool* value) const {
+bool PrefsBase::GetBoolean(const string& key, bool* value) const {
string str_value;
if (!GetString(key, &str_value))
return false;
@@ -92,20 +73,16 @@
return false;
}
-bool Prefs::SetBoolean(const string& key, const bool value) {
+bool PrefsBase::SetBoolean(const string& key, const bool value) {
return SetString(key, value ? "true" : "false");
}
-bool Prefs::Exists(const string& key) const {
- base::FilePath filename;
- TEST_AND_RETURN_FALSE(GetFileNameForKey(key, &filename));
- return base::PathExists(filename);
+bool PrefsBase::Exists(const string& key) const {
+ return storage_->KeyExists(key);
}
-bool Prefs::Delete(const string& key) {
- base::FilePath filename;
- TEST_AND_RETURN_FALSE(GetFileNameForKey(key, &filename));
- TEST_AND_RETURN_FALSE(base::DeleteFile(filename, false));
+bool PrefsBase::Delete(const string& key) {
+ TEST_AND_RETURN_FALSE(storage_->DeleteKey(key));
const auto observers_for_key = observers_.find(key);
if (observers_for_key != observers_.end()) {
std::vector<ObserverInterface*> copy_observers(observers_for_key->second);
@@ -115,11 +92,11 @@
return true;
}
-void Prefs::AddObserver(const string& key, ObserverInterface* observer) {
+void PrefsBase::AddObserver(const string& key, ObserverInterface* observer) {
observers_[key].push_back(observer);
}
-void Prefs::RemoveObserver(const string& key, ObserverInterface* observer) {
+void PrefsBase::RemoveObserver(const string& key, ObserverInterface* observer) {
std::vector<ObserverInterface*>& observers_for_key = observers_[key];
auto observer_it =
std::find(observers_for_key.begin(), observers_for_key.end(), observer);
@@ -127,8 +104,55 @@
observers_for_key.erase(observer_it);
}
-bool Prefs::GetFileNameForKey(const string& key,
- base::FilePath* filename) const {
+// Prefs
+
+bool Prefs::Init(const base::FilePath& prefs_dir) {
+ return file_storage_.Init(prefs_dir);
+}
+
+bool Prefs::FileStorage::Init(const base::FilePath& prefs_dir) {
+ prefs_dir_ = prefs_dir;
+ return true;
+}
+
+bool Prefs::FileStorage::GetKey(const string& key, string* value) const {
+ base::FilePath filename;
+ TEST_AND_RETURN_FALSE(GetFileNameForKey(key, &filename));
+ if (!base::ReadFileToString(filename, value)) {
+ LOG(INFO) << key << " not present in " << prefs_dir_.value();
+ return false;
+ }
+ return true;
+}
+
+bool Prefs::FileStorage::SetKey(const string& key, const string& value) {
+ base::FilePath filename;
+ TEST_AND_RETURN_FALSE(GetFileNameForKey(key, &filename));
+ if (!base::DirectoryExists(filename.DirName())) {
+ // Only attempt to create the directory if it doesn't exist to avoid calls
+ // to parent directories where we might not have permission to write to.
+ TEST_AND_RETURN_FALSE(base::CreateDirectory(filename.DirName()));
+ }
+ TEST_AND_RETURN_FALSE(base::WriteFile(filename, value.data(), value.size()) ==
+ static_cast<int>(value.size()));
+ return true;
+}
+
+bool Prefs::FileStorage::KeyExists(const string& key) const {
+ base::FilePath filename;
+ TEST_AND_RETURN_FALSE(GetFileNameForKey(key, &filename));
+ return base::PathExists(filename);
+}
+
+bool Prefs::FileStorage::DeleteKey(const string& key) {
+ base::FilePath filename;
+ TEST_AND_RETURN_FALSE(GetFileNameForKey(key, &filename));
+ TEST_AND_RETURN_FALSE(base::DeleteFile(filename, false));
+ return true;
+}
+
+bool Prefs::FileStorage::GetFileNameForKey(const string& key,
+ base::FilePath* filename) const {
// Allows only non-empty keys containing [A-Za-z0-9_-].
TEST_AND_RETURN_FALSE(!key.empty());
for (size_t i = 0; i < key.size(); ++i) {
@@ -140,4 +164,33 @@
return true;
}
+// MemoryPrefs
+
+bool MemoryPrefs::MemoryStorage::GetKey(const string& key,
+ string* value) const {
+ auto it = values_.find(key);
+ if (it == values_.end())
+ return false;
+ *value = it->second;
+ return true;
+}
+
+bool MemoryPrefs::MemoryStorage::SetKey(const string& key,
+ const string& value) {
+ values_[key] = value;
+ return true;
+}
+
+bool MemoryPrefs::MemoryStorage::KeyExists(const string& key) const {
+ return values_.find(key) != values_.end();
+}
+
+bool MemoryPrefs::MemoryStorage::DeleteKey(const string& key) {
+ auto it = values_.find(key);
+ if (it == values_.end())
+ return false;
+ values_.erase(it);
+ return true;
+}
+
} // namespace chromeos_update_engine
diff --git a/common/prefs.h b/common/prefs.h
index f11abc3..0116454 100644
--- a/common/prefs.h
+++ b/common/prefs.h
@@ -28,18 +28,36 @@
namespace chromeos_update_engine {
-// Implements a preference store by storing the value associated with
-// a key in a separate file named after the key under a preference
-// store directory.
-
-class Prefs : public PrefsInterface {
+// Implements a preference store by storing the value associated with a key
+// in a given storage passed during construction.
+class PrefsBase : public PrefsInterface {
public:
- Prefs() = default;
+ // Storage interface used to set and retrieve keys.
+ class StorageInterface {
+ public:
+ StorageInterface() = default;
+ virtual ~StorageInterface() = default;
- // Initializes the store by associating this object with |prefs_dir|
- // as the preference store directory. Returns true on success, false
- // otherwise.
- bool Init(const base::FilePath& prefs_dir);
+ // Get the key named |key| and store its value in the referenced |value|.
+ // Returns whether the operation succeeded.
+ virtual bool GetKey(const std::string& key, std::string* value) const = 0;
+
+ // Set the value of the key named |key| to |value| regardless of the
+ // previous value. Returns whether the operation succeeded.
+ virtual bool SetKey(const std::string& key, const std::string& value) = 0;
+
+ // Returns whether the key named |key| exists.
+ virtual bool KeyExists(const std::string& key) const = 0;
+
+ // Deletes the value associated with the key name |key|. Returns whether the
+ // key was deleted.
+ virtual bool DeleteKey(const std::string& key) = 0;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(StorageInterface);
+ };
+
+ explicit PrefsBase(StorageInterface* storage) : storage_(storage) {}
// PrefsInterface methods.
bool GetString(const std::string& key, std::string* value) const override;
@@ -58,24 +76,93 @@
ObserverInterface* observer) override;
private:
+ // The registered observers watching for changes.
+ std::map<std::string, std::vector<ObserverInterface*>> observers_;
+
+ // The concrete implementation of the storage used for the keys.
+ StorageInterface* storage_;
+
+ DISALLOW_COPY_AND_ASSIGN(PrefsBase);
+};
+
+// Implements a preference store by storing the value associated with
+// a key in a separate file named after the key under a preference
+// store directory.
+
+class Prefs : public PrefsBase {
+ public:
+ Prefs() : PrefsBase(&file_storage_) {}
+
+ // Initializes the store by associating this object with |prefs_dir|
+ // as the preference store directory. Returns true on success, false
+ // otherwise.
+ bool Init(const base::FilePath& prefs_dir);
+
+ private:
FRIEND_TEST(PrefsTest, GetFileNameForKey);
FRIEND_TEST(PrefsTest, GetFileNameForKeyBadCharacter);
FRIEND_TEST(PrefsTest, GetFileNameForKeyEmpty);
- // Sets |filename| to the full path to the file containing the data
- // associated with |key|. Returns true on success, false otherwise.
- bool GetFileNameForKey(const std::string& key,
- base::FilePath* filename) const;
+ class FileStorage : public PrefsBase::StorageInterface {
+ public:
+ FileStorage() = default;
- // Preference store directory.
- base::FilePath prefs_dir_;
+ bool Init(const base::FilePath& prefs_dir);
- // The registered observers watching for changes.
- std::map<std::string, std::vector<ObserverInterface*>> observers_;
+ // PrefsBase::StorageInterface overrides.
+ bool GetKey(const std::string& key, std::string* value) const override;
+ bool SetKey(const std::string& key, const std::string& value) override;
+ bool KeyExists(const std::string& key) const override;
+ bool DeleteKey(const std::string& key) override;
+
+ private:
+ FRIEND_TEST(PrefsTest, GetFileNameForKey);
+ FRIEND_TEST(PrefsTest, GetFileNameForKeyBadCharacter);
+ FRIEND_TEST(PrefsTest, GetFileNameForKeyEmpty);
+
+ // Sets |filename| to the full path to the file containing the data
+ // associated with |key|. Returns true on success, false otherwise.
+ bool GetFileNameForKey(const std::string& key,
+ base::FilePath* filename) const;
+
+ // Preference store directory.
+ base::FilePath prefs_dir_;
+ };
+
+ // The concrete file storage implementation.
+ FileStorage file_storage_;
DISALLOW_COPY_AND_ASSIGN(Prefs);
};
+// Implements a preference store in memory. The stored values are lost when the
+// object is destroyed.
+
+class MemoryPrefs : public PrefsBase {
+ public:
+ MemoryPrefs() : PrefsBase(&mem_storage_) {}
+
+ private:
+ class MemoryStorage : public PrefsBase::StorageInterface {
+ public:
+ MemoryStorage() = default;
+
+ // PrefsBase::StorageInterface overrides.
+ bool GetKey(const std::string& key, std::string* value) const override;
+ bool SetKey(const std::string& key, const std::string& value) override;
+ bool KeyExists(const std::string& key) const override;
+ bool DeleteKey(const std::string& key) override;
+
+ private:
+ // The std::map holding the values in memory.
+ std::map<std::string, std::string> values_;
+ };
+
+ // The concrete memory storage implementation.
+ MemoryStorage mem_storage_;
+
+ DISALLOW_COPY_AND_ASSIGN(MemoryPrefs);
+};
} // namespace chromeos_update_engine
#endif // UPDATE_ENGINE_COMMON_PREFS_H_
diff --git a/common/prefs_unittest.cc b/common/prefs_unittest.cc
index d94623a..0822599 100644
--- a/common/prefs_unittest.cc
+++ b/common/prefs_unittest.cc
@@ -18,6 +18,7 @@
#include <inttypes.h>
+#include <limits>
#include <string>
#include <base/files/file_util.h>
@@ -62,18 +63,18 @@
const char kAllvalidCharsKey[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_-";
base::FilePath path;
- EXPECT_TRUE(prefs_.GetFileNameForKey(kAllvalidCharsKey, &path));
+ EXPECT_TRUE(prefs_.file_storage_.GetFileNameForKey(kAllvalidCharsKey, &path));
EXPECT_EQ(prefs_dir_.Append(kAllvalidCharsKey).value(), path.value());
}
TEST_F(PrefsTest, GetFileNameForKeyBadCharacter) {
base::FilePath path;
- EXPECT_FALSE(prefs_.GetFileNameForKey("ABC abc", &path));
+ EXPECT_FALSE(prefs_.file_storage_.GetFileNameForKey("ABC abc", &path));
}
TEST_F(PrefsTest, GetFileNameForKeyEmpty) {
base::FilePath path;
- EXPECT_FALSE(prefs_.GetFileNameForKey("", &path));
+ EXPECT_FALSE(prefs_.file_storage_.GetFileNameForKey("", &path));
}
TEST_F(PrefsTest, GetString) {
@@ -338,4 +339,24 @@
prefs_.RemoveObserver(kInvalidKey, &mock_obserser);
}
+class MemoryPrefsTest : public ::testing::Test {
+ protected:
+ MemoryPrefs prefs_;
+};
+
+TEST_F(MemoryPrefsTest, BasicTest) {
+ EXPECT_FALSE(prefs_.Exists(kKey));
+ int64_t value = 0;
+ EXPECT_FALSE(prefs_.GetInt64(kKey, &value));
+
+ EXPECT_TRUE(prefs_.SetInt64(kKey, 1234));
+ EXPECT_TRUE(prefs_.Exists(kKey));
+ EXPECT_TRUE(prefs_.GetInt64(kKey, &value));
+ EXPECT_EQ(1234, value);
+
+ EXPECT_TRUE(prefs_.Delete(kKey));
+ EXPECT_FALSE(prefs_.Exists(kKey));
+ EXPECT_FALSE(prefs_.Delete(kKey));
+}
+
} // namespace chromeos_update_engine
diff --git a/common/test_utils.h b/common/test_utils.h
index 60ec90e..ed64c80 100644
--- a/common/test_utils.h
+++ b/common/test_utils.h
@@ -174,7 +174,7 @@
unlinker_.reset(new ScopedPathUnlinker(path_));
}
- const std::string& path() { return path_; }
+ const std::string& path() const { return path_; }
private:
std::string path_;
diff --git a/common/utils.cc b/common/utils.cc
index 5d15da0..1338268 100644
--- a/common/utils.cc
+++ b/common/utils.cc
@@ -87,6 +87,11 @@
// The path to the kernel's boot_id.
const char kBootIdPath[] = "/proc/sys/kernel/random/boot_id";
+// A pointer to a null-terminated string containing the root directory where all
+// the temporary files should be created. If null, the system default is used
+// instead.
+const char* root_temp_dir = nullptr;
+
// Return true if |disk_name| is an MTD or a UBI device. Note that this test is
// simply based on the name of the device.
bool IsMtdDeviceName(const string& disk_name) {
@@ -143,13 +148,17 @@
}
base::FilePath temp_dir;
+ if (root_temp_dir) {
+ temp_dir = base::FilePath(root_temp_dir);
+ } else {
#ifdef __ANDROID__
- temp_dir = base::FilePath(constants::kNonVolatileDirectory).Append("tmp");
+ temp_dir = base::FilePath(constants::kNonVolatileDirectory).Append("tmp");
+#else
+ TEST_AND_RETURN_FALSE(base::GetTempDir(&temp_dir));
+#endif // __ANDROID__
+ }
if (!base::PathExists(temp_dir))
TEST_AND_RETURN_FALSE(base::CreateDirectory(temp_dir));
-#else
- TEST_AND_RETURN_FALSE(base::GetTempDir(&temp_dir));
-#endif // __ANDROID__
*template_path = temp_dir.Append(path);
return true;
}
@@ -158,6 +167,10 @@
namespace utils {
+void SetRootTempDir(const char* new_root_temp_dir) {
+ root_temp_dir = new_root_temp_dir;
+}
+
string ParseECVersion(string input_line) {
base::TrimWhitespaceASCII(input_line, base::TRIM_ALL, &input_line);
@@ -692,14 +705,24 @@
}
bool UnmountFilesystem(const string& mountpoint) {
- for (int num_retries = 0; ; ++num_retries) {
+ int num_retries = 1;
+ for (;; ++num_retries) {
if (umount(mountpoint.c_str()) == 0)
+ return true;
+ if (errno != EBUSY || num_retries >= kUnmountMaxNumOfRetries)
break;
-
- TEST_AND_RETURN_FALSE_ERRNO(errno == EBUSY &&
- num_retries < kUnmountMaxNumOfRetries);
usleep(kUnmountRetryIntervalInMicroseconds);
}
+ if (errno == EINVAL) {
+ LOG(INFO) << "Not a mountpoint: " << mountpoint;
+ return false;
+ }
+ PLOG(WARNING) << "Error unmounting " << mountpoint << " after " << num_retries
+ << " attempts. Lazy unmounting instead, error was";
+ if (umount2(mountpoint.c_str(), MNT_DETACH) != 0) {
+ PLOG(ERROR) << "Lazy unmount failed";
+ return false;
+ }
return true;
}
@@ -1055,35 +1078,6 @@
return base_code;
}
-bool CreatePowerwashMarkerFile(const char* file_path) {
- const char* marker_file = file_path ? file_path : kPowerwashMarkerFile;
- bool result = utils::WriteFile(marker_file,
- kPowerwashCommand,
- strlen(kPowerwashCommand));
- if (result) {
- LOG(INFO) << "Created " << marker_file << " to powerwash on next reboot";
- } else {
- PLOG(ERROR) << "Error in creating powerwash marker file: " << marker_file;
- }
-
- return result;
-}
-
-bool DeletePowerwashMarkerFile(const char* file_path) {
- const char* marker_file = file_path ? file_path : kPowerwashMarkerFile;
- const base::FilePath kPowerwashMarkerPath(marker_file);
- bool result = base::DeleteFile(kPowerwashMarkerPath, false);
-
- if (result)
- LOG(INFO) << "Successfully deleted the powerwash marker file : "
- << marker_file;
- else
- PLOG(ERROR) << "Could not delete the powerwash marker file : "
- << marker_file;
-
- return result;
-}
-
Time TimeFromStructTimespec(struct timespec *ts) {
int64_t us = static_cast<int64_t>(ts->tv_sec) * Time::kMicrosecondsPerSecond +
static_cast<int64_t>(ts->tv_nsec) / Time::kNanosecondsPerMicrosecond;
diff --git a/common/utils.h b/common/utils.h
index 88c33b7..24bf702 100644
--- a/common/utils.h
+++ b/common/utils.h
@@ -131,6 +131,13 @@
// only returns true if "/dev/ubi%d_0" becomes available in |timeout| seconds.
bool TryAttachingUbiVolume(int volume_num, int timeout);
+// Setup the directory |new_root_temp_dir| to be used as the root directory for
+// temporary files instead of the system's default. If the directory doesn't
+// exists, it will be created when first used.
+// NOTE: The memory pointed by |new_root_temp_dir| must be available until this
+// function is called again with a different value.
+void SetRootTempDir(const char* new_root_temp_dir);
+
// If |base_filename_template| is neither absolute (starts with "/") nor
// explicitly relative to the current working directory (starts with "./" or
// "../"), then it is prepended the system's temporary directory. On success,
@@ -315,16 +322,6 @@
// it'll return the same value again.
ErrorCode GetBaseErrorCode(ErrorCode code);
-// Creates the powerwash marker file with the appropriate commands in it. Uses
-// |file_path| as the path to the marker file if non-null, otherwise uses the
-// global default. Returns true if successfully created. False otherwise.
-bool CreatePowerwashMarkerFile(const char* file_path);
-
-// Deletes the marker file used to trigger Powerwash using clobber-state. Uses
-// |file_path| as the path to the marker file if non-null, otherwise uses the
-// global default. Returns true if successfully deleted. False otherwise.
-bool DeletePowerwashMarkerFile(const char* file_path);
-
// Decodes the data in |base64_encoded| and stores it in a temporary
// file. Returns false if the given data is empty, not well-formed
// base64 or if an error occurred. If true is returned, the decoded
diff --git a/common/utils_unittest.cc b/common/utils_unittest.cc
index f840a75..b0beff9 100644
--- a/common/utils_unittest.cc
+++ b/common/utils_unittest.cc
@@ -17,7 +17,9 @@
#include "update_engine/common/utils.h"
#include <errno.h>
+#include <fcntl.h>
#include <stdint.h>
+#include <sys/mount.h>
#include <sys/stat.h>
#include <sys/types.h>
@@ -545,4 +547,37 @@
EXPECT_TRUE(BoolMacroTestHelper());
}
+TEST(UtilsTest, UnmountFilesystemFailureTest) {
+ EXPECT_FALSE(utils::UnmountFilesystem("/path/to/non-existing-dir"));
+}
+
+TEST(UtilsTest, UnmountFilesystemBusyFailureTest) {
+ string tmp_image;
+ EXPECT_TRUE(utils::MakeTempFile("img.XXXXXX", &tmp_image, nullptr));
+ ScopedPathUnlinker tmp_image_unlinker(tmp_image);
+
+ EXPECT_TRUE(base::CopyFile(
+ test_utils::GetBuildArtifactsPath().Append("gen/disk_ext2_4k.img"),
+ base::FilePath(tmp_image)));
+
+ base::ScopedTempDir mnt_dir;
+ EXPECT_TRUE(mnt_dir.CreateUniqueTempDir());
+
+ string loop_dev;
+ test_utils::ScopedLoopbackDeviceBinder loop_binder(
+ tmp_image, true, &loop_dev);
+
+ // This is the actual test part. While we hold a file descriptor open for the
+ // mounted filesystem, umount should still succeed.
+ EXPECT_TRUE(utils::MountFilesystem(
+ loop_dev, mnt_dir.path().value(), MS_RDONLY, "ext4", ""));
+ string target_file = mnt_dir.path().Append("empty-file").value();
+ int fd = HANDLE_EINTR(open(target_file.c_str(), O_RDONLY));
+ EXPECT_GE(fd, 0);
+ EXPECT_TRUE(utils::UnmountFilesystem(mnt_dir.path().value()));
+ IGNORE_EINTR(close(fd));
+ // The filesystem was already unmounted so this call should fail.
+ EXPECT_FALSE(utils::UnmountFilesystem(mnt_dir.path().value()));
+}
+
} // namespace chromeos_update_engine
diff --git a/daemon_state_android.cc b/daemon_state_android.cc
index cf1e892..0960b1a 100644
--- a/daemon_state_android.cc
+++ b/daemon_state_android.cc
@@ -58,6 +58,11 @@
return false;
}
+ // The CertificateChecker singleton is used by the update attempter.
+ certificate_checker_.reset(
+ new CertificateChecker(prefs_.get(), &openssl_wrapper_));
+ certificate_checker_->Init();
+
// Initialize the UpdateAttempter before the UpdateManager.
update_attempter_.reset(new UpdateAttempterAndroid(
this, prefs_.get(), boot_control_.get(), hardware_.get()));
diff --git a/daemon_state_android.h b/daemon_state_android.h
index 5db3ef0..928a14e 100644
--- a/daemon_state_android.h
+++ b/daemon_state_android.h
@@ -20,8 +20,8 @@
#include <memory>
#include <set>
+#include "update_engine/certificate_checker.h"
#include "update_engine/common/boot_control_interface.h"
-#include "update_engine/common/certificate_checker.h"
#include "update_engine/common/hardware_interface.h"
#include "update_engine/common/prefs_interface.h"
#include "update_engine/daemon_state_interface.h"
@@ -43,7 +43,7 @@
void AddObserver(ServiceObserverInterface* observer) override;
void RemoveObserver(ServiceObserverInterface* observer) override;
- const std::set<ServiceObserverInterface*>& service_observers() {
+ const std::set<ServiceObserverInterface*>& service_observers() override {
return service_observers_;
}
@@ -64,6 +64,11 @@
// The main class handling the updates.
std::unique_ptr<UpdateAttempterAndroid> update_attempter_;
+
+ // OpenSSLWrapper and CertificateChecker used for checking changes in SSL
+ // certificates.
+ OpenSSLWrapper openssl_wrapper_;
+ std::unique_ptr<CertificateChecker> certificate_checker_;
};
} // namespace chromeos_update_engine
diff --git a/daemon_state_interface.h b/daemon_state_interface.h
index a0944aa..2356816 100644
--- a/daemon_state_interface.h
+++ b/daemon_state_interface.h
@@ -20,6 +20,7 @@
#include "update_engine/service_observer_interface.h"
#include <memory>
+#include <set>
namespace chromeos_update_engine {
@@ -36,6 +37,9 @@
virtual void AddObserver(ServiceObserverInterface* observer) = 0;
virtual void RemoveObserver(ServiceObserverInterface* observer) = 0;
+ // Return the set of current observers.
+ virtual const std::set<ServiceObserverInterface*>& service_observers() = 0;
+
protected:
DaemonStateInterface() = default;
};
diff --git a/hardware_android.cc b/hardware_android.cc
index 277a6c6..5b13eec 100644
--- a/hardware_android.cc
+++ b/hardware_android.cc
@@ -16,12 +16,22 @@
#include "update_engine/hardware_android.h"
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <algorithm>
+
+#include <bootloader.h>
+
#include <base/files/file_util.h>
#include <brillo/make_unique_ptr.h>
#include <cutils/properties.h>
#include "update_engine/common/hardware.h"
#include "update_engine/common/platform_constants.h"
+#include "update_engine/common/utils.h"
+#include "update_engine/utils_android.h"
using std::string;
@@ -29,8 +39,48 @@
namespace {
+// The powerwash arguments passed to recovery. Arguments are separated by \n.
+const char kAndroidRecoveryPowerwashCommand[] =
+ "recovery\n"
+ "--wipe_data\n"
+ "--reason=wipe_data_from_ota\n";
+
const char kPropBuildDateUTC[] = "ro.build.date.utc";
+// Write a recovery command line |message| to the BCB. The arguments to recovery
+// must be separated by '\n'. An empty string will erase the BCB.
+bool WriteBootloaderRecoveryMessage(const string& message) {
+ base::FilePath misc_device;
+ if (!utils::DeviceForMountPoint("/misc", &misc_device))
+ return false;
+
+ // Setup a bootloader_message with just the command and recovery fields set.
+ bootloader_message boot = {};
+ if (!message.empty()) {
+ strncpy(boot.command, "boot-recovery", sizeof(boot.command) - 1);
+ memcpy(boot.recovery,
+ message.data(),
+ std::min(message.size(), sizeof(boot.recovery) - 1));
+ }
+
+ int fd =
+ HANDLE_EINTR(open(misc_device.value().c_str(), O_WRONLY | O_SYNC, 0600));
+ if (fd < 0) {
+ PLOG(ERROR) << "Opening misc";
+ return false;
+ }
+ ScopedFdCloser fd_closer(&fd);
+ // We only re-write the first part of the bootloader_message, up to and
+ // including the recovery message.
+ size_t boot_size =
+ offsetof(bootloader_message, recovery) + sizeof(boot.recovery);
+ if (!utils::WriteAll(fd, &boot, boot_size)) {
+ PLOG(ERROR) << "Writing recovery command to misc";
+ return false;
+ }
+ return true;
+}
+
} // namespace
namespace hardware {
@@ -101,6 +151,15 @@
return 0;
}
+bool HardwareAndroid::SchedulePowerwash() {
+ LOG(INFO) << "Scheduling a powerwash to BCB.";
+ return WriteBootloaderRecoveryMessage(kAndroidRecoveryPowerwashCommand);
+}
+
+bool HardwareAndroid::CancelPowerwash() {
+ return WriteBootloaderRecoveryMessage("");
+}
+
bool HardwareAndroid::GetNonVolatileDirectory(base::FilePath* path) const {
base::FilePath local_path(constants::kNonVolatileDirectory);
if (!base::PathExists(local_path)) {
diff --git a/hardware_android.h b/hardware_android.h
index c239ee3..6561377 100644
--- a/hardware_android.h
+++ b/hardware_android.h
@@ -41,6 +41,8 @@
std::string GetFirmwareVersion() const override;
std::string GetECVersion() const override;
int GetPowerwashCount() const override;
+ bool SchedulePowerwash() override;
+ bool CancelPowerwash() override;
bool GetNonVolatileDirectory(base::FilePath* path) const override;
bool GetPowerwashSafeDirectory(base::FilePath* path) const override;
int64_t GetBuildTimestamp() const override;
diff --git a/hardware_chromeos.cc b/hardware_chromeos.cc
index 4d467ef..88b2783 100644
--- a/hardware_chromeos.cc
+++ b/hardware_chromeos.cc
@@ -50,6 +50,12 @@
// a powerwash is performed.
const char kPowerwashCountMarker[] = "powerwash_count";
+// UpdateManager config path.
+const char* kConfigFilePath = "/etc/update_manager.conf";
+
+// UpdateManager config options:
+const char* kConfigOptsIsOOBEEnabled = "is_oobe_enabled";
+
} // namespace
namespace chromeos_update_engine {
@@ -140,6 +146,32 @@
return powerwash_count;
}
+bool HardwareChromeOS::SchedulePowerwash() {
+ bool result = utils::WriteFile(
+ kPowerwashMarkerFile, kPowerwashCommand, strlen(kPowerwashCommand));
+ if (result) {
+ LOG(INFO) << "Created " << marker_file << " to powerwash on next reboot";
+ } else {
+ PLOG(ERROR) << "Error in creating powerwash marker file: " << marker_file;
+ }
+
+ return result;
+}
+
+bool HardwareChromeOS::CancelPowerwash() {
+ bool result = base::DeleteFile(base::FilePath(kPowerwashMarkerFile), false);
+
+ if (result) {
+ LOG(INFO) << "Successfully deleted the powerwash marker file : "
+ << marker_file;
+ } else {
+ PLOG(ERROR) << "Could not delete the powerwash marker file : "
+ << marker_file;
+ }
+
+ return result;
+}
+
bool HardwareChromeOS::GetNonVolatileDirectory(base::FilePath* path) const {
*path = base::FilePath(constants::kNonVolatileDirectory);
return true;
diff --git a/hardware_chromeos.h b/hardware_chromeos.h
index c5b306b..e3f086f 100644
--- a/hardware_chromeos.h
+++ b/hardware_chromeos.h
@@ -42,6 +42,8 @@
std::string GetFirmwareVersion() const override;
std::string GetECVersion() const override;
int GetPowerwashCount() const override;
+ bool SchedulePowerwash() override;
+ bool CancelPowerwash() override;
bool GetNonVolatileDirectory(base::FilePath* path) const override;
bool GetPowerwashSafeDirectory(base::FilePath* path) const override;
int64_t GetBuildTimestamp() const override;
diff --git a/common/libcurl_http_fetcher.cc b/libcurl_http_fetcher.cc
similarity index 99%
rename from common/libcurl_http_fetcher.cc
rename to libcurl_http_fetcher.cc
index b2dffc2..04d5c06 100644
--- a/common/libcurl_http_fetcher.cc
+++ b/libcurl_http_fetcher.cc
@@ -14,7 +14,7 @@
// limitations under the License.
//
-#include "update_engine/common/libcurl_http_fetcher.h"
+#include "update_engine/libcurl_http_fetcher.h"
#include <algorithm>
#include <string>
@@ -26,7 +26,7 @@
#include <base/strings/string_util.h>
#include <base/strings/stringprintf.h>
-#include "update_engine/common/certificate_checker.h"
+#include "update_engine/certificate_checker.h"
#include "update_engine/common/hardware_interface.h"
#include "update_engine/common/platform_constants.h"
diff --git a/common/libcurl_http_fetcher.h b/libcurl_http_fetcher.h
similarity index 97%
rename from common/libcurl_http_fetcher.h
rename to libcurl_http_fetcher.h
index d126171..199c495 100644
--- a/common/libcurl_http_fetcher.h
+++ b/libcurl_http_fetcher.h
@@ -14,8 +14,8 @@
// limitations under the License.
//
-#ifndef UPDATE_ENGINE_COMMON_LIBCURL_HTTP_FETCHER_H_
-#define UPDATE_ENGINE_COMMON_LIBCURL_HTTP_FETCHER_H_
+#ifndef UPDATE_ENGINE_LIBCURL_HTTP_FETCHER_H_
+#define UPDATE_ENGINE_LIBCURL_HTTP_FETCHER_H_
#include <map>
#include <memory>
@@ -28,7 +28,7 @@
#include <base/macros.h>
#include <brillo/message_loops/message_loop.h>
-#include "update_engine/common/certificate_checker.h"
+#include "update_engine/certificate_checker.h"
#include "update_engine/common/hardware_interface.h"
#include "update_engine/common/http_fetcher.h"
@@ -266,4 +266,4 @@
} // namespace chromeos_update_engine
-#endif // UPDATE_ENGINE_COMMON_LIBCURL_HTTP_FETCHER_H_
+#endif // UPDATE_ENGINE_LIBCURL_HTTP_FETCHER_H_
diff --git a/metrics.h b/metrics.h
index bb9fa5e..7c369ee 100644
--- a/metrics.h
+++ b/metrics.h
@@ -19,7 +19,7 @@
#include <base/time/time.h>
-#include "update_engine/common/certificate_checker.h"
+#include "update_engine/certificate_checker.h"
#include "update_engine/common/constants.h"
#include "update_engine/common/error_code.h"
diff --git a/common/mock_certificate_checker.h b/mock_certificate_checker.h
similarity index 81%
rename from common/mock_certificate_checker.h
rename to mock_certificate_checker.h
index 1f55ca1..c86f502 100644
--- a/common/mock_certificate_checker.h
+++ b/mock_certificate_checker.h
@@ -14,13 +14,13 @@
// limitations under the License.
//
-#ifndef UPDATE_ENGINE_COMMON_MOCK_CERTIFICATE_CHECKER_H_
-#define UPDATE_ENGINE_COMMON_MOCK_CERTIFICATE_CHECKER_H_
+#ifndef UPDATE_ENGINE_MOCK_CERTIFICATE_CHECKER_H_
+#define UPDATE_ENGINE_MOCK_CERTIFICATE_CHECKER_H_
#include <gmock/gmock.h>
#include <openssl/ssl.h>
-#include "update_engine/common/certificate_checker.h"
+#include "update_engine/certificate_checker.h"
namespace chromeos_update_engine {
@@ -35,4 +35,4 @@
} // namespace chromeos_update_engine
-#endif // UPDATE_ENGINE_COMMON_MOCK_CERTIFICATE_CHECKER_H_
+#endif // UPDATE_ENGINE_MOCK_CERTIFICATE_CHECKER_H_
diff --git a/network_selector.h b/network_selector.h
new file mode 100644
index 0000000..22aed8e
--- /dev/null
+++ b/network_selector.h
@@ -0,0 +1,33 @@
+//
+// Copyright (C) 2016 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.
+//
+
+#ifndef UPDATE_ENGINE_NETWORK_SELECTOR_H_
+#define UPDATE_ENGINE_NETWORK_SELECTOR_H_
+
+#include <memory>
+
+#include "update_engine/network_selector_interface.h"
+
+namespace chromeos_update_engine {
+namespace network {
+
+// Creates the NetworkSelectorInterface instance for the given platform.
+std::unique_ptr<NetworkSelectorInterface> CreateNetworkSelector();
+
+} // namespace network
+} // namespace chromeos_update_engine
+
+#endif // UPDATE_ENGINE_NETWORK_SELECTOR_H_
diff --git a/network_selector_android.cc b/network_selector_android.cc
new file mode 100644
index 0000000..6879b69
--- /dev/null
+++ b/network_selector_android.cc
@@ -0,0 +1,45 @@
+//
+// Copyright (C) 2016 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 "update_engine/network_selector_android.h"
+
+#include <android/multinetwork.h>
+#include <base/logging.h>
+#include <brillo/make_unique_ptr.h>
+
+namespace chromeos_update_engine {
+
+namespace network {
+
+// Factory defined in network_selector.h.
+std::unique_ptr<NetworkSelectorInterface> CreateNetworkSelector() {
+ return brillo::make_unique_ptr(new NetworkSelectorAndroid());
+}
+
+} // namespace network
+
+// Defined in network_selector_interface.h.
+const NetworkId kDefaultNetworkId = NETWORK_UNSPECIFIED;
+
+bool NetworkSelectorAndroid::SetProcessNetwork(NetworkId network_id) {
+ if (android_setprocnetwork(network_id) < 0) {
+ PLOG(ERROR) << "Binding the network to " << network_id;
+ return false;
+ }
+ return true;
+}
+
+} // namespace chromeos_update_engine
diff --git a/network_selector_android.h b/network_selector_android.h
new file mode 100644
index 0000000..135536c
--- /dev/null
+++ b/network_selector_android.h
@@ -0,0 +1,40 @@
+//
+// Copyright (C) 2016 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.
+//
+
+#ifndef UPDATE_ENGINE_NETWORK_SELECTOR_ANDROID_H_
+#define UPDATE_ENGINE_NETWORK_SELECTOR_ANDROID_H_
+
+#include <base/macros.h>
+
+#include "update_engine/network_selector_interface.h"
+
+namespace chromeos_update_engine {
+
+class NetworkSelectorAndroid final : public NetworkSelectorInterface {
+ public:
+ NetworkSelectorAndroid() = default;
+ ~NetworkSelectorAndroid() override = default;
+
+ // NetworkSelectorInterface overrides.
+ bool SetProcessNetwork(NetworkId network_id) override;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(NetworkSelectorAndroid);
+};
+
+} // namespace chromeos_update_engine
+
+#endif // UPDATE_ENGINE_NETWORK_SELECTOR_ANDROID_H_
diff --git a/network_selector_interface.h b/network_selector_interface.h
new file mode 100644
index 0000000..6c17b2c
--- /dev/null
+++ b/network_selector_interface.h
@@ -0,0 +1,49 @@
+//
+// Copyright (C) 2016 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.
+//
+
+#ifndef UPDATE_ENGINE_NETWORK_SELECTOR_INTERFACE_H_
+#define UPDATE_ENGINE_NETWORK_SELECTOR_INTERFACE_H_
+
+#include <cstdint>
+
+namespace chromeos_update_engine {
+
+typedef uint64_t NetworkId;
+
+// A constant value used to indicate the default network id. Defined in the
+// network_selector_*.cc file.
+extern const NetworkId kDefaultNetworkId;
+
+// A class that handles the network used for the connections performed from this
+// process in a platform-specific way.
+
+class NetworkSelectorInterface {
+ public:
+
+ virtual ~NetworkSelectorInterface() = default;
+
+ // Set the current process network. All sockets created in the future will be
+ // bound to this particular network. Call this with the special value
+ // kNetworkId to use the default network.
+ virtual bool SetProcessNetwork(NetworkId network_id) = 0;
+
+ protected:
+ NetworkSelectorInterface() = default;
+};
+
+} // namespace chromeos_update_engine
+
+#endif // UPDATE_ENGINE_NETWORK_SELECTOR_INTERFACE_H_
diff --git a/network_selector_stub.cc b/network_selector_stub.cc
new file mode 100644
index 0000000..218d454
--- /dev/null
+++ b/network_selector_stub.cc
@@ -0,0 +1,44 @@
+//
+// Copyright (C) 2016 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 "update_engine/network_selector_stub.h"
+
+#include <base/logging.h>
+#include <brillo/make_unique_ptr.h>
+
+namespace chromeos_update_engine {
+
+namespace network {
+
+// Factory defined in network_selector.h.
+std::unique_ptr<NetworkSelectorInterface> CreateNetworkSelector() {
+ return brillo::make_unique_ptr(new NetworkSelectorStub());
+}
+
+} // namespace network
+
+// Defined in network_selector_interface.h.
+const NetworkId kDefaultNetworkId = 0;
+
+bool NetworkSelectorStub::SetProcessNetwork(NetworkId network_id) {
+ if (network_id != kDefaultNetworkId) {
+ LOG(ERROR) << "SetProcessNetwork not implemented.";
+ return false;
+ }
+ return true;
+}
+
+} // namespace chromeos_update_engine
diff --git a/network_selector_stub.h b/network_selector_stub.h
new file mode 100644
index 0000000..b3f7b48
--- /dev/null
+++ b/network_selector_stub.h
@@ -0,0 +1,40 @@
+//
+// Copyright (C) 2016 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.
+//
+
+#ifndef UPDATE_ENGINE_NETWORK_SELECTOR_STUB_H_
+#define UPDATE_ENGINE_NETWORK_SELECTOR_STUB_H_
+
+#include <base/macros.h>
+
+#include "update_engine/network_selector_interface.h"
+
+namespace chromeos_update_engine {
+
+class NetworkSelectorStub final : public NetworkSelectorInterface {
+ public:
+ NetworkSelectorStub() = default;
+ ~NetworkSelectorStub() override = default;
+
+ // NetworkSelectorInterface overrides.
+ bool SetProcessNetwork(NetworkId network_id) override;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(NetworkSelectorStub);
+};
+
+} // namespace chromeos_update_engine
+
+#endif // UPDATE_ENGINE_NETWORK_SELECTOR_STUB_H_
diff --git a/payload_consumer/delta_performer.cc b/payload_consumer/delta_performer.cc
index a0ac43b..b338d34 100644
--- a/payload_consumer/delta_performer.cc
+++ b/payload_consumer/delta_performer.cc
@@ -28,6 +28,7 @@
#include <base/files/file_util.h>
#include <base/format_macros.h>
+#include <base/strings/string_number_conversions.h>
#include <base/strings/string_util.h>
#include <base/strings/stringprintf.h>
#include <brillo/data_encoding.h>
@@ -129,6 +130,35 @@
*err = 0;
return fd;
}
+
+// Discard the tail of the block device referenced by |fd|, from the offset
+// |data_size| until the end of the block device. Returns whether the data was
+// discarded.
+bool DiscardPartitionTail(FileDescriptorPtr fd, uint64_t data_size) {
+ uint64_t part_size = fd->BlockDevSize();
+ if (!part_size || part_size <= data_size)
+ return false;
+
+ const vector<int> requests = {
+ BLKSECDISCARD,
+ BLKDISCARD,
+#ifdef BLKZEROOUT
+ BLKZEROOUT,
+#endif
+ };
+ for (int request : requests) {
+ int error = 0;
+ if (fd->BlkIoctl(request, data_size, part_size - data_size, &error) &&
+ error == 0) {
+ return true;
+ }
+ LOG(WARNING) << "Error discarding the last "
+ << (part_size - data_size) / 1024 << " KiB using ioctl("
+ << request << ")";
+ }
+ return false;
+}
+
} // namespace
@@ -248,9 +278,15 @@
if (op_result)
return true;
+ size_t partition_first_op_num =
+ current_partition_ ? acc_num_operations_[current_partition_ - 1] : 0;
LOG(ERROR) << "Failed to perform " << op_type_name << " operation "
- << next_operation_num_;
- *error = ErrorCode::kDownloadOperationExecutionError;
+ << next_operation_num_ << ", which is the operation "
+ << next_operation_num_ - partition_first_op_num
+ << " in partition \""
+ << partitions_[current_partition_].partition_name() << "\"";
+ if (*error == ErrorCode::kSuccess)
+ *error = ErrorCode::kDownloadOperationExecutionError;
return false;
}
@@ -319,6 +355,15 @@
<< ", file " << target_path_;
return false;
}
+
+ LOG(INFO) << "Applying " << partition.operations().size()
+ << " operations to partition \"" << partition.partition_name()
+ << "\"";
+
+ // Discard the end of the partition, but ignore failures.
+ DiscardPartitionTail(
+ target_fd_, install_plan_->partitions[current_partition_].target_size);
+
return true;
}
@@ -676,10 +721,10 @@
op_result = PerformBsdiffOperation(op);
break;
case InstallOperation::SOURCE_COPY:
- op_result = PerformSourceCopyOperation(op);
+ op_result = PerformSourceCopyOperation(op, error);
break;
case InstallOperation::SOURCE_BSDIFF:
- op_result = PerformSourceBsdiffOperation(op);
+ op_result = PerformSourceBsdiffOperation(op, error);
break;
default:
op_result = false;
@@ -799,6 +844,7 @@
(partition.has_postinstall_path() ? partition.postinstall_path()
: kPostinstallDefaultScript);
install_part.filesystem_type = partition.filesystem_type();
+ install_part.postinstall_optional = partition.postinstall_optional();
}
if (partition.has_old_partition_info()) {
@@ -1004,16 +1050,34 @@
}
// Compare |calculated_hash| with source hash in |operation|, return false and
-// dump hash if don't match.
+// dump hash and set |error| if don't match.
bool ValidateSourceHash(const brillo::Blob& calculated_hash,
- const InstallOperation& operation) {
+ const InstallOperation& operation,
+ ErrorCode* error) {
brillo::Blob expected_source_hash(operation.src_sha256_hash().begin(),
operation.src_sha256_hash().end());
if (calculated_hash != expected_source_hash) {
- LOG(ERROR) << "Hash verification failed. Expected hash = ";
- utils::HexDumpVector(expected_source_hash);
- LOG(ERROR) << "Calculated hash = ";
- utils::HexDumpVector(calculated_hash);
+ LOG(ERROR) << "The hash of the source data on disk for this operation "
+ << "doesn't match the expected value. This could mean that the "
+ << "delta update payload was targeted for another version, or "
+ << "that the source partition was modified after it was "
+ << "installed, for example, by mounting a filesystem.";
+ LOG(ERROR) << "Expected: sha256|hex = "
+ << base::HexEncode(expected_source_hash.data(),
+ expected_source_hash.size());
+ LOG(ERROR) << "Calculated: sha256|hex = "
+ << base::HexEncode(calculated_hash.data(),
+ calculated_hash.size());
+
+ vector<string> source_extents;
+ for (const Extent& ext : operation.src_extents()) {
+ source_extents.push_back(base::StringPrintf(
+ "%" PRIu64 ":%" PRIu64, ext.start_block(), ext.num_blocks()));
+ }
+ LOG(ERROR) << "Operation source (offset:size) in blocks: "
+ << base::JoinString(source_extents, ",");
+
+ *error = ErrorCode::kDownloadStateInitializationError;
return false;
}
return true;
@@ -1022,7 +1086,7 @@
} // namespace
bool DeltaPerformer::PerformSourceCopyOperation(
- const InstallOperation& operation) {
+ const InstallOperation& operation, ErrorCode* error) {
if (operation.has_src_length())
TEST_AND_RETURN_FALSE(operation.src_length() % block_size_ == 0);
if (operation.has_dst_length())
@@ -1075,7 +1139,7 @@
if (operation.has_src_sha256_hash()) {
TEST_AND_RETURN_FALSE(source_hasher.Finalize());
TEST_AND_RETURN_FALSE(
- ValidateSourceHash(source_hasher.raw_hash(), operation));
+ ValidateSourceHash(source_hasher.raw_hash(), operation, error));
}
DCHECK_EQ(bytes_read, static_cast<ssize_t>(blocks_to_read * block_size_));
@@ -1163,7 +1227,7 @@
}
bool DeltaPerformer::PerformSourceBsdiffOperation(
- const InstallOperation& operation) {
+ const InstallOperation& operation, ErrorCode* error) {
// Since we delete data off the beginning of the buffer as we use it,
// the data we need should be exactly at the beginning of the buffer.
TEST_AND_RETURN_FALSE(buffer_offset_ == operation.data_offset());
@@ -1193,7 +1257,7 @@
}
TEST_AND_RETURN_FALSE(source_hasher.Finalize());
TEST_AND_RETURN_FALSE(
- ValidateSourceHash(source_hasher.raw_hash(), operation));
+ ValidateSourceHash(source_hasher.raw_hash(), operation, error));
}
string input_positions;
diff --git a/payload_consumer/delta_performer.h b/payload_consumer/delta_performer.h
index 0a60020..fdfcb5b 100644
--- a/payload_consumer/delta_performer.h
+++ b/payload_consumer/delta_performer.h
@@ -250,12 +250,16 @@
bool PerformInstallOperation(const InstallOperation& operation);
// These perform a specific type of operation and return true on success.
+ // |error| will be set if source hash mismatch, otherwise |error| might not be
+ // set even if it fails.
bool PerformReplaceOperation(const InstallOperation& operation);
bool PerformZeroOrDiscardOperation(const InstallOperation& operation);
bool PerformMoveOperation(const InstallOperation& operation);
bool PerformBsdiffOperation(const InstallOperation& operation);
- bool PerformSourceCopyOperation(const InstallOperation& operation);
- bool PerformSourceBsdiffOperation(const InstallOperation& operation);
+ bool PerformSourceCopyOperation(const InstallOperation& operation,
+ ErrorCode* error);
+ bool PerformSourceBsdiffOperation(const InstallOperation& operation,
+ ErrorCode* error);
// Extracts the payload signature message from the blob on the |operation| if
// the offset matches the one specified by the manifest. Returns whether the
diff --git a/payload_consumer/download_action.h b/payload_consumer/download_action.h
index fc32068..285930a 100644
--- a/payload_consumer/download_action.h
+++ b/payload_consumer/download_action.h
@@ -24,8 +24,6 @@
#include <memory>
#include <string>
-#include <curl/curl.h>
-
#include "update_engine/common/action.h"
#include "update_engine/common/boot_control_interface.h"
#include "update_engine/common/http_fetcher.h"
diff --git a/payload_consumer/file_descriptor.cc b/payload_consumer/file_descriptor.cc
index 309c60d..8a23dea 100644
--- a/payload_consumer/file_descriptor.cc
+++ b/payload_consumer/file_descriptor.cc
@@ -24,6 +24,8 @@
#include <base/posix/eintr_wrapper.h>
+#include "update_engine/common/utils.h"
+
namespace chromeos_update_engine {
bool EintrSafeFileDescriptor::Open(const char* path, int flags, mode_t mode) {
@@ -65,6 +67,20 @@
return lseek64(fd_, offset, whence);
}
+uint64_t EintrSafeFileDescriptor::BlockDevSize() {
+ if (fd_ < 0)
+ return 0;
+ struct stat stbuf;
+ if (fstat(fd_, &stbuf) < 0) {
+ PLOG(ERROR) << "Error stat-ing fd " << fd_;
+ return 0;
+ }
+ if (!S_ISBLK(stbuf.st_mode))
+ return 0;
+ off_t block_size = utils::BlockDevSize(fd_);
+ return block_size < 0 ? 0 : block_size;
+}
+
bool EintrSafeFileDescriptor::BlkIoctl(int request,
uint64_t start,
uint64_t length,
diff --git a/payload_consumer/file_descriptor.h b/payload_consumer/file_descriptor.h
index 3c15415..7bb2974 100644
--- a/payload_consumer/file_descriptor.h
+++ b/payload_consumer/file_descriptor.h
@@ -78,6 +78,10 @@
// may set errno accordingly.
virtual off64_t Seek(off64_t offset, int whence) = 0;
+ // Return the size of the block device in bytes, or 0 if the device is not a
+ // block device or an error occurred.
+ virtual uint64_t BlockDevSize() = 0;
+
// Runs a ioctl() on the file descriptor if supported. Returns whether
// the operation is supported. The |request| can be one of BLKDISCARD,
// BLKZEROOUT and BLKSECDISCARD to discard, write zeros or securely discard
@@ -119,6 +123,7 @@
ssize_t Read(void* buf, size_t count) override;
ssize_t Write(const void* buf, size_t count) override;
off64_t Seek(off64_t offset, int whence) override;
+ uint64_t BlockDevSize() override;
bool BlkIoctl(int request,
uint64_t start,
uint64_t length,
diff --git a/payload_consumer/install_plan.cc b/payload_consumer/install_plan.cc
index 51e85b3..b04da74 100644
--- a/payload_consumer/install_plan.cc
+++ b/payload_consumer/install_plan.cc
@@ -115,7 +115,8 @@
target_hash == that.target_hash &&
run_postinstall == that.run_postinstall &&
postinstall_path == that.postinstall_path &&
- filesystem_type == that.filesystem_type);
+ filesystem_type == that.filesystem_type &&
+ postinstall_optional == that.postinstall_optional);
}
} // namespace chromeos_update_engine
diff --git a/payload_consumer/install_plan.h b/payload_consumer/install_plan.h
index 454dd78..f15775e 100644
--- a/payload_consumer/install_plan.h
+++ b/payload_consumer/install_plan.h
@@ -95,6 +95,7 @@
bool run_postinstall{false};
std::string postinstall_path;
std::string filesystem_type;
+ bool postinstall_optional{false};
};
std::vector<Partition> partitions;
diff --git a/payload_consumer/mtd_file_descriptor.h b/payload_consumer/mtd_file_descriptor.h
index 9ac1ec1..6c945b2 100644
--- a/payload_consumer/mtd_file_descriptor.h
+++ b/payload_consumer/mtd_file_descriptor.h
@@ -40,6 +40,7 @@
ssize_t Read(void* buf, size_t count) override;
ssize_t Write(const void* buf, size_t count) override;
off64_t Seek(off64_t offset, int whence) override;
+ uint64_t BlockDevSize() override { return 0; }
bool BlkIoctl(int request,
uint64_t start,
uint64_t length,
@@ -75,6 +76,7 @@
ssize_t Read(void* buf, size_t count) override;
ssize_t Write(const void* buf, size_t count) override;
off64_t Seek(off64_t offset, int whence) override;
+ uint64_t BlockDevSize() override { return 0; }
bool BlkIoctl(int request,
uint64_t start,
uint64_t length,
diff --git a/payload_consumer/postinstall_runner_action.cc b/payload_consumer/postinstall_runner_action.cc
index 98f4eaf..84f1edf 100644
--- a/payload_consumer/postinstall_runner_action.cc
+++ b/payload_consumer/postinstall_runner_action.cc
@@ -56,8 +56,8 @@
install_plan_ = GetInputObject();
if (install_plan_.powerwash_required) {
- if (utils::CreatePowerwashMarkerFile(powerwash_marker_file_)) {
- powerwash_marker_created_ = true;
+ if (hardware_->SchedulePowerwash()) {
+ powerwash_scheduled_ = true;
} else {
return CompletePostinstall(ErrorCode::kPostinstallPowerwashError);
}
@@ -301,7 +301,14 @@
// to get back to FW A.
error_code = ErrorCode::kPostinstallFirmwareRONotUpdatable;
}
- return CompletePostinstall(error_code);
+
+ // If postinstall script for this partition is optional we can ignore the
+ // result.
+ if (install_plan_.partitions[current_partition_].postinstall_optional) {
+ LOG(INFO) << "Ignoring postinstall failure since it is optional";
+ } else {
+ return CompletePostinstall(error_code);
+ }
}
accumulated_weight_ += partition_weight_[current_partition_];
current_partition_++;
@@ -324,9 +331,9 @@
if (error_code != ErrorCode::kSuccess) {
LOG(ERROR) << "Postinstall action failed.";
- // Undo any changes done to trigger Powerwash using clobber-state.
- if (powerwash_marker_created_)
- utils::DeletePowerwashMarkerFile(powerwash_marker_file_);
+ // Undo any changes done to trigger Powerwash.
+ if (powerwash_scheduled_)
+ hardware_->CancelPowerwash();
return;
}
diff --git a/payload_consumer/postinstall_runner_action.h b/payload_consumer/postinstall_runner_action.h
index 4cdc47e..2bde3ca 100644
--- a/payload_consumer/postinstall_runner_action.h
+++ b/payload_consumer/postinstall_runner_action.h
@@ -24,6 +24,8 @@
#include <gtest/gtest_prod.h>
#include "update_engine/common/action.h"
+#include "update_engine/common/boot_control_interface.h"
+#include "update_engine/common/hardware_interface.h"
#include "update_engine/payload_consumer/install_plan.h"
// The Postinstall Runner Action is responsible for running the postinstall
@@ -35,8 +37,9 @@
class PostinstallRunnerAction : public InstallPlanAction {
public:
- explicit PostinstallRunnerAction(BootControlInterface* boot_control)
- : PostinstallRunnerAction(boot_control, nullptr) {}
+ PostinstallRunnerAction(BootControlInterface* boot_control,
+ HardwareInterface* hardware)
+ : boot_control_(boot_control), hardware_(hardware) {}
// InstallPlanAction overrides.
void PerformAction() override;
@@ -63,12 +66,6 @@
friend class PostinstallRunnerActionTest;
FRIEND_TEST(PostinstallRunnerActionTest, ProcessProgressLineTest);
- // Special constructor used for testing purposes.
- PostinstallRunnerAction(BootControlInterface* boot_control,
- const char* powerwash_marker_file)
- : boot_control_(boot_control),
- powerwash_marker_file_(powerwash_marker_file) {}
-
void PerformPartitionPostinstall();
// Called whenever the |progress_fd_| has data available to read.
@@ -127,13 +124,12 @@
// The BootControlInerface used to mark the new slot as ready.
BootControlInterface* boot_control_;
- // True if Powerwash Marker was created before invoking post-install script.
- // False otherwise. Used for cleaning up if post-install fails.
- bool powerwash_marker_created_{false};
+ // HardwareInterface used to signal powerwash.
+ HardwareInterface* hardware_;
- // Non-null value will cause post-install to override the default marker
- // file name; used for testing.
- const char* powerwash_marker_file_;
+ // Whether the Powerwash was scheduled before invoking post-install script.
+ // Used for cleaning up if post-install fails.
+ bool powerwash_scheduled_{false};
// Postinstall command currently running, or 0 if no program running.
pid_t current_command_{0};
diff --git a/payload_consumer/postinstall_runner_action_unittest.cc b/payload_consumer/postinstall_runner_action_unittest.cc
index d5b05a6..26fcbd9 100644
--- a/payload_consumer/postinstall_runner_action_unittest.cc
+++ b/payload_consumer/postinstall_runner_action_unittest.cc
@@ -37,6 +37,7 @@
#include "update_engine/common/constants.h"
#include "update_engine/common/fake_boot_control.h"
+#include "update_engine/common/fake_hardware.h"
#include "update_engine/common/test_utils.h"
#include "update_engine/common/utils.h"
@@ -87,10 +88,6 @@
loop_.SetAsCurrent();
async_signal_handler_.Init();
subprocess_.Init(&async_signal_handler_);
- ASSERT_TRUE(utils::MakeTempDirectory(
- "postinstall_runner_action_unittest-XXXXXX", &working_dir_));
- // We use a test-specific powerwash marker file, to avoid race conditions.
- powerwash_marker_file_ = working_dir_ + "/factory_install_reset";
// These tests use the postinstall files generated by "generate_images.sh"
// stored in the "disk_ext2_unittest.img" image.
postinstall_image_ = test_utils::GetBuildArtifactsPath()
@@ -160,14 +157,11 @@
brillo::AsynchronousSignalHandler async_signal_handler_;
Subprocess subprocess_;
- // A temporary working directory used for the test.
- string working_dir_;
- string powerwash_marker_file_;
-
// The path to the postinstall sample image.
string postinstall_image_;
FakeBootControl fake_boot_control_;
+ FakeHardware fake_hardware_;
PostinstActionProcessorDelegate processor_delegate_;
// The PostinstallRunnerAction delegate receiving the progress updates.
@@ -195,8 +189,7 @@
install_plan.download_url = "http://127.0.0.1:8080/update";
install_plan.powerwash_required = powerwash_required;
feeder_action.set_obj(install_plan);
- PostinstallRunnerAction runner_action(&fake_boot_control_,
- powerwash_marker_file_.c_str());
+ PostinstallRunnerAction runner_action(&fake_boot_control_, &fake_hardware_);
postinstall_action_ = &runner_action;
runner_action.set_delegate(setup_action_delegate_);
BondActions(&feeder_action, &runner_action);
@@ -222,8 +215,7 @@
}
TEST_F(PostinstallRunnerActionTest, ProcessProgressLineTest) {
- PostinstallRunnerAction action(&fake_boot_control_,
- powerwash_marker_file_.c_str());
+ PostinstallRunnerAction action(&fake_boot_control_, &fake_hardware_);
testing::StrictMock<MockPostinstallRunnerActionDelegate> mock_delegate_;
action.set_delegate(&mock_delegate_);
@@ -252,7 +244,7 @@
EXPECT_TRUE(processor_delegate_.processing_done_called_);
// Since powerwash_required was false, this should not trigger a powerwash.
- EXPECT_FALSE(utils::FileExists(powerwash_marker_file_.c_str()));
+ EXPECT_FALSE(fake_hardware_.IsPowerwashScheduled());
}
TEST_F(PostinstallRunnerActionTest, RunAsRootRunSymlinkFileTest) {
@@ -267,11 +259,8 @@
RunPosinstallAction(loop.dev(), "bin/postinst_example", true);
EXPECT_EQ(ErrorCode::kSuccess, processor_delegate_.code_);
- // Check that the powerwash marker file was set.
- string actual_cmd;
- EXPECT_TRUE(base::ReadFileToString(base::FilePath(powerwash_marker_file_),
- &actual_cmd));
- EXPECT_EQ(kPowerwashCommand, actual_cmd);
+ // Check that powerwash was scheduled.
+ EXPECT_TRUE(fake_hardware_.IsPowerwashScheduled());
}
// Runs postinstall from a partition file that doesn't mount, so it should
@@ -282,7 +271,7 @@
// In case of failure, Postinstall should not signal a powerwash even if it
// was requested.
- EXPECT_FALSE(utils::FileExists(powerwash_marker_file_.c_str()));
+ EXPECT_FALSE(fake_hardware_.IsPowerwashScheduled());
}
// Check that the failures from the postinstall script cause the action to
diff --git a/payload_generator/delta_diff_generator.cc b/payload_generator/delta_diff_generator.cc
index 7ca42fc..3295df0 100644
--- a/payload_generator/delta_diff_generator.cc
+++ b/payload_generator/delta_diff_generator.cc
@@ -97,15 +97,6 @@
if (!old_part.path.empty() &&
!utils::IsSquashfsFilesystem(new_part.path)) {
// Delta update.
- if (utils::IsExtFilesystem(new_part.path)) {
- LOG_IF(WARNING, old_part.size != new_part.size)
- << "Old and new filesystems have different size.";
- // TODO(deymo): Our tools only support growing the filesystem size
- // during an update. Remove this check when that's fixed.
- // crbug.com/192136
- LOG_IF(FATAL, old_part.size > new_part.size)
- << "Shirking the filesystem size is not supported at the moment.";
- }
if (config.version.minor == kInPlaceMinorPayloadVersion) {
LOG(INFO) << "Using generator InplaceGenerator().";
strategy.reset(new InplaceGenerator());
diff --git a/payload_generator/delta_diff_utils.cc b/payload_generator/delta_diff_utils.cc
index 0782a8c..8fce055 100644
--- a/payload_generator/delta_diff_utils.cc
+++ b/payload_generator/delta_diff_utils.cc
@@ -180,8 +180,8 @@
aops,
old_part.path,
new_part.path,
- old_part.fs_interface ? old_part.fs_interface->GetBlockCount() : 0,
- new_part.fs_interface->GetBlockCount(),
+ old_part.size / kBlockSize,
+ new_part.size / kBlockSize,
soft_chunk_blocks,
version,
blob_file,
@@ -320,6 +320,13 @@
for (uint64_t block = old_num_blocks; block-- > 0; ) {
if (old_block_ids[block] != 0 && !old_visited_blocks->ContainsBlock(block))
old_blocks_map[old_block_ids[block]].push_back(block);
+
+ // Mark all zeroed blocks in the old image as "used" since it doesn't make
+ // any sense to spend I/O to read zeros from the source partition and more
+ // importantly, these could sometimes be blocks discarded in the SSD which
+ // would read non-zero values.
+ if (old_block_ids[block] == 0)
+ old_visited_blocks->AddBlock(block);
}
// The collection of blocks in the new partition with just zeros. This is a
diff --git a/payload_generator/inplace_generator.cc b/payload_generator/inplace_generator.cc
index 30dbafc..be8b487 100644
--- a/payload_generator/inplace_generator.cc
+++ b/payload_generator/inplace_generator.cc
@@ -677,17 +677,13 @@
enum BlockField { READER = 0, WRITER, BLOCK_FIELD_COUNT };
for (int field = READER; field < BLOCK_FIELD_COUNT; field++) {
- const int extents_size =
- (field == READER) ? operation.src_extents_size() :
- operation.dst_extents_size();
const char* past_participle = (field == READER) ? "read" : "written";
const google::protobuf::RepeatedPtrField<Extent>& extents =
(field == READER) ? operation.src_extents() : operation.dst_extents();
Vertex::Index Block::*access_type = (field == READER) ?
&Block::reader : &Block::writer;
- for (int i = 0; i < extents_size; i++) {
- const Extent& extent = extents.Get(i);
+ for (const Extent& extent : extents) {
for (uint64_t block = extent.start_block();
block < (extent.start_block() + extent.num_blocks()); block++) {
if ((*blocks)[block].*access_type != Vertex::kInvalidIndex) {
@@ -738,6 +734,7 @@
}
bool InplaceGenerator::ResolveReadAfterWriteDependencies(
+ const PartitionConfig& old_part,
const PartitionConfig& new_part,
uint64_t partition_size,
size_t block_size,
@@ -746,7 +743,7 @@
// Convert the operations to the graph.
Graph graph;
CheckGraph(graph);
- vector<Block> blocks(new_part.size / block_size);
+ vector<Block> blocks(std::max(old_part.size, new_part.size) / block_size);
for (const auto& aop : *aops) {
AddInstallOpToGraph(
&graph, Vertex::kInvalidIndex, &blocks, aop.op, aop.name);
@@ -815,12 +812,8 @@
blob_file));
LOG(INFO) << "Done reading " << new_part.name;
- TEST_AND_RETURN_FALSE(
- ResolveReadAfterWriteDependencies(new_part,
- partition_size,
- config.block_size,
- blob_file,
- aops));
+ TEST_AND_RETURN_FALSE(ResolveReadAfterWriteDependencies(
+ old_part, new_part, partition_size, config.block_size, blob_file, aops));
LOG(INFO) << "Done reordering " << new_part.name;
return true;
}
diff --git a/payload_generator/inplace_generator.h b/payload_generator/inplace_generator.h
index 4839824..48a1fac 100644
--- a/payload_generator/inplace_generator.h
+++ b/payload_generator/inplace_generator.h
@@ -212,6 +212,7 @@
// On success, stores the new operations in |aops| in the right order and
// returns true.
static bool ResolveReadAfterWriteDependencies(
+ const PartitionConfig& old_part,
const PartitionConfig& new_part,
uint64_t partition_size,
size_t block_size,
diff --git a/payload_generator/inplace_generator_unittest.cc b/payload_generator/inplace_generator_unittest.cc
index cde4bfc..20ac50b 100644
--- a/payload_generator/inplace_generator_unittest.cc
+++ b/payload_generator/inplace_generator_unittest.cc
@@ -33,6 +33,7 @@
#include "update_engine/common/utils.h"
#include "update_engine/payload_generator/cycle_breaker.h"
#include "update_engine/payload_generator/delta_diff_generator.h"
+#include "update_engine/payload_generator/delta_diff_utils.h"
#include "update_engine/payload_generator/extent_ranges.h"
#include "update_engine/payload_generator/graph_types.h"
#include "update_engine/payload_generator/graph_utils.h"
@@ -118,6 +119,16 @@
blob_file_.reset(new BlobFileWriter(blob_fd_, &blob_file_size_));
}
+ // Dump the list of operations |aops| in case of test failure.
+ void DumpAopsOnFailure(const vector<AnnotatedOperation>& aops) {
+ if (HasNonfatalFailure()) {
+ LOG(INFO) << "Result operation list:";
+ for (const auto& aop : aops) {
+ LOG(INFO) << aop;
+ }
+ }
+ }
+
// Blob file name, file descriptor and file size used to store operation
// blobs.
string blob_path_;
@@ -618,19 +629,21 @@
// space, forcing it to create a new full operation and the second case with
// one extra block in the partition that can be used for the move operation.
for (const auto part_blocks : vector<uint64_t>{num_blocks, num_blocks + 1}) {
- SCOPED_TRACE(base::StringPrintf("Using partition_blocs=%" PRIu64,
- part_blocks));
+ SCOPED_TRACE(
+ base::StringPrintf("Using partition_blocks=%" PRIu64, part_blocks));
vector<AnnotatedOperation> result_aops = aops;
EXPECT_TRUE(InplaceGenerator::ResolveReadAfterWriteDependencies(
- part, part_blocks * block_size, block_size, blob_file_.get(),
- &result_aops));
+ part,
+ part,
+ part_blocks * block_size,
+ block_size,
+ blob_file_.get(),
+ &result_aops));
size_t full_ops = 0;
for (const auto& aop : result_aops) {
- if (aop.op.type() == InstallOperation::REPLACE ||
- aop.op.type() == InstallOperation::REPLACE_BZ) {
+ if (diff_utils::IsAReplaceOperation(aop.op.type()))
full_ops++;
- }
if (aop.op.type() != InstallOperation::MOVE)
continue;
@@ -648,13 +661,90 @@
// operation for it.
EXPECT_EQ(part_blocks == num_blocks ? 2U : 1U, full_ops);
- if (HasNonfatalFailure()) {
- LOG(INFO) << "Result operation list:";
- for (const auto& aop : result_aops) {
- LOG(INFO) << aop;
+ DumpAopsOnFailure(result_aops);
+ }
+}
+
+// Test that we can shrink a filesystem and break cycles.
+TEST_F(InplaceGeneratorTest, ResolveReadAfterWriteDependenciesShrinkData) {
+ size_t block_size = 4096;
+ size_t old_blocks = 10;
+ size_t new_blocks = 8;
+ vector<AnnotatedOperation> aops;
+
+ // Create a loop using the blocks 1-6 and one other operation writing to the
+ // block 7 from outside the new partition. The loop in the blocks 1-6 uses
+ // two-block operations, so it needs two blocks of scratch space. It can't use
+ // the block 0 as scratch space (see previous test) and it can't use the
+ // blocks 7 or 8 due the last move operation.
+
+ aops.emplace_back();
+ aops.back().name = base::StringPrintf("<bz-block-0>");
+ aops.back().op.set_type(InstallOperation::REPLACE_BZ);
+ StoreExtents({ExtentForRange(0, 1)}, aops.back().op.mutable_dst_extents());
+
+ const size_t num_ops = 3;
+ for (size_t i = 0; i < num_ops; i++) {
+ AnnotatedOperation aop;
+ aop.name = base::StringPrintf("<op-%" PRIuS ">", i);
+ aop.op.set_type(InstallOperation::BSDIFF);
+ StoreExtents({ExtentForRange(1 + 2 * i, 2)}, aop.op.mutable_src_extents());
+ StoreExtents({ExtentForRange(1 + 2 * ((i + 1) % num_ops), 2)},
+ aop.op.mutable_dst_extents());
+ aops.push_back(aop);
+ }
+
+ {
+ AnnotatedOperation aop;
+ aop.name = "<op-shrink>";
+ aop.op.set_type(InstallOperation::BSDIFF);
+ StoreExtents({ExtentForRange(8, 1)}, aop.op.mutable_src_extents());
+ StoreExtents({ExtentForRange(7, 1)}, aop.op.mutable_dst_extents());
+ aops.push_back(aop);
+ }
+
+ PartitionConfig old_part("part");
+ old_part.path = "/dev/zero";
+ old_part.size = old_blocks * block_size;
+
+ PartitionConfig new_part("part");
+ new_part.path = "/dev/zero";
+ new_part.size = new_blocks * block_size;
+
+ CreateBlobFile();
+
+ EXPECT_TRUE(InplaceGenerator::ResolveReadAfterWriteDependencies(
+ old_part,
+ new_part,
+ (old_blocks + 2) * block_size, // enough scratch space.
+ block_size,
+ blob_file_.get(),
+ &aops));
+
+ size_t full_ops = 0;
+ for (const auto& aop : aops) {
+ if (diff_utils::IsAReplaceOperation(aop.op.type()))
+ full_ops++;
+ }
+ // There should be only one REPLACE* operation, the one we added for block 0.
+ EXPECT_EQ(1U, full_ops);
+
+ // There should be only one MOVE operation, the one used to break the loop
+ // which should write to scratch space past the block 7 (the last block of the
+ // new partition) which is being written later.
+ size_t move_ops = 0;
+ for (const auto& aop : aops) {
+ if (aop.op.type() == InstallOperation::MOVE) {
+ move_ops++;
+ for (const Extent& extent : aop.op.dst_extents()) {
+ EXPECT_LE(7U, extent.start_block()) << "On dst extents for aop: "
+ << aop;
}
}
}
+ EXPECT_EQ(1U, move_ops);
+
+ DumpAopsOnFailure(aops);
}
} // namespace chromeos_update_engine
diff --git a/payload_generator/payload_file.cc b/payload_generator/payload_file.cc
index d7046eb..d2ae706 100644
--- a/payload_generator/payload_file.cc
+++ b/payload_generator/payload_file.cc
@@ -139,6 +139,7 @@
partition->set_postinstall_path(part.postinstall.path);
if (!part.postinstall.filesystem_type.empty())
partition->set_filesystem_type(part.postinstall.filesystem_type);
+ partition->set_postinstall_optional(part.postinstall.optional);
}
for (const AnnotatedOperation& aop : part.aops) {
*partition->add_operations() = aop.op;
diff --git a/payload_generator/payload_generation_config.cc b/payload_generator/payload_generation_config.cc
index dc2ced6..8ef30a0 100644
--- a/payload_generator/payload_generation_config.cc
+++ b/payload_generator/payload_generation_config.cc
@@ -27,7 +27,7 @@
namespace chromeos_update_engine {
bool PostInstallConfig::IsEmpty() const {
- return run == false && path.empty() && filesystem_type.empty();
+ return !run && path.empty() && filesystem_type.empty() && !optional;
}
bool PartitionConfig::ValidateExists() const {
@@ -95,6 +95,8 @@
store.GetString("POSTINSTALL_PATH_" + part.name, &part.postinstall.path);
store.GetString("FILESYSTEM_TYPE_" + part.name,
&part.postinstall.filesystem_type);
+ store.GetBoolean("POSTINSTALL_OPTIONAL_" + part.name,
+ &part.postinstall.optional);
}
if (!found_postinstall) {
LOG(ERROR) << "No valid postinstall config found.";
diff --git a/payload_generator/payload_generation_config.h b/payload_generator/payload_generation_config.h
index 8bb7737..074738b 100644
--- a/payload_generator/payload_generation_config.h
+++ b/payload_generator/payload_generation_config.h
@@ -46,6 +46,9 @@
// The filesystem type used to mount the partition in order to run the
// post-install program.
std::string filesystem_type;
+
+ // Whether this postinstall script should be ignored if it fails.
+ bool optional = false;
};
struct PartitionConfig {
diff --git a/payload_generator/payload_generation_config_unittest.cc b/payload_generator/payload_generation_config_unittest.cc
index 122d94a..3545056 100644
--- a/payload_generator/payload_generation_config_unittest.cc
+++ b/payload_generator/payload_generation_config_unittest.cc
@@ -29,12 +29,14 @@
EXPECT_TRUE(
store.LoadFromString("RUN_POSTINSTALL_root=true\n"
"POSTINSTALL_PATH_root=postinstall\n"
- "FILESYSTEM_TYPE_root=ext4"));
+ "FILESYSTEM_TYPE_root=ext4\n"
+ "POSTINSTALL_OPTIONAL_root=true"));
EXPECT_TRUE(image_config.LoadPostInstallConfig(store));
EXPECT_FALSE(image_config.partitions[0].postinstall.IsEmpty());
EXPECT_EQ(true, image_config.partitions[0].postinstall.run);
EXPECT_EQ("postinstall", image_config.partitions[0].postinstall.path);
EXPECT_EQ("ext4", image_config.partitions[0].postinstall.filesystem_type);
+ EXPECT_TRUE(image_config.partitions[0].postinstall.optional);
}
TEST_F(PayloadGenerationConfigTest, LoadPostInstallConfigNameMismatchTest) {
diff --git a/real_system_state.h b/real_system_state.h
index 071e3e0..480b4b7 100644
--- a/real_system_state.h
+++ b/real_system_state.h
@@ -20,6 +20,7 @@
#include "update_engine/system_state.h"
#include <memory>
+#include <set>
#include <debugd/dbus-proxies.h>
#include <metrics/metrics_library.h>
@@ -27,8 +28,8 @@
#include <power_manager/dbus-proxies.h>
#include <session_manager/dbus-proxies.h>
+#include "update_engine/certificate_checker.h"
#include "update_engine/common/boot_control_interface.h"
-#include "update_engine/common/certificate_checker.h"
#include "update_engine/common/clock.h"
#include "update_engine/common/hardware_interface.h"
#include "update_engine/common/prefs.h"
@@ -63,6 +64,10 @@
void AddObserver(ServiceObserverInterface* observer) override;
void RemoveObserver(ServiceObserverInterface* observer) override;
+ const std::set<ServiceObserverInterface*>& service_observers() override {
+ CHECK(update_attempter_.get());
+ return update_attempter_->service_observers();
+ }
// SystemState overrides.
inline void set_device_policy(
diff --git a/scripts/brillo_update_payload b/scripts/brillo_update_payload
index f1044c0..1c49138 100755
--- a/scripts/brillo_update_payload
+++ b/scripts/brillo_update_payload
@@ -223,6 +223,18 @@
echo "${default_value}"
}
+# truncate_file <file_path> <file_size>
+#
+# Truncate the given |file_path| to |file_size| using perl.
+# The truncate binary might not be available.
+truncate_file() {
+ local file_path="$1"
+ local file_size="$2"
+ perl -e "open(FILE, \"+<\", \$ARGV[0]); \
+ truncate(FILE, ${file_size}); \
+ close(FILE);" "${file_path}"
+}
+
# Create a temporary file in the work_dir with an optional pattern name.
# Prints the name of the newly created file.
create_tempfile() {
@@ -412,13 +424,20 @@
part_file="${temp_raw}"
fi
- # delta_generator only supports images multiple of 4 KiB, so we pad with
- # zeros if needed.
+ # delta_generator only supports images multiple of 4 KiB. For target images
+ # we pad the data with zeros if needed, but for source images we truncate
+ # down the data since the last block of the old image could be padded on
+ # disk with unknown data.
filesize=$(stat -c%s "${part_file}")
if [[ $(( filesize % 4096 )) -ne 0 ]]; then
- echo "Rounding up partition ${part}.img to multiple of 4 KiB."
- : $(( filesize = (filesize + 4095) & -4096 ))
- truncate --size="${filesize}" "${part_file}"
+ if [[ "${partitions_array}" == "SRC_PARTITIONS" ]]; then
+ echo "Rounding DOWN partition ${part}.img to a multiple of 4 KiB."
+ : $(( filesize = filesize & -4096 ))
+ else
+ echo "Rounding UP partition ${part}.img to a multiple of 4 KiB."
+ : $(( filesize = (filesize + 4095) & -4096 ))
+ fi
+ truncate_file "${part_file}" "${filesize}"
fi
eval "${partitions_array}[\"${part}\"]=\"${part_file}\""
diff --git a/sideload_main.cc b/sideload_main.cc
new file mode 100644
index 0000000..35ed11b
--- /dev/null
+++ b/sideload_main.cc
@@ -0,0 +1,227 @@
+//
+// Copyright (C) 2016 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 <xz.h>
+
+#include <string>
+#include <vector>
+
+#include <base/command_line.h>
+#include <base/logging.h>
+#include <base/strings/string_split.h>
+#include <base/strings/stringprintf.h>
+#include <brillo/asynchronous_signal_handler.h>
+#include <brillo/flag_helper.h>
+#include <brillo/make_unique_ptr.h>
+#include <brillo/message_loops/base_message_loop.h>
+#include <brillo/streams/file_stream.h>
+#include <brillo/streams/stream.h>
+
+#include "update_engine/common/boot_control.h"
+#include "update_engine/common/error_code_utils.h"
+#include "update_engine/common/hardware.h"
+#include "update_engine/common/prefs.h"
+#include "update_engine/common/subprocess.h"
+#include "update_engine/common/terminator.h"
+#include "update_engine/common/utils.h"
+#include "update_engine/update_attempter_android.h"
+
+using std::string;
+using std::vector;
+using update_engine::UpdateStatus;
+
+namespace {
+// The root directory used for temporary files in update_engine_sideload.
+const char kSideloadRootTempDir[] = "/tmp/update_engine_sideload";
+} // namespace
+
+namespace chromeos_update_engine {
+namespace {
+
+void SetupLogging() {
+ string log_file;
+ logging::LoggingSettings log_settings;
+ log_settings.lock_log = logging::DONT_LOCK_LOG_FILE;
+ log_settings.delete_old = logging::APPEND_TO_OLD_LOG_FILE;
+ log_settings.log_file = nullptr;
+ log_settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG;
+
+ logging::InitLogging(log_settings);
+}
+
+class SideloadDaemonState : public DaemonStateInterface,
+ public ServiceObserverInterface {
+ public:
+ explicit SideloadDaemonState(brillo::StreamPtr status_stream)
+ : status_stream_(std::move(status_stream)) {
+ // Add this class as the only observer.
+ observers_.insert(this);
+ }
+ ~SideloadDaemonState() override = default;
+
+ // DaemonStateInterface overrides.
+ bool StartUpdater() override { return true; }
+ void AddObserver(ServiceObserverInterface* observer) override {}
+ void RemoveObserver(ServiceObserverInterface* observer) override {}
+ const std::set<ServiceObserverInterface*>& service_observers() override {
+ return observers_;
+ }
+
+ // ServiceObserverInterface overrides.
+ void SendStatusUpdate(int64_t last_checked_time,
+ double progress,
+ UpdateStatus status,
+ const string& new_version,
+ int64_t new_size) override {
+ if (status_ != status && (status == UpdateStatus::DOWNLOADING ||
+ status == UpdateStatus::FINALIZING)) {
+ // Split the progress bar in two parts for the two stages DOWNLOADING and
+ // FINALIZING.
+ ReportStatus(base::StringPrintf(
+ "ui_print Step %d/2", status == UpdateStatus::DOWNLOADING ? 1 : 2));
+ ReportStatus(base::StringPrintf("progress 0.5 0"));
+ }
+ if (status_ != status || fabs(progress - progress_) > 0.005) {
+ ReportStatus(base::StringPrintf("set_progress %.lf", progress));
+ }
+ progress_ = progress;
+ status_ = status;
+ }
+
+ void SendPayloadApplicationComplete(ErrorCode error_code) override {
+ if (error_code != ErrorCode::kSuccess) {
+ ReportStatus(
+ base::StringPrintf("ui_print Error applying update: %d (%s)",
+ error_code,
+ utils::ErrorCodeToString(error_code).c_str()));
+ }
+ error_code_ = error_code;
+ brillo::MessageLoop::current()->BreakLoop();
+ }
+
+ void SendChannelChangeUpdate(const string& tracking_channel) override {}
+
+ // Getters.
+ UpdateStatus status() { return status_; }
+ ErrorCode error_code() { return error_code_; }
+
+ private:
+ // Report a status message in the status_stream_, if any. These messages
+ // should conform to the specification defined in the Android recovery.
+ void ReportStatus(const string& message) {
+ if (!status_stream_)
+ return;
+ string status_line = message + "\n";
+ status_stream_->WriteAllBlocking(
+ status_line.data(), status_line.size(), nullptr);
+ }
+
+ std::set<ServiceObserverInterface*> observers_;
+ brillo::StreamPtr status_stream_;
+
+ // The last status and error code reported.
+ UpdateStatus status_{UpdateStatus::IDLE};
+ ErrorCode error_code_{ErrorCode::kSuccess};
+ double progress_{-1.};
+};
+
+// Apply an update payload directly from the given payload URI.
+bool ApplyUpdatePayload(const string& payload,
+ int64_t payload_offset,
+ int64_t payload_size,
+ const vector<string>& headers,
+ int64_t status_fd) {
+ base::MessageLoopForIO base_loop;
+ brillo::BaseMessageLoop loop(&base_loop);
+ loop.SetAsCurrent();
+
+ // Setup the subprocess handler.
+ brillo::AsynchronousSignalHandler handler;
+ handler.Init();
+ Subprocess subprocess;
+ subprocess.Init(&handler);
+
+ SideloadDaemonState sideload_daemon_state(
+ brillo::FileStream::FromFileDescriptor(status_fd, true, nullptr));
+
+ // During the sideload we don't access the prefs persisted on disk but instead
+ // use a temporary memory storage.
+ MemoryPrefs prefs;
+
+ std::unique_ptr<BootControlInterface> boot_control =
+ boot_control::CreateBootControl();
+ if (!boot_control) {
+ LOG(ERROR) << "Error initializing the BootControlInterface.";
+ return false;
+ }
+
+ std::unique_ptr<HardwareInterface> hardware = hardware::CreateHardware();
+ if (!hardware) {
+ LOG(ERROR) << "Error initializing the HardwareInterface.";
+ return false;
+ }
+
+ UpdateAttempterAndroid update_attempter(
+ &sideload_daemon_state, &prefs, boot_control.get(), hardware.get());
+ update_attempter.Init();
+
+ TEST_AND_RETURN_FALSE(update_attempter.ApplyPayload(
+ payload, payload_offset, payload_size, headers, nullptr));
+
+ loop.Run();
+ return sideload_daemon_state.status() == UpdateStatus::UPDATED_NEED_REBOOT;
+}
+
+} // namespace
+} // namespace chromeos_update_engine
+
+int main(int argc, char** argv) {
+ DEFINE_string(payload,
+ "file:///data/payload.bin",
+ "The URI to the update payload to use.");
+ DEFINE_int64(
+ offset, 0, "The offset in the payload where the CrAU update starts. ");
+ DEFINE_int64(size,
+ 0,
+ "The size of the CrAU part of the payload. If 0 is passed, it "
+ "will be autodetected.");
+ DEFINE_string(headers,
+ "",
+ "A list of key-value pairs, one element of the list per line.");
+ DEFINE_int64(status_fd, -1, "A file descriptor to notify the update status.");
+
+ chromeos_update_engine::Terminator::Init();
+ chromeos_update_engine::SetupLogging();
+ brillo::FlagHelper::Init(argc, argv, "Update Engine Sideload");
+
+ LOG(INFO) << "Update Engine Sideloading starting";
+
+ // xz-embedded requires to initialize its CRC-32 table once on startup.
+ xz_crc32_init();
+
+ // When called from recovery, /data is not accessible, so we need to use
+ // /tmp for temporary files.
+ chromeos_update_engine::utils::SetRootTempDir(kSideloadRootTempDir);
+
+ vector<string> headers = base::SplitString(
+ FLAGS_headers, "\n", base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
+
+ if (!chromeos_update_engine::ApplyUpdatePayload(
+ FLAGS_payload, FLAGS_offset, FLAGS_size, headers, FLAGS_status_fd))
+ return 1;
+
+ return 0;
+}
diff --git a/update_attempter.cc b/update_attempter.cc
index a0f3bd4..c773063 100644
--- a/update_attempter.cc
+++ b/update_attempter.cc
@@ -41,18 +41,18 @@
#include <power_manager/dbus-proxies.h>
#include <update_engine/dbus-constants.h>
+#include "update_engine/certificate_checker.h"
#include "update_engine/common/boot_control_interface.h"
-#include "update_engine/common/certificate_checker.h"
#include "update_engine/common/clock_interface.h"
#include "update_engine/common/constants.h"
#include "update_engine/common/hardware_interface.h"
-#include "update_engine/common/libcurl_http_fetcher.h"
#include "update_engine/common/multi_range_http_fetcher.h"
#include "update_engine/common/platform_constants.h"
#include "update_engine/common/prefs_interface.h"
#include "update_engine/common/subprocess.h"
#include "update_engine/common/utils.h"
#include "update_engine/dbus_service.h"
+#include "update_engine/libcurl_http_fetcher.h"
#include "update_engine/metrics.h"
#include "update_engine/omaha_request_action.h"
#include "update_engine/omaha_request_params.h"
@@ -585,7 +585,8 @@
void UpdateAttempter::BuildPostInstallActions(
InstallPlanAction* previous_action) {
shared_ptr<PostinstallRunnerAction> postinstall_runner_action(
- new PostinstallRunnerAction(system_state_->boot_control()));
+ new PostinstallRunnerAction(system_state_->boot_control(),
+ system_state_->hardware()));
postinstall_runner_action->set_delegate(this);
actions_.push_back(shared_ptr<AbstractAction>(postinstall_runner_action));
BondActions(previous_action,
diff --git a/update_attempter.h b/update_attempter.h
index b045614..92683e6 100644
--- a/update_attempter.h
+++ b/update_attempter.h
@@ -31,9 +31,9 @@
#include "debugd/dbus-proxies.h"
#include "update_engine/chrome_browser_proxy_resolver.h"
+#include "update_engine/certificate_checker.h"
#include "update_engine/client_library/include/update_engine/update_status.h"
#include "update_engine/common/action_processor.h"
-#include "update_engine/common/certificate_checker.h"
#include "update_engine/common/cpu_limiter.h"
#include "update_engine/libcros_proxy.h"
#include "update_engine/omaha_request_params.h"
@@ -244,6 +244,10 @@
service_observers_.erase(observer);
}
+ const std::set<ServiceObserverInterface*>& service_observers() {
+ return service_observers_;
+ }
+
// Remove all the observers.
void ClearObservers() { service_observers_.clear(); }
diff --git a/update_attempter_android.cc b/update_attempter_android.cc
index 66cb600..dcc2dfd 100644
--- a/update_attempter_android.cc
+++ b/update_attempter_android.cc
@@ -29,15 +29,22 @@
#include <log/log.h>
#include "update_engine/common/constants.h"
-#include "update_engine/common/libcurl_http_fetcher.h"
+#include "update_engine/common/file_fetcher.h"
#include "update_engine/common/multi_range_http_fetcher.h"
#include "update_engine/common/utils.h"
-#include "update_engine/daemon_state_android.h"
+#include "update_engine/daemon_state_interface.h"
+#include "update_engine/network_selector.h"
#include "update_engine/payload_consumer/download_action.h"
#include "update_engine/payload_consumer/filesystem_verifier_action.h"
#include "update_engine/payload_consumer/postinstall_runner_action.h"
#include "update_engine/update_status_utils.h"
+#ifndef _UE_SIDELOAD
+// Do not include support for external HTTP(s) urls when building
+// update_engine_sideload.
+#include "update_engine/libcurl_http_fetcher.h"
+#endif
+
using base::Bind;
using base::TimeDelta;
using base::TimeTicks;
@@ -71,7 +78,7 @@
} // namespace
UpdateAttempterAndroid::UpdateAttempterAndroid(
- DaemonStateAndroid* daemon_state,
+ DaemonStateInterface* daemon_state,
PrefsInterface* prefs,
BootControlInterface* boot_control,
HardwareInterface* hardware)
@@ -80,6 +87,7 @@
boot_control_(boot_control),
hardware_(hardware),
processor_(new ActionProcessor()) {
+ network_selector_ = network::CreateNetworkSelector();
}
UpdateAttempterAndroid::~UpdateAttempterAndroid() {
@@ -169,12 +177,30 @@
install_plan_.source_slot = boot_control_->GetCurrentSlot();
install_plan_.target_slot = install_plan_.source_slot == 0 ? 1 : 0;
- install_plan_.powerwash_required = false;
+
+ int data_wipe = 0;
+ install_plan_.powerwash_required =
+ base::StringToInt(headers[kPayloadPropertyPowerwash], &data_wipe) &&
+ data_wipe != 0;
+
+ NetworkId network_id = kDefaultNetworkId;
+ if (!headers[kPayloadPropertyNetworkId].empty()) {
+ if (!base::StringToUint64(headers[kPayloadPropertyNetworkId],
+ &network_id)) {
+ return LogAndSetError(
+ error,
+ FROM_HERE,
+ "Invalid network_id: " + headers[kPayloadPropertyNetworkId]);
+ }
+ if (!network_selector_->SetProcessNetwork(network_id)) {
+ LOG(WARNING) << "Unable to set network_id, continuing with the update.";
+ }
+ }
LOG(INFO) << "Using this install plan:";
install_plan_.Dump();
- BuildUpdateActions();
+ BuildUpdateActions(payload_url);
SetupDownload();
// Setup extra headers.
HttpFetcher* fetcher = download_action_->http_fetcher();
@@ -256,16 +282,35 @@
ErrorCode code) {
LOG(INFO) << "Processing Done.";
- if (code == ErrorCode::kSuccess) {
- // Update succeeded.
- WriteUpdateCompletedMarker();
- prefs_->SetInt64(kPrefsDeltaUpdateFailures, 0);
- DeltaPerformer::ResetUpdateProgress(prefs_, false);
+ switch (code) {
+ case ErrorCode::kSuccess:
+ // Update succeeded.
+ WriteUpdateCompletedMarker();
+ prefs_->SetInt64(kPrefsDeltaUpdateFailures, 0);
+ DeltaPerformer::ResetUpdateProgress(prefs_, false);
- LOG(INFO) << "Update successfully applied, waiting to reboot.";
- } else if (code == ErrorCode::kPayloadTimestampError) {
- // SafetyNet logging, b/36232423
- android_errorWriteLog(0x534e4554, "36232423");
+ LOG(INFO) << "Update successfully applied, waiting to reboot.";
+ break;
+
+ case ErrorCode::kFilesystemCopierError:
+ case ErrorCode::kNewRootfsVerificationError:
+ case ErrorCode::kNewKernelVerificationError:
+ case ErrorCode::kFilesystemVerifierError:
+ case ErrorCode::kDownloadStateInitializationError:
+ // Reset the ongoing update for these errors so it starts from the
+ // beginning next time.
+ DeltaPerformer::ResetUpdateProgress(prefs_, false);
+ LOG(INFO) << "Resetting update progress.";
+ break;
+
+ case ErrorCode::kPayloadTimestampError:
+ // SafetyNet logging, b/36232423
+ android_errorWriteLog(0x534e4554, "36232423");
+ break;
+
+ default:
+ // Ignore all other error codes.
+ break;
}
TerminateUpdateAndNotify(code);
@@ -386,7 +431,7 @@
last_notify_time_ = TimeTicks::Now();
}
-void UpdateAttempterAndroid::BuildUpdateActions() {
+void UpdateAttempterAndroid::BuildUpdateActions(const string& url) {
CHECK(!processor_->IsRunning());
processor_->set_delegate(this);
@@ -394,9 +439,20 @@
shared_ptr<InstallPlanAction> install_plan_action(
new InstallPlanAction(install_plan_));
- LibcurlHttpFetcher* download_fetcher =
- new LibcurlHttpFetcher(&proxy_resolver_, hardware_);
- download_fetcher->set_server_to_check(ServerToCheck::kDownload);
+ HttpFetcher* download_fetcher = nullptr;
+ if (FileFetcher::SupportedUrl(url)) {
+ DLOG(INFO) << "Using FileFetcher for file URL.";
+ download_fetcher = new FileFetcher();
+ } else {
+#ifdef _UE_SIDELOAD
+ LOG(FATAL) << "Unsupported sideload URI: " << url;
+#else
+ LibcurlHttpFetcher* libcurl_fetcher =
+ new LibcurlHttpFetcher(&proxy_resolver_, hardware_);
+ libcurl_fetcher->set_server_to_check(ServerToCheck::kDownload);
+ download_fetcher = libcurl_fetcher;
+#endif // _UE_SIDELOAD
+ }
shared_ptr<DownloadAction> download_action(new DownloadAction(
prefs_,
boot_control_,
@@ -408,10 +464,11 @@
VerifierMode::kVerifyTargetHash));
shared_ptr<PostinstallRunnerAction> postinstall_runner_action(
- new PostinstallRunnerAction(boot_control_));
+ new PostinstallRunnerAction(boot_control_, hardware_));
download_action->set_delegate(this);
download_action_ = download_action;
+ postinstall_runner_action->set_delegate(this);
actions_.push_back(shared_ptr<AbstractAction>(install_plan_action));
actions_.push_back(shared_ptr<AbstractAction>(download_action));
diff --git a/update_attempter_android.h b/update_attempter_android.h
index 4fdac2c..2617318 100644
--- a/update_attempter_android.h
+++ b/update_attempter_android.h
@@ -31,6 +31,8 @@
#include "update_engine/common/cpu_limiter.h"
#include "update_engine/common/hardware_interface.h"
#include "update_engine/common/prefs_interface.h"
+#include "update_engine/daemon_state_interface.h"
+#include "update_engine/network_selector_interface.h"
#include "update_engine/payload_consumer/download_action.h"
#include "update_engine/payload_consumer/postinstall_runner_action.h"
#include "update_engine/service_delegate_android_interface.h"
@@ -38,8 +40,6 @@
namespace chromeos_update_engine {
-class DaemonStateAndroid;
-
class UpdateAttempterAndroid
: public ServiceDelegateAndroidInterface,
public ActionProcessorDelegate,
@@ -48,7 +48,7 @@
public:
using UpdateStatus = update_engine::UpdateStatus;
- UpdateAttempterAndroid(DaemonStateAndroid* daemon_state,
+ UpdateAttempterAndroid(DaemonStateInterface* daemon_state,
PrefsInterface* prefs,
BootControlInterface* boot_control_,
HardwareInterface* hardware_);
@@ -108,8 +108,8 @@
void SetStatusAndNotify(UpdateStatus status);
// Helper method to construct the sequence of actions to be performed for
- // applying an update.
- void BuildUpdateActions();
+ // applying an update from the given |url|.
+ void BuildUpdateActions(const std::string& url);
// Sets up the download parameters based on the update requested on the
// |install_plan_|.
@@ -122,7 +122,7 @@
// Returns whether an update was completed in the current boot.
bool UpdateCompletedOnThisBoot();
- DaemonStateAndroid* daemon_state_;
+ DaemonStateInterface* daemon_state_;
// DaemonStateAndroid pointers.
PrefsInterface* prefs_;
@@ -163,6 +163,9 @@
// CPU limiter during the update.
CPULimiter cpu_limiter_;
+ // Helper class to select the network to use during the update.
+ std::unique_ptr<NetworkSelectorInterface> network_selector_;
+
// Whether we have marked the current slot as good. This step is required
// before applying an update to the other slot.
bool updated_boot_flags_ = false;
diff --git a/update_attempter_unittest.cc b/update_attempter_unittest.cc
index 35bd206..c8f103d 100644
--- a/update_attempter_unittest.cc
+++ b/update_attempter_unittest.cc
@@ -291,7 +291,7 @@
GetErrorCodeForAction(&filesystem_verifier_action,
ErrorCode::kError));
PostinstallRunnerAction postinstall_runner_action(
- fake_system_state.fake_boot_control());
+ fake_system_state.fake_boot_control(), fake_system_state.fake_hardware());
EXPECT_EQ(ErrorCode::kPostinstallRunnerError,
GetErrorCodeForAction(&postinstall_runner_action,
ErrorCode::kError));
diff --git a/update_engine.gyp b/update_engine.gyp
index 3a1d635..1bd83b1 100644
--- a/update_engine.gyp
+++ b/update_engine.gyp
@@ -484,6 +484,7 @@
'common/certificate_checker_unittest.cc',
'common/cpu_limiter_unittest.cc',
'common/fake_prefs.cc',
+ 'common/file_fetcher.cc', # Only required for tests.
'common/hash_calculator_unittest.cc',
'common/http_fetcher_unittest.cc',
'common/hwid_override_unittest.cc',
diff --git a/update_engine.rc b/update_engine.rc
index 62bbcc7..88cf120 100644
--- a/update_engine.rc
+++ b/update_engine.rc
@@ -1,5 +1,5 @@
service update_engine /system/bin/update_engine --logtostderr --foreground
class late_start
user root
- group root system wakelock dbus inet
+ group root system wakelock dbus inet cache
writepid /dev/cpuset/system-background/tasks
diff --git a/update_metadata.proto b/update_metadata.proto
index de7e668..596a04e 100644
--- a/update_metadata.proto
+++ b/update_metadata.proto
@@ -235,6 +235,10 @@
// associated operation blobs (in operations[i].data_offset, data_length)
// should be stored contiguously and in the same order.
repeated InstallOperation operations = 8;
+
+ // Whether a failure in the postinstall step for this partition should be
+ // ignored.
+ optional bool postinstall_optional = 9;
}
message DeltaArchiveManifest {
diff --git a/utils_android.cc b/utils_android.cc
new file mode 100644
index 0000000..a4f1ea8
--- /dev/null
+++ b/utils_android.cc
@@ -0,0 +1,71 @@
+//
+// Copyright (C) 2016 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 "update_engine/utils_android.h"
+
+#include <cutils/properties.h>
+#include <fs_mgr.h>
+
+using std::string;
+
+namespace chromeos_update_engine {
+
+namespace {
+
+// Open the appropriate fstab file and fallback to /fstab.device if
+// that's what's being used.
+static struct fstab* OpenFSTab() {
+ char propbuf[PROPERTY_VALUE_MAX];
+ struct fstab* fstab;
+
+ property_get("ro.hardware", propbuf, "");
+ string fstab_name = string("/fstab.") + propbuf;
+ fstab = fs_mgr_read_fstab(fstab_name.c_str());
+ if (fstab != nullptr)
+ return fstab;
+
+ fstab = fs_mgr_read_fstab("/fstab.device");
+ return fstab;
+}
+
+} // namespace
+
+namespace utils {
+
+bool DeviceForMountPoint(const string& mount_point, base::FilePath* device) {
+ struct fstab* fstab;
+ struct fstab_rec* record;
+
+ fstab = OpenFSTab();
+ if (fstab == nullptr) {
+ LOG(ERROR) << "Error opening fstab file.";
+ return false;
+ }
+ record = fs_mgr_get_entry_for_mount_point(fstab, mount_point.c_str());
+ if (record == nullptr) {
+ LOG(ERROR) << "Error finding " << mount_point << " entry in fstab file.";
+ fs_mgr_free_fstab(fstab);
+ return false;
+ }
+
+ *device = base::FilePath(record->blk_device);
+ fs_mgr_free_fstab(fstab);
+ return true;
+}
+
+} // namespace utils
+
+} // namespace chromeos_update_engine
diff --git a/utils_android.h b/utils_android.h
new file mode 100644
index 0000000..18dd8ab
--- /dev/null
+++ b/utils_android.h
@@ -0,0 +1,37 @@
+//
+// Copyright (C) 2016 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.
+//
+
+#ifndef UPDATE_ENGINE_UTILS_ANDROID_H_
+#define UPDATE_ENGINE_UTILS_ANDROID_H_
+
+#include <string>
+
+#include <base/files/file_util.h>
+
+namespace chromeos_update_engine {
+
+namespace utils {
+
+// Find the block device that should be mounted in the |mount_point| path and
+// store it in |device|. Returns whether a device was found on the fstab.
+bool DeviceForMountPoint(const std::string& mount_point,
+ base::FilePath* device);
+
+} // namespace utils
+
+} // namespace chromeos_update_engine
+
+#endif // UPDATE_ENGINE_UTILS_ANDROID_H_