| // Copyright 2018 The Fuchsia 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 <lib/paver/device-partitioner.h> |
| |
| #include <dirent.h> |
| #include <errno.h> |
| #include <fcntl.h> |
| #include <libgen.h> |
| |
| #include <chromeos-disk-setup/chromeos-disk-setup.h> |
| #include <fbl/auto_call.h> |
| #include <fbl/function.h> |
| #include <fs-management/fvm.h> |
| #include <fuchsia/device/c/fidl.h> |
| #include <fuchsia/hardware/block/c/fidl.h> |
| #include <fuchsia/hardware/skipblock/c/fidl.h> |
| #include <fuchsia/sysinfo/c/fidl.h> |
| #include <gpt/cros.h> |
| #include <lib/fdio/directory.h> |
| #include <lib/fdio/fd.h> |
| #include <lib/fdio/fdio.h> |
| #include <lib/fdio/unsafe.h> |
| #include <lib/fdio/watcher.h> |
| #include <lib/fzl/fdio.h> |
| #include <zircon/status.h> |
| #include <zxcrypt/volume.h> |
| |
| #include <utility> |
| |
| #include "pave-logging.h" |
| #include "pave-utils.h" |
| |
| namespace paver { |
| |
| bool (*TestBlockFilter)(const fbl::unique_fd&) = nullptr; |
| |
| namespace { |
| |
| constexpr char kEfiName[] = "EFI Gigaboot"; |
| constexpr char kGptDriverName[] = "/boot/driver/gpt.so"; |
| constexpr char kFvmPartitionName[] = "fvm"; |
| constexpr char kZirconAName[] = "ZIRCON-A"; |
| constexpr char kZirconBName[] = "ZIRCON-B"; |
| constexpr char kZirconRName[] = "ZIRCON-R"; |
| |
| bool KernelFilterCallback(const gpt_partition_t& part, const uint8_t kern_type[GPT_GUID_LEN], fbl::StringPiece partition_name) { |
| char cstring_name[GPT_NAME_LEN]; |
| utf16_to_cstring(cstring_name, reinterpret_cast<const uint16_t*>(part.name), GPT_NAME_LEN); |
| return memcmp(part.type, kern_type, GPT_GUID_LEN) == 0 && |
| strncmp(cstring_name, partition_name.data(), partition_name.length()) == 0; |
| } |
| |
| bool IsFvmPartition(const gpt_partition_t& part) { |
| const uint8_t partition_type[GPT_GUID_LEN] = GUID_FVM_VALUE; |
| return memcmp(part.type, partition_type, GPT_GUID_LEN) == 0; |
| } |
| |
| bool IsGigabootPartition(const gpt_partition_t& part) { |
| const uint8_t efi_type[GPT_GUID_LEN] = GUID_EFI_VALUE; |
| char cstring_name[GPT_NAME_LEN]; |
| utf16_to_cstring(cstring_name, reinterpret_cast<const uint16_t*>(part.name), GPT_NAME_LEN); |
| // Disk-paved EFI: Identified by "EFI Gigaboot" label. |
| const bool gigaboot_efi = strncmp(cstring_name, kEfiName, strlen(kEfiName)) == 0; |
| return memcmp(part.type, efi_type, GPT_GUID_LEN) == 0 && gigaboot_efi; |
| } |
| |
| constexpr size_t ReservedHeaderBlocks(size_t blk_size) { |
| constexpr size_t kReservedEntryBlocks = (16 * 1024); |
| return (kReservedEntryBlocks + 2 * blk_size) / blk_size; |
| } |
| |
| // Helper function to auto-deduce type. |
| template <typename T> |
| fbl::unique_ptr<T> WrapUnique(T* ptr) { |
| return fbl::unique_ptr<T>(ptr); |
| } |
| |
| zx_status_t OpenPartition(const fbl::unique_fd& devfs_root, const char* path, |
| fbl::Function<bool(const fbl::unique_fd&)> should_filter_file, |
| zx_duration_t timeout, fbl::unique_fd* out_partition) { |
| ZX_ASSERT(path != nullptr); |
| |
| struct CallbackInfo { |
| fbl::unique_fd* out_partition; |
| fbl::Function<bool(const fbl::unique_fd&)> should_filter_file; |
| }; |
| |
| CallbackInfo info = { |
| .out_partition = out_partition, |
| .should_filter_file = std::move(should_filter_file), |
| }; |
| |
| auto cb = [](int dirfd, int event, const char* filename, void* cookie) { |
| if (event != WATCH_EVENT_ADD_FILE) { |
| return ZX_OK; |
| } |
| if ((strcmp(filename, ".") == 0) || strcmp(filename, "..") == 0) { |
| return ZX_OK; |
| } |
| fbl::unique_fd devfd(openat(dirfd, filename, O_RDWR)); |
| if (!devfd) { |
| return ZX_OK; |
| } |
| auto info = static_cast<CallbackInfo*>(cookie); |
| if (info->should_filter_file(devfd)) { |
| return ZX_OK; |
| } |
| if (info->out_partition) { |
| *(info->out_partition) = std::move(devfd); |
| } |
| return ZX_ERR_STOP; |
| }; |
| |
| fbl::unique_fd dir_fd(openat(devfs_root.get(), path, O_RDONLY)); |
| if (!dir_fd) { |
| return ZX_ERR_IO; |
| } |
| DIR* dir = fdopendir(dir_fd.release()); |
| if (dir == nullptr) { |
| return ZX_ERR_IO; |
| } |
| const auto closer = fbl::MakeAutoCall([&dir]() { closedir(dir); }); |
| |
| zx_time_t deadline = zx_deadline_after(timeout); |
| if (fdio_watch_directory(dirfd(dir), cb, deadline, &info) != ZX_ERR_STOP) { |
| return ZX_ERR_NOT_FOUND; |
| } |
| return ZX_OK; |
| } |
| |
| constexpr char kBlockDevPath[] = "class/block/"; |
| |
| zx_status_t OpenBlockPartition(const fbl::unique_fd& devfs_root, const uint8_t* unique_guid, |
| const uint8_t* type_guid, zx_duration_t timeout, |
| fbl::unique_fd* out_fd) { |
| ZX_ASSERT(unique_guid || type_guid); |
| |
| auto cb = [&](const fbl::unique_fd& fd) { |
| if (TestBlockFilter && TestBlockFilter(fd)) { |
| return true; |
| } |
| fzl::UnownedFdioCaller caller(fd.get()); |
| zx::unowned_channel channel(caller.borrow_channel()); |
| fuchsia_hardware_block_partition_GUID guid; |
| zx_status_t io_status, status; |
| if (type_guid) { |
| io_status = fuchsia_hardware_block_partition_PartitionGetTypeGuid(channel->get(), |
| &status, &guid); |
| if (io_status != ZX_OK || status != ZX_OK || |
| memcmp(guid.value, type_guid, GUID_LEN) != 0) { |
| return true; |
| } |
| } |
| if (unique_guid) { |
| io_status = fuchsia_hardware_block_partition_PartitionGetInstanceGuid(channel->get(), |
| &status, &guid); |
| if (io_status != ZX_OK || status != ZX_OK || |
| memcmp(guid.value, unique_guid, GUID_LEN) != 0) { |
| return true; |
| } |
| } |
| return false; |
| }; |
| |
| return OpenPartition(devfs_root, kBlockDevPath, cb, timeout, out_fd); |
| } |
| |
| constexpr char kSkipBlockDevPath[] = "class/skip-block/"; |
| |
| zx_status_t OpenSkipBlockPartition(const fbl::unique_fd& devfs_root, const uint8_t* type_guid, |
| zx_duration_t timeout, fbl::unique_fd* out_fd) { |
| ZX_ASSERT(type_guid); |
| |
| auto cb = [&](const fbl::unique_fd& fd) { |
| fzl::UnownedFdioCaller caller(fd.get()); |
| |
| zx_status_t status; |
| fuchsia_hardware_skipblock_PartitionInfo info; |
| fuchsia_hardware_skipblock_SkipBlockGetPartitionInfo(caller.borrow_channel(), &status, |
| &info); |
| if (status != ZX_OK || memcmp(info.partition_guid, type_guid, GUID_LEN) != 0) { |
| return true; |
| } |
| return false; |
| }; |
| |
| return OpenPartition(devfs_root, kSkipBlockDevPath, cb, timeout, out_fd); |
| } |
| |
| bool HasSkipBlockDevice(const fbl::unique_fd& devfs_root) { |
| // Our proxy for detected a skip-block device is by checking for the |
| // existence of a device enumerated under the skip-block class. |
| const uint8_t type[GPT_GUID_LEN] = GUID_ZIRCON_A_VALUE; |
| return OpenSkipBlockPartition(devfs_root, type, ZX_SEC(1), nullptr) == ZX_OK; |
| } |
| |
| // Attempts to open and overwrite the first block of the underlying |
| // partition. Does not rebind partition drivers. |
| // |
| // At most one of |unique_guid| and |type_guid| may be nullptr. |
| zx_status_t WipeBlockPartition(const fbl::unique_fd& devfs_root, const uint8_t* unique_guid, |
| const uint8_t* type_guid) { |
| zx_status_t status = ZX_OK; |
| fbl::unique_fd fd; |
| if ((status = OpenBlockPartition(devfs_root, unique_guid, type_guid, ZX_SEC(3), &fd)) != ZX_OK) { |
| ERROR("Warning: Could not open partition to wipe: %s\n", |
| zx_status_get_string(status)); |
| return status; |
| } |
| |
| fzl::UnownedFdioCaller caller(fd.get()); |
| fuchsia_hardware_block_BlockInfo info; |
| zx_status_t io_status = fuchsia_hardware_block_BlockGetInfo(caller.borrow_channel(), &status, |
| &info); |
| if (io_status != ZX_OK) { |
| status = io_status; |
| } |
| if (status != ZX_OK) { |
| ERROR("Warning: Could not acquire block info: %s\n", zx_status_get_string(status)); |
| return status; |
| } |
| |
| // Overwrite the first block to (hackily) ensure the destroyed partition |
| // doesn't "reappear" in place. |
| char buf[info.block_size]; |
| memset(buf, 0, info.block_size); |
| |
| if (pwrite(fd.get(), buf, info.block_size, 0) != info.block_size) { |
| ERROR("Warning: Could not write to block device: %s\n", strerror(errno)); |
| return ZX_ERR_IO; |
| } |
| |
| if ((status = FlushBlockDevice(fd)) != ZX_OK) { |
| ERROR("Warning: Failed to synchronize block device: %s\n", |
| zx_status_get_string(status)); |
| return status; |
| } |
| |
| return ZX_OK; |
| } |
| |
| } // namespace |
| |
| const char* PartitionName(Partition type) { |
| switch (type) { |
| case Partition::kBootloader: |
| return "Bootloader"; |
| case Partition::kZirconA: |
| return "Zircon A"; |
| case Partition::kZirconB: |
| return "Zircon B"; |
| case Partition::kZirconR: |
| return "Zircon R"; |
| case Partition::kVbMetaA: |
| return "VBMeta A"; |
| case Partition::kVbMetaB: |
| return "VBMeta B"; |
| case Partition::kVbMetaR: |
| return "VBMeta R"; |
| case Partition::kFuchsiaVolumeManager: |
| return "Fuchsia Volume Manager"; |
| default: |
| return "Unknown"; |
| } |
| } |
| |
| fbl::unique_ptr<DevicePartitioner> DevicePartitioner::Create(fbl::unique_fd devfs_root, |
| zx::channel sysinfo) { |
| // TODO(surajmalhotra): Only use injected devfs_root for skip-block until |
| // ramdisks spawn in isolated devmgr. |
| fbl::unique_fd block_devfs_root((open("/dev", O_RDONLY))); |
| fbl::unique_ptr<DevicePartitioner> device_partitioner; |
| if ((SkipBlockDevicePartitioner::Initialize(std::move(devfs_root), |
| &device_partitioner) == ZX_OK) || |
| (CrosDevicePartitioner::Initialize(block_devfs_root.duplicate(), sysinfo, |
| &device_partitioner) == ZX_OK) || |
| (EfiDevicePartitioner::Initialize(block_devfs_root.duplicate(), sysinfo, |
| &device_partitioner) == ZX_OK) || |
| (FixedDevicePartitioner::Initialize(std::move(block_devfs_root), |
| &device_partitioner) == ZX_OK)) { |
| return device_partitioner; |
| } |
| return nullptr; |
| } |
| |
| /*====================================================* |
| * GPT Common * |
| *====================================================*/ |
| |
| bool GptDevicePartitioner::FindTargetGptPath(const fbl::unique_fd& devfs_root, fbl::String* out) { |
| fbl::unique_fd d_fd(openat(devfs_root.get(), kBlockDevPath, O_RDONLY)); |
| if (!d_fd) { |
| ERROR("Cannot inspect block devices\n"); |
| return false; |
| } |
| DIR* d = fdopendir(d_fd.release()); |
| if (d == nullptr) { |
| ERROR("Cannot inspect block devices\n"); |
| return false; |
| } |
| const auto closer = fbl::MakeAutoCall([&]() { closedir(d); }); |
| |
| struct dirent* de; |
| while ((de = readdir(d)) != nullptr) { |
| fbl::unique_fd fd(openat(dirfd(d), de->d_name, O_RDWR)); |
| if (!fd) { |
| continue; |
| } |
| out->Set(PATH_MAX, '\0'); |
| |
| if (TestBlockFilter && TestBlockFilter(fd)) { |
| continue; |
| } |
| |
| zx::channel dev; |
| zx_status_t status = fdio_get_service_handle(fd.release(), dev.reset_and_get_address()); |
| |
| fuchsia_hardware_block_BlockInfo info; |
| |
| zx_status_t io_status = fuchsia_hardware_block_BlockGetInfo(dev.get(), |
| &status, &info); |
| if (io_status != ZX_OK || status != ZX_OK) { |
| continue; |
| } |
| size_t path_len; |
| char* data = const_cast<char*>(out->data()); |
| io_status = fuchsia_device_ControllerGetTopologicalPath(dev.get(), &status, data, |
| PATH_MAX - 1, &path_len); |
| if (io_status != ZX_OK || status != ZX_OK) { |
| continue; |
| } |
| data[path_len] = 0; |
| |
| // TODO(ZX-1344): This is a hack, but practically, will work for our |
| // usage. |
| // |
| // The GPT which will contain an FVM should be the first non-removable |
| // block device that isn't a partition itself. |
| if (!(info.flags & BLOCK_FLAG_REMOVABLE) && strstr(out->c_str(), "part-") == nullptr) { |
| return true; |
| } |
| } |
| |
| ERROR("No candidate GPT found\n"); |
| return false; |
| } |
| |
| bool GptDevicePartitioner::CheckValidBoard(const zx::channel& sysinfo) { |
| char real_board_name[ZX_MAX_NAME_LEN] = {}; |
| zx_status_t status; |
| size_t actual_size; |
| zx_status_t io_status = fuchsia_sysinfo_DeviceGetBoardName( |
| sysinfo.get(), &status, real_board_name, sizeof(real_board_name), &actual_size); |
| if (io_status != ZX_OK || status != ZX_OK) { |
| return false; |
| } |
| |
| return strcmp(real_board_name, "pc") == 0; |
| } |
| |
| zx_status_t GptDevicePartitioner::InitializeGpt(fbl::unique_fd devfs_root, |
| const zx::channel& sysinfo, |
| fbl::unique_ptr<GptDevicePartitioner>* gpt_out) { |
| if (!CheckValidBoard(sysinfo)) { |
| return ZX_ERR_NOT_FOUND; |
| } |
| fbl::String gpt_path; |
| if (!FindTargetGptPath(devfs_root, &gpt_path)) { |
| ERROR("Failed to find GPT\n"); |
| return ZX_ERR_NOT_FOUND; |
| } |
| fbl::unique_fd fd(open(gpt_path.c_str(), O_RDWR)); |
| if (!fd) { |
| ERROR("Failed to open GPT\n"); |
| return ZX_ERR_NOT_FOUND; |
| } |
| |
| fzl::UnownedFdioCaller caller(fd.get()); |
| fuchsia_hardware_block_BlockInfo block_info; |
| zx_status_t status; |
| zx_status_t io_status = fuchsia_hardware_block_BlockGetInfo(caller.borrow_channel(), &status, |
| &block_info); |
| if (io_status != ZX_OK) { |
| status = io_status; |
| } |
| if (status != ZX_OK) { |
| ERROR("Warning: Could not acquire GPT block info: %s\n", zx_status_get_string(status)); |
| return status; |
| } |
| |
| fbl::unique_ptr<GptDevice> gpt; |
| if (GptDevice::Create(fd.get(), block_info.block_size, block_info.block_count, &gpt) != ZX_OK) { |
| ERROR("Failed to get GPT info\n"); |
| return ZX_ERR_BAD_STATE; |
| } |
| |
| if (!gpt->Valid()) { |
| ERROR("Located GPT is invalid; Attempting to initialize\n"); |
| if (gpt->RemoveAllPartitions() != ZX_OK) { |
| ERROR("Failed to create empty GPT\n"); |
| return ZX_ERR_BAD_STATE; |
| } |
| if (gpt->Sync() != ZX_OK) { |
| ERROR("Failed to sync empty GPT\n"); |
| return ZX_ERR_BAD_STATE; |
| } |
| // Try to rebind the GPT, in case a prior GPT driver was actually |
| // up and running. |
| io_status = fuchsia_hardware_block_BlockRebindDevice(caller.borrow_channel(), &status); |
| if (io_status != ZX_OK) { |
| status = io_status; |
| } |
| if (status != ZX_OK) { |
| ERROR("Failed to re-read GPT\n"); |
| return ZX_ERR_BAD_STATE; |
| } |
| |
| // Manually re-bind the GPT driver, since it is almost certainly |
| // too late to be noticed by the block watcher. |
| io_status = fuchsia_device_ControllerBind( |
| caller.borrow_channel(), kGptDriverName, strlen(kGptDriverName), |
| &status); |
| if (io_status != ZX_OK || status != ZX_OK) { |
| ERROR("Failed to bind GPT\n"); |
| return ZX_ERR_BAD_STATE; |
| } |
| } |
| |
| *gpt_out = WrapUnique(new GptDevicePartitioner(std::move(devfs_root), std::move(fd), |
| std::move(gpt), block_info)); |
| return ZX_OK; |
| } |
| |
| struct PartitionPosition { |
| size_t start; // Block, inclusive |
| size_t length; // In Blocks |
| }; |
| |
| zx_status_t GptDevicePartitioner::FindFirstFit(size_t bytes_requested, size_t* start_out, |
| size_t* length_out) const { |
| LOG("Looking for space\n"); |
| // Gather GPT-related information. |
| size_t blocks_requested = |
| (bytes_requested + block_info_.block_size - 1) / block_info_.block_size; |
| |
| // Sort all partitions by starting block. |
| // For simplicity, include the 'start' and 'end' reserved spots as |
| // partitions. |
| size_t partition_count = 0; |
| PartitionPosition partitions[gpt::kPartitionCount + 2]; |
| const size_t reserved_blocks = ReservedHeaderBlocks(block_info_.block_size); |
| partitions[partition_count].start = 0; |
| partitions[partition_count++].length = reserved_blocks; |
| partitions[partition_count].start = block_info_.block_count - reserved_blocks; |
| partitions[partition_count++].length = reserved_blocks; |
| |
| for (uint32_t i = 0; i < gpt::kPartitionCount; i++) { |
| const gpt_partition_t* p = gpt_->GetPartition(i); |
| if (!p) { |
| continue; |
| } |
| partitions[partition_count].start = p->first; |
| partitions[partition_count].length = p->last - p->first + 1; |
| LOG("Partition seen with start %zu, end %zu (length %zu)\n", p->first, p->last, |
| partitions[partition_count].length); |
| partition_count++; |
| } |
| LOG("Sorting\n"); |
| qsort(partitions, partition_count, sizeof(PartitionPosition), |
| [](const void* p1, const void* p2) { |
| ssize_t s1 = static_cast<ssize_t>(static_cast<const PartitionPosition*>(p1)->start); |
| ssize_t s2 = static_cast<ssize_t>(static_cast<const PartitionPosition*>(p2)->start); |
| return static_cast<int>(s1 - s2); |
| }); |
| |
| // Look for space between the partitions. Since the reserved spots of the |
| // GPT were included in |partitions|, all available space will be located |
| // "between" partitions. |
| for (size_t i = 0; i < partition_count - 1; i++) { |
| const size_t next = partitions[i].start + partitions[i].length; |
| LOG("Partition[%zu] From Block [%zu, %zu) ... (next partition starts at block %zu)\n", |
| i, partitions[i].start, next, partitions[i + 1].start); |
| |
| if (next > partitions[i + 1].start) { |
| ERROR("Corrupted GPT\n"); |
| return ZX_ERR_IO; |
| } |
| const size_t free_blocks = partitions[i + 1].start - next; |
| LOG(" There are %zu free blocks (%zu requested)\n", free_blocks, blocks_requested); |
| if (free_blocks >= blocks_requested) { |
| *start_out = next; |
| *length_out = free_blocks; |
| return ZX_OK; |
| } |
| } |
| ERROR("No GPT space found\n"); |
| return ZX_ERR_NO_RESOURCES; |
| } |
| |
| zx_status_t GptDevicePartitioner::CreateGptPartition(const char* name, uint8_t* type, |
| uint64_t offset, uint64_t blocks, |
| uint8_t* out_guid) const { |
| zx_cprng_draw(out_guid, GPT_GUID_LEN); |
| |
| zx_status_t status; |
| if ((status = gpt_->AddPartition(name, type, out_guid, offset, blocks, 0)) != ZX_OK) { |
| ERROR("Failed to add partition\n"); |
| return ZX_ERR_IO; |
| } |
| if ((status = gpt_->Sync()) != ZX_OK) { |
| ERROR("Failed to sync GPT\n"); |
| return ZX_ERR_IO; |
| } |
| if ((status = gpt_->ClearPartition(offset, 1)) != ZX_OK) { |
| ERROR("Failed to clear first block of new partition\n"); |
| return status; |
| } |
| zx_status_t io_status = fuchsia_hardware_block_BlockRebindDevice(Channel()->get(), &status); |
| if (io_status != ZX_OK) { |
| status = io_status; |
| } |
| if (status != ZX_OK) { |
| ERROR("Failed to rebind GPT\n"); |
| return status; |
| } |
| |
| return ZX_OK; |
| } |
| |
| zx_status_t GptDevicePartitioner::AddPartition( |
| const char* name, uint8_t* type, size_t minimum_size_bytes, |
| size_t optional_reserve_bytes, fbl::unique_fd* out_fd) const { |
| |
| uint64_t start, length; |
| zx_status_t status; |
| if ((status = FindFirstFit(minimum_size_bytes, &start, &length)) != ZX_OK) { |
| ERROR("Couldn't find fit\n"); |
| return status; |
| } |
| LOG("Found space in GPT - OK %zu @ %zu\n", length, start); |
| |
| if (optional_reserve_bytes) { |
| // If we can fulfill the requested size, and we still have space for the |
| // optional reserve section, then we should shorten the amount of blocks |
| // we're asking for. |
| // |
| // This isn't necessary, but it allows growing the GPT later, if necessary. |
| const size_t optional_reserve_blocks = optional_reserve_bytes / block_info_.block_size; |
| if (length - optional_reserve_bytes > (minimum_size_bytes / block_info_.block_size)) { |
| LOG("Space for reserve - OK\n"); |
| length -= optional_reserve_blocks; |
| } |
| } else { |
| length = fbl::round_up(minimum_size_bytes, block_info_.block_size) / block_info_.block_size; |
| } |
| LOG("Final space in GPT - OK %zu @ %zu\n", length, start); |
| |
| uint8_t guid[GPT_GUID_LEN]; |
| if ((status = CreateGptPartition(name, type, start, length, guid)) != ZX_OK) { |
| return status; |
| } |
| LOG("Added partition, waiting for bind\n"); |
| |
| if ((status = OpenBlockPartition(devfs_root_, guid, type, ZX_SEC(5), out_fd)) != ZX_OK) { |
| ERROR("Added partition, waiting for bind - NOT FOUND\n"); |
| return status; |
| } |
| LOG("Added partition, waiting for bind - OK\n"); |
| return ZX_OK; |
| } |
| |
| zx_status_t GptDevicePartitioner::FindPartition(FilterCallback filter, gpt_partition_t** out, |
| fbl::unique_fd* out_fd) const { |
| for (uint32_t i = 0; i < gpt::kPartitionCount; i++) { |
| gpt_partition_t* p = gpt_->GetPartition(i); |
| if (!p) { |
| continue; |
| } |
| |
| if (filter(*p)) { |
| LOG("Found partition in GPT, partition %u\n", i); |
| if (out) { |
| *out = p; |
| } |
| if (out_fd) { |
| zx_status_t status; |
| status = OpenBlockPartition(devfs_root_, p->guid, p->type, ZX_SEC(5), out_fd); |
| if (status != ZX_OK) { |
| ERROR("Couldn't open partition\n"); |
| return status; |
| } |
| } |
| return ZX_OK; |
| } |
| } |
| return ZX_ERR_NOT_FOUND; |
| } |
| |
| zx_status_t GptDevicePartitioner::FindPartition(FilterCallback filter, |
| fbl::unique_fd* out_fd) const { |
| for (uint32_t i = 0; i < gpt::kPartitionCount; i++) { |
| const gpt_partition_t* p = gpt_->GetPartition(i); |
| if (!p) { |
| continue; |
| } |
| |
| if (filter(*p)) { |
| LOG("Found partition in GPT, partition %u\n", i); |
| if (out_fd) { |
| zx_status_t status; |
| status = OpenBlockPartition(devfs_root_, p->guid, p->type, ZX_SEC(5), out_fd); |
| if (status != ZX_OK) { |
| ERROR("Couldn't open partition\n"); |
| return status; |
| } |
| } |
| return ZX_OK; |
| } |
| } |
| return ZX_ERR_NOT_FOUND; |
| } |
| |
| zx_status_t GptDevicePartitioner::WipeFvm() const { |
| bool modify = false; |
| for (uint32_t i = 0; i < gpt::kPartitionCount; i++) { |
| const gpt_partition_t* p = gpt_->GetPartition(i); |
| if (!p) { |
| continue; |
| } |
| if (!IsFvmPartition(*p)) { |
| continue; |
| } |
| |
| modify = true; |
| |
| // Ignore the return status; wiping is a best-effort approach anyway. |
| WipeBlockPartition(devfs_root_, p->guid, p->type); |
| |
| if (gpt_->RemovePartition(p->guid) != ZX_OK) { |
| ERROR("Warning: Could not remove partition\n"); |
| } else { |
| // If we successfully clear the partition, then all subsequent |
| // partitions get shifted down. If we just deleted partition 'i', |
| // we now need to look at partition 'i' again, since it's now |
| // occupied by what was in 'i+1'. |
| i--; |
| } |
| } |
| if (modify) { |
| gpt_->Sync(); |
| LOG("Immediate reboot strongly recommended\n"); |
| } |
| zx_status_t status; |
| fuchsia_hardware_block_BlockRebindDevice(Channel()->get(), &status); |
| return ZX_OK; |
| } |
| |
| /*====================================================* |
| * EFI SPECIFIC * |
| *====================================================*/ |
| |
| zx_status_t EfiDevicePartitioner::Initialize(fbl::unique_fd devfs_root, const zx::channel& sysinfo, |
| fbl::unique_ptr<DevicePartitioner>* partitioner) { |
| fbl::unique_ptr<GptDevicePartitioner> gpt; |
| zx_status_t status = GptDevicePartitioner::InitializeGpt(std::move(devfs_root), sysinfo, &gpt); |
| if (status != ZX_OK) { |
| return status; |
| } |
| if (is_cros(gpt->GetGpt())) { |
| ERROR("Use CrOS Device Partitioner."); |
| return ZX_ERR_NOT_SUPPORTED; |
| } |
| |
| LOG("Successfully initialized EFI Device Partitioner\n"); |
| *partitioner = WrapUnique(new EfiDevicePartitioner(std::move(gpt))); |
| return ZX_OK; |
| } |
| |
| zx_status_t EfiDevicePartitioner::AddPartition(Partition partition_type, |
| fbl::unique_fd* out_fd) const { |
| const char* name; |
| uint8_t type[GPT_GUID_LEN]; |
| size_t minimum_size_bytes = 0; |
| size_t optional_reserve_bytes = 0; |
| |
| switch (partition_type) { |
| case Partition::kBootloader: { |
| const uint8_t efi_type[GPT_GUID_LEN] = GUID_EFI_VALUE; |
| memcpy(type, efi_type, GPT_GUID_LEN); |
| minimum_size_bytes = 20LU * (1 << 20); |
| name = kEfiName; |
| break; |
| } |
| case Partition::kZirconA: { |
| const uint8_t zircon_a_type[GPT_GUID_LEN] = GUID_ZIRCON_A_VALUE; |
| memcpy(type, zircon_a_type, GPT_GUID_LEN); |
| minimum_size_bytes = 16LU * (1 << 20); |
| name = kZirconAName; |
| break; |
| } |
| case Partition::kZirconB: { |
| const uint8_t zircon_b_type[GPT_GUID_LEN] = GUID_ZIRCON_B_VALUE; |
| memcpy(type, zircon_b_type, GPT_GUID_LEN); |
| minimum_size_bytes = 16LU * (1 << 20); |
| name = kZirconBName; |
| break; |
| } |
| case Partition::kZirconR: { |
| const uint8_t zircon_r_type[GPT_GUID_LEN] = GUID_ZIRCON_R_VALUE; |
| memcpy(type, zircon_r_type, GPT_GUID_LEN); |
| minimum_size_bytes = 24LU * (1 << 20); |
| name = kZirconRName; |
| break; |
| } |
| case Partition::kFuchsiaVolumeManager: { |
| const uint8_t fvm_type[GPT_GUID_LEN] = GUID_FVM_VALUE; |
| memcpy(type, fvm_type, GPT_GUID_LEN); |
| minimum_size_bytes = 8LU * (1 << 30); |
| name = kFvmPartitionName; |
| break; |
| } |
| default: |
| ERROR("EFI partitioner cannot add unknown partition type\n"); |
| return ZX_ERR_NOT_SUPPORTED; |
| } |
| |
| return gpt_->AddPartition(name, type, minimum_size_bytes, |
| optional_reserve_bytes, out_fd); |
| } |
| |
| zx_status_t EfiDevicePartitioner::FindPartition(Partition partition_type, |
| fbl::unique_fd* out_fd) const { |
| switch (partition_type) { |
| case Partition::kBootloader: { |
| return gpt_->FindPartition(IsGigabootPartition, out_fd); |
| } |
| case Partition::kZirconA: { |
| const auto filter = [](const gpt_partition_t& part) { |
| const uint8_t guid[GPT_GUID_LEN] = GUID_ZIRCON_A_VALUE; |
| return KernelFilterCallback(part, guid, kZirconAName); |
| }; |
| return gpt_->FindPartition(filter, out_fd); |
| } |
| case Partition::kZirconB: { |
| const auto filter = [](const gpt_partition_t& part) { |
| const uint8_t guid[GPT_GUID_LEN] = GUID_ZIRCON_B_VALUE; |
| return KernelFilterCallback(part, guid, kZirconBName); |
| }; |
| return gpt_->FindPartition(filter, out_fd); |
| } |
| case Partition::kZirconR: { |
| const auto filter = [](const gpt_partition_t& part) { |
| const uint8_t guid[GPT_GUID_LEN] = GUID_ZIRCON_R_VALUE; |
| return KernelFilterCallback(part, guid, kZirconRName); |
| }; |
| return gpt_->FindPartition(filter, out_fd); |
| } |
| case Partition::kFuchsiaVolumeManager: |
| return gpt_->FindPartition(IsFvmPartition, out_fd); |
| |
| default: |
| ERROR("EFI partitioner cannot find unknown partition type\n"); |
| return ZX_ERR_NOT_SUPPORTED; |
| } |
| } |
| |
| zx_status_t EfiDevicePartitioner::WipeFvm() const { |
| return gpt_->WipeFvm(); |
| } |
| |
| zx_status_t EfiDevicePartitioner::GetBlockSize(const fbl::unique_fd& device_fd, |
| uint32_t* block_size) const { |
| fuchsia_hardware_block_BlockInfo info; |
| zx_status_t status = gpt_->GetBlockInfo(&info); |
| if (status == ZX_OK) { |
| *block_size = info.block_size; |
| } |
| return status; |
| } |
| |
| /*====================================================* |
| * CROS SPECIFIC * |
| *====================================================*/ |
| |
| zx_status_t CrosDevicePartitioner::Initialize(fbl::unique_fd devfs_root, const zx::channel& sysinfo, |
| fbl::unique_ptr<DevicePartitioner>* partitioner) { |
| fbl::unique_ptr<GptDevicePartitioner> gpt_partitioner; |
| zx_status_t status = GptDevicePartitioner::InitializeGpt(std::move(devfs_root), sysinfo, |
| &gpt_partitioner); |
| if (status != ZX_OK) { |
| return status; |
| } |
| |
| GptDevice* gpt = gpt_partitioner->GetGpt(); |
| if (!is_cros(gpt)) { |
| return ZX_ERR_NOT_FOUND; |
| } |
| |
| fuchsia_hardware_block_BlockInfo info; |
| gpt_partitioner->GetBlockInfo(&info); |
| |
| if (!is_ready_to_pave(gpt, &info, SZ_ZX_PART)) { |
| if ((status = config_cros_for_fuchsia(gpt, &info, SZ_ZX_PART)) != ZX_OK) { |
| ERROR("Failed to configure CrOS for Fuchsia.\n"); |
| return status; |
| } |
| if ((status = gpt->Sync()) != ZX_OK) { |
| ERROR("Failed to sync CrOS for Fuchsia.\n"); |
| return status; |
| } |
| fuchsia_hardware_block_BlockRebindDevice(gpt_partitioner->Channel()->get(), &status); |
| } |
| |
| LOG("Successfully initialized CrOS Device Partitioner\n"); |
| *partitioner = WrapUnique(new CrosDevicePartitioner(std::move(gpt_partitioner))); |
| return ZX_OK; |
| } |
| |
| zx_status_t CrosDevicePartitioner::AddPartition(Partition partition_type, |
| fbl::unique_fd* out_fd) const { |
| const char* name; |
| uint8_t type[GPT_GUID_LEN]; |
| size_t minimum_size_bytes = 0; |
| size_t optional_reserve_bytes = 0; |
| |
| switch (partition_type) { |
| case Partition::kZirconA: { |
| const uint8_t kernc_type[GPT_GUID_LEN] = GUID_CROS_KERNEL_VALUE; |
| memcpy(type, kernc_type, GPT_GUID_LEN); |
| minimum_size_bytes = 64LU * (1 << 20); |
| name = kZirconAName; |
| break; |
| } |
| case Partition::kZirconR: { |
| const uint8_t zircon_r_type[GPT_GUID_LEN] = GUID_ZIRCON_R_VALUE; |
| memcpy(type, zircon_r_type, GPT_GUID_LEN); |
| minimum_size_bytes = 24LU * (1 << 20); |
| name = kZirconRName; |
| break; |
| } |
| case Partition::kFuchsiaVolumeManager: { |
| const uint8_t fvm_type[GPT_GUID_LEN] = GUID_FVM_VALUE; |
| memcpy(type, fvm_type, GPT_GUID_LEN); |
| minimum_size_bytes = 8LU * (1 << 30); |
| name = kFvmPartitionName; |
| break; |
| } |
| default: |
| ERROR("Cros partitioner cannot add unknown partition type\n"); |
| return ZX_ERR_NOT_SUPPORTED; |
| } |
| return gpt_->AddPartition(name, type, minimum_size_bytes, |
| optional_reserve_bytes, out_fd); |
| } |
| |
| zx_status_t CrosDevicePartitioner::FindPartition(Partition partition_type, |
| fbl::unique_fd* out_fd) const { |
| switch (partition_type) { |
| case Partition::kZirconA: { |
| const auto filter = [](const gpt_partition_t& part) { |
| const uint8_t guid[GPT_GUID_LEN] = GUID_CROS_KERNEL_VALUE; |
| return KernelFilterCallback(part, guid, kZirconAName); |
| }; |
| return gpt_->FindPartition(filter, out_fd); |
| } |
| case Partition::kZirconR: { |
| const auto filter = [](const gpt_partition_t& part) { |
| const uint8_t guid[GPT_GUID_LEN] = GUID_ZIRCON_R_VALUE; |
| return KernelFilterCallback(part, guid, kZirconRName); |
| }; |
| return gpt_->FindPartition(filter, out_fd); |
| } |
| case Partition::kFuchsiaVolumeManager: |
| return gpt_->FindPartition(IsFvmPartition, out_fd); |
| |
| default: |
| ERROR("Cros partitioner cannot find unknown partition type\n"); |
| return ZX_ERR_NOT_SUPPORTED; |
| } |
| } |
| |
| zx_status_t CrosDevicePartitioner::FinalizePartition(Partition partition_type) const { |
| // Special partition finalization is only necessary for Zircon partitions. |
| if (partition_type != Partition::kZirconA) { |
| return ZX_OK; |
| } |
| |
| uint8_t top_priority = 0; |
| |
| const uint8_t kern_type[GPT_GUID_LEN] = GUID_CROS_KERNEL_VALUE; |
| constexpr char kPrefix[] = "ZIRCON-"; |
| uint16_t zircon_prefix[strlen(kPrefix) * 2]; |
| cstring_to_utf16(&zircon_prefix[0], kPrefix, strlen(kPrefix)); |
| |
| for (uint32_t i = 0; i < gpt::kPartitionCount; ++i) { |
| const gpt_partition_t* part = gpt_->GetGpt()->GetPartition(i); |
| if (part == NULL) { |
| continue; |
| } |
| if (memcmp(part->type, kern_type, GPT_GUID_LEN)) { |
| continue; |
| } |
| if (memcmp(part->name, zircon_prefix, strlen(kPrefix) * 2)) { |
| const uint8_t priority = gpt_cros_attr_get_priority(part->flags); |
| if (priority > top_priority) { |
| top_priority = priority; |
| } |
| } |
| } |
| |
| const auto filter_zircona = [](const gpt_partition_t& part) { |
| const uint8_t guid[GPT_GUID_LEN] = GUID_CROS_KERNEL_VALUE; |
| return KernelFilterCallback(part, guid, kZirconAName); |
| }; |
| zx_status_t status; |
| gpt_partition_t* partition; |
| if ((status = gpt_->FindPartition(filter_zircona, &partition, nullptr)) != ZX_OK) { |
| ERROR("Cannot find %s partition\n", kZirconAName); |
| return status; |
| } |
| |
| // Priority for Zircon A set to higher priority than all other kernels. |
| if (top_priority == UINT8_MAX) { |
| ERROR("Cannot set CrOS partition priority higher than other kernels\n"); |
| return ZX_ERR_OUT_OF_RANGE; |
| } |
| |
| // TODO(raggi): when other (B/R) partitions are paved, set their priority |
| // appropriately as well. |
| |
| if (gpt_cros_attr_set_priority(&partition->flags, ++top_priority) != 0) { |
| ERROR("Cannot set CrOS partition priority for ZIRCON-A\n"); |
| return ZX_ERR_OUT_OF_RANGE; |
| } |
| // Successful set to 'true' to encourage the bootloader to |
| // use this partition. |
| gpt_cros_attr_set_successful(&partition->flags, true); |
| // Maximize the number of attempts to boot this partition before |
| // we fall back to a different kernel. |
| if (gpt_cros_attr_set_tries(&partition->flags, 15) != 0) { |
| ERROR("Cannot set CrOS partition 'tries' for KERN-C\n"); |
| return ZX_ERR_OUT_OF_RANGE; |
| } |
| if ((status = gpt_->GetGpt()->Sync()) == ZX_OK) { |
| ERROR("Failed to sync CrOS partition 'tries' for KERN-C.\n"); |
| return status; |
| } |
| |
| return ZX_OK; |
| } |
| |
| zx_status_t CrosDevicePartitioner::WipeFvm() const { |
| return gpt_->WipeFvm(); |
| } |
| |
| zx_status_t CrosDevicePartitioner::GetBlockSize(const fbl::unique_fd& device_fd, |
| uint32_t* block_size) const { |
| fuchsia_hardware_block_BlockInfo info; |
| zx_status_t status = gpt_->GetBlockInfo(&info); |
| if (status == ZX_OK) { |
| *block_size = info.block_size; |
| } |
| return status; |
| } |
| |
| /*====================================================* |
| * FIXED PARTITION MAP * |
| *====================================================*/ |
| |
| zx_status_t FixedDevicePartitioner::Initialize(fbl::unique_fd devfs_root, |
| fbl::unique_ptr<DevicePartitioner>* partitioner) { |
| if (HasSkipBlockDevice(devfs_root)) { |
| return ZX_ERR_NOT_SUPPORTED; |
| } |
| LOG("Successfully initialized FixedDevicePartitioner Device Partitioner\n"); |
| *partitioner = WrapUnique(new FixedDevicePartitioner(std::move(devfs_root))); |
| return ZX_OK; |
| } |
| |
| zx_status_t FixedDevicePartitioner::AddPartition(Partition partition_type, |
| fbl::unique_fd* out_fd) const { |
| ERROR("Cannot add partitions to a fixed-map partition device\n"); |
| return ZX_ERR_NOT_SUPPORTED; |
| } |
| |
| zx_status_t FixedDevicePartitioner::FindPartition(Partition partition_type, |
| fbl::unique_fd* out_fd) const { |
| uint8_t type[GPT_GUID_LEN]; |
| |
| switch (partition_type) { |
| case Partition::kZirconA: { |
| const uint8_t zircon_a_type[GPT_GUID_LEN] = GUID_ZIRCON_A_VALUE; |
| memcpy(type, zircon_a_type, GPT_GUID_LEN); |
| break; |
| } |
| case Partition::kZirconB: { |
| const uint8_t zircon_b_type[GPT_GUID_LEN] = GUID_ZIRCON_B_VALUE; |
| memcpy(type, zircon_b_type, GPT_GUID_LEN); |
| break; |
| } |
| case Partition::kZirconR: { |
| const uint8_t zircon_r_type[GPT_GUID_LEN] = GUID_ZIRCON_R_VALUE; |
| memcpy(type, zircon_r_type, GPT_GUID_LEN); |
| break; |
| } |
| case Partition::kVbMetaA: { |
| const uint8_t vbmeta_a_type[GPT_GUID_LEN] = GUID_VBMETA_A_VALUE; |
| memcpy(type, vbmeta_a_type, GPT_GUID_LEN); |
| break; |
| } |
| case Partition::kVbMetaB: { |
| const uint8_t vbmeta_b_type[GPT_GUID_LEN] = GUID_VBMETA_B_VALUE; |
| memcpy(type, vbmeta_b_type, GPT_GUID_LEN); |
| break; |
| } |
| case Partition::kVbMetaR: { |
| const uint8_t vbmeta_r_type[GPT_GUID_LEN] = GUID_VBMETA_R_VALUE; |
| memcpy(type, vbmeta_r_type, GPT_GUID_LEN); |
| break; |
| } |
| case Partition::kFuchsiaVolumeManager: { |
| const uint8_t fvm_type[GPT_GUID_LEN] = GUID_FVM_VALUE; |
| memcpy(type, fvm_type, GPT_GUID_LEN); |
| break; |
| } |
| default: |
| ERROR("partition_type is invalid!\n"); |
| return ZX_ERR_NOT_SUPPORTED; |
| } |
| |
| return OpenBlockPartition(devfs_root_, nullptr, type, ZX_SEC(5), out_fd); |
| } |
| |
| zx_status_t FixedDevicePartitioner::WipeFvm() const { |
| const uint8_t fvm_type[GPT_GUID_LEN] = GUID_FVM_VALUE; |
| zx_status_t status; |
| if ((status = WipeBlockPartition(devfs_root_, nullptr, fvm_type)) != ZX_OK) { |
| ERROR("Failed to wipe FVM.\n"); |
| } else { |
| LOG("Wiped FVM successfully.\n"); |
| } |
| LOG("Immediate reboot strongly recommended\n"); |
| return ZX_OK; |
| } |
| |
| zx_status_t FixedDevicePartitioner::GetBlockSize(const fbl::unique_fd& device_fd, |
| uint32_t* block_size) const { |
| fzl::UnownedFdioCaller caller(device_fd.get()); |
| fuchsia_hardware_block_BlockInfo block_info; |
| |
| zx_status_t status; |
| zx_status_t io_status = fuchsia_hardware_block_BlockGetInfo(caller.borrow_channel(), &status, |
| &block_info); |
| if (io_status != ZX_OK) { |
| status = io_status; |
| } |
| if (status != ZX_OK) { |
| return status; |
| } |
| *block_size = block_info.block_size; |
| return ZX_OK; |
| } |
| |
| /*====================================================* |
| * SKIP BLOCK SPECIFIC * |
| *====================================================*/ |
| |
| zx_status_t SkipBlockDevicePartitioner::Initialize( |
| fbl::unique_fd devfs_root, fbl::unique_ptr<DevicePartitioner>* partitioner) { |
| |
| // TODO(surajmalhtora): Use common devfs_root fd for both block and |
| // skip-block devices. |
| fbl::unique_fd block_devfs_root(open("/dev", O_RDONLY)); |
| |
| if (!HasSkipBlockDevice(devfs_root)) { |
| return ZX_ERR_NOT_SUPPORTED; |
| } |
| LOG("Successfully initialized SkipBlockDevicePartitioner Device Partitioner\n"); |
| *partitioner = WrapUnique(new SkipBlockDevicePartitioner(std::move(devfs_root), |
| std::move(block_devfs_root))); |
| return ZX_OK; |
| } |
| |
| zx_status_t SkipBlockDevicePartitioner::AddPartition(Partition partition_type, |
| fbl::unique_fd* out_fd) const { |
| ERROR("Cannot add partitions to a skip-block, fixed partition device\n"); |
| return ZX_ERR_NOT_SUPPORTED; |
| } |
| |
| zx_status_t SkipBlockDevicePartitioner::FindPartition(Partition partition_type, |
| fbl::unique_fd* out_fd) const { |
| uint8_t type[GPT_GUID_LEN]; |
| |
| switch (partition_type) { |
| case Partition::kBootloader: { |
| const uint8_t bootloader_type[GPT_GUID_LEN] = GUID_BOOTLOADER_VALUE; |
| memcpy(type, bootloader_type, GPT_GUID_LEN); |
| break; |
| } |
| case Partition::kZirconA: { |
| const uint8_t zircon_a_type[GPT_GUID_LEN] = GUID_ZIRCON_A_VALUE; |
| memcpy(type, zircon_a_type, GPT_GUID_LEN); |
| break; |
| } |
| case Partition::kZirconB: { |
| const uint8_t zircon_b_type[GPT_GUID_LEN] = GUID_ZIRCON_B_VALUE; |
| memcpy(type, zircon_b_type, GPT_GUID_LEN); |
| break; |
| } |
| case Partition::kZirconR: { |
| const uint8_t zircon_r_type[GPT_GUID_LEN] = GUID_ZIRCON_R_VALUE; |
| memcpy(type, zircon_r_type, GPT_GUID_LEN); |
| break; |
| } |
| case Partition::kVbMetaA: { |
| const uint8_t vbmeta_a_type[GPT_GUID_LEN] = GUID_VBMETA_A_VALUE; |
| memcpy(type, vbmeta_a_type, GPT_GUID_LEN); |
| break; |
| } |
| case Partition::kVbMetaB: { |
| const uint8_t vbmeta_b_type[GPT_GUID_LEN] = GUID_VBMETA_B_VALUE; |
| memcpy(type, vbmeta_b_type, GPT_GUID_LEN); |
| break; |
| } |
| case Partition::kVbMetaR: { |
| const uint8_t vbmeta_r_type[GPT_GUID_LEN] = GUID_VBMETA_R_VALUE; |
| memcpy(type, vbmeta_r_type, GPT_GUID_LEN); |
| break; |
| } |
| case Partition::kFuchsiaVolumeManager: { |
| const uint8_t fvm_type[GPT_GUID_LEN] = GUID_FVM_VALUE; |
| memcpy(type, fvm_type, GPT_GUID_LEN); |
| // FVM partition is managed so it should expose a normal block device. |
| return OpenBlockPartition(block_devfs_root_, nullptr, type, ZX_SEC(5), out_fd); |
| } |
| default: |
| ERROR("partition_type is invalid!\n"); |
| return ZX_ERR_NOT_SUPPORTED; |
| } |
| |
| return OpenSkipBlockPartition(devfs_root_, type, ZX_SEC(5), out_fd); |
| } |
| |
| zx_status_t SkipBlockDevicePartitioner::WipeFvm() const { |
| const uint8_t fvm_type[GPT_GUID_LEN] = GUID_FVM_VALUE; |
| zx_status_t status; |
| fbl::unique_fd block_fd; |
| status = OpenBlockPartition(block_devfs_root_, nullptr, fvm_type, ZX_SEC(3), &block_fd); |
| if (status != ZX_OK) { |
| ERROR("Warning: Could not open partition to wipe: %s\n", zx_status_get_string(status)); |
| return ZX_OK; |
| } |
| |
| zx::channel block_dev; |
| status = fdio_get_service_handle(block_fd.release(), block_dev.reset_and_get_address()); |
| if (status != ZX_OK) { |
| ERROR("Warning: Could not get block service handle: %s\n", zx_status_get_string(status)); |
| return status; |
| } |
| char name[PATH_MAX + 1]; |
| size_t name_len; |
| zx_status_t call_status; |
| status = fuchsia_device_ControllerGetTopologicalPath(block_dev.get(), &call_status, name, |
| sizeof(name) - 1, &name_len); |
| if (status == ZX_OK) { |
| status = call_status; |
| } |
| if (status != ZX_OK) { |
| ERROR("Warning: Could not get name for partition: %s\n", |
| zx_status_get_string(status)); |
| return status; |
| } |
| name[name_len] = 0; |
| |
| const char* parent = dirname(name); |
| |
| fbl::unique_fd parent_fd(open(parent, O_RDONLY)); |
| if (!parent_fd) { |
| ERROR("Warning: Unable to open block parent device.\n"); |
| return ZX_ERR_IO; |
| } |
| |
| zx::channel svc; |
| status = fdio_get_service_handle(parent_fd.release(), svc.reset_and_get_address()); |
| if (status != ZX_OK) { |
| ERROR("Warning: Could not get service handle: %s\n", zx_status_get_string(status)); |
| return status; |
| } |
| zx_status_t status2; |
| status = fuchsia_hardware_block_FtlFormat(svc.get(), &status2); |
| |
| return status == ZX_OK ? status2 : status; |
| } |
| |
| zx_status_t SkipBlockDevicePartitioner::GetBlockSize(const fbl::unique_fd& device_fd, |
| uint32_t* block_size) const { |
| fzl::UnownedFdioCaller caller(device_fd.get()); |
| fuchsia_hardware_block_BlockInfo block_info; |
| |
| // Just in case we are trying to get info about a block-based device. |
| // |
| // Clone ahead of time; if it is NOT a block device, the connection will be terminated. |
| zx::channel maybe_block(fdio_service_clone(caller.borrow_channel())); |
| zx_status_t status; |
| zx_status_t io_status = fuchsia_hardware_block_BlockGetInfo(maybe_block.get(), &status, |
| &block_info); |
| if (io_status == ZX_OK && status == ZX_OK) { |
| *block_size = block_info.block_size; |
| return ZX_OK; |
| } |
| |
| fuchsia_hardware_skipblock_PartitionInfo part_info; |
| io_status = fuchsia_hardware_skipblock_SkipBlockGetPartitionInfo(caller.borrow_channel(), |
| &status, &part_info); |
| if (io_status != ZX_OK) { |
| status = io_status; |
| } |
| if (status != ZX_OK) { |
| ERROR("Failed to get partition info with status: %d\n", status); |
| return status; |
| } |
| *block_size = static_cast<uint32_t>(part_info.block_size_bytes); |
| |
| return ZX_OK; |
| } |
| |
| } // namespace paver |