root/src/add-ons/kernel/partitioning_systems/gpt/gpt.cpp
/*
 * Copyright 2009, Michael Lotz, mmlr@mlotz.ch. All rights reserved.
 * Copyright 2007-2013, Axel Dörfler, axeld@pinc-software.de.
 *
 * Distributed under the terms of the MIT License.
 */


#include "gpt.h"

#include <KernelExport.h>
#include <disk_device_manager/ddm_modules.h>
#include <disk_device_types.h>
#ifdef _BOOT_MODE
#       include <boot/partitions.h>
#else
#       include <DiskDeviceTypes.h>
#       include "PartitionLocker.h"
#endif
#include <util/kernel_cpp.h>

#include <errno.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>

#ifndef _BOOT_MODE
#include "uuid.h"
#endif

#include "Header.h"
#include "utility.h"


#define TRACE_EFI_GPT
#ifdef TRACE_EFI_GPT
#       define TRACE(x) dprintf x
#else
#       define TRACE(x) ;
#endif


#define EFI_PARTITION_MODULE_NAME "partitioning_systems/efi_gpt/v1"


#ifndef _BOOT_MODE
static off_t
block_align(partition_data* partition, off_t offset, bool upwards)
{
        // Take HDs into account that hide the fact they are using a
        // block size of 4096 bytes, and round to that.
        uint32 blockSize = max_c(partition->block_size, 4096);
        if (upwards)
                return ((offset + blockSize - 1) / blockSize) * blockSize;

        return (offset / blockSize) * blockSize;
}
#endif // !_BOOT_MODE


//      #pragma mark - public module interface


static status_t
efi_gpt_std_ops(int32 op, ...)
{
        switch (op) {
                case B_MODULE_INIT:
                case B_MODULE_UNINIT:
                        return B_OK;
        }

        return B_ERROR;
}


static float
efi_gpt_identify_partition(int fd, partition_data* partition, void** _cookie)
{
        EFI::Header* header = new (std::nothrow) EFI::Header(fd,
                (partition->size - 1) / partition->block_size, partition->block_size);
        status_t status = header->InitCheck();
        if (status != B_OK) {
                delete header;
                return -1;
        }

        *_cookie = header;
        if (header->IsDirty()) {
                // Either the main or the backup table is missing, it looks like someone
                // tried to erase the GPT with something else. Let's lower the priority,
                // so that other partitioning systems which use either only the start or
                // only the end of the drive, have a chance to run instead.
                return 0.75;
        }
        return 0.96;
                // This must be higher as Intel partitioning, as EFI can contain this
                // partitioning for compatibility
}


static status_t
efi_gpt_scan_partition(int fd, partition_data* partition, void* _cookie)
{
        TRACE(("efi_gpt_scan_partition(cookie = %p)\n", _cookie));
        EFI::Header* header = (EFI::Header*)_cookie;

        partition->status = B_PARTITION_VALID;
        partition->flags |= B_PARTITION_PARTITIONING_SYSTEM;
        partition->content_size = partition->size;
        partition->content_cookie = header;

        // scan all children

        uint32 index = 0;

        for (uint32 i = 0; i < header->EntryCount(); i++) {
                const gpt_partition_entry& entry = header->EntryAt(i);

                if (entry.partition_type == kEmptyGUID)
                        continue;

                if (entry.EndBlock() * partition->block_size
                                > (uint64)partition->size) {
                        TRACE(("efi_gpt: child partition exceeds existing space (ends at "
                                "block %" B_PRIu64 ")\n", entry.EndBlock()));
                        continue;
                }

                if (entry.StartBlock() * partition->block_size == 0) {
                        TRACE(("efi_gpt: child partition starts at 0 (recursive entry)\n"));
                        continue;
                }

                partition_data* child = create_child_partition(partition->id, index++,
                        partition->offset + entry.StartBlock() * partition->block_size,
                        entry.BlockCount() * partition->block_size, -1);
                if (child == NULL) {
                        TRACE(("efi_gpt: Creating child at index %" B_PRIu32 " failed\n",
                                index - 1));
                        return B_ERROR;
                }

                char name[B_OS_NAME_LENGTH];
                to_utf8(entry.name, EFI_PARTITION_NAME_LENGTH, name, sizeof(name));
                child->name = strdup(name);
                child->type = strdup(get_partition_type(entry.partition_type));
                child->block_size = partition->block_size;
                child->cookie = (void*)(addr_t)i;
                child->content_cookie = header;
        }

        return B_OK;
}


