Merge "update_engine: New BootControlInterface class."
diff --git a/boot_control_chromeos.cc b/boot_control_chromeos.cc
new file mode 100644
index 0000000..ad82401
--- /dev/null
+++ b/boot_control_chromeos.cc
@@ -0,0 +1,229 @@
+//
+// Copyright (C) 2015 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/boot_control_chromeos.h"
+
+#include <string>
+
+#include <base/files/file_path.h>
+#include <base/files/file_util.h>
+#include <base/strings/string_util.h>
+#include <rootdev/rootdev.h>
+
+extern "C" {
+#include <vboot/vboot_host.h>
+}
+
+#include "update_engine/utils.h"
+
+using std::string;
+
+namespace {
+
+const char* kChromeOSPartitionNameKernel = "kernel";
+const char* kChromeOSPartitionNameRoot = "root";
+const char* kAndroidPartitionNameKernel = "boot";
+const char* kAndroidPartitionNameRoot = "system";
+
+// Returns the currently booted rootfs partition. "/dev/sda3", for example.
+string GetBootDevice() {
+ char boot_path[PATH_MAX];
+ // Resolve the boot device path fully, including dereferencing through
+ // dm-verity.
+ int ret = rootdev(boot_path, sizeof(boot_path), true, false);
+ if (ret < 0) {
+ LOG(ERROR) << "rootdev failed to find the root device";
+ return "";
+ }
+ LOG_IF(WARNING, ret > 0) << "rootdev found a device name with no device node";
+
+ // This local variable is used to construct the return string and is not
+ // passed around after use.
+ return boot_path;
+}
+
+} // namespace
+
+namespace chromeos_update_engine {
+
+bool BootControlChromeOS::Init() {
+ string boot_device = GetBootDevice();
+ if (boot_device.empty())
+ return false;
+
+ int partition_num;
+ if (!utils::SplitPartitionName(boot_device, &boot_disk_name_, &partition_num))
+ return false;
+
+ // All installed Chrome OS devices have two slots. We don't update removable
+ // devices, so we will pretend we have only one slot in that case.
+ if (IsRemovableDevice(boot_disk_name_)) {
+ LOG(INFO)
+ << "Booted from a removable device, pretending we have only one slot.";
+ num_slots_ = 1;
+ } else {
+ // TODO(deymo): Look at the actual number of slots reported in the GPT.
+ num_slots_ = 2;
+ }
+
+ // Search through the slots to see which slot has the partition_num we booted
+ // from. This should map to one of the existing slots, otherwise something is
+ // very wrong.
+ current_slot_ = 0;
+ while (current_slot_ < num_slots_ &&
+ partition_num !=
+ GetPartitionNumber(kChromeOSPartitionNameRoot, current_slot_)) {
+ current_slot_++;
+ }
+ if (current_slot_ >= num_slots_) {
+ LOG(ERROR) << "Couldn't find the slot number corresponding to the "
+ "partition " << boot_device
+ << ", number of slots: " << num_slots_
+ << ". This device is not updateable.";
+ num_slots_ = 1;
+ current_slot_ = BootControlInterface::kInvalidSlot;
+ return false;
+ }
+
+ LOG(INFO) << "Booted from slot " << current_slot_ << " (slot "
+ << BootControlInterface::SlotName(current_slot_) << ") of "
+ << num_slots_ << " slots present on disk " << boot_disk_name_;
+ return true;
+}
+
+unsigned int BootControlChromeOS::GetNumSlots() const {
+ return num_slots_;
+}
+
+BootControlInterface::Slot BootControlChromeOS::GetCurrentSlot() const {
+ return current_slot_;
+}
+
+bool BootControlChromeOS::GetPartitionDevice(const string& partition_name,
+ unsigned int slot,
+ string* device) const {
+ int partition_num = GetPartitionNumber(partition_name, slot);
+ if (partition_num < 0)
+ return false;
+
+ string part_device = utils::MakePartitionName(boot_disk_name_, partition_num);
+ if (part_device.empty())
+ return false;
+
+ *device = part_device;
+ return true;
+}
+
+bool BootControlChromeOS::IsSlotBootable(Slot slot) const {
+ int partition_num = GetPartitionNumber(kChromeOSPartitionNameKernel, slot);
+ if (partition_num < 0)
+ return false;
+
+ CgptAddParams params;
+ memset(¶ms, '\0', sizeof(params));
+ params.drive_name = const_cast<char*>(boot_disk_name_.c_str());
+ params.partition = partition_num;
+
+ int retval = CgptGetPartitionDetails(¶ms);
+ if (retval != CGPT_OK)
+ return false;
+
+ return params.successful || params.tries > 0;
+}
+
+bool BootControlChromeOS::MarkSlotUnbootable(Slot slot) {
+ LOG(INFO) << "Marking slot " << BootControlInterface::SlotName(slot)
+ << " unbootable";
+
+ if (slot == current_slot_) {
+ LOG(ERROR) << "Refusing to mark current slot as unbootable.";
+ return false;
+ }
+
+ int partition_num = GetPartitionNumber(kChromeOSPartitionNameKernel, slot);
+ if (partition_num < 0)
+ return false;
+
+ CgptAddParams params;
+ memset(¶ms, 0, sizeof(params));
+
+ params.drive_name = const_cast<char*>(boot_disk_name_.c_str());
+ params.partition = partition_num;
+
+ params.successful = false;
+ params.set_successful = true;
+
+ params.tries = 0;
+ params.set_tries = true;
+
+ int retval = CgptSetAttributes(¶ms);
+ if (retval != CGPT_OK) {
+ LOG(ERROR) << "Marking kernel unbootable failed.";
+ return false;
+ }
+
+ return true;
+}
+
+// static
+string BootControlChromeOS::SysfsBlockDevice(const string& device) {
+ base::FilePath device_path(device);
+ if (device_path.DirName().value() != "/dev") {
+ return "";
+ }
+ return base::FilePath("/sys/block").Append(device_path.BaseName()).value();
+}
+
+// static
+bool BootControlChromeOS::IsRemovableDevice(const string& device) {
+ string sysfs_block = SysfsBlockDevice(device);
+ string removable;
+ if (sysfs_block.empty() ||
+ !base::ReadFileToString(base::FilePath(sysfs_block).Append("removable"),
+ &removable)) {
+ return false;
+ }
+ base::TrimWhitespaceASCII(removable, base::TRIM_ALL, &removable);
+ return removable == "1";
+}
+
+int BootControlChromeOS::GetPartitionNumber(
+ const string partition_name,
+ BootControlInterface::Slot slot) const {
+ if (slot >= num_slots_) {
+ LOG(ERROR) << "Invalid slot number: " << slot << ", we only have "
+ << num_slots_ << " slot(s)";
+ return -1;
+ }
+
+ // In Chrome OS, the partition numbers are hard-coded:
+ // KERNEL-A=2, ROOT-A=3, KERNEL-B=4, ROOT-B=4, ...
+ // To help compatibility between different we accept both lowercase and
+ // uppercase names in the ChromeOS or Brillo standard names.
+ // See http://www.chromium.org/chromium-os/chromiumos-design-docs/disk-format
+ string partition_lower = base::StringToLowerASCII(partition_name);
+ int base_part_num = 2 + 2 * slot;
+ if (partition_lower == kChromeOSPartitionNameKernel ||
+ partition_lower == kAndroidPartitionNameKernel)
+ return base_part_num + 0;
+ if (partition_lower == kChromeOSPartitionNameRoot ||
+ partition_lower == kAndroidPartitionNameRoot)
+ return base_part_num + 1;
+ LOG(ERROR) << "Unknown Chrome OS partition name \"" << partition_name << "\"";
+ return -1;
+}
+
+} // namespace chromeos_update_engine
diff --git a/boot_control_chromeos.h b/boot_control_chromeos.h
new file mode 100644
index 0000000..a7c269e
--- /dev/null
+++ b/boot_control_chromeos.h
@@ -0,0 +1,82 @@
+//
+// Copyright (C) 2015 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_BOOT_CONTROL_CHROMEOS_H_
+#define UPDATE_ENGINE_BOOT_CONTROL_CHROMEOS_H_
+
+#include <string>
+
+#include <gtest/gtest_prod.h> // for FRIEND_TEST
+
+#include "update_engine/boot_control_interface.h"
+
+namespace chromeos_update_engine {
+
+// The Chrome OS implementation of the BootControlInterface. This interface
+// assumes the partition names and numbers used in Chrome OS devices.
+class BootControlChromeOS : public BootControlInterface {
+ public:
+ BootControlChromeOS() = default;
+ ~BootControlChromeOS() = default;
+
+ // Initialize the BootControl instance loading the constant values. Returns
+ // whether the operation succeeded. In case of failure, normally meaning
+ // some critical failure such as we couldn't determine the slot that we
+ // booted from, the implementation will pretend that there's only one slot and
+ // therefore A/B updates are disabled.
+ bool Init();
+
+ // BootControlInterface overrides.
+ unsigned int GetNumSlots() const override;
+ BootControlInterface::Slot GetCurrentSlot() const override;
+ bool GetPartitionDevice(const std::string& partition_name,
+ BootControlInterface::Slot slot,
+ std::string* device) const override;
+ bool IsSlotBootable(BootControlInterface::Slot slot) const override;
+ bool MarkSlotUnbootable(BootControlInterface::Slot slot) override;
+
+ private:
+ friend class BootControlChromeOSTest;
+ FRIEND_TEST(BootControlChromeOSTest, SysfsBlockDeviceTest);
+ FRIEND_TEST(BootControlChromeOSTest, GetPartitionNumberTest);
+
+ // Returns the sysfs block device for a root block device. For example,
+ // SysfsBlockDevice("/dev/sda") returns "/sys/block/sda". Returns an empty
+ // string if the input device is not of the "/dev/xyz" form.
+ static std::string SysfsBlockDevice(const std::string& device);
+
+ // Returns true if the root |device| (e.g., "/dev/sdb") is known to be
+ // removable, false otherwise.
+ static bool IsRemovableDevice(const std::string& device);
+
+ // Return the hard-coded partition number used in Chrome OS for the passed
+ // |partition_name| and |slot|. In case of invalid data, returns -1.
+ int GetPartitionNumber(const std::string partition_name,
+ BootControlInterface::Slot slot) const;
+
+ // Cached values for GetNumSlots() and GetCurrentSlot().
+ BootControlInterface::Slot num_slots_{1};
+ BootControlInterface::Slot current_slot_{BootControlInterface::kInvalidSlot};
+
+ // The block device of the disk we booted from, without the partition number.
+ std::string boot_disk_name_;
+
+ DISALLOW_COPY_AND_ASSIGN(BootControlChromeOS);
+};
+
+} // namespace chromeos_update_engine
+
+#endif // UPDATE_ENGINE_BOOT_CONTROL_CHROMEOS_H_
diff --git a/boot_control_chromeos_unittest.cc b/boot_control_chromeos_unittest.cc
new file mode 100644
index 0000000..6a60009
--- /dev/null
+++ b/boot_control_chromeos_unittest.cc
@@ -0,0 +1,70 @@
+//
+// Copyright (C) 2015 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/boot_control_chromeos.h"
+
+#include <gtest/gtest.h>
+
+namespace chromeos_update_engine {
+
+class BootControlChromeOSTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ // We don't run Init() for bootctl_, we set its internal values instead.
+ bootctl_.num_slots_ = 2;
+ bootctl_.current_slot_ = 0;
+ bootctl_.boot_disk_name_ = "/dev/null";
+ }
+
+ BootControlChromeOS bootctl_; // BootControlChromeOS under test.
+};
+
+TEST_F(BootControlChromeOSTest, SysfsBlockDeviceTest) {
+ EXPECT_EQ("/sys/block/sda", bootctl_.SysfsBlockDevice("/dev/sda"));
+ EXPECT_EQ("", bootctl_.SysfsBlockDevice("/foo/sda"));
+ EXPECT_EQ("", bootctl_.SysfsBlockDevice("/dev/foo/bar"));
+ EXPECT_EQ("", bootctl_.SysfsBlockDevice("/"));
+ EXPECT_EQ("", bootctl_.SysfsBlockDevice("./"));
+ EXPECT_EQ("", bootctl_.SysfsBlockDevice(""));
+}
+
+TEST_F(BootControlChromeOSTest, GetPartitionNumberTest) {
+ // The partition name should not be case-sensitive.
+ EXPECT_EQ(2, bootctl_.GetPartitionNumber("kernel", 0));
+ EXPECT_EQ(2, bootctl_.GetPartitionNumber("boot", 0));
+ EXPECT_EQ(2, bootctl_.GetPartitionNumber("KERNEL", 0));
+ EXPECT_EQ(2, bootctl_.GetPartitionNumber("BOOT", 0));
+
+ EXPECT_EQ(3, bootctl_.GetPartitionNumber("ROOT", 0));
+ EXPECT_EQ(3, bootctl_.GetPartitionNumber("system", 0));
+
+ EXPECT_EQ(3, bootctl_.GetPartitionNumber("ROOT", 0));
+ EXPECT_EQ(3, bootctl_.GetPartitionNumber("system", 0));
+
+ // Slot B.
+ EXPECT_EQ(4, bootctl_.GetPartitionNumber("KERNEL", 1));
+ EXPECT_EQ(5, bootctl_.GetPartitionNumber("ROOT", 1));
+
+ // Slot C doesn't exists.
+ EXPECT_EQ(-1, bootctl_.GetPartitionNumber("KERNEL", 2));
+ EXPECT_EQ(-1, bootctl_.GetPartitionNumber("ROOT", 2));
+
+ // Non A/B partitions are ignored.
+ EXPECT_EQ(-1, bootctl_.GetPartitionNumber("OEM", 0));
+ EXPECT_EQ(-1, bootctl_.GetPartitionNumber("A little panda", 0));
+}
+
+} // namespace chromeos_update_engine
diff --git a/boot_control_interface.h b/boot_control_interface.h
new file mode 100644
index 0000000..135d2eb
--- /dev/null
+++ b/boot_control_interface.h
@@ -0,0 +1,85 @@
+//
+// Copyright (C) 2015 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_BOOT_CONTROL_INTERFACE_H_
+#define UPDATE_ENGINE_BOOT_CONTROL_INTERFACE_H_
+
+#include <climits>
+#include <string>
+
+#include <base/macros.h>
+
+namespace chromeos_update_engine {
+
+// The abstract boot control interface defines the interaction with the
+// platform's bootloader hiding vendor-specific details from the rest of
+// update_engine. This interface is used for controlling where the device should
+// boot from.
+class BootControlInterface {
+ public:
+ using Slot = unsigned int;
+
+ static const Slot kInvalidSlot = UINT_MAX;
+
+ virtual ~BootControlInterface() = default;
+
+ // Return the number of update slots in the system. A system will normally
+ // have two slots, named "A" and "B" in the documentation, but sometimes
+ // images running from other media can have only one slot, like some USB
+ // image. Systems with only one slot won't be able to update.
+ virtual unsigned int GetNumSlots() const = 0;
+
+ // Return the slot where we are running the system from. On success, the
+ // result is a number between 0 and GetNumSlots() - 1. Otherwise, log an error
+ // and return kInvalidSlot.
+ virtual Slot GetCurrentSlot() const = 0;
+
+ // Determines the block device for the given partition name and slot number.
+ // The |slot| number must be between 0 and GetNumSlots() - 1 and the
+ // |partition_name| is a platform-specific name that identifies a partition on
+ // every slot. On success, returns true and stores the block device in
+ // |device|.
+ virtual bool GetPartitionDevice(const std::string& partition_name,
+ Slot slot,
+ std::string* device) const = 0;
+
+ // Returns whether the passed |slot| is marked as bootable. Returns false if
+ // the slot is invalid.
+ virtual bool IsSlotBootable(Slot slot) const = 0;
+
+ // Mark the specified slot unbootable. No other slot flags are modified.
+ // Returns true on success.
+ virtual bool MarkSlotUnbootable(Slot slot) = 0;
+
+ // Return a human-readable slot name used for logging.
+ static std::string SlotName(Slot slot) {
+ if (slot == kInvalidSlot)
+ return "INVALID";
+ if (slot < 26)
+ return std::string(1, 'A' + slot);
+ return "TOO_BIG";
+ }
+
+ protected:
+ BootControlInterface() = default;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(BootControlInterface);
+};
+
+} // namespace chromeos_update_engine
+
+#endif // UPDATE_ENGINE_BOOT_CONTROL_INTERFACE_H_
diff --git a/dbus_service.cc b/dbus_service.cc
index db9a6eb..7bf8a69 100644
--- a/dbus_service.cc
+++ b/dbus_service.cc
@@ -312,13 +312,26 @@
bool UpdateEngineService::GetRollbackPartition(
ErrorPtr* /* error */,
string* out_rollback_partition_name) {
- string name = system_state_->update_attempter()->GetRollbackPartition();
+ BootControlInterface::Slot rollback_slot =
+ system_state_->update_attempter()->GetRollbackSlot();
+
+ if (rollback_slot == BootControlInterface::kInvalidSlot) {
+ out_rollback_partition_name->clear();
+ return true;
+ }
+
+ string name;
+ if (!system_state_->boot_control()->GetPartitionDevice(
+ "KERNEL", rollback_slot, &name)) {
+ LOG(ERROR) << "Invalid rollback device";
+ return false;
+ }
+
LOG(INFO) << "Getting rollback partition name. Result: " << name;
*out_rollback_partition_name = name;
return true;
}
-
UpdateEngineAdaptor::UpdateEngineAdaptor(SystemState* system_state,
const scoped_refptr<dbus::Bus>& bus)
: org::chromium::UpdateEngineInterfaceAdaptor(&dbus_service_),
diff --git a/fake_boot_control.h b/fake_boot_control.h
new file mode 100644
index 0000000..508f578f
--- /dev/null
+++ b/fake_boot_control.h
@@ -0,0 +1,103 @@
+//
+// Copyright (C) 2015 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_FAKE_BOOT_CONTROL_H_
+#define UPDATE_ENGINE_FAKE_BOOT_CONTROL_H_
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include <base/time/time.h>
+
+#include "update_engine/boot_control_interface.h"
+
+namespace chromeos_update_engine {
+
+// Implements a fake bootloader control interface used for testing.
+class FakeBootControl : public BootControlInterface {
+ public:
+ FakeBootControl() {
+ SetNumSlots(num_slots_);
+ // The current slot should be bootable.
+ is_bootable_[current_slot_] = true;
+ }
+
+ // BootControlInterface overrides.
+ unsigned int GetNumSlots() const override { return num_slots_; }
+ BootControlInterface::Slot GetCurrentSlot() const override {
+ return current_slot_;
+ }
+
+ bool GetPartitionDevice(const std::string& partition_name,
+ BootControlInterface::Slot slot,
+ std::string* device) const override {
+ if (slot >= num_slots_)
+ return false;
+ auto part_it = devices_[slot].find(partition_name);
+ if (part_it == devices_[slot].end())
+ return false;
+ *device = part_it->second;
+ return true;
+ }
+
+ bool IsSlotBootable(BootControlInterface::Slot slot) const override {
+ return slot < num_slots_ && is_bootable_[slot];
+ }
+
+ bool MarkSlotUnbootable(BootControlInterface::Slot slot) override {
+ if (slot >= num_slots_)
+ return false;
+ is_bootable_[slot] = false;
+ return true;
+ }
+
+ // Setters
+ void SetNumSlots(unsigned int num_slots) {
+ num_slots_ = num_slots;
+ is_bootable_.resize(num_slots_, false);
+ devices_.resize(num_slots_);
+ }
+
+ void SetCurrentSlot(BootControlInterface::Slot slot) {
+ current_slot_ = slot;
+ }
+
+ void SetPartitionDevice(const std::string partition_name,
+ BootControlInterface::Slot slot,
+ const std::string device) {
+ DCHECK(slot < num_slots_);
+ devices_[slot][partition_name] = device;
+ }
+
+ void SetSlotBootable(BootControlInterface::Slot slot, bool bootable) {
+ DCHECK(slot < num_slots_);
+ is_bootable_[slot] = bootable;
+ }
+
+ private:
+ BootControlInterface::Slot num_slots_{2};
+ BootControlInterface::Slot current_slot_{0};
+
+ std::vector<bool> is_bootable_;
+ std::vector<std::map<std::string, std::string>> devices_;
+
+ DISALLOW_COPY_AND_ASSIGN(FakeBootControl);
+};
+
+} // namespace chromeos_update_engine
+
+#endif // UPDATE_ENGINE_FAKE_BOOT_CONTROL_H_
diff --git a/fake_hardware.h b/fake_hardware.h
index 39cbf42..5d3da1a 100644
--- a/fake_hardware.h
+++ b/fake_hardware.h
@@ -36,43 +36,15 @@
static const int kPowerwashCountNotSet = -1;
FakeHardware()
- : kernel_device_("/dev/sdz4"),
- boot_device_("/dev/sdz5"),
- is_boot_device_removable_(false),
- kernel_devices_({"/dev/sdz2", "/dev/sdz4"}),
- is_official_build_(true),
- is_normal_boot_mode_(true),
- is_oobe_complete_(false),
- hardware_class_("Fake HWID BLAH-1234"),
- firmware_version_("Fake Firmware v1.0.1"),
- ec_version_("Fake EC v1.0a"),
- powerwash_count_(kPowerwashCountNotSet) {}
+ : is_official_build_(true),
+ is_normal_boot_mode_(true),
+ is_oobe_complete_(false),
+ hardware_class_("Fake HWID BLAH-1234"),
+ firmware_version_("Fake Firmware v1.0.1"),
+ ec_version_("Fake EC v1.0a"),
+ powerwash_count_(kPowerwashCountNotSet) {}
// HardwareInterface methods.
- std::string BootKernelDevice() const override { return kernel_device_; }
-
- std::string BootDevice() const override { return boot_device_; }
-
- bool IsBootDeviceRemovable() const override {
- return is_boot_device_removable_;
- }
-
- std::vector<std::string> GetKernelDevices() const override {
- return kernel_devices_;
- }
-
- bool IsKernelBootable(const std::string& kernel_device,
- bool* bootable) const override {
- auto i = is_bootable_.find(kernel_device);
- *bootable = (i != is_bootable_.end()) ? i->second : true;
- return true;
- }
-
- bool MarkKernelUnbootable(const std::string& kernel_device) override {
- is_bootable_[kernel_device] = false;
- return true;
- }
-
bool IsOfficialBuild() const override { return is_official_build_; }
bool IsNormalBootMode() const override { return is_normal_boot_mode_; }
@@ -92,14 +64,6 @@
int GetPowerwashCount() const override { return powerwash_count_; }
// Setters
- void SetBootDevice(const std::string& boot_device) {
- boot_device_ = boot_device;
- }
-
- void SetIsBootDeviceRemovable(bool is_boot_device_removable) {
- is_boot_device_removable_ = is_boot_device_removable;
- }
-
void SetIsOfficialBuild(bool is_official_build) {
is_official_build_ = is_official_build;
}
@@ -136,11 +100,6 @@
}
private:
- std::string kernel_device_;
- std::string boot_device_;
- bool is_boot_device_removable_;
- std::vector<std::string> kernel_devices_;
- std::map<std::string, bool> is_bootable_;
bool is_official_build_;
bool is_normal_boot_mode_;
bool is_oobe_complete_;
diff --git a/fake_system_state.h b/fake_system_state.h
index 59d25b6..30ba7e9 100644
--- a/fake_system_state.h
+++ b/fake_system_state.h
@@ -24,6 +24,7 @@
#include "metrics/metrics_library_mock.h"
#include "power_manager/dbus-proxies.h"
#include "power_manager/dbus-proxy-mocks.h"
+#include "update_engine/fake_boot_control.h"
#include "update_engine/fake_clock.h"
#include "update_engine/fake_hardware.h"
#include "update_engine/mock_connection_manager.h"
@@ -47,6 +48,8 @@
// various members, either the default (fake/mock) or the one set to override
// it by client code.
+ BootControlInterface* boot_control() override { return boot_control_; }
+
inline ClockInterface* clock() override { return clock_; }
inline void set_device_policy(
@@ -103,6 +106,10 @@
// implementations. For convenience, setting to a null pointer will restore
// the default implementation.
+ void set_boot_control(BootControlInterface* boot_control) {
+ boot_control_ = boot_control ? boot_control : &fake_boot_control_;
+ }
+
inline void set_clock(ClockInterface* clock) {
clock_ = clock ? clock : &fake_clock_;
}
@@ -162,6 +169,11 @@
// whenever the requested default was overridden by a different
// implementation.
+ inline FakeBootControl* fake_boot_control() {
+ CHECK(boot_control_ == &fake_boot_control_);
+ return &fake_boot_control_;
+ }
+
inline FakeClock* fake_clock() {
CHECK(clock_ == &fake_clock_);
return &fake_clock_;
@@ -219,6 +231,7 @@
private:
// Default mock/fake implementations (owned).
+ FakeBootControl fake_boot_control_;
FakeClock fake_clock_;
testing::NiceMock<MockConnectionManager> mock_connection_manager_;
FakeHardware fake_hardware_;
@@ -234,6 +247,7 @@
// Pointers to objects that client code can override. They are initialized to
// the default implementations above.
+ BootControlInterface* boot_control_{&fake_boot_control_};
ClockInterface* clock_;
ConnectionManagerInterface* connection_manager_;
HardwareInterface* hardware_;
diff --git a/filesystem_verifier_action.cc b/filesystem_verifier_action.cc
index bfe3da7..df54f80 100644
--- a/filesystem_verifier_action.cc
+++ b/filesystem_verifier_action.cc
@@ -28,7 +28,7 @@
#include <base/bind.h>
#include <chromeos/streams/file_stream.h>
-#include "update_engine/hardware_interface.h"
+#include "update_engine/boot_control_interface.h"
#include "update_engine/system_state.h"
#include "update_engine/utils.h"
@@ -40,6 +40,21 @@
const off_t kReadFileBufferSize = 128 * 1024;
} // namespace
+string PartitionTypeToString(const PartitionType partition_type) {
+ // TODO(deymo): The PartitionType class should be replaced with just the
+ // string name that comes from the payload. This function should be deleted
+ // then.
+ switch (partition_type) {
+ case PartitionType::kRootfs:
+ case PartitionType::kSourceRootfs:
+ return kLegacyPartitionNameRoot;
+ case PartitionType::kKernel:
+ case PartitionType::kSourceKernel:
+ return kLegacyPartitionNameKernel;
+ }
+ return "<unknown>";
+}
+
FilesystemVerifierAction::FilesystemVerifierAction(
SystemState* system_state,
PartitionType partition_type)
@@ -57,10 +72,11 @@
}
install_plan_ = GetInputObject();
+ // TODO(deymo): Remove this from the FileSystemVerifierAction.
if (partition_type_ == PartitionType::kKernel) {
LOG(INFO) << "verifying kernel, marking as unbootable";
- if (!system_state_->hardware()->MarkKernelUnbootable(
- install_plan_.kernel_install_path)) {
+ if (!system_state_->boot_control()->MarkSlotUnbootable(
+ install_plan_.target_slot)) {
PLOG(ERROR) << "Unable to clear kernel GPT boot flags: " <<
install_plan_.kernel_install_path;
}
@@ -78,33 +94,35 @@
}
string target_path;
+ string partition_name = PartitionTypeToString(partition_type_);
switch (partition_type_) {
case PartitionType::kRootfs:
target_path = install_plan_.install_path;
if (target_path.empty()) {
- utils::GetInstallDev(system_state_->hardware()->BootDevice(),
- &target_path);
+ system_state_->boot_control()->GetPartitionDevice(
+ partition_name, install_plan_.target_slot, &target_path);
}
break;
case PartitionType::kKernel:
target_path = install_plan_.kernel_install_path;
if (target_path.empty()) {
- string rootfs_path;
- utils::GetInstallDev(system_state_->hardware()->BootDevice(),
- &rootfs_path);
- target_path = utils::KernelDeviceOfBootDevice(rootfs_path);
+ system_state_->boot_control()->GetPartitionDevice(
+ partition_name, install_plan_.target_slot, &target_path);
}
break;
case PartitionType::kSourceRootfs:
- target_path = install_plan_.source_path.empty() ?
- system_state_->hardware()->BootDevice() :
- install_plan_.source_path;
+ target_path = install_plan_.source_path;
+ if (target_path.empty()) {
+ system_state_->boot_control()->GetPartitionDevice(
+ partition_name, install_plan_.source_slot, &target_path);
+ }
break;
case PartitionType::kSourceKernel:
- target_path = install_plan_.kernel_source_path.empty() ?
- utils::KernelDeviceOfBootDevice(
- system_state_->hardware()->BootDevice()) :
- install_plan_.kernel_source_path;
+ target_path = install_plan_.kernel_source_path;
+ if (target_path.empty()) {
+ system_state_->boot_control()->GetPartitionDevice(
+ partition_name, install_plan_.source_slot, &target_path);
+ }
break;
}
@@ -239,8 +257,7 @@
return true;
}
-void FilesystemVerifierAction::DetermineFilesystemSize(
- const std::string& path) {
+void FilesystemVerifierAction::DetermineFilesystemSize(const string& path) {
switch (partition_type_) {
case PartitionType::kRootfs:
remaining_size_ = install_plan_.rootfs_size;
diff --git a/filesystem_verifier_action.h b/filesystem_verifier_action.h
index 9935dea..7a6930e 100644
--- a/filesystem_verifier_action.h
+++ b/filesystem_verifier_action.h
@@ -45,6 +45,9 @@
kKernel,
};
+// Return the partition name string for the passed partition type.
+std::string PartitionTypeToString(const PartitionType partition_type);
+
class FilesystemVerifierAction : public InstallPlanAction {
public:
FilesystemVerifierAction(SystemState* system_state,
diff --git a/filesystem_verifier_action_unittest.cc b/filesystem_verifier_action_unittest.cc
index 37cdf38..32f3c59 100644
--- a/filesystem_verifier_action_unittest.cc
+++ b/filesystem_verifier_action_unittest.cc
@@ -131,11 +131,6 @@
bool FilesystemVerifierActionTest::DoTest(bool terminate_early,
bool hash_fail,
PartitionType partition_type) {
- // We need MockHardware to verify MarkUnbootable calls, but don't want
- // warnings about other usages.
- testing::NiceMock<MockHardware> mock_hardware;
- fake_system_state_.set_hardware(&mock_hardware);
-
string a_loop_file;
if (!(utils::MakeTempFile("a_loop_file.XXXXXX", &a_loop_file, nullptr))) {
@@ -170,6 +165,8 @@
// Set up the action objects
InstallPlan install_plan;
+ install_plan.source_slot = 0;
+ install_plan.target_slot = 1;
switch (partition_type) {
case PartitionType::kRootfs:
install_plan.rootfs_size = kLoopFileSize - (hash_fail ? 1 : 0);
@@ -207,9 +204,8 @@
break;
}
- EXPECT_CALL(mock_hardware,
- MarkKernelUnbootable(a_dev)).Times(
- partition_type == PartitionType::kKernel ? 1 : 0);
+ fake_system_state_.fake_boot_control()->SetSlotBootable(
+ install_plan.target_slot, true);
ActionProcessor processor;
@@ -269,12 +265,12 @@
success = success && is_install_plan_eq;
LOG(INFO) << "Verifying bootable flag on: " << a_dev;
- bool bootable;
- EXPECT_TRUE(mock_hardware.fake().IsKernelBootable(a_dev, &bootable));
+
// We should always mark a partition as unbootable if it's a kernel
// partition, but never if it's anything else.
- EXPECT_EQ(bootable, (partition_type != PartitionType::kKernel));
-
+ EXPECT_EQ((partition_type != PartitionType::kKernel),
+ fake_system_state_.fake_boot_control()->IsSlotBootable(
+ install_plan.target_slot));
return success;
}
diff --git a/hardware.cc b/hardware.cc
index bdc1d41..96edaa5 100644
--- a/hardware.cc
+++ b/hardware.cc
@@ -20,7 +20,6 @@
#include <base/logging.h>
#include <base/strings/string_number_conversions.h>
#include <base/strings/string_util.h>
-#include <rootdev/rootdev.h>
#include <vboot/crossystem.h>
extern "C" {
@@ -48,116 +47,6 @@
namespace chromeos_update_engine {
-Hardware::Hardware() {}
-
-Hardware::~Hardware() {}
-
-string Hardware::BootKernelDevice() const {
- return utils::KernelDeviceOfBootDevice(Hardware::BootDevice());
-}
-
-string Hardware::BootDevice() const {
- char boot_path[PATH_MAX];
- // Resolve the boot device path fully, including dereferencing
- // through dm-verity.
- int ret = rootdev(boot_path, sizeof(boot_path), true, false);
-
- if (ret < 0) {
- LOG(ERROR) << "rootdev failed to find the root device";
- return "";
- }
- LOG_IF(WARNING, ret > 0) << "rootdev found a device name with no device node";
-
- // This local variable is used to construct the return string and is not
- // passed around after use.
- return boot_path;
-}
-
-bool Hardware::IsBootDeviceRemovable() const {
- return utils::IsRemovableDevice(utils::GetDiskName(BootDevice()));
-}
-
-bool Hardware::IsKernelBootable(const string& kernel_device,
- bool* bootable) const {
- CgptAddParams params;
- memset(¶ms, '\0', sizeof(params));
-
- string disk_name;
- int partition_num = 0;
-
- if (!utils::SplitPartitionName(kernel_device, &disk_name, &partition_num))
- return false;
-
- params.drive_name = const_cast<char *>(disk_name.c_str());
- params.partition = partition_num;
-
- int retval = CgptGetPartitionDetails(¶ms);
- if (retval != CGPT_OK)
- return false;
-
- *bootable = params.successful || (params.tries > 0);
- return true;
-}
-
-vector<string> Hardware::GetKernelDevices() const {
- LOG(INFO) << "GetAllKernelDevices";
-
- string disk_name = utils::GetDiskName(Hardware::BootKernelDevice());
- if (disk_name.empty()) {
- LOG(ERROR) << "Failed to get the current kernel boot disk name";
- return vector<string>();
- }
-
- vector<string> devices;
- for (int partition_num : {2, 4}) { // for now, only #2, #4 for slot A & B
- string device = utils::MakePartitionName(disk_name, partition_num);
- if (!device.empty()) {
- devices.push_back(std::move(device));
- } else {
- LOG(ERROR) << "Cannot make a partition name for disk: "
- << disk_name << ", partition: " << partition_num;
- }
- }
-
- return devices;
-}
-
-
-bool Hardware::MarkKernelUnbootable(const string& kernel_device) {
- LOG(INFO) << "MarkPartitionUnbootable: " << kernel_device;
-
- if (kernel_device == BootKernelDevice()) {
- LOG(ERROR) << "Refusing to mark current kernel as unbootable.";
- return false;
- }
-
- string disk_name;
- int partition_num = 0;
-
- if (!utils::SplitPartitionName(kernel_device, &disk_name, &partition_num))
- return false;
-
- CgptAddParams params;
- memset(¶ms, 0, sizeof(params));
-
- params.drive_name = const_cast<char *>(disk_name.c_str());
- params.partition = partition_num;
-
- params.successful = false;
- params.set_successful = true;
-
- params.tries = 0;
- params.set_tries = true;
-
- int retval = CgptSetAttributes(¶ms);
- if (retval != CGPT_OK) {
- LOG(ERROR) << "Marking kernel unbootable failed.";
- return false;
- }
-
- return true;
-}
-
bool Hardware::IsOfficialBuild() const {
return VbGetSystemPropertyInt("debug_build") == 0;
}
diff --git a/hardware.h b/hardware.h
index 0e0205c..4f03cf1 100644
--- a/hardware.h
+++ b/hardware.h
@@ -29,17 +29,10 @@
// Implements the real interface with the hardware.
class Hardware : public HardwareInterface {
public:
- Hardware();
- ~Hardware() override;
+ Hardware() = default;
+ ~Hardware() override = default;
// HardwareInterface methods.
- std::string BootKernelDevice() const override;
- std::string BootDevice() const override;
- bool IsBootDeviceRemovable() const override;
- std::vector<std::string> GetKernelDevices() const override;
- bool IsKernelBootable(const std::string& kernel_device,
- bool* bootable) const override;
- bool MarkKernelUnbootable(const std::string& kernel_device) override;
bool IsOfficialBuild() const override;
bool IsNormalBootMode() const override;
bool IsOOBEComplete(base::Time* out_time_of_oobe) const override;
diff --git a/hardware_interface.h b/hardware_interface.h
index 2010384..7dc4e53 100644
--- a/hardware_interface.h
+++ b/hardware_interface.h
@@ -24,38 +24,14 @@
namespace chromeos_update_engine {
-// The hardware interface allows access to the following parts of the system,
-// closely related to the hardware:
-// * crossystem exposed properties: firmware, hwid, etc.
-// * Physical disk: partition booted from and partition name conversions.
+// The hardware interface allows access to the crossystem exposed properties,
+// such as the firmware version, hwid, verified boot mode.
// These stateless functions are tied together in this interface to facilitate
// unit testing.
class HardwareInterface {
public:
virtual ~HardwareInterface() {}
- // Returns the currently booted kernel partition. "/dev/sda2", for example.
- virtual std::string BootKernelDevice() const = 0;
-
- // Returns the currently booted rootfs partition. "/dev/sda3", for example.
- virtual std::string BootDevice() const = 0;
-
- // Return whether the BootDevice() is a removable device.
- virtual bool IsBootDeviceRemovable() const = 0;
-
- // Returns a list of all kernel partitions available (whether bootable or not)
- virtual std::vector<std::string> GetKernelDevices() const = 0;
-
- // Is the specified kernel partition currently bootable, based on GPT flags?
- // Returns success.
- virtual bool IsKernelBootable(const std::string& kernel_device,
- bool* bootable) const = 0;
-
- // Mark the specified kernel partition unbootable in GPT flags. We mark
- // the other kernel as bootable inside postinst, not inside the UE.
- // Returns success.
- virtual bool MarkKernelUnbootable(const std::string& kernel_device) = 0;
-
// Returns true if this is an official Chrome OS build, false otherwise.
virtual bool IsOfficialBuild() const = 0;
diff --git a/install_plan.cc b/install_plan.cc
index 59fc45c..c378860 100644
--- a/install_plan.cc
+++ b/install_plan.cc
@@ -24,6 +24,9 @@
namespace chromeos_update_engine {
+const char* kLegacyPartitionNameKernel = "KERNEL";
+const char* kLegacyPartitionNameRoot = "ROOT";
+
InstallPlan::InstallPlan(bool is_resume,
bool is_full_update,
const string& url,
@@ -53,15 +56,6 @@
powerwash_required(false),
public_key_rsa(public_key_rsa) {}
-InstallPlan::InstallPlan() : is_resume(false),
- is_full_update(false), // play it safe.
- payload_size(0),
- metadata_size(0),
- kernel_size(0),
- rootfs_size(0),
- hash_checks_mandatory(false),
- powerwash_required(false) {}
-
bool InstallPlan::operator==(const InstallPlan& that) const {
return ((is_resume == that.is_resume) &&
@@ -71,6 +65,8 @@
(payload_hash == that.payload_hash) &&
(metadata_size == that.metadata_size) &&
(metadata_signature == that.metadata_signature) &&
+ (source_slot == that.source_slot) &&
+ (target_slot == that.target_slot) &&
(install_path == that.install_path) &&
(kernel_install_path == that.kernel_install_path) &&
(source_path == that.source_path) &&
@@ -85,6 +81,8 @@
LOG(INFO) << "InstallPlan: "
<< (is_resume ? "resume" : "new_update")
<< ", payload type: " << (is_full_update ? "full" : "delta")
+ << ", source_slot: " << BootControlInterface::SlotName(source_slot)
+ << ", target_slot: " << BootControlInterface::SlotName(target_slot)
<< ", url: " << download_url
<< ", payload size: " << payload_size
<< ", payload hash: " << payload_hash
@@ -100,4 +98,28 @@
powerwash_required);
}
+bool InstallPlan::LoadPartitionsFromSlots(SystemState* system_state) {
+ bool result = true;
+ if (source_slot != BootControlInterface::kInvalidSlot) {
+ result = system_state->boot_control()->GetPartitionDevice(
+ kLegacyPartitionNameRoot, source_slot, &source_path) && result;
+ result = system_state->boot_control()->GetPartitionDevice(
+ kLegacyPartitionNameKernel, source_slot, &kernel_source_path) && result;
+ } else {
+ source_path.clear();
+ kernel_source_path.clear();
+ }
+
+ if (target_slot != BootControlInterface::kInvalidSlot) {
+ result = system_state->boot_control()->GetPartitionDevice(
+ kLegacyPartitionNameRoot, target_slot, &install_path) && result;
+ result = system_state->boot_control()->GetPartitionDevice(
+ kLegacyPartitionNameKernel, target_slot, &kernel_install_path) && result;
+ } else {
+ install_path.clear();
+ kernel_install_path.clear();
+ }
+ return result;
+}
+
} // namespace chromeos_update_engine
diff --git a/install_plan.h b/install_plan.h
index b5a5be1..6e6f7ae 100644
--- a/install_plan.h
+++ b/install_plan.h
@@ -24,11 +24,18 @@
#include <chromeos/secure_blob.h>
#include "update_engine/action.h"
+#include "update_engine/boot_control_interface.h"
+#include "update_engine/system_state.h"
// InstallPlan is a simple struct that contains relevant info for many
// parts of the update system about the install that should happen.
namespace chromeos_update_engine {
+// TODO(deymo): Remove these constants from this interface once the InstallPlan
+// doesn't list the partitions explicitly.
+extern const char* kLegacyPartitionNameKernel;
+extern const char* kLegacyPartitionNameRoot;
+
struct InstallPlan {
InstallPlan(bool is_resume,
bool is_full_update,
@@ -43,24 +50,31 @@
const std::string& kernel_source_path,
const std::string& public_key_rsa);
- // Default constructor: Initialize all members which don't have a class
- // initializer.
- InstallPlan();
+ // Default constructor.
+ InstallPlan() = default;
bool operator==(const InstallPlan& that) const;
bool operator!=(const InstallPlan& that) const;
void Dump() const;
- bool is_resume;
- bool is_full_update;
+ bool LoadPartitionsFromSlots(SystemState* system_state);
+
+ bool is_resume{false};
+ bool is_full_update{false};
std::string download_url; // url to download from
std::string version; // version we are installing.
- uint64_t payload_size; // size of the payload
- std::string payload_hash; // SHA256 hash of the payload
- uint64_t metadata_size; // size of the metadata
+ uint64_t payload_size{0}; // size of the payload
+ std::string payload_hash; // SHA256 hash of the payload
+ uint64_t metadata_size{0}; // size of the metadata
std::string metadata_signature; // signature of the metadata
+
+ // The partition slots used for the update.
+ BootControlInterface::Slot source_slot{BootControlInterface::kInvalidSlot};
+ BootControlInterface::Slot target_slot{BootControlInterface::kInvalidSlot};
+
+ // TODO(deymo): Deprecate these fields and use the slots instead.
std::string install_path; // path to install device
std::string kernel_install_path; // path to kernel install device
std::string source_path; // path to source device
@@ -77,8 +91,8 @@
//
// 3. FilesystemVerifierAction computes and verifies the applied and source
// partition sizes and hashes against the expected values.
- uint64_t kernel_size;
- uint64_t rootfs_size;
+ uint64_t kernel_size{0};
+ uint64_t rootfs_size{0};
chromeos::Blob kernel_hash;
chromeos::Blob rootfs_hash;
chromeos::Blob source_kernel_hash;
@@ -86,11 +100,11 @@
// True if payload hash checks are mandatory based on the system state and
// the Omaha response.
- bool hash_checks_mandatory;
+ bool hash_checks_mandatory{false};
// True if Powerwash is required on reboot after applying the payload.
// False otherwise.
- bool powerwash_required;
+ bool powerwash_required{false};
// If not blank, a base-64 encoded representation of the PEM-encoded
// public key in the response.
diff --git a/mock_hardware.h b/mock_hardware.h
index 1f22c4f..5cdccb9 100644
--- a/mock_hardware.h
+++ b/mock_hardware.h
@@ -31,24 +31,6 @@
public:
MockHardware() {
// Delegate all calls to the fake instance
- ON_CALL(*this, BootKernelDevice())
- .WillByDefault(testing::Invoke(&fake_,
- &FakeHardware::BootKernelDevice));
- ON_CALL(*this, BootDevice())
- .WillByDefault(testing::Invoke(&fake_,
- &FakeHardware::BootDevice));
- ON_CALL(*this, IsBootDeviceRemovable())
- .WillByDefault(testing::Invoke(&fake_,
- &FakeHardware::IsBootDeviceRemovable));
- ON_CALL(*this, GetKernelDevices())
- .WillByDefault(testing::Invoke(&fake_,
- &FakeHardware::GetKernelDevices));
- ON_CALL(*this, IsKernelBootable(testing::_, testing::_))
- .WillByDefault(testing::Invoke(&fake_,
- &FakeHardware::IsKernelBootable));
- ON_CALL(*this, MarkKernelUnbootable(testing::_))
- .WillByDefault(testing::Invoke(&fake_,
- &FakeHardware::MarkKernelUnbootable));
ON_CALL(*this, IsOfficialBuild())
.WillByDefault(testing::Invoke(&fake_,
&FakeHardware::IsOfficialBuild));
@@ -72,17 +54,9 @@
&FakeHardware::GetPowerwashCount));
}
- ~MockHardware() override {}
+ ~MockHardware() override = default;
// Hardware overrides.
- MOCK_CONST_METHOD0(BootKernelDevice, std::string());
- MOCK_CONST_METHOD0(BootDevice, std::string());
- MOCK_CONST_METHOD0(IsBootDeviceRemovable, bool());
- MOCK_CONST_METHOD0(GetKernelDevices, std::vector<std::string>());
- MOCK_CONST_METHOD2(IsKernelBootable,
- bool(const std::string& kernel_device, bool* bootable));
- MOCK_METHOD1(MarkKernelUnbootable,
- bool(const std::string& kernel_device));
MOCK_CONST_METHOD0(IsOfficialBuild, bool());
MOCK_CONST_METHOD0(IsNormalBootMode, bool());
MOCK_CONST_METHOD1(IsOOBEComplete, bool(base::Time* out_time_of_oobe));
diff --git a/omaha_response_handler_action.cc b/omaha_response_handler_action.cc
index f7afc77..dba3d74 100644
--- a/omaha_response_handler_action.cc
+++ b/omaha_response_handler_action.cc
@@ -111,20 +111,13 @@
}
install_plan_.is_full_update = !response.is_delta_payload;
- TEST_AND_RETURN(utils::GetInstallDev(
- (!boot_device_.empty() ? boot_device_ :
- system_state_->hardware()->BootDevice()),
- &install_plan_.install_path));
- install_plan_.kernel_install_path =
- utils::KernelDeviceOfBootDevice(install_plan_.install_path);
- install_plan_.source_path = system_state_->hardware()->BootDevice();
- install_plan_.kernel_source_path =
- utils::KernelDeviceOfBootDevice(install_plan_.source_path);
+ install_plan_.source_slot = system_state_->boot_control()->GetCurrentSlot();
+ install_plan_.target_slot = install_plan_.source_slot == 0 ? 1 : 0;
+ TEST_AND_RETURN(install_plan_.LoadPartitionsFromSlots(system_state_));
if (params->to_more_stable_channel() && params->is_powerwash_allowed())
install_plan_.powerwash_required = true;
-
TEST_AND_RETURN(HasOutputPipe());
if (HasOutputPipe())
SetOutputObject(install_plan_);
diff --git a/omaha_response_handler_action.h b/omaha_response_handler_action.h
index f20c9b4..5611375 100644
--- a/omaha_response_handler_action.h
+++ b/omaha_response_handler_action.h
@@ -56,11 +56,6 @@
// never be called
void TerminateProcessing() override { CHECK(false); }
- // For unit-testing
- void set_boot_device(const std::string& boot_device) {
- boot_device_ = boot_device;
- }
-
bool GotNoUpdateResponse() const { return got_no_update_response_; }
const InstallPlan& install_plan() const { return install_plan_; }
@@ -77,9 +72,6 @@
// Global system context.
SystemState* system_state_;
- // set to non-empty in unit tests
- std::string boot_device_;
-
// The install plan, if we have an update.
InstallPlan install_plan_;
diff --git a/omaha_response_handler_action_unittest.cc b/omaha_response_handler_action_unittest.cc
index 1cf8d25..20d0166 100644
--- a/omaha_response_handler_action_unittest.cc
+++ b/omaha_response_handler_action_unittest.cc
@@ -34,18 +34,26 @@
namespace chromeos_update_engine {
class OmahaResponseHandlerActionTest : public ::testing::Test {
- public:
+ protected:
+ void SetUp() override {
+ FakeBootControl* fake_boot_control = fake_system_state_.fake_boot_control();
+ fake_boot_control->SetPartitionDevice(
+ kLegacyPartitionNameKernel, 0, "/dev/sdz2");
+ fake_boot_control->SetPartitionDevice(
+ kLegacyPartitionNameRoot, 0, "/dev/sdz3");
+ fake_boot_control->SetPartitionDevice(
+ kLegacyPartitionNameKernel, 1, "/dev/sdz4");
+ fake_boot_control->SetPartitionDevice(
+ kLegacyPartitionNameRoot, 1, "/dev/sdz5");
+ }
+
// Return true iff the OmahaResponseHandlerAction succeeded.
// If out is non-null, it's set w/ the response from the action.
- bool DoTestCommon(FakeSystemState* fake_system_state,
- const OmahaResponse& in,
- const string& boot_dev,
- const string& deadline_file,
- InstallPlan* out);
bool DoTest(const OmahaResponse& in,
- const string& boot_dev,
const string& deadline_file,
InstallPlan* out);
+
+ FakeSystemState fake_system_state_;
};
class OmahaResponseHandlerActionProcessorDelegate
@@ -79,10 +87,8 @@
const char* const kBadVersion = "don't update me";
} // namespace
-bool OmahaResponseHandlerActionTest::DoTestCommon(
- FakeSystemState* fake_system_state,
+bool OmahaResponseHandlerActionTest::DoTest(
const OmahaResponse& in,
- const string& boot_dev,
const string& test_deadline_file,
InstallPlan* out) {
ActionProcessor processor;
@@ -92,20 +98,19 @@
ObjectFeederAction<OmahaResponse> feeder_action;
feeder_action.set_obj(in);
if (in.update_exists && in.version != kBadVersion) {
- EXPECT_CALL(*(fake_system_state->mock_prefs()),
+ EXPECT_CALL(*(fake_system_state_.mock_prefs()),
SetString(kPrefsUpdateCheckResponseHash, in.hash))
.WillOnce(Return(true));
}
string current_url = in.payload_urls.size() ? in.payload_urls[0] : "";
- EXPECT_CALL(*(fake_system_state->mock_payload_state()), GetCurrentUrl())
+ EXPECT_CALL(*(fake_system_state_.mock_payload_state()), GetCurrentUrl())
.WillRepeatedly(Return(current_url));
OmahaResponseHandlerAction response_handler_action(
- fake_system_state,
+ &fake_system_state_,
(test_deadline_file.empty() ?
OmahaResponseHandlerAction::kDeadlineFile : test_deadline_file));
- response_handler_action.set_boot_device(boot_dev);
BondActions(&feeder_action, &response_handler_action);
ObjectCollectorAction<InstallPlan> collector_action;
BondActions(&response_handler_action, &collector_action);
@@ -121,14 +126,6 @@
return delegate.code_ == ErrorCode::kSuccess;
}
-bool OmahaResponseHandlerActionTest::DoTest(const OmahaResponse& in,
- const string& boot_dev,
- const string& deadline_file,
- InstallPlan* out) {
- FakeSystemState fake_system_state;
- return DoTestCommon(&fake_system_state, in, boot_dev, deadline_file, out);
-}
-
TEST_F(OmahaResponseHandlerActionTest, SimpleTest) {
string test_deadline_file;
CHECK(utils::MakeTempFile(
@@ -146,10 +143,10 @@
in.prompt = false;
in.deadline = "20101020";
InstallPlan install_plan;
- EXPECT_TRUE(DoTest(in, "/dev/sda3", test_deadline_file, &install_plan));
+ EXPECT_TRUE(DoTest(in, test_deadline_file, &install_plan));
EXPECT_EQ(in.payload_urls[0], install_plan.download_url);
EXPECT_EQ(in.hash, install_plan.payload_hash);
- EXPECT_EQ("/dev/sda5", install_plan.install_path);
+ EXPECT_EQ(1, install_plan.target_slot);
string deadline;
EXPECT_TRUE(utils::ReadFile(test_deadline_file, &deadline));
EXPECT_EQ("20101020", deadline);
@@ -169,10 +166,12 @@
in.size = 12;
in.prompt = true;
InstallPlan install_plan;
- EXPECT_TRUE(DoTest(in, "/dev/sda5", test_deadline_file, &install_plan));
+ // Set the other slot as current.
+ fake_system_state_.fake_boot_control()->SetCurrentSlot(1);
+ EXPECT_TRUE(DoTest(in, test_deadline_file, &install_plan));
EXPECT_EQ(in.payload_urls[0], install_plan.download_url);
EXPECT_EQ(in.hash, install_plan.payload_hash);
- EXPECT_EQ("/dev/sda3", install_plan.install_path);
+ EXPECT_EQ(0, install_plan.target_slot);
string deadline;
EXPECT_TRUE(utils::ReadFile(test_deadline_file, &deadline) &&
deadline.empty());
@@ -189,10 +188,11 @@
in.prompt = true;
in.deadline = "some-deadline";
InstallPlan install_plan;
- EXPECT_TRUE(DoTest(in, "/dev/sda3", test_deadline_file, &install_plan));
+ fake_system_state_.fake_boot_control()->SetCurrentSlot(0);
+ EXPECT_TRUE(DoTest(in, test_deadline_file, &install_plan));
EXPECT_EQ(in.payload_urls[0], install_plan.download_url);
EXPECT_EQ(in.hash, install_plan.payload_hash);
- EXPECT_EQ("/dev/sda5", install_plan.install_path);
+ EXPECT_EQ(1, install_plan.target_slot);
string deadline;
EXPECT_TRUE(utils::ReadFile(test_deadline_file, &deadline));
EXPECT_EQ("some-deadline", deadline);
@@ -204,7 +204,7 @@
OmahaResponse in;
in.update_exists = false;
InstallPlan install_plan;
- EXPECT_FALSE(DoTest(in, "/dev/sda1", "", &install_plan));
+ EXPECT_FALSE(DoTest(in, "", &install_plan));
EXPECT_EQ("", install_plan.download_url);
EXPECT_EQ("", install_plan.payload_hash);
EXPECT_EQ("", install_plan.install_path);
@@ -219,14 +219,12 @@
in.more_info_url = "http://more/info";
in.hash = "HASHj+";
in.size = 12;
- FakeSystemState fake_system_state;
// Hash checks are always skipped for non-official update URLs.
- EXPECT_CALL(*(fake_system_state.mock_request_params()),
+ EXPECT_CALL(*(fake_system_state_.mock_request_params()),
IsUpdateUrlOfficial())
.WillRepeatedly(Return(true));
InstallPlan install_plan;
- EXPECT_TRUE(DoTestCommon(&fake_system_state, in, "/dev/sda5", "",
- &install_plan));
+ EXPECT_TRUE(DoTest(in, "", &install_plan));
EXPECT_EQ(in.payload_urls[0], install_plan.download_url);
EXPECT_EQ(in.hash, install_plan.payload_hash);
EXPECT_TRUE(install_plan.hash_checks_mandatory);
@@ -241,13 +239,11 @@
in.more_info_url = "http://more/info";
in.hash = "HASHj+";
in.size = 12;
- FakeSystemState fake_system_state;
- EXPECT_CALL(*(fake_system_state.mock_request_params()),
+ EXPECT_CALL(*(fake_system_state_.mock_request_params()),
IsUpdateUrlOfficial())
.WillRepeatedly(Return(false));
InstallPlan install_plan;
- EXPECT_TRUE(DoTestCommon(&fake_system_state, in, "/dev/sda5", "",
- &install_plan));
+ EXPECT_TRUE(DoTest(in, "", &install_plan));
EXPECT_EQ(in.payload_urls[0], install_plan.download_url);
EXPECT_EQ(in.hash, install_plan.payload_hash);
EXPECT_FALSE(install_plan.hash_checks_mandatory);
@@ -264,14 +260,12 @@
in.more_info_url = "http://more/info";
in.hash = "HASHj+";
in.size = 12;
- FakeSystemState fake_system_state;
- EXPECT_CALL(*(fake_system_state.mock_request_params()),
+ EXPECT_CALL(*(fake_system_state_.mock_request_params()),
IsUpdateUrlOfficial())
.WillRepeatedly(Return(true));
- fake_system_state.fake_hardware()->SetIsOfficialBuild(false);
+ fake_system_state_.fake_hardware()->SetIsOfficialBuild(false);
InstallPlan install_plan;
- EXPECT_TRUE(DoTestCommon(&fake_system_state, in, "/dev/sda5", "",
- &install_plan));
+ EXPECT_TRUE(DoTest(in, "", &install_plan));
EXPECT_EQ(in.payload_urls[0], install_plan.download_url);
EXPECT_EQ(in.hash, install_plan.payload_hash);
EXPECT_FALSE(install_plan.hash_checks_mandatory);
@@ -286,13 +280,11 @@
in.more_info_url = "http://more/info";
in.hash = "HASHj+";
in.size = 12;
- FakeSystemState fake_system_state;
- EXPECT_CALL(*(fake_system_state.mock_request_params()),
+ EXPECT_CALL(*(fake_system_state_.mock_request_params()),
IsUpdateUrlOfficial())
.WillRepeatedly(Return(true));
InstallPlan install_plan;
- EXPECT_TRUE(DoTestCommon(&fake_system_state, in, "/dev/sda5", "",
- &install_plan));
+ EXPECT_TRUE(DoTest(in, "", &install_plan));
EXPECT_EQ(in.payload_urls[0], install_plan.download_url);
EXPECT_EQ(in.hash, install_plan.payload_hash);
EXPECT_FALSE(install_plan.hash_checks_mandatory);
@@ -308,13 +300,11 @@
in.more_info_url = "http://more/info";
in.hash = "HASHj+";
in.size = 12;
- FakeSystemState fake_system_state;
- EXPECT_CALL(*(fake_system_state.mock_request_params()),
+ EXPECT_CALL(*(fake_system_state_.mock_request_params()),
IsUpdateUrlOfficial())
.WillRepeatedly(Return(true));
InstallPlan install_plan;
- EXPECT_TRUE(DoTestCommon(&fake_system_state, in, "/dev/sda5", "",
- &install_plan));
+ EXPECT_TRUE(DoTest(in, "", &install_plan));
EXPECT_EQ(in.payload_urls[0], install_plan.download_url);
EXPECT_EQ(in.hash, install_plan.payload_hash);
EXPECT_TRUE(install_plan.hash_checks_mandatory);
@@ -346,8 +336,7 @@
"CHROMEOS_IS_POWERWASH_ALLOWED=true\n"
"CHROMEOS_RELEASE_TRACK=stable-channel\n"));
- FakeSystemState fake_system_state;
- OmahaRequestParams params(&fake_system_state);
+ OmahaRequestParams params(&fake_system_state_);
params.set_root(test_dir);
params.SetLockDown(false);
params.Init("1.2.3.4", "", 0);
@@ -356,10 +345,9 @@
EXPECT_TRUE(params.to_more_stable_channel());
EXPECT_TRUE(params.is_powerwash_allowed());
- fake_system_state.set_request_params(¶ms);
+ fake_system_state_.set_request_params(¶ms);
InstallPlan install_plan;
- EXPECT_TRUE(DoTestCommon(&fake_system_state, in, "/dev/sda5", "",
- &install_plan));
+ EXPECT_TRUE(DoTest(in, "", &install_plan));
EXPECT_TRUE(install_plan.powerwash_required);
ASSERT_TRUE(test_utils::RecursiveUnlinkDir(test_dir));
@@ -389,8 +377,7 @@
test_dir + kStatefulPartition + "/etc/lsb-release",
"CHROMEOS_RELEASE_TRACK=canary-channel\n"));
- FakeSystemState fake_system_state;
- OmahaRequestParams params(&fake_system_state);
+ OmahaRequestParams params(&fake_system_state_);
params.set_root(test_dir);
params.SetLockDown(false);
params.Init("5.6.7.8", "", 0);
@@ -400,10 +387,9 @@
EXPECT_FALSE(params.to_more_stable_channel());
EXPECT_FALSE(params.is_powerwash_allowed());
- fake_system_state.set_request_params(¶ms);
+ fake_system_state_.set_request_params(¶ms);
InstallPlan install_plan;
- EXPECT_TRUE(DoTestCommon(&fake_system_state, in, "/dev/sda5", "",
- &install_plan));
+ EXPECT_TRUE(DoTest(in, "", &install_plan));
EXPECT_FALSE(install_plan.powerwash_required);
ASSERT_TRUE(test_utils::RecursiveUnlinkDir(test_dir));
@@ -418,26 +404,24 @@
in.hash = "HASHj+";
in.size = 12;
- FakeSystemState fake_system_state;
- OmahaRequestParams params(&fake_system_state);
+ OmahaRequestParams params(&fake_system_state_);
// We're using a real OmahaRequestParams object here so we can't mock
// IsUpdateUrlOfficial(), but setting the update URL to the AutoUpdate test
// server will cause IsUpdateUrlOfficial() to return true.
params.set_update_url(kAUTestOmahaUrl);
- fake_system_state.set_request_params(¶ms);
+ fake_system_state_.set_request_params(¶ms);
- EXPECT_CALL(*fake_system_state.mock_payload_state(),
+ EXPECT_CALL(*fake_system_state_.mock_payload_state(),
SetUsingP2PForDownloading(true));
string p2p_url = "http://9.8.7.6/p2p";
- EXPECT_CALL(*fake_system_state.mock_payload_state(), GetP2PUrl())
+ EXPECT_CALL(*fake_system_state_.mock_payload_state(), GetP2PUrl())
.WillRepeatedly(Return(p2p_url));
- EXPECT_CALL(*fake_system_state.mock_payload_state(),
+ EXPECT_CALL(*fake_system_state_.mock_payload_state(),
GetUsingP2PForDownloading()).WillRepeatedly(Return(true));
InstallPlan install_plan;
- EXPECT_TRUE(DoTestCommon(&fake_system_state, in, "/dev/sda5", "",
- &install_plan));
+ EXPECT_TRUE(DoTest(in, "", &install_plan));
EXPECT_EQ(in.hash, install_plan.payload_hash);
EXPECT_EQ(install_plan.download_url, p2p_url);
EXPECT_TRUE(install_plan.hash_checks_mandatory);
diff --git a/payload_state.cc b/payload_state.cc
index e7904c1..78c048b 100644
--- a/payload_state.cc
+++ b/payload_state.cc
@@ -1422,8 +1422,14 @@
LOG(ERROR) << "Error reading TargetVersionInstalledFrom on reboot.";
return;
}
- if (static_cast<int>(installed_from) ==
- utils::GetPartitionNumber(system_state_->hardware()->BootDevice())) {
+ // Old Chrome OS devices will write 2 or 4 in this setting, with the
+ // partition number. We are now using slot numbers (0 or 1) instead, so
+ // the following comparison will not match if we are comparing an old
+ // partition number against a new slot number, which is the correct outcome
+ // since we successfully booted the new update in that case. If the boot
+ // failed, we will read this value from the same version, so it will always
+ // be compatible.
+ if (installed_from == system_state_->boot_control()->GetCurrentSlot()) {
// A reboot was pending, but the chromebook is again in the same
// BootDevice where the update was installed from.
int64_t target_attempt;
@@ -1483,8 +1489,7 @@
prefs_->SetInt64(kPrefsTargetVersionAttempt, target_attempt + 1);
prefs_->SetInt64(kPrefsTargetVersionInstalledFrom,
- utils::GetPartitionNumber(
- system_state_->hardware()->BootDevice()));
+ system_state_->boot_control()->GetCurrentSlot());
}
void PayloadState::ResetUpdateStatus() {
@@ -1496,7 +1501,7 @@
// Also decrement the attempt number if it exists.
int64_t target_attempt;
if (prefs_->GetInt64(kPrefsTargetVersionAttempt, &target_attempt))
- prefs_->SetInt64(kPrefsTargetVersionAttempt, target_attempt-1);
+ prefs_->SetInt64(kPrefsTargetVersionAttempt, target_attempt - 1);
}
int PayloadState::GetP2PNumAttempts() {
diff --git a/payload_state_unittest.cc b/payload_state_unittest.cc
index aea5389..e4eca89 100644
--- a/payload_state_unittest.cc
+++ b/payload_state_unittest.cc
@@ -1492,9 +1492,6 @@
FakePrefs fake_prefs;
fake_system_state.set_prefs(&fake_prefs);
- FakeHardware* fake_hardware = fake_system_state.fake_hardware();
- fake_hardware->SetBootDevice("/dev/sda3");
-
EXPECT_TRUE(payload_state.Initialize(&fake_system_state));
SetupPayloadStateWith2Urls("Hash3141", true, &payload_state, &response);
@@ -1540,8 +1537,8 @@
FakePrefs fake_prefs;
fake_system_state.set_prefs(&fake_prefs);
- FakeHardware* fake_hardware = fake_system_state.fake_hardware();
- fake_hardware->SetBootDevice("/dev/sda3");
+ FakeBootControl* fake_boot_control = fake_system_state.fake_boot_control();
+ fake_boot_control->SetCurrentSlot(0);
EXPECT_TRUE(payload_state.Initialize(&fake_system_state));
SetupPayloadStateWith2Urls("Hash3141", true, &payload_state, &response);
@@ -1552,7 +1549,7 @@
payload_state.ExpectRebootInNewVersion("Version:12345678");
// Change the BootDevice to a different one, no metric should be sent.
- fake_hardware->SetBootDevice("/dev/sda5");
+ fake_boot_control->SetCurrentSlot(1);
EXPECT_CALL(*fake_system_state.mock_metrics_lib(), SendToUMA(
"Installer.RebootToNewPartitionAttempt", _, _, _, _))
@@ -1562,9 +1559,9 @@
.Times(0);
payload_state.ReportFailedBootIfNeeded();
- // A second reboot in eiher partition should not send a metric.
+ // A second reboot in either partition should not send a metric.
payload_state.ReportFailedBootIfNeeded();
- fake_hardware->SetBootDevice("/dev/sda3");
+ fake_boot_control->SetCurrentSlot(0);
payload_state.ReportFailedBootIfNeeded();
}
diff --git a/real_system_state.cc b/real_system_state.cc
index d374cc1..5477dd6 100644
--- a/real_system_state.cc
+++ b/real_system_state.cc
@@ -20,6 +20,7 @@
#include <base/time/time.h>
#include <chromeos/dbus/service_constants.h>
+#include "update_engine/boot_control_chromeos.h"
#include "update_engine/constants.h"
#include "update_engine/update_manager/state_factory.h"
#include "update_engine/utils.h"
@@ -37,6 +38,13 @@
bool RealSystemState::Initialize() {
metrics_lib_.Init();
+ // TODO(deymo): Initialize BootControl based on the build environment.
+ BootControlChromeOS* boot_control_cros = new BootControlChromeOS();
+ boot_control_.reset(boot_control_cros);
+ if (!boot_control_cros->Init()) {
+ LOG(ERROR) << "Ignoring BootControlChromeOS failure. We won't run updates.";
+ }
+
if (!shill_proxy_.Init()) {
LOG(ERROR) << "Failed to initialize shill proxy.";
return false;
diff --git a/real_system_state.h b/real_system_state.h
index 9eaa6cb..a51e6ef 100644
--- a/real_system_state.h
+++ b/real_system_state.h
@@ -27,6 +27,7 @@
#include "debugd/dbus-proxies.h"
#include "login_manager/dbus-proxies.h"
#include "power_manager/dbus-proxies.h"
+#include "update_engine/boot_control_interface.h"
#include "update_engine/clock.h"
#include "update_engine/connection_manager.h"
#include "update_engine/hardware.h"
@@ -60,6 +61,10 @@
return device_policy_;
}
+ inline BootControlInterface* boot_control() override {
+ return boot_control_.get();
+ }
+
inline ClockInterface* clock() override { return &clock_; }
inline ConnectionManagerInterface* connection_manager() override {
@@ -112,6 +117,9 @@
LibCrosProxy libcros_proxy_;
// Interface for the clock.
+ std::unique_ptr<BootControlInterface> boot_control_;
+
+ // Interface for the clock.
Clock clock_;
// The latest device policy object from the policy provider.
diff --git a/system_state.h b/system_state.h
index 4d4c74a..0190ee2 100644
--- a/system_state.h
+++ b/system_state.h
@@ -38,6 +38,7 @@
// SystemState is the root class within the update engine. So we should avoid
// any circular references in header file inclusion. Hence forward-declaring
// the required classes.
+class BootControlInterface;
class ClockInterface;
class ConnectionManagerInterface;
class HardwareInterface;
@@ -63,6 +64,9 @@
virtual void set_device_policy(const policy::DevicePolicy* device_policy) = 0;
virtual const policy::DevicePolicy* device_policy() = 0;
+ // Gets the interface object for the bootloader control interface.
+ virtual BootControlInterface* boot_control() = 0;
+
// Gets the interface object for the clock.
virtual ClockInterface* clock() = 0;
diff --git a/update_attempter.cc b/update_attempter.cc
index cfa44bf..82831e4 100644
--- a/update_attempter.cc
+++ b/update_attempter.cc
@@ -735,15 +735,10 @@
LOG(INFO) << "Setting rollback options.";
InstallPlan install_plan;
- TEST_AND_RETURN_FALSE(utils::GetInstallDev(
- system_state_->hardware()->BootDevice(),
- &install_plan.install_path));
+ install_plan.target_slot = GetRollbackSlot();
+ install_plan.source_slot = system_state_->boot_control()->GetCurrentSlot();
- install_plan.kernel_install_path =
- utils::KernelDeviceOfBootDevice(install_plan.install_path);
- install_plan.source_path = system_state_->hardware()->BootDevice();
- install_plan.kernel_source_path =
- utils::KernelDeviceOfBootDevice(install_plan.source_path);
+ TEST_AND_RETURN_FALSE(install_plan.LoadPartitionsFromSlots(system_state_));
install_plan.powerwash_required = powerwash;
LOG(INFO) << "Using this install plan:";
@@ -776,59 +771,53 @@
bool UpdateAttempter::CanRollback() const {
// We can only rollback if the update_engine isn't busy and we have a valid
// rollback partition.
- return (status_ == UPDATE_STATUS_IDLE && !GetRollbackPartition().empty());
+ return (status_ == UPDATE_STATUS_IDLE &&
+ GetRollbackSlot() != BootControlInterface::kInvalidSlot);
}
-string UpdateAttempter::GetRollbackPartition() const {
- vector<string> kernel_devices =
- system_state_->hardware()->GetKernelDevices();
+BootControlInterface::Slot UpdateAttempter::GetRollbackSlot() const {
+ LOG(INFO) << "UpdateAttempter::GetRollbackSlot";
+ const unsigned int num_slots = system_state_->boot_control()->GetNumSlots();
+ const BootControlInterface::Slot current_slot =
+ system_state_->boot_control()->GetCurrentSlot();
- string boot_kernel_device =
- system_state_->hardware()->BootKernelDevice();
+ LOG(INFO) << " Installed slots: " << num_slots;
+ LOG(INFO) << " Booted from slot: "
+ << BootControlInterface::SlotName(current_slot);
- LOG(INFO) << "UpdateAttempter::GetRollbackPartition";
- for (const auto& name : kernel_devices)
- LOG(INFO) << " Available kernel device = " << name;
- LOG(INFO) << " Boot kernel device = " << boot_kernel_device;
-
- auto current = std::find(kernel_devices.begin(), kernel_devices.end(),
- boot_kernel_device);
-
- if (current == kernel_devices.end()) {
- LOG(ERROR) << "Unable to find the boot kernel device in the list of "
- << "available devices";
- return string();
+ if (current_slot == BootControlInterface::kInvalidSlot || num_slots < 2) {
+ LOG(INFO) << "Device is not updateable.";
+ return BootControlInterface::kInvalidSlot;
}
- for (string const& device_name : kernel_devices) {
- if (device_name != *current) {
- bool bootable = false;
- if (system_state_->hardware()->IsKernelBootable(device_name, &bootable) &&
- bootable) {
- return device_name;
- }
+ vector<BootControlInterface::Slot> bootable_slots;
+ for(BootControlInterface::Slot slot = 0; slot < num_slots; slot++) {
+ if (slot != current_slot &&
+ system_state_->boot_control()->IsSlotBootable(slot)) {
+ LOG(INFO) << "Found bootable slot "
+ << BootControlInterface::SlotName(slot);
+ return slot;
}
}
-
- return string();
+ LOG(INFO) << "No other bootable slot found.";
+ return BootControlInterface::kInvalidSlot;
}
-vector<std::pair<string, bool>>
- UpdateAttempter::GetKernelDevices() const {
- vector<string> kernel_devices =
- system_state_->hardware()->GetKernelDevices();
-
- string boot_kernel_device =
- system_state_->hardware()->BootKernelDevice();
+vector<std::pair<string, bool>> UpdateAttempter::GetKernelDevices() const {
+ const unsigned int num_slots = system_state_->boot_control()->GetNumSlots();
+ const BootControlInterface::Slot current_slot =
+ system_state_->boot_control()->GetCurrentSlot();
vector<std::pair<string, bool>> info_list;
- info_list.reserve(kernel_devices.size());
-
- for (string device_name : kernel_devices) {
- bool bootable = false;
- system_state_->hardware()->IsKernelBootable(device_name, &bootable);
+ for (BootControlInterface::Slot slot = 0; slot < num_slots; slot++) {
+ bool bootable = system_state_->boot_control()->IsSlotBootable(slot);
+ string device_name;
+ if (!system_state_->boot_control()->GetPartitionDevice(
+ kLegacyPartitionNameKernel, slot, &device_name)) {
+ continue;
+ }
// Add '*' to the name of the partition we booted from.
- if (device_name == boot_kernel_device)
+ if (slot == current_slot)
device_name += '*';
info_list.emplace_back(device_name, bootable);
}
diff --git a/update_attempter.h b/update_attempter.h
index 19b7b81..a884924 100644
--- a/update_attempter.h
+++ b/update_attempter.h
@@ -155,7 +155,7 @@
// This is the internal entry point for getting a rollback partition name,
// if one exists. It returns the bootable rollback kernel device partition
// name or empty string if none is available.
- std::string GetRollbackPartition() const;
+ BootControlInterface::Slot GetRollbackSlot() const;
// Returns a list of available kernel partitions along with information
// whether it is possible to boot from it.
diff --git a/update_attempter_unittest.cc b/update_attempter_unittest.cc
index a50c50a..546dd03 100644
--- a/update_attempter_unittest.cc
+++ b/update_attempter_unittest.cc
@@ -452,12 +452,21 @@
EXPECT_CALL(*device_policy, LoadPolicy()).WillRepeatedly(Return(true));
fake_system_state_.set_device_policy(device_policy);
- if (!valid_slot) {
- // References bootable kernels in fake_hardware.h
- string rollback_kernel = "/dev/sdz2";
- LOG(INFO) << "Test Mark Unbootable: " << rollback_kernel;
- fake_system_state_.fake_hardware()->MarkKernelUnbootable(
- rollback_kernel);
+ FakeBootControl* fake_boot_control = fake_system_state_.fake_boot_control();
+ fake_boot_control->SetPartitionDevice(
+ kLegacyPartitionNameKernel, 0, "/dev/sdz2");
+ fake_boot_control->SetPartitionDevice(
+ kLegacyPartitionNameRoot, 0, "/dev/sdz3");
+
+ if (valid_slot) {
+ BootControlInterface::Slot rollback_slot = 1;
+ LOG(INFO) << "Test Mark Bootable: "
+ << BootControlInterface::SlotName(rollback_slot);
+ fake_boot_control->SetSlotBootable(rollback_slot, true);
+ fake_boot_control->SetPartitionDevice(
+ kLegacyPartitionNameKernel, rollback_slot, "/dev/sdz4");
+ fake_boot_control->SetPartitionDevice(
+ kLegacyPartitionNameRoot, rollback_slot, "/dev/sdz5");
}
bool is_rollback_allowed = false;
@@ -510,10 +519,8 @@
InstallPlanAction* install_plan_action =
dynamic_cast<InstallPlanAction*>(attempter_.actions_[0].get());
InstallPlan* install_plan = install_plan_action->install_plan();
- // Matches fake_hardware.h -> rollback should move from kernel/boot device
- // pair to other pair.
- EXPECT_EQ(install_plan->install_path, string("/dev/sdz3"));
- EXPECT_EQ(install_plan->kernel_install_path, string("/dev/sdz2"));
+ EXPECT_EQ(install_plan->install_path, string("/dev/sdz5"));
+ EXPECT_EQ(install_plan->kernel_install_path, string("/dev/sdz4"));
EXPECT_EQ(install_plan->powerwash_required, true);
loop_.BreakLoop();
}
diff --git a/update_engine.gyp b/update_engine.gyp
index 2b8c1ac..e0df1e9 100644
--- a/update_engine.gyp
+++ b/update_engine.gyp
@@ -198,6 +198,7 @@
},
'sources': [
'action_processor.cc',
+ 'boot_control_chromeos.cc',
'bzip.cc',
'bzip_extent_writer.cc',
'certificate_checker.cc',
@@ -455,6 +456,7 @@
'action_pipe_unittest.cc',
'action_processor_unittest.cc',
'action_unittest.cc',
+ 'boot_control_chromeos_unittest.cc',
'bzip_extent_writer_unittest.cc',
'certificate_checker_unittest.cc',
'chrome_browser_proxy_resolver_unittest.cc',
diff --git a/update_manager/chromeos_policy.cc b/update_manager/chromeos_policy.cc
index 80ef63b..1c77318 100644
--- a/update_manager/chromeos_policy.cc
+++ b/update_manager/chromeos_policy.cc
@@ -187,10 +187,10 @@
// Do not perform any updates if booted from removable device. This decision
// is final.
- const bool* is_boot_device_removable_p = ec->GetValue(
- system_provider->var_is_boot_device_removable());
- if (is_boot_device_removable_p && *is_boot_device_removable_p) {
- LOG(INFO) << "Booted from removable device, disabling update checks.";
+ const unsigned int* num_slots_p = ec->GetValue(
+ system_provider->var_num_slots());
+ if (!num_slots_p || *num_slots_p < 2) {
+ LOG(INFO) << "Not enough slots for A/B updates, disabling update checks.";
result->updates_enabled = false;
return EvalStatus::kSucceeded;
}
diff --git a/update_manager/chromeos_policy_unittest.cc b/update_manager/chromeos_policy_unittest.cc
index 05a5e4b..e456e19 100644
--- a/update_manager/chromeos_policy_unittest.cc
+++ b/update_manager/chromeos_policy_unittest.cc
@@ -91,8 +91,7 @@
new bool(true));
fake_state_.system_provider()->var_is_oobe_complete()->reset(
new bool(true));
- fake_state_.system_provider()->var_is_boot_device_removable()->reset(
- new bool(false));
+ fake_state_.system_provider()->var_num_slots()->reset(new unsigned int(2));
// Connection is wifi, untethered.
fake_state_.shill_provider()->var_conn_type()->
@@ -421,8 +420,7 @@
// UpdateCheckAllowed should return false (kSucceeded) if the image booted
// from a removable device.
- fake_state_.system_provider()->var_is_boot_device_removable()->reset(
- new bool(true));
+ fake_state_.system_provider()->var_num_slots()->reset(new unsigned int(1));
UpdateCheckParams result;
ExpectPolicyStatus(EvalStatus::kSucceeded,
diff --git a/update_manager/fake_system_provider.h b/update_manager/fake_system_provider.h
index 5124231..6036198 100644
--- a/update_manager/fake_system_provider.h
+++ b/update_manager/fake_system_provider.h
@@ -39,8 +39,8 @@
return &var_is_oobe_complete_;
}
- FakeVariable<bool>* var_is_boot_device_removable() override {
- return &var_is_boot_device_removable_;
+ FakeVariable<unsigned int>* var_num_slots() override {
+ return &var_num_slots_;
}
private:
@@ -50,9 +50,7 @@
"is_official_build", kVariableModeConst};
FakeVariable<bool> var_is_oobe_complete_{ // NOLINT(whitespace/braces)
"is_oobe_complete", kVariableModePoll};
- FakeVariable<bool>
- var_is_boot_device_removable_{ // NOLINT(whitespace/braces)
- "is_boot_device_removable", kVariableModePoll};
+ FakeVariable<unsigned int> var_num_slots_{"num_slots", kVariableModePoll};
DISALLOW_COPY_AND_ASSIGN(FakeSystemProvider);
};
diff --git a/update_manager/real_system_provider.cc b/update_manager/real_system_provider.cc
index abb920e..d0d788d 100644
--- a/update_manager/real_system_provider.cc
+++ b/update_manager/real_system_provider.cc
@@ -51,9 +51,9 @@
base::Bind(&chromeos_update_engine::HardwareInterface::IsOOBEComplete,
base::Unretained(hardware_), nullptr)));
- var_is_boot_device_removable_.reset(
- new ConstCopyVariable<bool>("is_boot_device_removable",
- hardware_->IsBootDeviceRemovable()));
+ var_num_slots_.reset(
+ new ConstCopyVariable<unsigned int>(
+ "num_slots", boot_control_->GetNumSlots()));
return true;
}
diff --git a/update_manager/real_system_provider.h b/update_manager/real_system_provider.h
index 3fc2d8d..a46a698 100644
--- a/update_manager/real_system_provider.h
+++ b/update_manager/real_system_provider.h
@@ -20,6 +20,7 @@
#include <memory>
#include <string>
+#include "update_engine/boot_control_interface.h"
#include "update_engine/hardware_interface.h"
#include "update_engine/update_manager/system_provider.h"
@@ -29,8 +30,9 @@
class RealSystemProvider : public SystemProvider {
public:
explicit RealSystemProvider(
- chromeos_update_engine::HardwareInterface* hardware)
- : hardware_(hardware) {}
+ chromeos_update_engine::HardwareInterface* hardware,
+ chromeos_update_engine::BootControlInterface* boot_control)
+ : hardware_(hardware), boot_control_(boot_control) {}
// Initializes the provider and returns whether it succeeded.
bool Init();
@@ -47,17 +49,18 @@
return var_is_oobe_complete_.get();
}
- Variable<bool>* var_is_boot_device_removable() override {
- return var_is_boot_device_removable_.get();
+ Variable<unsigned int>* var_num_slots() override {
+ return var_num_slots_.get();
}
private:
std::unique_ptr<Variable<bool>> var_is_normal_boot_mode_;
std::unique_ptr<Variable<bool>> var_is_official_build_;
std::unique_ptr<Variable<bool>> var_is_oobe_complete_;
- std::unique_ptr<Variable<bool>> var_is_boot_device_removable_;
+ std::unique_ptr<Variable<unsigned int>> var_num_slots_;
chromeos_update_engine::HardwareInterface* hardware_;
+ chromeos_update_engine::BootControlInterface* boot_control_;
DISALLOW_COPY_AND_ASSIGN(RealSystemProvider);
};
diff --git a/update_manager/real_system_provider_unittest.cc b/update_manager/real_system_provider_unittest.cc
index e28cc5c..35e9be1 100644
--- a/update_manager/real_system_provider_unittest.cc
+++ b/update_manager/real_system_provider_unittest.cc
@@ -21,6 +21,7 @@
#include <base/time/time.h>
#include <gtest/gtest.h>
+#include "update_engine/fake_boot_control.h"
#include "update_engine/fake_hardware.h"
#include "update_engine/update_manager/umtest_utils.h"
@@ -31,11 +32,13 @@
class UmRealSystemProviderTest : public ::testing::Test {
protected:
void SetUp() override {
- provider_.reset(new RealSystemProvider(&fake_hardware_));
+ provider_.reset(
+ new RealSystemProvider(&fake_hardware_, &fake_boot_control_));
EXPECT_TRUE(provider_->Init());
}
chromeos_update_engine::FakeHardware fake_hardware_;
+ chromeos_update_engine::FakeBootControl fake_boot_control_;
unique_ptr<RealSystemProvider> provider_;
};
diff --git a/update_manager/state_factory.cc b/update_manager/state_factory.cc
index 9bd028a..f90bd6e 100644
--- a/update_manager/state_factory.cc
+++ b/update_manager/state_factory.cc
@@ -48,7 +48,8 @@
unique_ptr<RealShillProvider> shill_provider(
new RealShillProvider(shill_proxy, clock));
unique_ptr<RealSystemProvider> system_provider(
- new RealSystemProvider(system_state->hardware()));
+ new RealSystemProvider(system_state->hardware(),
+ system_state->boot_control()));
unique_ptr<RealTimeProvider> time_provider(new RealTimeProvider(clock));
unique_ptr<RealUpdaterProvider> updater_provider(
new RealUpdaterProvider(system_state));
diff --git a/update_manager/system_provider.h b/update_manager/system_provider.h
index 5edec18..00fb9af 100644
--- a/update_manager/system_provider.h
+++ b/update_manager/system_provider.h
@@ -39,8 +39,8 @@
// Returns a variable that tells whether OOBE was completed.
virtual Variable<bool>* var_is_oobe_complete() = 0;
- // Returns a variable that tells the boot device is removable (USB stick etc).
- virtual Variable<bool>* var_is_boot_device_removable() = 0;
+ // Returns a variable that tells the number of slots in the system.
+ virtual Variable<unsigned int>* var_num_slots() = 0;
protected:
SystemProvider() {}
diff --git a/utils.cc b/utils.cc
index ebc849e..a50e56a 100644
--- a/utils.cc
+++ b/utils.cc
@@ -155,25 +155,6 @@
return "";
}
-
-const string KernelDeviceOfBootDevice(const string& boot_device) {
- string kernel_partition_name;
-
- string disk_name;
- int partition_num;
- if (SplitPartitionName(boot_device, &disk_name, &partition_num)) {
- // Currently this assumes the partition number of the boot device is
- // 3, 5, or 7, and changes it to 2, 4, or 6, respectively, to
- // get the kernel device.
- if (partition_num == 3 || partition_num == 5 || partition_num == 7) {
- kernel_partition_name = MakePartitionName(disk_name, partition_num - 1);
- }
- }
-
- return kernel_partition_name;
-}
-
-
bool WriteFile(const char* path, const void* data, int data_len) {
DirectFileWriter writer;
TEST_AND_RETURN_FALSE_ERRNO(0 == writer.Open(path,
@@ -438,18 +419,6 @@
}
}
-string GetDiskName(const string& partition_name) {
- string disk_name;
- return SplitPartitionName(partition_name, &disk_name, nullptr) ?
- disk_name : string();
-}
-
-int GetPartitionNumber(const string& partition_name) {
- int partition_num = 0;
- return SplitPartitionName(partition_name, nullptr, &partition_num) ?
- partition_num : 0;
-}
-
bool SplitPartitionName(const string& partition_name,
string* out_disk_name,
int* out_partition_num) {
@@ -543,26 +512,6 @@
return part_name;
}
-string SysfsBlockDevice(const string& device) {
- base::FilePath device_path(device);
- if (device_path.DirName().value() != "/dev") {
- return "";
- }
- return base::FilePath("/sys/block").Append(device_path.BaseName()).value();
-}
-
-bool IsRemovableDevice(const string& device) {
- string sysfs_block = SysfsBlockDevice(device);
- string removable;
- if (sysfs_block.empty() ||
- !base::ReadFileToString(base::FilePath(sysfs_block).Append("removable"),
- &removable)) {
- return false;
- }
- base::TrimWhitespaceASCII(removable, base::TRIM_ALL, &removable);
- return removable == "1";
-}
-
string ErrnoNumberAsString(int err) {
char buf[100];
buf[0] = '\0';
@@ -1488,27 +1437,6 @@
return result;
}
-bool GetInstallDev(const string& boot_dev, string* install_dev) {
- string disk_name;
- int partition_num;
- if (!SplitPartitionName(boot_dev, &disk_name, &partition_num))
- return false;
-
- // Right now, we just switch '3' and '5' partition numbers.
- if (partition_num == 3) {
- partition_num = 5;
- } else if (partition_num == 5) {
- partition_num = 3;
- } else {
- return false;
- }
-
- if (install_dev)
- *install_dev = MakePartitionName(disk_name, partition_num);
-
- return true;
-}
-
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/utils.h b/utils.h
index b5c3fec..b165562 100644
--- a/utils.h
+++ b/utils.h
@@ -65,11 +65,6 @@
// "mosys" command.
std::string ParseECVersion(std::string input_line);
-// Given the name of the block device of a boot partition, return the
-// name of the associated kernel partition (e.g. given "/dev/sda3",
-// return "/dev/sda2").
-const std::string KernelDeviceOfBootDevice(const std::string& boot_device);
-
// Writes the data passed to path. The file at path will be overwritten if it
// exists. Returns true on success, false otherwise.
bool WriteFile(const char* path, const void* data, int data_len);
@@ -155,16 +150,6 @@
bool MakeTempDirectory(const std::string& base_dirname_template,
std::string* dirname);
-// Returns the disk device name for a partition. For example,
-// GetDiskName("/dev/sda3") returns "/dev/sda". Returns an empty string
-// if the input device is not of the "/dev/xyz#" form.
-std::string GetDiskName(const std::string& partition_name);
-
-// Returns the partition number, of partition device name. For example,
-// GetPartitionNumber("/dev/sda3") returns 3.
-// Returns 0 on failure
-int GetPartitionNumber(const std::string& partition_name);
-
// Splits the partition device name into the block device name and partition
// number. For example, "/dev/sda3" will be split into {"/dev/sda", 3} and
// "/dev/mmcblk0p2" into {"/dev/mmcblk0", 2}
@@ -193,16 +178,6 @@
// /dev/sda3. Return empty string on error.
std::string MakePartitionNameForMount(const std::string& part_name);
-// Returns the sysfs block device for a root block device. For
-// example, SysfsBlockDevice("/dev/sda") returns
-// "/sys/block/sda". Returns an empty string if the input device is
-// not of the "/dev/xyz" form.
-std::string SysfsBlockDevice(const std::string& device);
-
-// Returns true if the root |device| (e.g., "/dev/sdb") is known to be
-// removable, false otherwise.
-bool IsRemovableDevice(const std::string& device);
-
// Synchronously mount or unmount a filesystem. Return true on success.
// When mounting, it will attempt to mount the the device as "ext3", "ext2" and
// "squashfs", with the passed |flags| options.
@@ -380,13 +355,6 @@
// global default. Returns true if successfully deleted. False otherwise.
bool DeletePowerwashMarkerFile(const char* file_path);
-// Assumes you want to install on the "other" device, where the other
-// device is what you get if you swap 1 for 2 or 3 for 4 or vice versa
-// for the number at the end of the boot device. E.g., /dev/sda1 -> /dev/sda2
-// or /dev/sda4 -> /dev/sda3. See
-// http://www.chromium.org/chromium-os/chromiumos-design-docs/disk-format
-bool GetInstallDev(const std::string& boot_dev, std::string* install_dev);
-
// 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/utils_unittest.cc b/utils_unittest.cc
index 41ea036..6842b21 100644
--- a/utils_unittest.cc
+++ b/utils_unittest.cc
@@ -62,37 +62,6 @@
EXPECT_EQ("", utils::ParseECVersion("b=1231a fw_version a=fasd2"));
}
-
-TEST(UtilsTest, KernelDeviceOfBootDevice) {
- EXPECT_EQ("", utils::KernelDeviceOfBootDevice(""));
- EXPECT_EQ("", utils::KernelDeviceOfBootDevice("foo"));
- EXPECT_EQ("", utils::KernelDeviceOfBootDevice("/dev/sda0"));
- EXPECT_EQ("", utils::KernelDeviceOfBootDevice("/dev/sda1"));
- EXPECT_EQ("", utils::KernelDeviceOfBootDevice("/dev/sda2"));
- EXPECT_EQ("/dev/sda2", utils::KernelDeviceOfBootDevice("/dev/sda3"));
- EXPECT_EQ("", utils::KernelDeviceOfBootDevice("/dev/sda4"));
- EXPECT_EQ("/dev/sda4", utils::KernelDeviceOfBootDevice("/dev/sda5"));
- EXPECT_EQ("", utils::KernelDeviceOfBootDevice("/dev/sda6"));
- EXPECT_EQ("/dev/sda6", utils::KernelDeviceOfBootDevice("/dev/sda7"));
- EXPECT_EQ("", utils::KernelDeviceOfBootDevice("/dev/sda8"));
- EXPECT_EQ("", utils::KernelDeviceOfBootDevice("/dev/sda9"));
-
- EXPECT_EQ("/dev/mmcblk0p2",
- utils::KernelDeviceOfBootDevice("/dev/mmcblk0p3"));
- EXPECT_EQ("", utils::KernelDeviceOfBootDevice("/dev/mmcblk0p4"));
-
- EXPECT_EQ("/dev/mtd2", utils::KernelDeviceOfBootDevice("/dev/ubi3"));
- EXPECT_EQ("", utils::KernelDeviceOfBootDevice("/dev/ubi4"));
-
- EXPECT_EQ("/dev/mtd2",
- utils::KernelDeviceOfBootDevice("/dev/ubiblock3_0"));
- EXPECT_EQ("/dev/mtd4",
- utils::KernelDeviceOfBootDevice("/dev/ubiblock5_0"));
- EXPECT_EQ("/dev/mtd6",
- utils::KernelDeviceOfBootDevice("/dev/ubiblock7_0"));
- EXPECT_EQ("", utils::KernelDeviceOfBootDevice("/dev/ubiblock4_0"));
-}
-
TEST(UtilsTest, ReadFileFailure) {
chromeos::Blob empty;
EXPECT_FALSE(utils::ReadFile("/this/doesn't/exist", &empty));
@@ -137,47 +106,47 @@
EXPECT_TRUE(test_utils::RecursiveUnlinkDir(temp_dir));
}
-TEST(UtilsTest, GetDiskNameTest) {
- EXPECT_EQ("/dev/sda", utils::GetDiskName("/dev/sda3"));
- EXPECT_EQ("/dev/sdp", utils::GetDiskName("/dev/sdp1234"));
- EXPECT_EQ("/dev/mmcblk0", utils::GetDiskName("/dev/mmcblk0p3"));
- EXPECT_EQ("", utils::GetDiskName("/dev/mmcblk0p"));
- EXPECT_EQ("", utils::GetDiskName("/dev/sda"));
- EXPECT_EQ("/dev/ubiblock", utils::GetDiskName("/dev/ubiblock3_2"));
- EXPECT_EQ("", utils::GetDiskName("/dev/foo/bar"));
- EXPECT_EQ("", utils::GetDiskName("/"));
- EXPECT_EQ("", utils::GetDiskName(""));
-}
+TEST(UtilsTest, SplitPartitionNameTest) {
+ string disk;
+ int part_num;
-TEST(UtilsTest, SysfsBlockDeviceTest) {
- EXPECT_EQ("/sys/block/sda", utils::SysfsBlockDevice("/dev/sda"));
- EXPECT_EQ("", utils::SysfsBlockDevice("/foo/sda"));
- EXPECT_EQ("", utils::SysfsBlockDevice("/dev/foo/bar"));
- EXPECT_EQ("", utils::SysfsBlockDevice("/"));
- EXPECT_EQ("", utils::SysfsBlockDevice("./"));
- EXPECT_EQ("", utils::SysfsBlockDevice(""));
-}
+ EXPECT_TRUE(utils::SplitPartitionName("/dev/sda3", &disk, &part_num));
+ EXPECT_EQ("/dev/sda", disk);
+ EXPECT_EQ(3, part_num);
-TEST(UtilsTest, IsRemovableDeviceTest) {
- EXPECT_FALSE(utils::IsRemovableDevice(""));
- EXPECT_FALSE(utils::IsRemovableDevice("/dev/non-existent-device"));
-}
+ EXPECT_TRUE(utils::SplitPartitionName("/dev/sdp1234", &disk, &part_num));
+ EXPECT_EQ("/dev/sdp", disk);
+ EXPECT_EQ(1234, part_num);
-TEST(UtilsTest, GetPartitionNumberTest) {
- EXPECT_EQ(3, utils::GetPartitionNumber("/dev/sda3"));
- EXPECT_EQ(3, utils::GetPartitionNumber("/dev/sdz3"));
- EXPECT_EQ(123, utils::GetPartitionNumber("/dev/sda123"));
- EXPECT_EQ(2, utils::GetPartitionNumber("/dev/mmcblk0p2"));
- EXPECT_EQ(0, utils::GetPartitionNumber("/dev/mmcblk0p"));
- EXPECT_EQ(3, utils::GetPartitionNumber("/dev/ubiblock3_2"));
- EXPECT_EQ(0, utils::GetPartitionNumber(""));
- EXPECT_EQ(0, utils::GetPartitionNumber("/"));
- EXPECT_EQ(0, utils::GetPartitionNumber("/dev/"));
- EXPECT_EQ(0, utils::GetPartitionNumber("/dev/sda"));
- EXPECT_EQ(10, utils::GetPartitionNumber("/dev/loop10"));
- EXPECT_EQ(11, utils::GetPartitionNumber("/dev/loop28p11"));
- EXPECT_EQ(10, utils::GetPartitionNumber("/dev/loop10_0"));
- EXPECT_EQ(11, utils::GetPartitionNumber("/dev/loop28p11_0"));
+ EXPECT_TRUE(utils::SplitPartitionName("/dev/mmcblk0p3", &disk, &part_num));
+ EXPECT_EQ("/dev/mmcblk0", disk);
+ EXPECT_EQ(3, part_num);
+
+ EXPECT_TRUE(utils::SplitPartitionName("/dev/ubiblock3_2", &disk, &part_num));
+ EXPECT_EQ("/dev/ubiblock", disk);
+ EXPECT_EQ(3, part_num);
+
+ EXPECT_TRUE(utils::SplitPartitionName("/dev/loop10", &disk, &part_num));
+ EXPECT_EQ("/dev/loop", disk);
+ EXPECT_EQ(10, part_num);
+
+ EXPECT_TRUE(utils::SplitPartitionName("/dev/loop28p11", &disk, &part_num));
+ EXPECT_EQ("/dev/loop28", disk);
+ EXPECT_EQ(11, part_num);
+
+ EXPECT_TRUE(utils::SplitPartitionName("/dev/loop10_0", &disk, &part_num));
+ EXPECT_EQ("/dev/loop", disk);
+ EXPECT_EQ(10, part_num);
+
+ EXPECT_TRUE(utils::SplitPartitionName("/dev/loop28p11_0", &disk, &part_num));
+ EXPECT_EQ("/dev/loop28", disk);
+ EXPECT_EQ(11, part_num);
+
+ EXPECT_FALSE(utils::SplitPartitionName("/dev/mmcblk0p", &disk, &part_num));
+ EXPECT_FALSE(utils::SplitPartitionName("/dev/sda", &disk, &part_num));
+ EXPECT_FALSE(utils::SplitPartitionName("/dev/foo/bar", &disk, &part_num));
+ EXPECT_FALSE(utils::SplitPartitionName("/", &disk, &part_num));
+ EXPECT_FALSE(utils::SplitPartitionName("", &disk, &part_num));
}
TEST(UtilsTest, MakePartitionNameTest) {
@@ -333,31 +302,6 @@
EXPECT_EQ(6, block_count);
}
-TEST(UtilsTest, GetInstallDevTest) {
- string boot_dev = "/dev/sda5";
- string install_dev;
- EXPECT_TRUE(utils::GetInstallDev(boot_dev, &install_dev));
- EXPECT_EQ(install_dev, "/dev/sda3");
-
- boot_dev = "/dev/sda3";
- EXPECT_TRUE(utils::GetInstallDev(boot_dev, &install_dev));
- EXPECT_EQ(install_dev, "/dev/sda5");
-
- boot_dev = "/dev/sda12";
- EXPECT_FALSE(utils::GetInstallDev(boot_dev, &install_dev));
-
- boot_dev = "/dev/ubiblock3_0";
- EXPECT_TRUE(utils::GetInstallDev(boot_dev, &install_dev));
- EXPECT_EQ(install_dev, "/dev/ubi5_0");
-
- boot_dev = "/dev/ubiblock5_0";
- EXPECT_TRUE(utils::GetInstallDev(boot_dev, &install_dev));
- EXPECT_EQ(install_dev, "/dev/ubi3_0");
-
- boot_dev = "/dev/ubiblock12_0";
- EXPECT_FALSE(utils::GetInstallDev(boot_dev, &install_dev));
-}
-
namespace {
void GetFileFormatTester(const string& expected,
const vector<uint8_t>& contents) {