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


#include "RootInode.h"

#include <string.h>

#include "MetadataCache.h"
#include "Request.h"


RootInode::RootInode()
        :
        fInfoCacheExpire(0),
        fName(NULL),
        fIOSize(0)
{
        mutex_init(&fInfoCacheLock, NULL);
}


RootInode::~RootInode()
{
        free(const_cast<char*>(fName));
        mutex_destroy(&fInfoCacheLock);
}


status_t
RootInode::ReadInfo(struct fs_info* info)
{
        ASSERT(info != NULL);

        status_t result = _UpdateInfo();
        if (result != B_OK)
                return result;

        memcpy(info, &fInfoCache, sizeof(struct fs_info));

        return B_OK;
}


status_t
RootInode::_UpdateInfo(bool force)
{
        if (!force && fInfoCacheExpire > time(NULL))
                return B_OK;

        MutexLocker _(fInfoCacheLock);

        if (fInfoCacheExpire > time(NULL))
                return B_OK;

        uint32 attempt = 0;
        do {
                RPC::Server* server = fFileSystem->Server();
                Request request(server, fFileSystem, geteuid(), getegid());
                RequestBuilder& req = request.Builder();

                req.PutFH(fInfo.fHandle);
                Attribute attr[] = { FATTR4_FILES_FREE, FATTR4_FILES_TOTAL,
                        FATTR4_MAXREAD, FATTR4_MAXWRITE, FATTR4_SPACE_FREE,
                        FATTR4_SPACE_TOTAL };
                req.GetAttr(attr, sizeof(attr) / sizeof(Attribute));

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

                ReplyInterpreter& reply = request.Reply();

                if (HandleErrors(attempt, reply.NFS4Error(), server))
                        continue;

                reply.PutFH();

                AttrValue* values;
                uint32 count, next = 0;
                result = reply.GetAttr(&values, &count);
                if (result != B_OK)
                        return result;

                if (count >= next && values[next].fAttribute == FATTR4_FILES_FREE) {
                        fInfoCache.free_nodes = values[next].fData.fValue64;
                        next++;
                }

                if (count >= next && values[next].fAttribute == FATTR4_FILES_TOTAL) {
                        fInfoCache.total_nodes = values[next].fData.fValue64;
                        next++;
                }

                uint64 ioSize = LONGLONG_MAX;
                if (count >= next && values[next].fAttribute == FATTR4_MAXREAD) {
                        ioSize = min_c(ioSize, values[next].fData.fValue64);
                        next++;
                }

                if (count >= next && values[next].fAttribute == FATTR4_MAXWRITE) {
                        ioSize = min_c(ioSize, values[next].fData.fValue64);
                        next++;
                }

                if (ioSize == LONGLONG_MAX)
                        ioSize = 32768;
                if (ioSize == 0)
                        ioSize = 4096;
                fInfoCache.io_size = ioSize;
                fInfoCache.block_size = ioSize;
                fIOSize = ioSize;

                if (count >= next && values[next].fAttribute == FATTR4_SPACE_FREE) {
                        fInfoCache.free_blocks = values[next].fData.fValue64 / ioSize;
                        next++;
                }

                if (count >= next && values[next].fAttribute == FATTR4_SPACE_TOTAL) {
                        fInfoCache.total_blocks = values[next].fData.fValue64 / ioSize;
                        next++;
                }

                delete[] values;

                break;
        } while (true);

        fInfoCache.flags =      B_FS_IS_PERSISTENT | B_FS_IS_SHARED
                | B_FS_SUPPORTS_NODE_MONITORING;

        if (fFileSystem->NamedAttrs()
                || fFileSystem->GetConfiguration().fEmulateNamedAttrs)
                fInfoCache.flags |= B_FS_HAS_MIME | B_FS_HAS_ATTR;

        strlcpy(fInfoCache.volume_name, fName, sizeof(fInfoCache.volume_name));

        fInfoCacheExpire = time(NULL) + MetadataCache::kExpirationTime;

        return B_OK;
}


bool
RootInode::ProbeMigration()
{
        uint32 attempt = 0;
        do {
                RPC::Server* server = fFileSystem->Server();
                Request request(server, fFileSystem, geteuid(), getegid());
                RequestBuilder& req = request.Builder();

                req.PutFH(fInfo.fHandle);
                req.Access();

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

                ReplyInterpreter& reply = request.Reply();

                if (reply.NFS4Error() == NFS4ERR_MOVED)
                        return true;

                if (HandleErrors(attempt, reply.NFS4Error(), server))
                        continue;

                return false;
        } while (true);
}


status_t
RootInode::GetLocations(AttrValue** attrv)
{
        ASSERT(attrv != NULL);

        uint32 attempt = 0;
        do {
                RPC::Server* server = fFileSystem->Server();
                Request request(server, fFileSystem, geteuid(), getegid());
                RequestBuilder& req = request.Builder();

                req.PutFH(fInfo.fHandle);
                Attribute attr[] = { FATTR4_FS_LOCATIONS };
                req.GetAttr(attr, sizeof(attr) / sizeof(Attribute));

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

                ReplyInterpreter& reply = request.Reply();

                if (HandleErrors(attempt, reply.NFS4Error(), server))
                        continue;

                reply.PutFH();

                uint32 count;
                result = reply.GetAttr(attrv, &count);
                if (result != B_OK)
                        return result;
                if (count < 1) {
                        delete[] *attrv;
                        return B_ERROR;
                }

                return B_OK;
        } while (true);

        return B_OK;
}


const char*
RootInode::Name() const
{
        ASSERT(fName != NULL);
        return fName;
}