static void
efi_gpt_free_identify_partition_cookie(partition_data* partition, void* _cookie)
{
        // Cookie is freed in efi_gpt_free_partition_content_cookie().
}


static void
efi_gpt_free_partition_content_cookie(partition_data* partition)
{
        delete (EFI::Header*)partition->content_cookie;
}


#ifndef _BOOT_MODE
static uint32
efi_gpt_get_supported_operations(partition_data* partition, uint32 mask)
{
        uint32 flags = B_DISK_SYSTEM_SUPPORTS_INITIALIZING
                | B_DISK_SYSTEM_SUPPORTS_SETTING_CONTENT_NAME
                | B_DISK_SYSTEM_SUPPORTS_MOVING
                | B_DISK_SYSTEM_SUPPORTS_RESIZING
                | B_DISK_SYSTEM_SUPPORTS_CREATING_CHILD;
                // TODO: check for available entries and partitionable space and only
                // add creating child support if both is valid

        return flags;
}


static uint32
efi_gpt_get_supported_child_operations(partition_data* partition,
        partition_data* child, uint32 mask)
{
        return B_DISK_SYSTEM_SUPPORTS_MOVING_CHILD
                | B_DISK_SYSTEM_SUPPORTS_RESIZING_CHILD
                | B_DISK_SYSTEM_SUPPORTS_SETTING_TYPE
                | B_DISK_SYSTEM_SUPPORTS_DELETING_CHILD;
}


static bool
efi_gpt_is_sub_system_for(partition_data* partition)
{
        // a GUID Partition Table doesn't usually live inside another partition
        return false;
}


static bool
efi_gpt_validate_resize(partition_data* partition, off_t* size)
{
        off_t newSize = *size;
        if (newSize == partition->size)
                return true;

        if (newSize < 0)
                newSize = 0;
        else
                newSize = block_align(partition, newSize, false);

        // growing
        if (newSize > partition->size) {
                *size = newSize;
                return true;
        }

        // shrinking, only so that no child would be truncated
        off_t newEnd = partition->offset + newSize;
        for (int32 i = 0; i < partition->child_count; i++) {
                partition_data* child = get_child_partition(partition->id, i);
                if (child == NULL)
                        continue;

                if (child->offset + child->size > newEnd)
                        newEnd = child->offset + child->size;
        }

        newSize = block_align(partition, newEnd - partition->offset, true);
        *size = newSize;
        return true;
}


static bool
efi_gpt_validate_resize_child(partition_data* partition, partition_data* child,
        off_t* size)
{
        off_t newSize = *size;
        if (newSize == child->size)
                return true;

        // shrinking
        if (newSize < child->size) {
                if (newSize < 0)
                        newSize = 0;

                *size = block_align(partition, newSize, false);
                return true;
        }

        // growing, but only so much that the child doesn't get bigger than
        // the parent
        if (child->offset + newSize > partition->offset + partition->size)
                newSize = partition->offset + partition->size - child->offset;

        // make sure that the child doesn't overlap any sibling partitions
        off_t newEnd = child->offset + newSize;
        for (int32 i = 0; i < partition->child_count; i++) {
                partition_data* other = get_child_partition(partition->id, i);
                if (other == NULL || other->id == child->id
                        || other->offset < child->offset)
                        continue;

                if (newEnd > other->offset)
                        newEnd = other->offset;
        }

        *size = block_align(partition, newEnd - child->offset, false);
        return true;
}


static bool
efi_gpt_validate_move(partition_data* partition, off_t* start)
{
        // nothing to do
        return true;
}


