| /* |
| * Copyright (C) 2016 The Android Open Source Project |
| * |
| * Permission is hereby granted, free of charge, to any person |
| * obtaining a copy of this software and associated documentation |
| * files (the "Software"), to deal in the Software without |
| * restriction, including without limitation the rights to use, copy, |
| * modify, merge, publish, distribute, sublicense, and/or sell copies |
| * of the Software, and to permit persons to whom the Software is |
| * furnished to do so, subject to the following conditions: |
| * |
| * The above copyright notice and this permission notice shall be |
| * included in all copies or substantial portions of the Software. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
| * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
| * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
| * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS |
| * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN |
| * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN |
| * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
| * SOFTWARE. |
| */ |
| |
| #include "avb_ops.h" |
| |
| #include <errno.h> |
| #include <fcntl.h> |
| #include <linux/fs.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <sys/ioctl.h> |
| #include <sys/stat.h> |
| |
| #include <string> |
| |
| #include <android-base/macros.h> |
| #include <android-base/strings.h> |
| #include <android-base/unique_fd.h> |
| #include <libavb/libavb.h> |
| #include <libdm/dm.h> |
| #include <utils/Compat.h> |
| |
| #include "util.h" |
| |
| using namespace std::literals; |
| |
| namespace android { |
| namespace fs_mgr { |
| |
| static AvbIOResult read_from_partition(AvbOps* ops, const char* partition, int64_t offset, |
| size_t num_bytes, void* buffer, size_t* out_num_read) { |
| return FsManagerAvbOps::GetInstanceFromAvbOps(ops)->ReadFromPartition( |
| partition, offset, num_bytes, buffer, out_num_read); |
| } |
| |
| static AvbIOResult no_op_read_rollback_index(AvbOps* ops ATTRIBUTE_UNUSED, |
| size_t rollback_index_location ATTRIBUTE_UNUSED, |
| uint64_t* out_rollback_index) { |
| // rollback_index has been checked in bootloader phase. |
| // In user-space, returns the smallest value 0 to pass the check. |
| *out_rollback_index = 0; |
| return AVB_IO_RESULT_OK; |
| } |
| |
| static AvbIOResult no_op_validate_vbmeta_public_key( |
| AvbOps* ops ATTRIBUTE_UNUSED, const uint8_t* public_key_data ATTRIBUTE_UNUSED, |
| size_t public_key_length ATTRIBUTE_UNUSED, |
| const uint8_t* public_key_metadata ATTRIBUTE_UNUSED, |
| size_t public_key_metadata_length ATTRIBUTE_UNUSED, bool* out_is_trusted) { |
| // vbmeta public key has been checked in bootloader phase. |
| // In user-space, returns true to pass the check. |
| // |
| // Addtionally, user-space should check |
| // androidboot.vbmeta.{hash_alg, size, digest} against the digest |
| // of all vbmeta images after invoking avb_slot_verify(). |
| *out_is_trusted = true; |
| return AVB_IO_RESULT_OK; |
| } |
| |
| static AvbIOResult no_op_read_is_device_unlocked(AvbOps* ops ATTRIBUTE_UNUSED, |
| bool* out_is_unlocked) { |
| // The function is for bootloader to update the value into |
| // androidboot.vbmeta.device_state in kernel cmdline. |
| // In user-space, returns true as we don't need to update it anymore. |
| *out_is_unlocked = true; |
| return AVB_IO_RESULT_OK; |
| } |
| |
| static AvbIOResult no_op_get_unique_guid_for_partition(AvbOps* ops ATTRIBUTE_UNUSED, |
| const char* partition ATTRIBUTE_UNUSED, |
| char* guid_buf, size_t guid_buf_size) { |
| // The function is for bootloader to set the correct UUID |
| // for a given partition in kernel cmdline. |
| // In user-space, returns a faking one as we don't need to update |
| // it anymore. |
| snprintf(guid_buf, guid_buf_size, "1234-fake-guid-for:%s", partition); |
| return AVB_IO_RESULT_OK; |
| } |
| |
| static AvbIOResult get_size_of_partition(AvbOps* ops ATTRIBUTE_UNUSED, |
| const char* partition ATTRIBUTE_UNUSED, |
| uint64_t* out_size_num_byte) { |
| return FsManagerAvbOps::GetInstanceFromAvbOps(ops)->GetSizeOfPartition(partition, |
| out_size_num_byte); |
| } |
| |
| // Converts a partition name (with ab_suffix) to the corresponding mount point. |
| // e.g., "system_a" => "/system", |
| // e.g., "vendor_a" => "/vendor", |
| static std::string DeriveMountPoint(const std::string& partition_name, |
| const std::string& ab_suffix) { |
| std::string mount_point(partition_name); |
| auto found = partition_name.rfind(ab_suffix); |
| if (found != std::string::npos) { |
| mount_point.erase(found); // converts system_a => system |
| } |
| |
| return "/" + mount_point; |
| } |
| |
| FsManagerAvbOps::FsManagerAvbOps(const std::string& slot_suffix) { |
| // We only need to provide the implementation of read_from_partition() |
| // operation since that's all what is being used by the avb_slot_verify(). |
| // Other I/O operations are only required in bootloader but not in |
| // user-space so we set them as no-op operations. Also zero the entire |
| // struct so operations added in the future will be set to NULL. |
| memset(&avb_ops_, 0, sizeof(AvbOps)); |
| avb_ops_.read_from_partition = read_from_partition; |
| avb_ops_.read_rollback_index = no_op_read_rollback_index; |
| avb_ops_.validate_vbmeta_public_key = no_op_validate_vbmeta_public_key; |
| avb_ops_.read_is_device_unlocked = no_op_read_is_device_unlocked; |
| avb_ops_.get_unique_guid_for_partition = no_op_get_unique_guid_for_partition; |
| avb_ops_.get_size_of_partition = get_size_of_partition; |
| |
| // Sets user_data for GetInstanceFromAvbOps() to convert it back to FsManagerAvbOps. |
| avb_ops_.user_data = this; |
| |
| slot_suffix_ = slot_suffix; |
| if (slot_suffix_.empty()) { |
| slot_suffix_ = fs_mgr_get_slot_suffix(); |
| } |
| } |
| |
| // Given a partition name (with ab_suffix), e.g., system_a, returns the corresponding |
| // dm-linear path for it. e.g., /dev/block/dm-0. If not found, returns an empty string. |
| // This assumes that the prefix of the partition name and the mount point are the same. |
| // e.g., partition vendor_a is mounted under /vendor, product_a is mounted under /product, etc. |
| // This might not be true for some special fstab files, e.g., fstab.postinstall. |
| // But it's good enough for the default fstab. Also note that the logical path is a |
| // fallback solution when the physical path (/dev/block/by-name/<partition>) cannot be found. |
| std::string FsManagerAvbOps::GetLogicalPath(const std::string& partition_name) { |
| if (fstab_.empty() && !ReadDefaultFstab(&fstab_)) { |
| return ""; |
| } |
| |
| const auto mount_point = DeriveMountPoint(partition_name, slot_suffix_); |
| if (mount_point.empty()) return ""; |
| |
| auto fstab_entry = GetEntryForMountPoint(&fstab_, mount_point); |
| if (!fstab_entry) return ""; |
| |
| std::string device_path; |
| if (fstab_entry->fs_mgr_flags.logical) { |
| dm::DeviceMapper& dm = dm::DeviceMapper::Instance(); |
| if (!dm.GetDmDevicePathByName(fstab_entry->blk_device, &device_path)) { |
| LERROR << "Failed to resolve logical device path for: " << fstab_entry->blk_device; |
| return ""; |
| } |
| return device_path; |
| } |
| |
| return ""; |
| } |
| std::string FsManagerAvbOps::GetPartitionPath(const char* partition) { |
| std::string path = "/dev/block/by-name/"s + partition; |
| if (!WaitForFile(path, 1s)) { |
| LERROR << "Device path not found: " << path; |
| // Falls back to logical path if the physical path is not found. |
| // This mostly only works for emulator (no bootloader). Because in normal |
| // device, bootloader is unable to read logical partitions. So if libavb in |
| // the bootloader failed to read a physical partition, it will failed to boot |
| // the HLOS and we won't reach the code here. |
| path = GetLogicalPath(partition); |
| if (path.empty() || !WaitForFile(path, 1s)) return ""; |
| } |
| return path; |
| } |
| |
| AvbIOResult FsManagerAvbOps::GetSizeOfPartition(const char* partition, |
| uint64_t* out_size_num_byte) { |
| const auto path = GetPartitionPath(partition); |
| if (path.empty()) { |
| return AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION; |
| } |
| android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_CLOEXEC))); |
| if (fd < 0) { |
| PERROR << "Failed to open " << path; |
| return AVB_IO_RESULT_ERROR_IO; |
| } |
| int err = ioctl(fd, BLKGETSIZE64, out_size_num_byte); |
| if (err) { |
| *out_size_num_byte = 0; |
| return AVB_IO_RESULT_ERROR_IO; |
| } |
| return AVB_IO_RESULT_OK; |
| } |
| |
| AvbIOResult FsManagerAvbOps::ReadFromPartition(const char* partition, int64_t offset, |
| size_t num_bytes, void* buffer, |
| size_t* out_num_read) { |
| std::string path = GetPartitionPath(partition); |
| if (path.empty()) { |
| return AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION; |
| } |
| |
| android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_CLOEXEC))); |
| if (fd < 0) { |
| PERROR << "Failed to open " << path; |
| return AVB_IO_RESULT_ERROR_IO; |
| } |
| |
| // If offset is negative, interprets its absolute value as the |
| // number of bytes from the end of the partition. |
| if (offset < 0) { |
| off64_t total_size = lseek64(fd, 0, SEEK_END); |
| if (total_size == -1) { |
| PERROR << "Failed to lseek64 to end of the partition"; |
| return AVB_IO_RESULT_ERROR_IO; |
| } |
| offset = total_size + offset; |
| // Repositions the offset to the beginning. |
| if (lseek64(fd, 0, SEEK_SET) == -1) { |
| PERROR << "Failed to lseek64 to the beginning of the partition"; |
| return AVB_IO_RESULT_ERROR_IO; |
| } |
| } |
| |
| // On Linux, we never get partial reads from block devices (except |
| // for EOF). |
| ssize_t num_read = TEMP_FAILURE_RETRY(pread64(fd, buffer, num_bytes, offset)); |
| if (num_read < 0 || (size_t)num_read != num_bytes) { |
| PERROR << "Failed to read " << num_bytes << " bytes from " << path << " offset " << offset; |
| return AVB_IO_RESULT_ERROR_IO; |
| } |
| |
| if (out_num_read != nullptr) { |
| *out_num_read = num_read; |
| } |
| |
| return AVB_IO_RESULT_OK; |
| } |
| |
| AvbSlotVerifyResult FsManagerAvbOps::AvbSlotVerify(const std::string& ab_suffix, |
| AvbSlotVerifyFlags flags, |
| std::vector<VBMetaData>* out_vbmeta_images) { |
| // Invokes avb_slot_verify() to load and verify all vbmeta images. |
| // Sets requested_partitions to nullptr as it's to copy the contents |
| // of HASH partitions into handle>avb_slot_data_, which is not required as |
| // fs_mgr only deals with HASHTREE partitions. |
| const char* requested_partitions[] = {nullptr}; |
| |
| // Local resource to store vbmeta images from avb_slot_verify(); |
| AvbSlotVerifyData* avb_slot_data; |
| |
| // The |hashtree_error_mode| field doesn't matter as it only |
| // influences the generated kernel cmdline parameters. |
| auto verify_result = |
| avb_slot_verify(&avb_ops_, requested_partitions, ab_suffix.c_str(), flags, |
| AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE, &avb_slot_data); |
| |
| if (!avb_slot_data) return verify_result; |
| // Copies avb_slot_data->vbmeta_images[]. |
| for (size_t i = 0; i < avb_slot_data->num_vbmeta_images; i++) { |
| out_vbmeta_images->emplace_back(VBMetaData(avb_slot_data->vbmeta_images[i].vbmeta_data, |
| avb_slot_data->vbmeta_images[i].vbmeta_size, |
| avb_slot_data->vbmeta_images[i].partition_name)); |
| } |
| |
| // Free the local resource. |
| avb_slot_verify_data_free(avb_slot_data); |
| |
| return verify_result; |
| } |
| |
| } // namespace fs_mgr |
| } // namespace android |