| // 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. |
| |
| #include <assert.h> |
| #include <errno.h> |
| #include <gpt/gpt.h> |
| #include <gpt/guid.h> |
| #include <inttypes.h> |
| #include <lib/cksum.h> |
| #include <stdbool.h> |
| #include <stddef.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <sys/param.h> |
| #include <zircon/device/block.h> |
| #include <zircon/syscalls.h> // for zx_cprng_draw |
| |
| #include <fuchsia/hardware/block/c/fidl.h> |
| #include <gpt/gpt.h> |
| #include <lib/fzl/fdio.h> |
| #include <zircon/device/block.h> |
| #include <zircon/syscalls.h> // for zx_cprng_draw |
| |
| #include "gpt/gpt.h" |
| |
| namespace gpt { |
| |
| namespace { |
| #define G_PRINTF(f, ...) \ |
| if (debug_out) \ |
| printf((f), ##__VA_ARGS__); |
| |
| bool debug_out = false; |
| |
| struct mbr_partition_t { |
| uint8_t status; |
| uint8_t chs_first[3]; |
| uint8_t type; |
| uint8_t chs_last[3]; |
| uint32_t lba; |
| uint32_t sectors; |
| }; |
| |
| static_assert(sizeof(gpt_header_t) == GPT_HEADER_SIZE, "unexpected gpt header size"); |
| |
| void print_array(const gpt_partition_t* const a[kPartitionCount], int c) { |
| char GUID[GPT_GUID_STRLEN]; |
| char name[GPT_NAME_LEN / 2 + 1]; |
| |
| for (int i = 0; i < c; ++i) { |
| uint8_to_guid_string(GUID, a[i]->type); |
| memset(name, 0, GPT_NAME_LEN / 2 + 1); |
| utf16_to_cstring(name, reinterpret_cast<const uint16_t*>(a[i]->name), GPT_NAME_LEN / 2); |
| |
| printf("Name: %s \n Start: %lu -- End: %lu \nType: %s\n", |
| name, a[i]->first, a[i]->last, GUID); |
| } |
| } |
| |
| void partition_init(gpt_partition_t* part, const char* name, const uint8_t* type, |
| const uint8_t* guid, uint64_t first, uint64_t last, uint64_t flags) { |
| memcpy(part->type, type, sizeof(part->type)); |
| memcpy(part->guid, guid, sizeof(part->guid)); |
| part->first = first; |
| part->last = last; |
| part->flags = flags; |
| cstring_to_utf16(reinterpret_cast<uint16_t*>(part->name), name, |
| sizeof(part->name) / sizeof(uint16_t)); |
| } |
| |
| zx_status_t gpt_sync_current(int fd, uint64_t blocksize, gpt_header_t* header, |
| gpt_partition_t* ptable) { |
| // write partition table first |
| ssize_t ret; |
| off_t offset; |
| offset = header->entries * blocksize; |
| size_t ptable_size = header->entries_count * header->entries_size; |
| ret = pwrite(fd, ptable, ptable_size, offset); |
| if (ret != static_cast<ssize_t>(ptable_size)) { |
| return ZX_ERR_IO; |
| } |
| |
| // then write the header |
| offset = header->current * blocksize; |
| |
| uint8_t block[blocksize]; |
| memset(block, 0, sizeof(blocksize)); |
| memcpy(block, header, sizeof(*header)); |
| ret = pwrite(fd, block, blocksize, offset); |
| if (ret != static_cast<ssize_t>(blocksize)) { |
| return ZX_ERR_IO; |
| } |
| return ZX_OK; |
| } |
| |
| int compare(const void* ls, const void* rs) { |
| const auto* l = *static_cast<gpt_partition_t* const*>(ls); |
| const auto* r = *static_cast<gpt_partition_t* const*>(rs); |
| if (l == NULL && r == NULL) { |
| return 0; |
| } |
| |
| if (l == NULL) { |
| return 1; |
| } |
| |
| if (r == NULL) { |
| return -1; |
| } |
| |
| if (l->first == r->first) { |
| return 0; |
| } |
| |
| return (l->first < r->first) ? -1 : 1; |
| } |
| |
| } // namespace |
| |
| __BEGIN_CDECLS |
| |
| void gpt_set_debug_output_enabled(bool enabled) { |
| debug_out = enabled; |
| } |
| |
| void gpt_sort_partitions(gpt_partition_t** base, size_t count) { |
| qsort(base, count, sizeof(gpt_partition_t*), compare); |
| } |
| |
| void cstring_to_utf16(uint16_t* dst, const char* src, size_t maxlen) { |
| size_t len = strlen(src); |
| if (len > maxlen) { |
| len = maxlen; |
| } |
| for (size_t i = 0; i < len; i++) { |
| *dst++ = static_cast<uint16_t>(*src++ & 0x7f); |
| } |
| } |
| |
| char* utf16_to_cstring(char* dst, const uint16_t* src, size_t len) { |
| size_t i = 0; |
| char* ptr = dst; |
| while (i < len) { |
| char c = src[i++] & 0x7f; |
| if (!c) { |
| continue; |
| } |
| *ptr++ = c; |
| } |
| return dst; |
| } |
| |
| bool gpt_is_sys_guid(uint8_t* guid, ssize_t len) { |
| static const uint8_t sys_guid[GPT_GUID_LEN] = GUID_SYSTEM_VALUE; |
| return len == GPT_GUID_LEN && !memcmp(guid, sys_guid, GPT_GUID_LEN); |
| } |
| |
| bool gpt_is_data_guid(uint8_t* guid, ssize_t len) { |
| static const uint8_t data_guid[GPT_GUID_LEN] = GUID_DATA_VALUE; |
| return len == GPT_GUID_LEN && !memcmp(guid, data_guid, GPT_GUID_LEN); |
| } |
| |
| bool gpt_is_install_guid(uint8_t* guid, ssize_t len) { |
| static const uint8_t install_guid[GPT_GUID_LEN] = GUID_INSTALL_VALUE; |
| return len == GPT_GUID_LEN && !memcmp(guid, install_guid, GPT_GUID_LEN); |
| } |
| |
| bool gpt_is_efi_guid(uint8_t* guid, ssize_t len) { |
| static const uint8_t efi_guid[GPT_GUID_LEN] = GUID_EFI_VALUE; |
| return len == GPT_GUID_LEN && !memcmp(guid, efi_guid, GPT_GUID_LEN); |
| } |
| |
| void uint8_to_guid_string(char* dst, const uint8_t* src) { |
| const guid_t* guid = reinterpret_cast<const guid_t*>(src); |
| sprintf(dst, "%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X", guid->data1, guid->data2, guid->data3, guid->data4[0], guid->data4[1], guid->data4[2], guid->data4[3], guid->data4[4], guid->data4[5], guid->data4[6], guid->data4[7]); |
| } |
| |
| const char* gpt_guid_to_type(const char* guid) { |
| return gpt::KnownGuid::GuidStrToName(guid); |
| } |
| |
| __END_CDECLS |
| |
| bool IsPartitionVisible(const gpt_partition_t* partition) { |
| return !((partition->flags & kFlagHidden) == kFlagHidden); |
| } |
| |
| void SetPartitionVisibility(gpt_partition_t* partition, bool visible) { |
| if (visible) { |
| partition->flags &= ~kFlagHidden; |
| } else { |
| partition->flags |= kFlagHidden; |
| } |
| } |
| |
| zx_status_t GptDevice::FinalizeAndSync(bool persist) { |
| |
| // write fake mbr if needed |
| uint8_t mbr[blocksize_]; |
| off_t offset; |
| ssize_t ret; |
| if (!mbr_) { |
| memset(mbr, 0, blocksize_); |
| mbr[0x1fe] = 0x55; |
| mbr[0x1ff] = 0xaa; |
| mbr_partition_t* mpart = reinterpret_cast<mbr_partition_t*>(mbr + 0x1be); |
| mpart->chs_first[1] = 0x1; |
| mpart->type = 0xee; // gpt protective mbr |
| mpart->chs_last[0] = 0xfe; |
| mpart->chs_last[1] = 0xff; |
| mpart->chs_last[2] = 0xff; |
| mpart->lba = 1; |
| mpart->sectors = blocks_ & 0xffffffff; |
| offset = 0; |
| ret = pwrite(fd_.get(), mbr, blocksize_, offset); |
| if (ret != static_cast<ssize_t>(blocksize_)) { |
| return ZX_ERR_IO; |
| } |
| mbr_ = true; |
| } |
| |
| // fill in the new header fields |
| gpt_header_t header; |
| memset(&header, 0, sizeof(header)); |
| header.magic = GPT_MAGIC; |
| header.revision = 0x00010000; // gpt version 1.0 |
| header.size = GPT_HEADER_SIZE; |
| if (valid_) { |
| header.current = header_.current; |
| header.backup = header_.backup; |
| memcpy(header.guid, header_.guid, 16); |
| } else { |
| header.current = 1; |
| // backup gpt is in the last block |
| header.backup = blocks_ - 1; |
| // generate a guid |
| zx_cprng_draw(header.guid, GPT_GUID_LEN); |
| } |
| |
| // always write 128 entries in partition table |
| size_t ptable_size = kPartitionCount * sizeof(gpt_partition_t); |
| fbl::unique_ptr<gpt_partition_t[]> buf(new gpt_partition_t[kPartitionCount]); |
| if (!buf) { |
| return ZX_ERR_NO_MEMORY; |
| } |
| memset(buf.get(), 0, ptable_size); |
| |
| // generate partition table |
| uint8_t* ptr = reinterpret_cast<uint8_t*>(buf.get()); |
| for (uint32_t i = 0; i < kPartitionCount && partitions_[i] != NULL; i++) { |
| memcpy(ptr, partitions_[i], GPT_ENTRY_SIZE); |
| ptr += GPT_ENTRY_SIZE; |
| } |
| |
| // fill in partition table fields in header |
| header.entries = valid_ ? header_.entries : 2; |
| header.entries_count = kPartitionCount; |
| header.entries_size = GPT_ENTRY_SIZE; |
| header.entries_crc = crc32(0, reinterpret_cast<uint8_t*>(buf.get()), ptable_size); |
| |
| uint64_t ptable_blocks = ptable_size / blocksize_; |
| header.first = header.entries + ptable_blocks; |
| header.last = header.backup - ptable_blocks - 1; |
| |
| // calculate header checksum |
| header.crc32 = crc32(0, reinterpret_cast<const uint8_t*>(&header), GPT_HEADER_SIZE); |
| |
| // the copy cached in priv is the primary copy |
| memcpy(&header_, &header, sizeof(header)); |
| |
| // the header copy on stack is now the backup copy... |
| header.current = header_.backup; |
| header.backup = header_.current; |
| header.entries = header_.last + 1; |
| header.crc32 = 0; |
| header.crc32 = crc32(0, reinterpret_cast<const uint8_t*>(&header), GPT_HEADER_SIZE); |
| |
| if (persist) { |
| zx_status_t status; |
| // write backup to disk |
| status = gpt_sync_current(fd_.get(), blocksize_, &header, buf.get()); |
| if (status != ZX_OK) { |
| return status; |
| } |
| |
| // write primary copy to disk |
| status = gpt_sync_current(fd_.get(), blocksize_, &header_, buf.get()); |
| if (status != ZX_OK) { |
| return status; |
| } |
| } |
| |
| // align backup with new on-disk state |
| memcpy(ptable_backup_, ptable_, sizeof(ptable_)); |
| |
| valid_ = true; |
| |
| return ZX_OK; |
| } |
| |
| void GptDevice::PrintTable() const { |
| int count = 0; |
| for (; partitions_[count] != NULL; ++count) |
| ; |
| print_array(partitions_, count); |
| } |
| |
| zx_status_t GptDevice::BlockRrPart() { |
| fzl::UnownedFdioCaller caller(fd_.get()); |
| zx_status_t status, io_status; |
| io_status = fuchsia_hardware_block_BlockRebindDevice(caller.borrow_channel(), &status); |
| if (io_status != ZX_OK) { |
| return io_status; |
| } |
| return status; |
| } |
| |
| zx_status_t GptDevice::GetDiffs(uint32_t idx, uint32_t* diffs) const { |
| |
| *diffs = 0; |
| |
| if (idx >= kPartitionCount) { |
| return ZX_ERR_OUT_OF_RANGE; |
| } |
| |
| if (partitions_[idx] == NULL) { |
| return ZX_ERR_NOT_FOUND; |
| } |
| |
| const gpt_partition_t* a = partitions_[idx]; |
| const gpt_partition_t* b = &ptable_backup_[idx]; |
| if (memcmp(a->type, b->type, sizeof(a->type))) { |
| *diffs |= kGptDiffType; |
| } |
| if (memcmp(a->guid, b->guid, sizeof(a->guid))) { |
| *diffs |= kGptDiffGuid; |
| } |
| if (a->first != b->first) { |
| *diffs |= kGptDiffFirst; |
| } |
| if (a->last != b->last) { |
| *diffs |= kGptDiffLast; |
| } |
| if (a->flags != b->flags) { |
| *diffs |= kGptDiffFlags; |
| } |
| if (memcmp(a->name, b->name, sizeof(a->name))) { |
| *diffs |= kGptDiffName; |
| } |
| |
| return ZX_OK; |
| } |
| |
| zx_status_t GptDevice::Init(int fd, uint32_t blocksize, uint64_t blocks) { |
| ssize_t ptable_size; |
| uint32_t saved_crc, crc; |
| gpt_partition_t* ptable; |
| gpt_header_t* header; |
| ssize_t ret; |
| off_t offset; |
| |
| fd_.reset(dup(fd)); |
| if (!fd_.is_valid()) { |
| G_PRINTF("failed to dup the fd\n"); |
| return ZX_ERR_INTERNAL; |
| } |
| |
| blocksize_ = blocksize; |
| blocks_ = blocks; |
| |
| uint8_t block[blocksize]; |
| |
| if (blocksize_ < 512) { |
| G_PRINTF("blocksize < 512 not supported\n"); |
| return ZX_ERR_INTERNAL; |
| } |
| |
| offset = 0; |
| ret = pread(fd_.get(), block, blocksize, offset); |
| if (ret != blocksize) { |
| return ZX_ERR_IO; |
| } |
| mbr_ = block[0x1fe] == 0x55 && block[0x1ff] == 0xaa; |
| |
| // read the gpt header (lba 1) |
| offset = blocksize; |
| ret = pread(fd_.get(), block, blocksize, offset); |
| if (ret != blocksize) { |
| return ZX_ERR_IO; |
| } |
| |
| header = &header_; |
| memcpy(header, block, sizeof(*header)); |
| |
| // is this a valid gpt header? |
| if (header->magic != GPT_MAGIC) { |
| G_PRINTF("invalid header magic!\n"); |
| // ok to have an invalid header |
| return ZX_OK; |
| } |
| |
| // header checksum |
| saved_crc = header->crc32; |
| header->crc32 = 0; |
| crc = crc32(0, reinterpret_cast<const uint8_t*>(header), header->size); |
| if (crc != saved_crc) { |
| G_PRINTF("header crc check failed\n"); |
| return ZX_OK; |
| } |
| |
| if (header->entries_count > kPartitionCount) { |
| G_PRINTF("too many partitions!\n"); |
| return ZX_OK; |
| } |
| |
| valid_ = true; |
| |
| if (header->entries_count == 0) { |
| return ZX_OK; |
| } |
| if (header->entries_count > kPartitionCount) { |
| G_PRINTF("too many partitions\n"); |
| return ZX_OK; |
| } |
| |
| ptable = ptable_; |
| ptable_size = header->entries_size * header->entries_count; |
| if (static_cast<size_t>(ptable_size) > SIZE_MAX) { |
| G_PRINTF("partition table too big\n"); |
| return ZX_OK; |
| } |
| |
| // read the partition table |
| offset = header->entries * blocksize; |
| ret = pread(fd_.get(), ptable, ptable_size, offset); |
| if (ret != ptable_size) { |
| return ZX_ERR_IO; |
| } |
| |
| // partition table checksum |
| crc = crc32(0, reinterpret_cast<const uint8_t*>(ptable), ptable_size); |
| if (crc != header->entries_crc) { |
| G_PRINTF("table crc check failed\n"); |
| return ZX_OK; |
| } |
| |
| // save original state so we can know what we changed |
| memcpy(ptable_backup_, ptable_, sizeof(ptable_)); |
| |
| // fill the table of valid partitions |
| for (unsigned i = 0; i < header->entries_count; i++) { |
| if (ptable[i].first == 0 && ptable[i].last == 0) continue; |
| partitions_[i] = &ptable[i]; |
| } |
| return ZX_OK; |
| } |
| |
| zx_status_t GptDevice::Create(int fd, uint32_t blocksize, uint64_t blocks, |
| fbl::unique_ptr<GptDevice>* out) { |
| fbl::unique_ptr<GptDevice> dev(new GptDevice()); |
| zx_status_t status = dev->Init(fd, blocksize, blocks); |
| if (status == ZX_OK) { |
| *out = std::move(dev); |
| } |
| |
| return status; |
| } |
| |
| zx_status_t GptDevice::Finalize() { |
| return FinalizeAndSync(false); |
| } |
| |
| zx_status_t GptDevice::Sync() { |
| return FinalizeAndSync(true); |
| } |
| |
| zx_status_t GptDevice::Range(uint64_t* block_start, uint64_t* block_end) const { |
| |
| if (!valid_) { |
| G_PRINTF("partition header invalid\n"); |
| return ZX_ERR_INTERNAL; |
| } |
| |
| // check range |
| *block_start = header_.first; |
| *block_end = header_.last; |
| return ZX_OK; |
| } |
| |
| zx_status_t GptDevice::AddPartition(const char* name, const uint8_t* type, const uint8_t* guid, |
| uint64_t offset, uint64_t blocks, uint64_t flags) { |
| |
| if (!valid_) { |
| G_PRINTF("partition header invalid, sync to generate a default header\n"); |
| return ZX_ERR_INTERNAL; |
| } |
| |
| if (blocks == 0) { |
| G_PRINTF("partition must be at least 1 block\n"); |
| return ZX_ERR_INVALID_ARGS; |
| } |
| |
| uint64_t first = offset; |
| uint64_t last = first + blocks - 1; |
| |
| // check range |
| if (last < first || first < header_.first || last > header_.last) { |
| G_PRINTF("partition must be in range of usable blocks[%" PRIu64 ", %" PRIu64 "]\n", |
| header_.first, header_.last); |
| return ZX_ERR_INVALID_ARGS; |
| } |
| |
| // check for overlap |
| uint32_t i; |
| int tail = -1; |
| for (i = 0; i < kPartitionCount; i++) { |
| if (!partitions_[i]) { |
| tail = i; |
| break; |
| } |
| if (first <= partitions_[i]->last && last >= partitions_[i]->first) { |
| G_PRINTF("partition range overlaps\n"); |
| return ZX_ERR_OUT_OF_RANGE; |
| } |
| } |
| if (tail == -1) { |
| G_PRINTF("too many partitions\n"); |
| return ZX_ERR_OUT_OF_RANGE; |
| } |
| |
| // find a free slot |
| gpt_partition_t* part = NULL; |
| for (i = 0; i < kPartitionCount; i++) { |
| if (ptable_[i].first == 0 && ptable_[i].last == 0) { |
| part = &ptable_[i]; |
| break; |
| } |
| } |
| assert(part); |
| |
| // insert the new element into the list |
| partition_init(part, name, type, guid, first, last, flags); |
| partitions_[tail] = part; |
| return ZX_OK; |
| } |
| |
| zx_status_t GptDevice::ClearPartition(uint64_t offset, uint64_t blocks) { |
| |
| if (!valid_) { |
| G_PRINTF("partition header invalid, sync to generate a default header\n"); |
| return ZX_ERR_WRONG_TYPE; |
| } |
| |
| if (blocks == 0) { |
| G_PRINTF("must clear at least 1 block\n"); |
| return ZX_ERR_NO_RESOURCES; |
| } |
| uint64_t first = offset; |
| uint64_t last = offset + blocks - 1; |
| |
| if (last < first || first < header_.first || last > header_.last) { |
| G_PRINTF("must clear in the range of usable blocks[%" PRIu64 ", %" PRIu64 "]\n", |
| header_.first, header_.last); |
| return ZX_ERR_OUT_OF_RANGE; |
| } |
| |
| char zero[blocksize_]; |
| memset(zero, 0, sizeof(zero)); |
| |
| for (size_t i = first; i <= last; i++) { |
| if (pwrite(fd_.get(), zero, sizeof(zero), blocksize_ * i) != |
| static_cast<ssize_t>(sizeof(zero))) { |
| G_PRINTF("Failed to write to block %zu; errno: %d\n", i, errno); |
| return ZX_ERR_IO; |
| } |
| } |
| |
| return ZX_OK; |
| } |
| |
| zx_status_t GptDevice::RemovePartition(const uint8_t* guid) { |
| // look for the entry in the partition list |
| uint32_t i; |
| for (i = 0; i < kPartitionCount; i++) { |
| if (!memcmp(partitions_[i]->guid, guid, sizeof(partitions_[i]->guid))) { |
| break; |
| } |
| } |
| if (i == kPartitionCount) { |
| G_PRINTF("partition not found\n"); |
| return ZX_ERR_NOT_FOUND; |
| } |
| // clear the entry |
| memset(partitions_[i], 0, GPT_ENTRY_SIZE); |
| // pack the partition list |
| for (i = i + 1; i < kPartitionCount; i++) { |
| if (partitions_[i] == NULL) { |
| partitions_[i - 1] = NULL; |
| } else { |
| partitions_[i - 1] = partitions_[i]; |
| } |
| } |
| return ZX_OK; |
| } |
| |
| zx_status_t GptDevice::RemoveAllPartitions() { |
| memset(partitions_, 0, sizeof(partitions_)); |
| return ZX_OK; |
| } |
| |
| gpt_partition_t* GptDevice::GetPartition(uint32_t partition_index) const { |
| if (partition_index >= kPartitionCount) { |
| return nullptr; |
| } |
| |
| return partitions_[partition_index]; |
| } |
| |
| zx_status_t GptDevice::SetPartitionType(uint32_t partition_index, const uint8_t* type) { |
| gpt_partition_t* p = GetPartition(partition_index); |
| if (p == nullptr) { |
| return ZX_ERR_OUT_OF_RANGE; |
| } |
| |
| memcpy(p->type, type, GPT_GUID_LEN); |
| return ZX_OK; |
| } |
| |
| zx_status_t GptDevice::SetPartitionGuid(uint32_t partition_index, const uint8_t* guid) { |
| gpt_partition_t* p = GetPartition(partition_index); |
| if (p == nullptr) { |
| return ZX_ERR_OUT_OF_RANGE; |
| } |
| memcpy(p->guid, guid, GPT_GUID_LEN); |
| return ZX_OK; |
| } |
| |
| zx_status_t GptDevice::SetPartitionVisibility(uint32_t partition_index, bool visible) { |
| gpt_partition_t* p = GetPartition(partition_index); |
| if (p == nullptr) { |
| return ZX_ERR_OUT_OF_RANGE; |
| } |
| gpt::SetPartitionVisibility(p, visible); |
| |
| return ZX_OK; |
| } |
| |
| zx_status_t GptDevice::SetPartitionRange(uint32_t partition_index, uint64_t start, uint64_t end) { |
| gpt_partition_t* p = GetPartition(partition_index); |
| if (p == nullptr) { |
| return ZX_ERR_OUT_OF_RANGE; |
| } |
| |
| zx_status_t ret; |
| uint64_t block_start, block_end; |
| if ((ret = Range(&block_start, &block_end)) != ZX_OK) { |
| return ret; |
| } |
| |
| if ((start < block_start) || (end > block_end) || (start >= end)) { |
| return ZX_ERR_INVALID_ARGS; |
| } |
| |
| for (uint32_t idx = 0; idx < kPartitionCount; idx++) { |
| // skip this partition and non-existent partitions |
| if ((idx == partition_index) || (GetPartition(idx) == NULL)) { |
| continue; |
| } |
| |
| // skip partitions we don't intersect |
| if ((start > GetPartition(idx)->last) || (end < GetPartition(idx)->first)) { |
| continue; |
| } |
| |
| return ZX_ERR_OUT_OF_RANGE; |
| } |
| |
| p->first = start; |
| p->last = end; |
| return ZX_OK; |
| } |
| |
| zx_status_t GptDevice::GetPartitionFlags(uint32_t partition_index, uint64_t* flags) const { |
| gpt_partition_t* p = GetPartition(partition_index); |
| if (p == nullptr) { |
| return ZX_ERR_OUT_OF_RANGE; |
| } |
| |
| *flags = p->flags; |
| return ZX_OK; |
| } |
| |
| // TODO(auradkar): flags are unckecked for invalid flags |
| zx_status_t GptDevice::SetPartitionFlags(uint32_t partition_index, uint64_t flags) { |
| gpt_partition_t* p = GetPartition(partition_index); |
| if (p == nullptr) { |
| return ZX_ERR_OUT_OF_RANGE; |
| } |
| |
| p->flags = flags; |
| return ZX_OK; |
| } |
| |
| void GptDevice::GetHeaderGuid(uint8_t (*disk_guid_out)[GPT_GUID_LEN]) const { |
| memcpy(disk_guid_out, header_.guid, GPT_GUID_LEN); |
| } |
| |
| } // namespace gpt |