static bool
efi_gpt_validate_move_child(partition_data* partition, partition_data* child,
        off_t* start)
{
        off_t newStart = *start;
        if (newStart < 0)
                newStart = 0;

        if (newStart + child->size > partition->size)
                newStart = partition->size - child->size;

        newStart = block_align(partition, newStart, false);
        if (newStart > child->offset) {
                for (int32 i = 0; i < partition->child_count; i++) {
                        partition_data* other = get_child_partition(partition->id, i);
                        if (other == NULL || other->id == child->id
                                || other->offset < child->offset)
                                continue;

                        if (other->offset < newStart + child->size)
                                newStart = other->offset - child->size;
                }

                newStart = block_align(partition, newStart, false);
        } else {
                for (int32 i = 0; i < partition->child_count; i++) {
                        partition_data* other = get_child_partition(partition->id, i);
                        if (other == NULL || other->id == child->id
                                || other->offset > child->offset)
                                continue;

                        if (other->offset + other->size > newStart)
                                newStart = other->offset + other->size;
                }

                newStart = block_align(partition, newStart, true);
        }

        *start = newStart;
        return true;
}


static bool
efi_gpt_validate_set_name(partition_data* partition, char* name)
{
        // TODO: should validate that the utf-8 -> ucs-2 is valid
        // TODO: should count actual utf-8 chars
        if (strlen(name) > EFI_PARTITION_NAME_LENGTH)
                name[EFI_PARTITION_NAME_LENGTH - 1] = 0;
        return true;
}


static bool
efi_gpt_validate_set_type(partition_data* partition, const char* type)
{
        guid_t typeGUID;
        return get_guid_for_partition_type(type, typeGUID);
}


static bool
efi_gpt_validate_initialize(partition_data* partition, char* name,
        const char* parameters)
{
        if ((efi_gpt_get_supported_operations(partition, ~0)
                & B_DISK_SYSTEM_SUPPORTS_INITIALIZING) == 0)
                return false;

        // name and parameters are ignored
        if (name != NULL)
                name[0] = 0;

        return true;
}


static bool
efi_gpt_validate_create_child(partition_data* partition, off_t* start,
        off_t* size, const char* type, const char* name, const char* parameters,
        int32* index)
{
        if ((efi_gpt_get_supported_operations(partition, ~0)
                        & B_DISK_SYSTEM_SUPPORTS_CREATING_CHILD) == 0)
                return false;

        if (!efi_gpt_validate_set_type(partition, type))
                return false;

        EFI::Header* header = (EFI::Header*)partition->content_cookie;
        int32 entryIndex = -1;
        for (uint32 i = 0; i < header->EntryCount(); i++) {
                const gpt_partition_entry& entry = header->EntryAt(i);
                if (entry.partition_type == kEmptyGUID) {
                        entryIndex = i;
                        break;
                }
        }

        if (entryIndex < 0)
                return false;

        *index = entryIndex;

        // ensure that child lies between first and last usable block
        off_t firstUsable = header->FirstUsableBlock() * partition->block_size;
        if (*start < firstUsable)
                *start = firstUsable;

        off_t lastUsable = header->LastUsableBlock() * partition->block_size;
        if (*start + *size > lastUsable) {
                if (*start > lastUsable)
                        return false;

                *size = lastUsable - *start;
        }

        // ensure that we don't overlap any siblings
        for (int32 i = 0; i < partition->child_count; i++) {
                partition_data* other = get_child_partition(partition->id, i);
                if (other == NULL)
                        continue;

                if (other->offset < *start && other->offset + other->size > *start)
                        *start = other->offset + other->size;

                if (other->offset > *start && other->offset < *start + *size)
                        *size = other->offset - *start;
        }

        *start = block_align(partition, *start, true);
        *size = block_align(partition, *size, false);

        // TODO: support parameters
        return true;
}


static status_t
efi_gpt_get_partitionable_spaces(partition_data* partition,
        partitionable_space_data* buffer, int32 count, int32* actualCount)
{
        // TODO: implement
        return B_ERROR;
}


