// 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. #pragma once #include #include #include #include #include #include #include #include #include namespace paver { enum class Partition { kUnknown, kBootloader, kKernelC, kEfi, kZirconA, kZirconB, kZirconR, kVbMetaA, kVbMetaB, kFuchsiaVolumeManager, // The following are only valid for WipePartition. kInstallType, kSystem, kBlob, kData, }; // A special filter for test injection. // API should return true if device passed in should be filtered out. extern bool (*TestBlockFilter)(const fbl::unique_fd&); // Abstract device partitioner definition. // This class defines common APIs for interacting with a device partitioner. class DevicePartitioner { public: // Factory method which automatically returns the correct DevicePartitioner // implementation. Returns nullptr on failure. static fbl::unique_ptr Create(); virtual ~DevicePartitioner() = default; virtual bool IsCros() const = 0; // Whether to use skip block interface or block interface for non-FVM // partitions. virtual bool UseSkipBlockInterface() const = 0; // Returns a file descriptor to a partition of type |partition_type|, creating it. // Assumes that the partition does not already exist. virtual zx_status_t AddPartition(Partition partition_type, fbl::unique_fd* out_fd) = 0; // Returns a file descriptor to a partition of type |partition_type| if one exists. virtual zx_status_t FindPartition(Partition partition_type, fbl::unique_fd* out_fd) const = 0; // Finalizes the partition of type |partition_type| after it has been // written. virtual zx_status_t FinalizePartition(Partition partition_type) = 0; // Wipes Fuchsia specific partitions. virtual zx_status_t WipePartitions() = 0; // Returns block size in bytes for specified device. virtual zx_status_t GetBlockSize(const fbl::unique_fd& device_fd, uint32_t* block_size) const = 0; }; // Useful for when a GPT table is available (e.g. x86 devices). Provides common // utility functions. class GptDevicePartitioner { public: using FilterCallback = fbl::Function; // Find and initialize a GPT based device. static zx_status_t InitializeGpt(fbl::unique_fd devfs_root, fbl::unique_ptr* gpt_out); virtual ~GptDevicePartitioner() { if (gpt_) { gpt_device_release(gpt_); } } // Returns block info for a specified block device. zx_status_t GetBlockInfo(block_info_t* block_info) const { memcpy(block_info, &block_info_, sizeof(*block_info)); return ZX_OK; } gpt_device_t* GetGpt() const { return gpt_; } int GetFd() const { return fd_.get(); } // Find the first spot that has at least |bytes_requested| of space. // // Returns the |start_out| block and |length_out| blocks, indicating // how much space was found, on success. This may be larger than // the number of bytes requested. zx_status_t FindFirstFit(size_t bytes_requested, size_t* start_out, size_t* length_out) const; // Creates a partition, adds an entry to the GPT, and returns a file descriptor to it. // Assumes that the partition does not already exist. zx_status_t AddPartition(const char* name, uint8_t* type, size_t minimum_size_bytes, size_t optional_reserve_bytes, fbl::unique_fd* out_fd); // Returns a file descriptor to a partition which can be paved, // if one exists. zx_status_t FindPartition(FilterCallback filter, gpt_partition_t** out, fbl::unique_fd* out_fd); zx_status_t FindPartition(FilterCallback filter, fbl::unique_fd* out_fd) const; // Wipes a specified partition from the GPT, and overwrites first 8KiB with // nonsense. zx_status_t WipePartitions(FilterCallback filter); private: // Find and return the topological path of the GPT which we will pave. static bool FindTargetGptPath(const fbl::unique_fd& devfs_root, fbl::String* out); GptDevicePartitioner(fbl::unique_fd devfs_root, fbl::unique_fd fd, gpt_device_t* gpt, block_info_t block_info) : devfs_root_(std::move(devfs_root)), fd_(std::move(fd)), gpt_(gpt), block_info_(block_info) {} zx_status_t CreateGptPartition(const char* name, uint8_t* type, uint64_t offset, uint64_t blocks, uint8_t* out_guid); fbl::unique_fd devfs_root_; fbl::unique_fd fd_; gpt_device_t* gpt_; block_info_t block_info_; }; // DevicePartitioner implementation for EFI based devices. class EfiDevicePartitioner : public DevicePartitioner { public: static zx_status_t Initialize(fbl::unique_fd devfs_root, fbl::unique_ptr* partitioner); bool IsCros() const override { return false; } bool UseSkipBlockInterface() const override { return false; } zx_status_t AddPartition(Partition partition_type, fbl::unique_fd* out_fd) override; zx_status_t FindPartition(Partition partition_type, fbl::unique_fd* out_fd) const override; zx_status_t FinalizePartition(Partition unused) override { return ZX_OK; } zx_status_t WipePartitions() override; zx_status_t GetBlockSize(const fbl::unique_fd& device_fd, uint32_t* block_size) const override; private: EfiDevicePartitioner(fbl::unique_ptr gpt) : gpt_(std::move(gpt)) {} fbl::unique_ptr gpt_; }; // DevicePartitioner implementation for ChromeOS devices. class CrosDevicePartitioner : public DevicePartitioner { public: static zx_status_t Initialize(fbl::unique_fd devfs_root, fbl::unique_ptr* partitioner); bool IsCros() const override { return true; } bool UseSkipBlockInterface() const override { return false; } zx_status_t AddPartition(Partition partition_type, fbl::unique_fd* out_fd) override; zx_status_t FindPartition(Partition partition_type, fbl::unique_fd* out_fd) const override; zx_status_t FinalizePartition(Partition unused) override; zx_status_t WipePartitions() override; zx_status_t GetBlockSize(const fbl::unique_fd& device_fd, uint32_t* block_size) const override; private: CrosDevicePartitioner(fbl::unique_ptr gpt) : gpt_(std::move(gpt)) {} fbl::unique_ptr gpt_; }; // DevicePartitioner implementation for devices which have fixed partition maps (e.g. ARM // devices). It will not attempt to write a partition map of any kind to the device. // Assumes standardized partition layout structure (e.g. ZIRCON-A, ZIRCON-B, // ZIRCON-R). class FixedDevicePartitioner : public DevicePartitioner { public: static zx_status_t Initialize(fbl::unique_fd devfs_root, fbl::unique_ptr* partitioner); bool IsCros() const override { return false; } bool UseSkipBlockInterface() const override { return false; } zx_status_t AddPartition(Partition partition_type, fbl::unique_fd* out_fd) override { return ZX_ERR_NOT_SUPPORTED; } zx_status_t FindPartition(Partition partition_type, fbl::unique_fd* out_fd) const override; zx_status_t FinalizePartition(Partition unused) override { return ZX_OK; } zx_status_t WipePartitions() override; zx_status_t GetBlockSize(const fbl::unique_fd& device_fd, uint32_t* block_size) const override; private: FixedDevicePartitioner(fbl::unique_fd devfs_root) : devfs_root_(std::move(devfs_root)) {} fbl::unique_fd devfs_root_; }; // DevicePartitioner implementation for devices which have fixed partition maps, but do not expose a // block device interface. Instead they expose devices with skip-block IOCTL interfaces. Like the // FixedDevicePartitioner, it will not attempt to write a partition map of any kind to the device. // Assumes standardized partition layout structure (e.g. ZIRCON-A, ZIRCON-B, // ZIRCON-R). class SkipBlockDevicePartitioner : public DevicePartitioner { public: static zx_status_t Initialize(fbl::unique_fd devfs_root, fbl::unique_ptr* partitioner); bool IsCros() const override { return false; } bool UseSkipBlockInterface() const override { return true; } zx_status_t AddPartition(Partition partition_type, fbl::unique_fd* out_fd) override { return ZX_ERR_NOT_SUPPORTED; } zx_status_t FindPartition(Partition partition_type, fbl::unique_fd* out_fd) const override; zx_status_t FinalizePartition(Partition unused) override { return ZX_OK; } zx_status_t WipePartitions() override; zx_status_t GetBlockSize(const fbl::unique_fd& device_fd, uint32_t* block_size) const override; private: SkipBlockDevicePartitioner(fbl::unique_fd devfs_root, fbl::unique_fd block_devfs_root) : devfs_root_(std::move(devfs_root)), block_devfs_root_(std::move(block_devfs_root)) {} fbl::unique_fd devfs_root_; fbl::unique_fd block_devfs_root_; }; } // namespace paver