blob: 045acdc29d93ac42767f9eeaa35c13212ddf9173 [file] [log] [blame]
// Copyright 2016 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.
#ifndef GPT_GPT_H_
#define GPT_GPT_H_
#include <lib/fit/result.h>
#include <memory>
#include <fbl/unique_fd.h>
#include <gpt/c/gpt.h>
namespace gpt {
// GPT magic number.
constexpr uint64_t kMagicNumber = GPT_MAGIC;
// GPT version 1.0
constexpr uint32_t kRevision = 0x00010000;
// GPT expect fixed size header. Verify that size of gpt_header_t meets
// the standards.
constexpr uint32_t kHeaderSize = GPT_HEADER_SIZE;
static_assert(kHeaderSize == sizeof(gpt_header_t), "invalid gpt header size");
// A copy of GPT is always at block 1. Location of backup copy is pointed by
// field within gpt_header_t.
constexpr uint64_t kPrimaryHeaderStartBlock = 1;
// Block size is expected to be large enough to hold gpt_header_t. GPT
// entries array start in the next block i.e. 2.
constexpr uint64_t kPrimaryEntriesStartBlock = kPrimaryHeaderStartBlock + 1;
// Last block should contain the header for GPT backup copy.
constexpr uint64_t BackupHeaderStartBlock(uint64_t block_count) { return block_count - 1; }
// Maximum number of partitions supported.
constexpr uint32_t kPartitionCount = 128;
// Number of blocks required to hold gpt_header_t. This should be always 1.
constexpr uint32_t kHeaderBlocks = 1;
// Minimum supperted block size.
constexpr uint32_t kMinimumBlockSize = 512;
// Maximum supported blocks size. 1MiB.
constexpr uint32_t kMaximumBlockSize = 1 << 20;
// GPT expect fixed size entry structure. Verify that size of gpt_entry_t meets
// the standards.
constexpr uint32_t kEntrySize = GPT_ENTRY_SIZE;
static_assert(kEntrySize == sizeof(gpt_entry_t), "invalid gpt entry size");
// Maximum size of the partition entry table.
constexpr size_t kMaxPartitionTableSize = kPartitionCount * kEntrySize;
// Size of array need to store "C12A7328-F81F-11D2-BA4B-00A0C93EC93B"
// There are other places where we use different macros to get this
// string length. Assert that we are in sync with the rest.
constexpr uint64_t kGuidStrLength = (2 * GPT_GUID_LEN) + (4 * sizeof('-')) + sizeof('\0');
static_assert(kGuidStrLength == GPT_GUID_STRLEN, "Guid print format changed");
// Size of null terminated char array to store non-utf16 GUID partition name.
constexpr uint64_t kGuidCNameLength = (GPT_NAME_LEN / 2) + 1;
constexpr uint32_t kGptDiffType = 0x01;
constexpr uint32_t kGptDiffGuid = 0x02;
constexpr uint32_t kGptDiffFirst = 0x04;
constexpr uint32_t kGptDiffLast = 0x08;
constexpr uint32_t kGptDiffFlags = 0x10;
constexpr uint32_t kGptDiffName = 0x20;
constexpr uint64_t kFlagHidden = 0x2;
// Returns the maximum size in bytes to hold header block and partition table.
constexpr fit::result<size_t, zx_status_t> MinimumBytesPerCopy(uint64_t block_size) {
if (block_size < kHeaderSize) {
return fit::error(ZX_ERR_INVALID_ARGS);
return fit::ok(block_size + kMaxPartitionTableSize);
// Returns the maximum blocks needed to hold header block and partition table.
constexpr fit::result<uint64_t, zx_status_t> MinimumBlocksPerCopy(uint64_t block_size) {
if (block_size < kHeaderSize) {
return fit::error(ZX_ERR_INVALID_ARGS);
return fit::ok((MinimumBytesPerCopy(block_size).value() + block_size - 1) / block_size);
// Returns the minimum blocks needed to hold two copies of GPT at appropriate
// offset (considering mbr block).
constexpr fit::result<uint64_t, zx_status_t> MinimumBlockDeviceSize(uint64_t block_size) {
if (block_size < kHeaderSize) {
return fit::error(ZX_ERR_INVALID_ARGS);
// There are two copies of GPT and a block for MBR(or such use).
return fit::ok(kPrimaryHeaderStartBlock + (2 * MinimumBlocksPerCopy(block_size).value()));
// Returns number of addressable blocks. On finding entry
// - nullptr, returns ZX_ERR_INVALID_ARGS
// - invalid, returns ZX_ERR_BAD_STATE
// - uninitialized, returns ZX_ERR_NOT_FOUND
fit::result<uint64_t, zx_status_t> EntryBlockCount(const gpt_entry_t* entry);
// Sets or clears partition visibility flag
void SetPartitionVisibility(gpt_partition_t* partition, bool visible);
// Returns true if partition's kHiddenFlag is not set i.e. partition
// is visible
bool IsPartitionVisible(const gpt_partition_t* partition);
class GptDevice {
static zx_status_t Create(int fd, uint32_t blocksize, uint64_t blocks,
std::unique_ptr<GptDevice>* out_dev);
// Loads gpt header and gpt entries array from |buffer| of length |size|
// belonging to "block device" with |blocks| number of blocks and each
// block of size |blocksize|. On finding valid header and entries, returns
// pointer to GptDevice in |oput_dev|.
static zx_status_t Load(const uint8_t* buffer, uint64_t size, uint32_t blocksize, uint64_t blocks,
std::unique_ptr<GptDevice>* out_dev);
// returns true if the partition table on the device is valid
bool Valid() const { return valid_; }
// Returns the range of usable blocks within the GPT, from [block_start, block_end] (inclusive)
zx_status_t Range(uint64_t* block_start, uint64_t* block_end) const;
// Writes changes to partition table to the device. If the device does not contain valid
// GPT, a gpt header gets created. Sync doesn't nudge block device driver to rescan the
// partitions. So it is the caller's responsibility to rescan partitions for the changes
// if needed.
zx_status_t Sync();
// perform all checks and computations on the in-memory representation, but DOES
// NOT write it out to disk. To perform checks AND write to disk, use Sync
zx_status_t Finalize();
// Adds a partition to in-memory instance of GptDevice. The changes stay visible
// only to this instace. Needs a Sync to write the changes to the device.
zx_status_t AddPartition(const char* name, const uint8_t* type, const uint8_t* guid,
uint64_t offset, uint64_t blocks, uint64_t flags);
// Writes zeroed blocks at an arbitrary offset (in blocks) within the device.
// Can be used alongside gpt_partition_add to ensure a newly created partition
// will not read stale superblock data.
zx_status_t ClearPartition(uint64_t offset, uint64_t blocks);
// Removes a partition from in-memory instance of GptDevice. The changes stay visible
// only to this instace. Needs a Sync to write the changes to the device.
zx_status_t RemovePartition(const uint8_t* guid);
// Removes all partitions from in-memory instance of GptDevice. The changes stay visible
// only to this instace. Needs a Sync to write the changes to the device.
zx_status_t RemoveAllPartitions();
// given a gpt device, get the GUID for the disk
void GetHeaderGuid(uint8_t (*disk_guid_out)[GPT_GUID_LEN]) const;
// return true if partition# idx has been locally modified
zx_status_t GetDiffs(uint32_t idx, uint32_t* diffs) const;
// Returns pointer to partition entry on finding a valid entry at given index. Else
// returns nullptr.
// TODO(auradkar): consider returning changing the prototype to
// zx_status_t GetPartition(uint32_t partition_index, const gpt_partition_t** out)
gpt_partition_t* GetPartition(uint32_t partition_index) const;
// Updates the type of partition at index partition_index
zx_status_t SetPartitionType(uint32_t partition_index, const uint8_t* type);
// Updates the guid(id) of partition at index partition_index
zx_status_t SetPartitionGuid(uint32_t partition_index, const uint8_t* guid);
// Makes partition visible if 'visible' is true
zx_status_t SetPartitionVisibility(uint32_t partition_index, bool visible);
// Changes partition's partitions start and end blocks. If there is conflict with
// either other partitions or the device, then returns non-ZX_OK value
zx_status_t SetPartitionRange(uint32_t partition_index, uint64_t start, uint64_t end);
// Returns current flags for partition at index partition_index
zx_status_t GetPartitionFlags(uint32_t partition_index, uint64_t* flags) const;
// Sets flags for partition at index partition_index
zx_status_t SetPartitionFlags(uint32_t partition_index, uint64_t flags);
// print out the GPT
void PrintTable() const;
// Return device's block size
uint64_t BlockSize() const { return blocksize_; }
uint64_t EntryCount() const {
if (!valid_) {
return kPartitionCount;
return header_.entries_count;
// Return number of bytes entries array occupies.
uint64_t EntryArraySize() const {
if (!valid_) {
return kMaxPartitionTableSize;
return (header_.entries_count * kEntrySize);
// Return number of blocks that entries array occupies.
uint64_t EntryArrayBlockCount() const {
return ((EntryArraySize() + blocksize_ - 1) / blocksize_);
// Return total number of blocks in the device
uint64_t TotalBlockCount() const { return blocks_; }
GptDevice() { valid_ = false; }
zx_status_t FinalizeAndSync(bool persist);
// read the partition table from the device.
static zx_status_t Init(int fd, uint32_t blocksize, uint64_t blocks,
std::unique_ptr<GptDevice>* out_dev);
zx_status_t LoadEntries(const uint8_t* buffer, uint64_t buffer_size);
// Walks entries array and returns error if crc doesn't match or ValidateEntry returns error.
zx_status_t ValidateEntries(const uint8_t* buffer) const;
// true if the partition table on the device is valid
bool valid_;
// pointer to a list of partitions
gpt_partition_t* partitions_[kPartitionCount] = {};
// device to use
fbl::unique_fd fd_ = {};
// block size in bytes
uint64_t blocksize_ = 0;
// number of blocks
uint64_t blocks_ = 0;
// header buffer, should be primary copy
gpt_header_t header_ = {};
// partition table buffer
gpt_partition_t ptable_[kPartitionCount] = {};
// copy of buffer from when last init'd or sync'd.
gpt_partition_t ptable_backup_[kPartitionCount] = {};
// On success returns initialized gpt header. On finding either |block_size| or
// |block_count| is not large enough, returns error.
fit::result<gpt_header_t, zx_status_t> InitializePrimaryHeader(uint64_t block_size,
uint64_t block_count);
// Validates gpt header. Each type of inconsistency leads to unique status code.
// The status can be used to print user friendly error messages.
zx_status_t ValidateHeader(const gpt_header_t* header, uint64_t block_count);
// A gpt entry can exists in three states
// - unused; where all fields are zeroed.
// - valid; field have sensible values;
// - error; one or more fields are in inconsistent state.
// Returns
// - true if the entry is valid
// - false if entry is unused
// - error if entry fields are inconsistent
fit::result<bool, zx_status_t> ValidateEntry(const gpt_entry_t* entry);
// Converts status returned by ValidateHeader to a human readable error message.
const char* HeaderStatusToCString(zx_status_t status);
} // namespace gpt
#endif // GPT_GPT_H_