static status_t
efi_gpt_get_next_supported_type(partition_data* partition, int32* cookie,
        char* type)
{
        // TODO: implement
        return B_ERROR;
}


static status_t
efi_gpt_shadow_changed(partition_data* partition, partition_data* child,
        uint32 operation)
{
        // TODO: implement
        return B_ERROR;
}


static status_t
efi_gpt_repair(int fd, partition_id partition, bool checkOnly, disk_job_id job)
{
        // TODO: implement, validate CRCs and restore from backup area if corrupt
        return B_ERROR;
}


static status_t
efi_gpt_resize(int fd, partition_id partitionID, off_t size, disk_job_id job)
{
        if (fd < 0)
                return B_ERROR;

        PartitionWriteLocker locker(partitionID);
        if (!locker.IsLocked())
                return B_ERROR;

        partition_data* partition = get_partition(partitionID);
        if (partition == NULL)
                return B_BAD_VALUE;

        off_t validatedSize = size;
        if (!efi_gpt_validate_resize(partition, &validatedSize))
                return B_BAD_VALUE;

        update_disk_device_job_progress(job, 0.0);

        partition->size = validatedSize;
        partition->content_size = validatedSize;

        update_disk_device_job_progress(job, 1.0);
        partition_modified(partitionID);
        return B_OK;
}


static status_t
efi_gpt_resize_child(int fd, partition_id partitionID, off_t size,
        disk_job_id job)
{
        if (fd < 0)
                return B_ERROR;

        PartitionWriteLocker locker(partitionID);
        if (!locker.IsLocked())
                return B_ERROR;

        partition_data* child = get_partition(partitionID);
        if (child == NULL)
                return B_BAD_VALUE;

        partition_data* partition = get_parent_partition(partitionID);
        if (partition == NULL)
                return B_BAD_VALUE;

        EFI::Header* header = (EFI::Header*)partition->content_cookie;
        if (header == NULL)
                return B_BAD_VALUE;

        uint32 entryIndex = (uint32)(addr_t)child->cookie;
        if (entryIndex >= header->EntryCount())
                return B_BAD_VALUE;

        off_t validatedSize = size;
        if (!efi_gpt_validate_resize_child(partition, child, &validatedSize))
                return B_BAD_VALUE;

        if (child->size == validatedSize)
                return B_OK;

        update_disk_device_job_progress(job, 0.0);

        gpt_partition_entry& entry = header->EntryAt(entryIndex);
        entry.SetBlockCount(validatedSize / partition->block_size);

        status_t result = header->WriteEntry(fd, entryIndex);
        if (result != B_OK) {
                entry.SetBlockCount(child->size / partition->block_size);
                return result;
        }

        child->size = validatedSize;

        update_disk_device_job_progress(job, 1.0);
        partition_modified(partitionID);
        return B_OK;
}


static status_t
efi_gpt_move(int fd, partition_id partition, off_t offset, disk_job_id job)
{
        // nothing to do here
        return B_OK;
}


static status_t
efi_gpt_move_child(int fd, partition_id partitionID, partition_id childID,
        off_t offset, disk_job_id job)
{
        if (fd < 0)
                return B_ERROR;

        PartitionWriteLocker locker(partitionID);
        if (!locker.IsLocked())
                return B_ERROR;

        partition_data* partition = get_partition(partitionID);
        if (partition == NULL)
                return B_BAD_VALUE;

        partition_data* child = get_partition(childID);
        if (child == NULL)
                return B_BAD_VALUE;

        EFI::Header* header = (EFI::Header*)partition->content_cookie;
        if (header == NULL)
                return B_BAD_VALUE;

        uint32 entryIndex = (uint32)(addr_t)child->cookie;
        if (entryIndex >= header->EntryCount())
                return B_BAD_VALUE;

        off_t validatedOffset = offset;
        if (!efi_gpt_validate_move_child(partition, child, &validatedOffset))
                return B_BAD_VALUE;

        if (child->offset == validatedOffset)
                return B_OK;

        // TODO: implement actual moving, need to move the partition content
        // (the raw data) here and need to take overlap into account
        return B_ERROR;

        update_disk_device_job_progress(job, 0.0);

        gpt_partition_entry& entry = header->EntryAt(entryIndex);
        uint64 blockCount = entry.BlockCount();
        entry.SetStartBlock((validatedOffset - partition->offset)
                / partition->block_size);
        entry.SetBlockCount(blockCount);

        status_t result = header->WriteEntry(fd, entryIndex);
        if (result != B_OK) {
                // fatal error: the data has been moved but the partition table could
                // not be updated to reflect that change!
                return result;
        }

        child->offset = validatedOffset;

        update_disk_device_job_progress(job, 1.0);
        partition_modified(childID);
        return B_OK;
}


