| // Copyright 2018 The Chromium OS Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "dlcservice/boot/boot_slot.h" |
| |
| #include <climits> |
| #include <utility> |
| |
| #include <base/files/file_util.h> |
| #include <base/strings/string_number_conversions.h> |
| #include <base/strings/string_util.h> |
| |
| #include "dlcservice/boot/boot_device.h" |
| #include "dlcservice/utils.h" |
| |
| using std::string; |
| using std::unique_ptr; |
| |
| namespace dlcservice { |
| |
| BootSlot::BootSlot(unique_ptr<BootDeviceInterface> boot_device) |
| : boot_device_(std::move(boot_device)) {} |
| |
| BootSlot::~BootSlot() = default; |
| |
| bool BootSlot::GetCurrentSlot(string* boot_disk_name_out, |
| Slot* current_slot_out, |
| bool* is_removable_out) const { |
| CHECK(boot_disk_name_out); |
| CHECK(current_slot_out); |
| |
| string boot_device = boot_device_->GetBootDevice(); |
| if (boot_device.empty()) |
| return false; |
| |
| int partition_num; |
| if (!SplitPartitionName(boot_device, boot_disk_name_out, &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. |
| const bool removable = boot_device_->IsRemovableDevice(*boot_disk_name_out); |
| if (is_removable_out) |
| *is_removable_out = removable; |
| if (removable) |
| LOG(INFO) |
| << "Booted from a removable device, pretending we have only one slot."; |
| |
| // Search through the slots to see which slot has the |partition_num| we |
| // booted from. |
| // In Chrome OS, the partition numbers are hard-coded: |
| // KERNEL-A=2, ROOT-A=3, KERNEL-B=4, ROOT-B=5, ... |
| // To help compatibility between different casing 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 |
| switch (partition_num) { |
| case 2: // KERNEL-A=2 |
| case 3: // ROOT-A=2 |
| *current_slot_out = Slot::A; |
| return true; |
| case 4: // KERNEL-B=4 |
| case 5: // ROOT-B=5 |
| *current_slot_out = Slot::B; |
| return true; |
| } |
| |
| // This should map to one of the existing slots, otherwise something is very |
| // wrong. |
| LOG(ERROR) << "Couldn't find the slot number corresponding to the " |
| "partition " |
| << boot_device << ". This device is not updateable."; |
| return false; |
| } |
| |
| bool BootSlot::SplitPartitionName(string partition_name, |
| string* disk_name_out, |
| int* partition_num_out) const { |
| CHECK(disk_name_out); |
| CHECK(partition_num_out); |
| if (!base::StartsWith(partition_name, "/dev/", |
| base::CompareCase::SENSITIVE)) { |
| LOG(ERROR) << "Invalid partition device name: " << partition_name; |
| return false; |
| } |
| |
| // Loop twice if we hit the '_' case to handle NAND block devices. |
| for (int i = 0; i <= 1; ++i) { |
| auto nondigit_pos = partition_name.find_last_not_of("0123456789"); |
| if (!isdigit(partition_name.back()) || nondigit_pos == string::npos) { |
| LOG(ERROR) << "Unable to parse partition device name: " << partition_name; |
| return false; |
| } |
| |
| switch (partition_name[nondigit_pos]) { |
| // NAND block devices have weird naming which could be something like |
| // "/dev/ubiblock2_0". We discard "_0" in such a case. |
| case '_': |
| LOG(INFO) << "Shortening partition_name: " << partition_name; |
| partition_name = partition_name.substr(0, nondigit_pos); |
| break; |
| // Special case for MMC devices which have the following naming scheme: |
| // mmcblk0p2 |
| case 'p': |
| if (nondigit_pos != 0 && isdigit(partition_name[nondigit_pos - 1])) { |
| *disk_name_out = partition_name.substr(0, nondigit_pos); |
| base::StringToInt(partition_name.substr(nondigit_pos + 1), |
| partition_num_out); |
| return true; |
| } |
| FALLTHROUGH; |
| default: |
| *disk_name_out = partition_name.substr(0, nondigit_pos + 1); |
| base::StringToInt(partition_name.substr(nondigit_pos + 1), |
| partition_num_out); |
| return true; |
| } |
| } |
| LOG(ERROR) << "Unable to parse partition device name: " << partition_name; |
| return false; |
| } |
| |
| // static |
| string BootSlot::ToString(BootSlot::Slot slot) { |
| return slot == BootSlot::Slot::A ? kDlcDirAName : kDlcDirBName; |
| } |
| |
| } // namespace dlcservice |