root/src/add-ons/disk_systems/gpt/GPTPartitionHandle.cpp
/*
 * Copyright 2013, Axel Dörfler, axeld@pinc-software.de.
 * Copyright 2007, Ingo Weinhold, bonefish@users.sf.net.
 * Distributed under the terms of the MIT License.
 */


#include "GPTPartitionHandle.h"

#include <new>
#include <stdio.h>

#include <DiskDeviceTypes.h>
#include <MutablePartition.h>
#include <PartitioningInfo.h>
#include <PartitionParameterEditor.h>
#include <Path.h>
#include <SupportDefs.h>

#include <AutoDeleter.h>

#include "guid.h"
#include "gpt_known_guids.h"
#include "utility.h"


//#define TRACE_GPT_PARTITION_HANDLE
#undef TRACE
#ifdef TRACE_GPT_PARTITION_HANDLE
#       define TRACE(x...) printf(x)
#else
#       define TRACE(x...) do {} while (false)
#endif


GPTPartitionHandle::GPTPartitionHandle(BMutablePartition* partition)
        :
        BPartitionHandle(partition)
{
}


GPTPartitionHandle::~GPTPartitionHandle()
{
}


status_t
GPTPartitionHandle::Init()
{
        // TODO: how to get the path of a BMutablePartition?
        //BPath path;
        //status_t status = Partition()->GetPath(&path);
        //if (status != B_OK)
                //return status;

        //fd = open(path.Path(), O_RDONLY);
        //if (fd < 0)
                //return errno;

        //fHeader = new EFI::Header(fd, Partition()->BlockSize(),
                //Partition()->BlockSize());
        //status = fHeader->InitCheck();
        //if (status != B_OK)
                //return status;

        //close(fd);
        return B_OK;
}


uint32
GPTPartitionHandle::SupportedOperations(uint32 mask)
{
        uint32 flags = B_DISK_SYSTEM_SUPPORTS_RESIZING
                | B_DISK_SYSTEM_SUPPORTS_MOVING
                | B_DISK_SYSTEM_SUPPORTS_SETTING_CONTENT_PARAMETERS
                | B_DISK_SYSTEM_SUPPORTS_NAME
                | B_DISK_SYSTEM_SUPPORTS_SETTING_NAME
                | B_DISK_SYSTEM_SUPPORTS_INITIALIZING;

        // creating child
        if ((mask & B_DISK_SYSTEM_SUPPORTS_CREATING_CHILD) != 0) {
                BPartitioningInfo info;
                if (GetPartitioningInfo(&info) == B_OK
                        && info.CountPartitionableSpaces() > 1) {
                        flags |= B_DISK_SYSTEM_SUPPORTS_CREATING_CHILD;
                }
        }

        return flags;
}


uint32
GPTPartitionHandle::SupportedChildOperations(const BMutablePartition* child,
        uint32 mask)
{
        return B_DISK_SYSTEM_SUPPORTS_NAME
                | B_DISK_SYSTEM_SUPPORTS_SETTING_NAME
                | B_DISK_SYSTEM_SUPPORTS_RESIZING_CHILD
                | B_DISK_SYSTEM_SUPPORTS_MOVING_CHILD
                | B_DISK_SYSTEM_SUPPORTS_SETTING_TYPE
                | B_DISK_SYSTEM_SUPPORTS_SETTING_PARAMETERS
                | B_DISK_SYSTEM_SUPPORTS_DELETING_CHILD;
}


status_t
GPTPartitionHandle::GetNextSupportedType(const BMutablePartition* child,
        int32* cookie, BString* type)
{
        int32 index = *cookie;
        TRACE("GPTPartitionHandle::GetNextSupportedType(child: %p, cookie: %" B_PRId32 ")\n",
                child, index);

        if (index >= int32(B_COUNT_OF(kTypeMap)))
                return B_ENTRY_NOT_FOUND;

        type->SetTo(kTypeMap[index].type);
        *cookie = index + 1;

        return B_OK;
}


status_t
GPTPartitionHandle::GetPartitioningInfo(BPartitioningInfo* info)
{
        // init to the full size (minus the GPT table header and entries)
        off_t size = Partition()->ContentSize();
        // TODO: use fHeader
        size_t headerSize = Partition()->BlockSize() + 16384;
        status_t status = info->SetTo(Partition()->BlockSize() + headerSize,
                size - Partition()->BlockSize() - 2 * headerSize);
        if (status != B_OK)
                return status;

        // Exclude the space of the existing partitions
        size_t count = Partition()->CountChildren();
        for (size_t index = 0; index < count; index++) {
                BMutablePartition* child = Partition()->ChildAt(index);
                status = info->ExcludeOccupiedSpace(child->Offset(), child->Size());
                if (status != B_OK)
                        return status;
        }

        return B_OK;
}


status_t
GPTPartitionHandle::GetParameterEditor(B_PARAMETER_EDITOR_TYPE type,
        BPartitionParameterEditor** editor)
{
        *editor = NULL;
        if (type == B_CREATE_PARAMETER_EDITOR || type == B_PROPERTIES_PARAMETER_EDITOR) {
                try {
                        *editor = new BPartitionParameterEditor();
                } catch (std::bad_alloc&) {
                        return B_NO_MEMORY;
                }
                return B_OK;
        }
        return B_NOT_SUPPORTED;
}


status_t
GPTPartitionHandle::ValidateSetName(const BMutablePartition *child, BString* name)
{
        // UCS-2 can use up to 2 code points per character, and GPT allows
        // a maximum of 36 code units;
        size_t length = name->CountChars();
        if (length == 0)
                return B_OK;

        size_t size = length * 2;
        uint16 buffer[size + 1];

        do {
                size = to_ucs2(name->String(), length, buffer, length * 2);
                if (size <= 36)
                        return B_OK;
                length--;
                name->TruncateChars(length, false);
        } while (size > 36 && length > 0);

        return B_OK;
}


status_t
GPTPartitionHandle::SetName(BMutablePartition* child, const char* name)
{
        return child->SetName(name);
}


status_t
GPTPartitionHandle::ValidateSetType(const BMutablePartition* child,
        const char* type)
{
        for (size_t i = 0; i < B_COUNT_OF(kTypeMap); i++) {
                if (strcmp(type, kTypeMap[i].type) == 0)
                        return B_OK;
        }
        return B_BAD_VALUE;
}


status_t
GPTPartitionHandle::SetType(BMutablePartition* child, const char* type)
{
        return child->SetType(type);
}


status_t
GPTPartitionHandle::ValidateCreateChild(off_t* _offset, off_t* _size,
        const char* typeString, BString* name, const char* parameters)
{
        return B_OK;
}


status_t
GPTPartitionHandle::CreateChild(off_t offset, off_t size,
        const char* typeString, const char* name, const char* parameters,
        BMutablePartition** _child)
{
        // create the child
        BMutablePartition* partition = Partition();
        BMutablePartition* child;
        status_t status = partition->CreateChild(partition->CountChildren(),
                typeString, name, parameters, &child);
        if (status != B_OK)
                return status;

        // init the child
        child->SetOffset(offset);
        child->SetSize(size);
        child->SetBlockSize(partition->BlockSize());

        *_child = child;
        return B_OK;
}


status_t
GPTPartitionHandle::DeleteChild(BMutablePartition* child)
{
        BMutablePartition* parent = child->Parent();
        return parent->DeleteChild(child);
}