static status_t
efi_gpt_set_name(int fd, partition_id partitionID, const char* name,
        disk_job_id job)
{
        if (fd < 0)
                return B_ERROR;

        PartitionWriteLocker locker(partitionID);
        if (!locker.IsLocked())
                return B_ERROR;

        partition_data* child = get_partition(partitionID);
        if (child == NULL)
                return B_BAD_VALUE;

        partition_data* partition = get_parent_partition(partitionID);
        if (partition == NULL)
                return B_BAD_VALUE;

        EFI::Header* header = (EFI::Header*)partition->content_cookie;
        if (header == NULL)
                return B_BAD_VALUE;

        uint32 entryIndex = (uint32)(addr_t)child->cookie;
        if (entryIndex >= header->EntryCount())
                return B_BAD_VALUE;

        update_disk_device_job_progress(job, 0.0);

        gpt_partition_entry& entry = header->EntryAt(entryIndex);
        to_ucs2(name, strlen(name), entry.name, EFI_PARTITION_NAME_LENGTH);

        status_t result = header->WriteEntry(fd, entryIndex);
        if (result != B_OK)
                return result;

        char newName[B_OS_NAME_LENGTH];
        to_utf8(entry.name, EFI_PARTITION_NAME_LENGTH, newName, sizeof(newName));
        child->name = strdup(newName);

        update_disk_device_job_progress(job, 1.0);
        partition_modified(partitionID);
        return B_OK;
}


static status_t
efi_gpt_set_type(int fd, partition_id partitionID, const char* type,
        disk_job_id job)
{
        if (fd < 0)
                return B_ERROR;

        PartitionWriteLocker locker(partitionID);
        if (!locker.IsLocked())
                return B_ERROR;

        partition_data* child = get_partition(partitionID);
        if (child == NULL)
                return B_BAD_VALUE;

        partition_data* partition = get_parent_partition(partitionID);
        if (partition == NULL)
                return B_BAD_VALUE;

        EFI::Header* header = (EFI::Header*)partition->content_cookie;
        if (header == NULL)
                return B_BAD_VALUE;

        uint32 entryIndex = (uint32)(addr_t)child->cookie;
        if (entryIndex >= header->EntryCount())
                return B_BAD_VALUE;

        guid_t typeGUID;
        if (!get_guid_for_partition_type(type, typeGUID))
                return B_BAD_VALUE;

        update_disk_device_job_progress(job, 0.0);

        gpt_partition_entry& entry = header->EntryAt(entryIndex);
        entry.partition_type = typeGUID;

        status_t result = header->WriteEntry(fd, entryIndex);
        if (result != B_OK)
                return result;

        child->type = strdup(type);

        update_disk_device_job_progress(job, 1.0);
        partition_modified(partitionID);
        return B_OK;
}


static status_t
efi_gpt_initialize(int fd, partition_id partitionID, const char* name,
        const char* parameters, off_t partitionSize, disk_job_id job)
{
        if (fd < 0)
                return B_ERROR;

        partition_data* partition = get_partition(partitionID);
        if (partition == NULL)
                return B_BAD_VALUE;

        update_disk_device_job_progress(job, 0.0);

        EFI::Header header((partitionSize - 1) / partition->block_size,
                partition->block_size);
        status_t result = header.InitCheck();
        if (result != B_OK)
                return result;

        result = header.Write(fd);
        if (result != B_OK)
                return result;

        result = scan_partition(partitionID);
        if (result != B_OK)
                return result;

        update_disk_device_job_progress(job, 1.0);
        partition_modified(partitionID);
        return B_OK;
}


