root/src/add-ons/kernel/file_systems/packagefs/nodes/UnpackingLeafNode.cpp
/*
 * Copyright 2009-2013, Ingo Weinhold, ingo_weinhold@gmx.de.
 * Distributed under the terms of the MIT License.
 */


#include "UnpackingLeafNode.h"

#include <string.h>

#include <algorithm>
#include <new>

#include "ClassCache.h"
#include "UnpackingAttributeCookie.h"
#include "UnpackingAttributeDirectoryCookie.h"
#include "Utils.h"


CLASS_CACHE(UnpackingLeafNode);


UnpackingLeafNode::UnpackingLeafNode(ino_t id)
        :
        Node(id),
        fFinalPackageNode(NULL)
{
}


UnpackingLeafNode::~UnpackingLeafNode()
{
        if (fFinalPackageNode != NULL)
                fFinalPackageNode->ReleaseReference();
}


status_t
UnpackingLeafNode::VFSInit(dev_t deviceID)
{
        status_t error = NodeInitVFS(deviceID, fID, _ActivePackageNode());
        if (error == B_OK)
                Node::VFSInit(deviceID);

        return error;
}


void
UnpackingLeafNode::VFSUninit()
{
        NodeUninitVFS(_ActivePackageNode(), fFlags);
        Node::VFSUninit();
}


mode_t
UnpackingLeafNode::Mode() const
{
        if (PackageLeafNode* packageNode = _ActivePackageNode())
                return packageNode->Mode();
        return S_IFREG | S_IRUSR | S_IRGRP | S_IROTH;
}


uid_t
UnpackingLeafNode::UserID() const
{
        if (PackageLeafNode* packageNode = _ActivePackageNode())
                return packageNode->UserID();
        return 0;
}


gid_t
UnpackingLeafNode::GroupID() const
{
        if (PackageLeafNode* packageNode = _ActivePackageNode())
                return packageNode->GroupID();
        return 0;
}


timespec
UnpackingLeafNode::ModifiedTime() const
{
        if (PackageLeafNode* packageNode = _ActivePackageNode())
                return packageNode->ModifiedTime();

        timespec time = { 0, 0 };
        return time;
}


off_t
UnpackingLeafNode::FileSize() const
{
        if (PackageLeafNode* packageNode = _ActivePackageNode()) {
                if (S_ISLNK(packageNode->Mode()))
                        return strlen(packageNode->SymlinkPath());
                return packageNode->FileSize();
        }
        return 0;
}


Node*
UnpackingLeafNode::GetNode()
{
        return this;
}


status_t
UnpackingLeafNode::AddPackageNode(PackageNode* packageNode, dev_t deviceID)
{
        ASSERT(fFinalPackageNode == NULL);

        if (S_ISDIR(packageNode->Mode()))
                return B_IS_A_DIRECTORY;

        PackageLeafNode* packageLeafNode
                = dynamic_cast<PackageLeafNode*>(packageNode);

        PackageLeafNode* headNode = fPackageNodes.Head();
        bool overridesHead = headNode == NULL
                || packageLeafNode->HasPrecedenceOver(headNode);

        if (overridesHead) {
                fPackageNodes.Add(packageLeafNode);
                NodeReinitVFS(deviceID, fID, packageLeafNode, headNode, fFlags);
        } else {
                // add after the head
                fPackageNodes.RemoveHead();
                fPackageNodes.Add(packageLeafNode);
                fPackageNodes.Add(headNode);
        }

        return B_OK;
}


void
UnpackingLeafNode::RemovePackageNode(PackageNode* packageNode, dev_t deviceID)
{
        ASSERT(fFinalPackageNode == NULL);

        bool isNewest = packageNode == fPackageNodes.Head();
        fPackageNodes.Remove(dynamic_cast<PackageLeafNode*>(packageNode));

        // when removing the newest node, we need to find the next node (the list
        // is not sorted)
        PackageLeafNode* newestNode = fPackageNodes.Head();
        if (isNewest && newestNode != NULL) {
                PackageLeafNodeList::ConstIterator it = fPackageNodes.GetIterator();
                it.Next();
                        // skip the first one
                while (PackageLeafNode* otherNode = it.Next()) {
                        if (otherNode->HasPrecedenceOver(newestNode))
                                newestNode = otherNode;
                }

                // re-add the newest node to the head
                fPackageNodes.Remove(newestNode);
                fPackageNodes.Add(newestNode);
                NodeReinitVFS(deviceID, fID, newestNode, packageNode, fFlags);
        }
}


