root/src/add-ons/kernel/file_systems/nfs4/FileInfo.cpp
/*
 * Copyright 2012 Haiku, Inc. All rights reserved.
 * Distributed under the terms of the MIT License.
 *
 * Authors:
 *              Paweł Dziepak, pdziepak@quarnos.org
 */


#include "FileInfo.h"

#include "FileSystem.h"
#include "Request.h"


/*! Print the handle.
        @pre The parent object is locked.
*/
void
FileHandle::Dump(void (*xprintf)(const char*, ...)) const
{
        for (int i = 0; i < fSize; ++i)
                xprintf("%d ", fData[i]);
        xprintf("\n");
}


InodeName::InodeName(InodeNames* parent, const char* name)
        :
        fParent(parent),
        fName(strdup(name))
{
        if (fParent != NULL)
                fParent->AcquireReference();
}


InodeName::~InodeName()
{
        if (fParent != NULL)
                fParent->ReleaseReference();
        free(const_cast<char*>(fName));
}


InodeNames::InodeNames()
{
        mutex_init(&fLock, NULL);
}


InodeNames::~InodeNames()
{
        while (!fNames.IsEmpty())
                delete fNames.RemoveHead();
        mutex_destroy(&fLock);
}


status_t
InodeNames::AddName(InodeNames* parent, const char* name)
{
        MutexLocker _(fLock);

        InodeName* current = fNames.Head();
        while (current != NULL) {
                if (current->fParent == parent && !strcmp(current->fName, name))
                        return B_OK;
                current = fNames.GetNext(current);
        }

        InodeName* newName = new InodeName(parent, name);
        if (newName == NULL)
                return B_NO_MEMORY;
        fNames.Add(newName);
        return B_OK;
}


bool
InodeNames::RemoveName(InodeNames* parent, const char* name)
{
        MutexLocker _(fLock);

        InodeName* previous = NULL;
        InodeName* current = fNames.Head();
        while (current != NULL) {
                if (current->fParent == parent && !strcmp(current->fName, name)) {
                        fNames.Remove(previous, current);
                        delete current;
                        break;
                }

                previous = current;
                current = fNames.GetNext(current);
        }

        return fNames.IsEmpty();
}


void
InodeNames::Dump(void (*xprintf)(const char*, ...))
{
        MutexLocker locker;
        if (xprintf != kprintf)
                locker.SetTo(fLock, false);

        _DumpLocked(xprintf);

        return;
}


void
InodeNames::_DumpLocked(void (*xprintf)(const char*, ...)) const
{
        xprintf("InodeNames ");
        for (SinglyLinkedList<InodeName>::ConstIterator it = fNames.GetIterator();
                const InodeName* name = it.Next();) {
                if (name->fName != NULL)
                        xprintf("%s ", name->fName);
        }
        xprintf("\n");

        return;
}


FileInfo::FileInfo()
        :
        fFileId(0),
        fNames(NULL)
{
}


FileInfo::~FileInfo()
{
        if (fNames != NULL)
                fNames->ReleaseReference();
}


FileInfo::FileInfo(const FileInfo& fi)
        :
        fFileId(fi.fFileId),
        fHandle(fi.fHandle),
        fNames(fi.fNames)
{
        if (fNames != NULL)
                fNames->AcquireReference();
}


FileInfo&
FileInfo::operator=(const FileInfo& fi)
{
        fFileId = fi.fFileId;
        fHandle = fi.fHandle;

        if (fNames != NULL)
                fNames->ReleaseReference();
        fNames = fi.fNames;
        if (fNames != NULL)
                fNames->AcquireReference();

        return *this;
}


status_t
FileInfo::UpdateFileHandles(FileSystem* fs)
{
        ASSERT(fs != NULL);

        Request request(fs->Server(), fs, geteuid(), getegid());
        RequestBuilder& req = request.Builder();

        req.PutRootFH();

        uint32 lookupCount = 0;
        const char** path = fs->Path();
        if (path != NULL) {
                for (; path[lookupCount] != NULL; lookupCount++)
                        req.LookUp(path[lookupCount]);
        }

        uint32 i;
        InodeNames* names = fNames;
        for (i = 0; names != NULL; i++) {
                if (names->fNames.IsEmpty())
                        return B_ENTRY_NOT_FOUND;

                names = names->fNames.Head()->fParent;
        }

        if (i > 0) {
                names = fNames;
                InodeNames** pathNames = new InodeNames*[i];
                if (pathNames == NULL)
                        return B_NO_MEMORY;

                for (i = 0; names != NULL; i++) {
                        pathNames[i] = names;
                        names = names->fNames.Head()->fParent;
                }

                for (; i > 0; i--) {
                        if (!strcmp(pathNames[i - 1]->fNames.Head()->fName, ""))
                                continue;

                        req.LookUp(pathNames[i - 1]->fNames.Head()->fName);
                        lookupCount++;
                }
                delete[] pathNames;
        }

        req.GetFH();

        if (fs->IsAttrSupported(FATTR4_FILEID)) {
                AttrValue attr;
                attr.fAttribute = FATTR4_FILEID;
                attr.fFreePointer = false;
                attr.fData.fValue64 = fFileId;
                req.Verify(&attr, 1);
        }

        status_t result = request.Send();
        if (result != B_OK)
                return result;

        ReplyInterpreter& reply = request.Reply();

        reply.PutRootFH();
        for (uint32 i = 0; i < lookupCount; i++)
                reply.LookUp();

        FileHandle handle;
        result = reply.GetFH(&handle);
        if (result != B_OK)
                return result;

        if (fs->IsAttrSupported(FATTR4_FILEID)) {
                result = reply.Verify();
                if (result != B_OK)
                        return result;
        }

        fHandle = handle;
        fNames->fHandle = handle;

        return B_OK;
}