static status_t
efi_gpt_uninitialize(int fd, partition_id partitionID, off_t partitionSize,
        uint32 blockSize, disk_job_id job)
{
        if (fd < 0)
                return B_ERROR;

        partition_data* partition = get_partition(partitionID);
        if (partition == NULL)
                return B_BAD_VALUE;

        update_disk_device_job_progress(job, 0.0);

        const int headerSize = partition->block_size * 3;
        // The first block is the protective MBR
        // The second block is the GPT header
        // The third block is the start of the partition list (it can span more
        // blocks, but that doesn't matter as soon as the header is erased).

        uint8 buffer[headerSize];
        memset(buffer, 0xE5, sizeof(buffer));

        // Erase the first blocks
        if (write_pos(fd, 0, &buffer, headerSize) < 0)
                return errno;

        // Erase the last blocks
        // Only 2 blocks, as there is no protective MBR
        if (write_pos(fd, partitionSize - 2 * partition->block_size,
                        &buffer, 2 * partition->block_size) < 0) {
                return errno;
        }

        update_disk_device_job_progress(job, 1.0);

        return B_OK;
}


static status_t
efi_gpt_create_child(int fd, partition_id partitionID, off_t offset,
        off_t size, const char* type, const char* name, const char* parameters,
        disk_job_id job, partition_id* childID)
{
        if (fd < 0)
                return B_ERROR;

        PartitionWriteLocker locker(partitionID);
        if (!locker.IsLocked())
                return B_ERROR;

        partition_data* partition = get_partition(partitionID);
        if (partition == NULL)
                return B_BAD_VALUE;

        EFI::Header* header = (EFI::Header*)partition->content_cookie;
        if (header == NULL)
                return B_BAD_VALUE;

        off_t validatedOffset = offset;
        off_t validatedSize = size;
        uint32 entryIndex = 0;

        if (!efi_gpt_validate_create_child(partition, &validatedOffset,
                        &validatedSize, type, name, parameters, (int32*)&entryIndex))
                return B_BAD_VALUE;

        guid_t typeGUID;
        if (!get_guid_for_partition_type(type, typeGUID))
                return B_BAD_VALUE;

        update_disk_device_job_progress(job, 0.0);

        partition_data* child = create_child_partition(partition->id, entryIndex,
                validatedOffset, validatedSize, *childID);
        if (child == NULL)
                return B_ERROR;

        gpt_partition_entry& entry = header->EntryAt(entryIndex);
        entry.partition_type = typeGUID;
        uuid_t uuid;
        uuid_generate_random(uuid);
        memcpy((uint8*)&entry.unique_guid, uuid, sizeof(guid_t));
        to_ucs2(name, strlen(name), entry.name, EFI_PARTITION_NAME_LENGTH);
        entry.SetStartBlock((validatedOffset - partition->offset)
                / partition->block_size);
        entry.SetBlockCount(validatedSize / partition->block_size);
        entry.SetAttributes(0); // TODO

        status_t result = header->WriteEntry(fd, entryIndex);
        if (result != B_OK) {
                delete_partition(child->id);
                return result;
        }

        *childID = child->id;
        child->block_size = partition->block_size;
        child->name = strdup(name);
        child->type = strdup(type);
        child->parameters = strdup(parameters);
        child->cookie = (void*)(addr_t)entryIndex;

        if (child->type == NULL || child->parameters == NULL) {
                delete_partition(child->id);
                return B_NO_MEMORY;
        }

        update_disk_device_job_progress(job, 1.0);
        partition_modified(partitionID);
        return B_OK;
}


