Make non-volatile directory platform-specific.
update_engine daemon needs to persist files (such as the progress of
the update) in a non-volatile storage and also used to store a
marker file on a volatile storage to detect when update_engine is
restarted, likelly due to a crash.
This patch moves the non-volatile directory configuration to the
Hardware interface, making it platform-specific. It also replaces the
system rebooted detection using the boot_id provided by the kernel
instead of storing a file under /tmp. This implementation works both
on Chrome OS and Android.
Bug: 24074252
Test: FEATURES=test emerge-link update_engine; `mma`; deployed and tested manually on brillo.
Change-Id: I921d767982adebfd04eb12e08d8a157d9102d1e3
diff --git a/constants.cc b/constants.cc
index 35ff835..d724564 100644
--- a/constants.cc
+++ b/constants.cc
@@ -23,18 +23,16 @@
const char kPowerwashCommand[] = "safe fast keepimg reason=update_engine\n";
-const char kPowerwashSafePrefsDir[] =
- "/mnt/stateful_partition/unencrypted/preserve/update_engine/prefs";
+const char kPowerwashSafePrefsSubDirectory[] = "update_engine/prefs";
-const char kPrefsDirectory[] = "/var/lib/update_engine/prefs";
+const char kPrefsSubDirectory[] = "prefs";
const char kStatefulPartition[] = "/mnt/stateful_partition";
-const char kSystemRebootedMarkerFile[] = "/tmp/update_engine_update_recorded";
-
// Constants defining keys for the persisted state of update engine.
const char kPrefsAttemptInProgress[] = "attempt-in-progress";
const char kPrefsBackoffExpiryTime[] = "backoff-expiry-time";
+const char kPrefsBootId[] = "boot-id";
const char kPrefsCertificateReportToSendDownload[] =
"certificate-report-to-send-download";
const char kPrefsCertificateReportToSendUpdate[] =
diff --git a/constants.h b/constants.h
index c07ff48..211dc96 100644
--- a/constants.h
+++ b/constants.h
@@ -23,17 +23,14 @@
// completes successfully so that the device is powerwashed on next reboot.
extern const char kPowerwashMarkerFile[];
-// Path to the marker file we use to indicate we've recorded a system reboot.
-extern const char kSystemRebootedMarkerFile[];
-
// The contents of the powerwash marker file.
extern const char kPowerwashCommand[];
// Directory for AU prefs that are preserved across powerwash.
-extern const char kPowerwashSafePrefsDir[];
+extern const char kPowerwashSafePrefsSubDirectory[];
// The location where we store the AU preferences (state etc).
-extern const char kPrefsDirectory[];
+extern const char kPrefsSubDirectory[];
// Path to the stateful partition on the root filesystem.
extern const char kStatefulPartition[];
@@ -41,6 +38,7 @@
// Constants related to preferences.
extern const char kPrefsAttemptInProgress[];
extern const char kPrefsBackoffExpiryTime[];
+extern const char kPrefsBootId[];
extern const char kPrefsCertificateReportToSendDownload[];
extern const char kPrefsCertificateReportToSendUpdate[];
extern const char kPrefsCurrentBytesDownloaded[];
diff --git a/fake_hardware.h b/fake_hardware.h
index 686c1cc..6036a40 100644
--- a/fake_hardware.h
+++ b/fake_hardware.h
@@ -62,6 +62,14 @@
int GetPowerwashCount() const override { return powerwash_count_; }
+ bool GetNonVolatileDirectory(base::FilePath* path) const override {
+ return false;
+ }
+
+ bool GetPowerwashSafeDirectory(base::FilePath* path) const override {
+ return false;
+ }
+
// Setters
void SetIsOfficialBuild(bool is_official_build) {
is_official_build_ = is_official_build;
diff --git a/hardware_android.cc b/hardware_android.cc
index 73fef99..6df5cd9 100644
--- a/hardware_android.cc
+++ b/hardware_android.cc
@@ -16,12 +16,20 @@
#include "update_engine/hardware_android.h"
+#include <base/files/file_util.h>
#include <chromeos/make_unique_ptr.h>
#include "update_engine/hardware.h"
using std::string;
+namespace {
+
+// The stateful directory used by update_engine.
+const char kNonVolatileDirectory[] = "/data/misc/update_engine";
+
+} // namespace
+
namespace chromeos_update_engine {
namespace hardware {
@@ -74,4 +82,19 @@
return 0;
}
+bool HardwareAndroid::GetNonVolatileDirectory(base::FilePath* path) const {
+ base::FilePath local_path(kNonVolatileDirectory);
+ if (!base::PathExists(local_path)) {
+ LOG(ERROR) << "Non-volatile directory not found: " << local_path.value();
+ return false;
+ }
+ *path = local_path;
+ return true;
+}
+
+bool HardwareAndroid::GetPowerwashSafeDirectory(base::FilePath* path) const {
+ // On Android, we don't have a directory persisted across powerwash.
+ return false;
+}
+
} // namespace chromeos_update_engine
diff --git a/hardware_android.h b/hardware_android.h
index 0a341c0..03bc9fc 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 GetNonVolatileDirectory(base::FilePath* path) const override;
+ bool GetPowerwashSafeDirectory(base::FilePath* path) const override;
private:
DISALLOW_COPY_AND_ASSIGN(HardwareAndroid);
diff --git a/hardware_chromeos.cc b/hardware_chromeos.cc
index 888772d..d8c659a 100644
--- a/hardware_chromeos.cc
+++ b/hardware_chromeos.cc
@@ -37,13 +37,21 @@
namespace {
-static const char kOOBECompletedMarker[] = "/home/chronos/.oobe_completed";
+const char kOOBECompletedMarker[] = "/home/chronos/.oobe_completed";
+
+// The stateful directory used by update_engine to store powerwash-safe files.
+// The files stored here must be whitelisted in the powerwash scripts.
+const char kPowerwashSafeDirectory[] =
+ "/mnt/stateful_partition/unencrypted/preserve";
// The powerwash_count marker file contains the number of times the device was
// powerwashed. This value is incremented by the clobber-state script when
// a powerwash is performed.
-static const char kPowerwashCountMarker[] =
- "/mnt/stateful_partition/unencrypted/preserve/powerwash_count";
+const char kPowerwashCountMarker[] = "powerwash_count";
+
+// The stateful directory used by update_engine. This directory is wiped during
+// powerwash.
+const char kNonVolatileDirectory[] = "/var/lib/update_engine";
} // namespace
@@ -125,8 +133,10 @@
int HardwareChromeOS::GetPowerwashCount() const {
int powerwash_count;
+ base::FilePath marker_path = base::FilePath(kPowerwashSafeDirectory).Append(
+ kPowerwashCountMarker);
string contents;
- if (!utils::ReadFile(kPowerwashCountMarker, &contents))
+ if (!utils::ReadFile(marker_path.value(), &contents))
return -1;
base::TrimWhitespaceASCII(contents, base::TRIM_TRAILING, &contents);
if (!base::StringToInt(contents, &powerwash_count))
@@ -134,4 +144,14 @@
return powerwash_count;
}
+bool HardwareChromeOS::GetNonVolatileDirectory(base::FilePath* path) const {
+ *path = base::FilePath(kNonVolatileDirectory);
+ return true;
+}
+
+bool HardwareChromeOS::GetPowerwashSafeDirectory(base::FilePath* path) const {
+ *path = base::FilePath(kPowerwashSafeDirectory);
+ return true;
+}
+
} // namespace chromeos_update_engine
diff --git a/hardware_chromeos.h b/hardware_chromeos.h
index 56f5ba8..e7fe486 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 GetNonVolatileDirectory(base::FilePath* path) const override;
+ bool GetPowerwashSafeDirectory(base::FilePath* path) const override;
private:
DISALLOW_COPY_AND_ASSIGN(HardwareChromeOS);
diff --git a/hardware_interface.h b/hardware_interface.h
index 7dc4e53..df8f227 100644
--- a/hardware_interface.h
+++ b/hardware_interface.h
@@ -20,6 +20,7 @@
#include <string>
#include <vector>
+#include <base/files/file_path.h>
#include <base/time/time.h>
namespace chromeos_update_engine {
@@ -60,6 +61,16 @@
// or is invalid, returns -1. Brand new machines out of the factory or after
// recovery don't have this value set.
virtual int GetPowerwashCount() const = 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.
+ virtual bool GetNonVolatileDirectory(base::FilePath* path) const = 0;
+
+ // Store in |path| the path to a non-volatile directory persisted across
+ // powerwash cycles. In case of an error, such as no directory available,
+ // returns false.
+ virtual bool GetPowerwashSafeDirectory(base::FilePath* path) const = 0;
};
} // namespace chromeos_update_engine
diff --git a/mock_hardware.h b/mock_hardware.h
index e082a4f..ff387f4 100644
--- a/mock_hardware.h
+++ b/mock_hardware.h
@@ -51,6 +51,12 @@
ON_CALL(*this, GetPowerwashCount())
.WillByDefault(testing::Invoke(&fake_,
&FakeHardware::GetPowerwashCount));
+ ON_CALL(*this, GetNonVolatileDirectory(testing::_))
+ .WillByDefault(testing::Invoke(&fake_,
+ &FakeHardware::GetNonVolatileDirectory));
+ ON_CALL(*this, GetPowerwashSafeDirectory(testing::_))
+ .WillByDefault(testing::Invoke(&fake_,
+ &FakeHardware::GetPowerwashSafeDirectory));
}
~MockHardware() override = default;
@@ -63,6 +69,8 @@
MOCK_CONST_METHOD0(GetFirmwareVersion, std::string());
MOCK_CONST_METHOD0(GetECVersion, std::string());
MOCK_CONST_METHOD0(GetPowerwashCount, int());
+ MOCK_CONST_METHOD1(GetNonVolatileDirectory, bool(base::FilePath*));
+ MOCK_CONST_METHOD1(GetPowerwashSafeDirectory, bool(base::FilePath*));
// Returns a reference to the underlying FakeHardware.
FakeHardware& fake() {
diff --git a/real_system_state.cc b/real_system_state.cc
index 03cbe2e..34ac5ed 100644
--- a/real_system_state.cc
+++ b/real_system_state.cc
@@ -53,21 +53,44 @@
return false;
}
- if (!prefs_.Init(base::FilePath(kPrefsDirectory))) {
+ // Initialize standard and powerwash-safe prefs.
+ base::FilePath non_volatile_path;
+ // TODO(deymo): Fall back to in-memory prefs if there's no physical directory
+ // available.
+ if (!hardware_->GetNonVolatileDirectory(&non_volatile_path)) {
+ LOG(ERROR) << "Failed to get a non-volatile directory.";
+ return false;
+ }
+ Prefs* prefs;
+ prefs_.reset(prefs = new Prefs());
+ if (!prefs->Init(non_volatile_path.Append(kPrefsSubDirectory))) {
LOG(ERROR) << "Failed to initialize preferences.";
return false;
}
- if (!powerwash_safe_prefs_.Init(base::FilePath(kPowerwashSafePrefsDir))) {
+ base::FilePath powerwash_safe_path;
+ if (!hardware_->GetPowerwashSafeDirectory(&powerwash_safe_path)) {
+ // TODO(deymo): Fall-back to in-memory prefs if there's no powerwash-safe
+ // directory, or disable powerwash feature.
+ powerwash_safe_path = non_volatile_path.Append("powerwash-safe");
+ LOG(WARNING) << "No powerwash-safe directory, using non-volatile one.";
+ }
+ powerwash_safe_prefs_.reset(prefs = new Prefs());
+ if (!prefs->Init(
+ powerwash_safe_path.Append(kPowerwashSafePrefsSubDirectory))) {
LOG(ERROR) << "Failed to initialize powerwash preferences.";
return false;
}
- if (!utils::FileExists(kSystemRebootedMarkerFile)) {
- if (!utils::WriteFile(kSystemRebootedMarkerFile, "", 0)) {
- LOG(ERROR) << "Could not create reboot marker file";
- return false;
- }
+ // Check the system rebooted marker file.
+ std::string boot_id;
+ if (utils::GetBootId(&boot_id)) {
+ std::string prev_boot_id;
+ system_rebooted_ = (!prefs_->GetString(kPrefsBootId, &prev_boot_id) ||
+ prev_boot_id != boot_id);
+ prefs_->SetString(kPrefsBootId, boot_id);
+ } else {
+ LOG(WARNING) << "Couldn't detect the bootid, assuming system was rebooted.";
system_rebooted_ = true;
}
diff --git a/real_system_state.h b/real_system_state.h
index 5797d18..fd2a684 100644
--- a/real_system_state.h
+++ b/real_system_state.h
@@ -77,11 +77,11 @@
return &metrics_lib_;
}
- inline PrefsInterface* prefs() override { return &prefs_; }
+ inline PrefsInterface* prefs() override { return prefs_.get(); }
inline PrefsInterface* powerwash_safe_prefs() override {
- return &powerwash_safe_prefs_;
- }
+ return powerwash_safe_prefs_.get();
+ }
inline PayloadStateInterface* payload_state() override {
return &payload_state_;
@@ -136,10 +136,10 @@
MetricsLibrary metrics_lib_;
// Interface for persisted store.
- Prefs prefs_;
+ std::unique_ptr<PrefsInterface> prefs_;
// Interface for persisted store that persists across powerwashes.
- Prefs powerwash_safe_prefs_;
+ std::unique_ptr<PrefsInterface> powerwash_safe_prefs_;
// All state pertaining to payload state such as response, URL, backoff
// states.
diff --git a/utils.cc b/utils.cc
index 2baa087..cf7913c 100644
--- a/utils.cc
+++ b/utils.cc
@@ -85,6 +85,9 @@
// in GetFileFormat.
const int kGetFileFormatMaxHeaderSize = 32;
+// The path to the kernel's boot_id.
+const char kBootIdPath[] = "/proc/sys/kernel/random/boot_id";
+
// 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) {
@@ -1609,6 +1612,13 @@
return true;
}
+bool GetBootId(string* boot_id) {
+ TEST_AND_RETURN_FALSE(
+ base::ReadFileToString(base::FilePath(kBootIdPath), boot_id));
+ base::TrimWhitespaceASCII(*boot_id, base::TRIM_TRAILING, boot_id);
+ return true;
+}
+
} // namespace utils
} // namespace chromeos_update_engine
diff --git a/utils.h b/utils.h
index 803a0d6..6668c39 100644
--- a/utils.h
+++ b/utils.h
@@ -416,6 +416,11 @@
chromeos::Blob* out_data, ssize_t out_data_size,
size_t block_size);
+// Read the current boot identifier and store it in |boot_id|. This identifier
+// is constants during the same boot of the kernel and is regenerated after
+// reboot. Returns whether it succeeded getting the boot_id.
+bool GetBootId(std::string* boot_id);
+
} // namespace utils