blob: d037de4b6f2640161e4efda27e2cdd4145248c4c [file] [log] [blame]
// 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 <stdbool.h>
#include <fbl/function.h>
#include <fbl/string.h>
#include <fbl/unique_fd.h>
#include <fbl/unique_ptr.h>
#include <fbl/vector.h>
#include <gpt/gpt.h>
#include <zircon/types.h>
#include <utility>
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<DevicePartitioner> 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<bool(const gpt_partition_t&)>;
// Find and initialize a GPT based device.
static zx_status_t InitializeGpt(fbl::unique_fd devfs_root,
fbl::unique_ptr<GptDevicePartitioner>* 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<DevicePartitioner>* 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<GptDevicePartitioner> gpt)
: gpt_(std::move(gpt)) {}
fbl::unique_ptr<GptDevicePartitioner> gpt_;
};
// DevicePartitioner implementation for ChromeOS devices.
class CrosDevicePartitioner : public DevicePartitioner {
public:
static zx_status_t Initialize(fbl::unique_fd devfs_root,
fbl::unique_ptr<DevicePartitioner>* 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<GptDevicePartitioner> gpt)
: gpt_(std::move(gpt)) {}
fbl::unique_ptr<GptDevicePartitioner> 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<DevicePartitioner>* 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<DevicePartitioner>* 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