static status_t
efi_gpt_delete_child(int fd, partition_id partitionID, partition_id childID,
        disk_job_id job)
{
        if (fd < 0)
                return B_ERROR;

        PartitionWriteLocker locker(partitionID);
        if (!locker.IsLocked())
                return B_ERROR;

        partition_data* partition = get_partition(partitionID);
        if (partition == NULL)
                return B_BAD_VALUE;

        partition_data* child = get_partition(childID);
        if (child == NULL)
                return B_BAD_VALUE;

        EFI::Header* header = (EFI::Header*)partition->content_cookie;
        if (header == NULL)
                return B_BAD_VALUE;

        uint32 entryIndex = (uint32)(addr_t)child->cookie;
        if (entryIndex >= header->EntryCount())
                return B_BAD_VALUE;

        update_disk_device_job_progress(job, 0.0);

        if (!delete_partition(childID))
                return B_ERROR;

        gpt_partition_entry& entry = header->EntryAt(entryIndex);
        memset(&entry, 0, sizeof(gpt_partition_entry));
        entry.partition_type = kEmptyGUID;

        status_t result = header->WriteEntry(fd, entryIndex);
        if (result != B_OK)
                return result;

        update_disk_device_job_progress(job, 1.0);
        partition_modified(partitionID);
        return B_OK;
}
#endif // !_BOOT_MODE


#ifndef _BOOT_MODE
static partition_module_info sEFIPartitionModule = {
#else
partition_module_info gEFIPartitionModule = {
#endif
        {
                EFI_PARTITION_MODULE_NAME,
                0,
                efi_gpt_std_ops
        },
        "gpt",                                                                  // short_name
        EFI_PARTITION_NAME,                                             // pretty_name
        0                                                                               // flags
        | B_DISK_SYSTEM_SUPPORTS_INITIALIZING
        | B_DISK_SYSTEM_SUPPORTS_MOVING
        | B_DISK_SYSTEM_SUPPORTS_RESIZING
        | B_DISK_SYSTEM_SUPPORTS_SETTING_TYPE
        | B_DISK_SYSTEM_SUPPORTS_MOVING_CHILD
        | B_DISK_SYSTEM_SUPPORTS_RESIZING_CHILD
        | B_DISK_SYSTEM_SUPPORTS_CREATING_CHILD
        | B_DISK_SYSTEM_SUPPORTS_DELETING_CHILD
        | B_DISK_SYSTEM_SUPPORTS_SETTING_NAME
        | B_DISK_SYSTEM_SUPPORTS_NAME
        ,

        // scanning
        efi_gpt_identify_partition,
        efi_gpt_scan_partition,
        efi_gpt_free_identify_partition_cookie,
        NULL, // free_partition_cookie
        efi_gpt_free_partition_content_cookie,

#ifndef _BOOT_MODE
        // querying
        efi_gpt_get_supported_operations,
        efi_gpt_get_supported_child_operations,
        NULL, // supports_initializing_child
        efi_gpt_is_sub_system_for,

        efi_gpt_validate_resize,
        efi_gpt_validate_resize_child,
        efi_gpt_validate_move,
        efi_gpt_validate_move_child,
        efi_gpt_validate_set_name,
        NULL, // validate_set_content_name
        efi_gpt_validate_set_type,
        NULL, // validate_set_parameters
        NULL, // validate_set_content_parameters
        efi_gpt_validate_initialize,
        efi_gpt_validate_create_child,
        efi_gpt_get_partitionable_spaces,
        efi_gpt_get_next_supported_type,
        NULL, // get_type_for_content_type

        // shadow partition modification
        efi_gpt_shadow_changed,

        // writing
        efi_gpt_repair,
        efi_gpt_resize,
        efi_gpt_resize_child,
        efi_gpt_move,
        efi_gpt_move_child,
        efi_gpt_set_name,
        NULL, // set_content_name
        efi_gpt_set_type,
        NULL, // set_parameters
        NULL, // set_content_parameters
        efi_gpt_initialize,
        efi_gpt_uninitialize,
        efi_gpt_create_child,
        efi_gpt_delete_child
#else
        NULL
#endif // _BOOT_MODE
};

#ifndef _BOOT_MODE
partition_module_info* modules[] = {
        &sEFIPartitionModule,
        NULL
};
#endif