PackageNode*
UnpackingLeafNode::GetPackageNode()
{
        return _ActivePackageNode();
}


bool
UnpackingLeafNode::IsOnlyPackageNode(PackageNode* node) const
{
        ASSERT(fFinalPackageNode == NULL);

        PackageLeafNode* head = fPackageNodes.Head();
        return node == head && fPackageNodes.GetNext(head) == NULL;
}


bool
UnpackingLeafNode::WillBeFirstPackageNode(PackageNode* packageNode) const
{
        PackageLeafNode* packageLeafNode
                = dynamic_cast<PackageLeafNode*>(packageNode);
        if (packageLeafNode == NULL)
                return false;

        PackageLeafNode* headNode = fPackageNodes.Head();
        return headNode == NULL
                || packageLeafNode->HasPrecedenceOver(headNode);
}

void
UnpackingLeafNode::PrepareForRemoval()
{
        ASSERT(fFinalPackageNode == NULL);

        fFinalPackageNode = fPackageNodes.Head();
        if (fFinalPackageNode != NULL) {
                fFinalPackageNode->AcquireReference();
                fPackageNodes.MakeEmpty();
        }
}


status_t
UnpackingLeafNode::CloneTransferPackageNodes(ino_t id, UnpackingNode*& _newNode)
{
        ASSERT(fFinalPackageNode == NULL);

        UnpackingLeafNode* clone = new UnpackingLeafNode(id);
        if (clone == NULL)
                return B_NO_MEMORY;

        status_t error = clone->Init(Name());
        if (error != B_OK) {
                delete clone;
                return error;
        }

        // We keep the old head as fFinalPackageNode, which will make us to behave
        // exactly as before with respect to FS operations.
        fFinalPackageNode = fPackageNodes.Head();
        if (fFinalPackageNode != NULL) {
                fFinalPackageNode->AcquireReference();
                clone->fPackageNodes.TakeFrom(&fPackageNodes);
        }

        _newNode = clone;
        return B_OK;
}


status_t
UnpackingLeafNode::Read(off_t offset, void* buffer, size_t* bufferSize)
{
        if (HasVFSInitError())
                return B_ERROR;

        if (PackageLeafNode* packageNode = _ActivePackageNode())
                return packageNode->Read(offset, buffer, bufferSize);
        return B_ERROR;
}


status_t
UnpackingLeafNode::Read(io_request* request)
{
        if (HasVFSInitError())
                return B_ERROR;

        if (PackageLeafNode* packageNode = _ActivePackageNode())
                return packageNode->Read(request);
        return EBADF;
}


status_t
UnpackingLeafNode::ReadSymlink(void* buffer, size_t* bufferSize)
{
        if (HasVFSInitError())
                return B_ERROR;

        PackageLeafNode* packageNode = _ActivePackageNode();
        if (packageNode == NULL)
                return B_BAD_VALUE;

        const String& linkPath = packageNode->SymlinkPath();
        if (linkPath[0] == '\0') {
                *bufferSize = 0;
                return B_OK;
        }

        size_t linkLength = strnlen(linkPath, B_PATH_NAME_LENGTH);

        size_t bytesToCopy = std::min(linkLength, *bufferSize);

        *bufferSize = linkLength;

        memcpy(buffer, linkPath, bytesToCopy);
        return B_OK;
}


status_t
UnpackingLeafNode::OpenAttributeDirectory(AttributeDirectoryCookie*& _cookie)
{
        if (HasVFSInitError())
                return B_ERROR;

        return UnpackingAttributeDirectoryCookie::Open(_ActivePackageNode(),
                _cookie);
}


status_t
UnpackingLeafNode::OpenAttribute(const StringKey& name, int openMode,
        AttributeCookie*& _cookie)
{
        if (HasVFSInitError())
                return B_ERROR;

        return UnpackingAttributeCookie::Open(_ActivePackageNode(), name, openMode,
                _cookie);
}


status_t
UnpackingLeafNode::IndexAttribute(AttributeIndexer* indexer)
{
        return UnpackingAttributeCookie::IndexAttribute(_ActivePackageNode(),
                indexer);
}


void*
UnpackingLeafNode::IndexCookieForAttribute(const StringKey& name) const
{
        if (PackageLeafNode* packageNode = _ActivePackageNode())
                return packageNode->IndexCookieForAttribute(name);
        return NULL;
}


PackageLeafNode*
UnpackingLeafNode::_ActivePackageNode() const
{
        if (PackageLeafNode* packageNode = fPackageNodes.Head())
                return packageNode;
        return fFinalPackageNode;
}