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


#include "MetadataCache.h"

#include <NodeMonitor.h>

#include "Inode.h"


MetadataCache::MetadataCache(Inode* inode)
        :
        fExpire(0),
        fForceValid(false),
        fInode(inode),
        fInited(false)
{
        ASSERT(inode != NULL);
        mutex_init(&fLock, "nfs4 MetadataCache");
}


MetadataCache::~MetadataCache()
{
        mutex_destroy(&fLock);
}


status_t
MetadataCache::GetStat(struct stat* st)
{
        ASSERT(st != NULL);

        MutexLocker _(fLock);
        if (fForceValid || fExpire > time(NULL)) {
                // Do not touch other members of struct stat
                st->st_size = fStatCache.st_size;
                st->st_mode = fStatCache.st_mode;
                st->st_nlink = fStatCache.st_nlink;
                st->st_uid = fStatCache.st_uid;
                st->st_gid = fStatCache.st_gid;
                st->st_atim = fStatCache.st_atim;
                st->st_ctim = fStatCache.st_ctim;
                st->st_crtim = fStatCache.st_crtim;
                st->st_mtim = fStatCache.st_mtim;
                st->st_blksize = fStatCache.st_blksize;
                st->st_blocks = fStatCache.st_blocks;
                return B_OK;
        }

        return B_ERROR;
}


void
MetadataCache::SetStat(const struct stat& st)
{
        MutexLocker _(fLock);
        if (fInited)
                NotifyChanges(&fStatCache, &st);

        fStatCache = st;
        fExpire = time(NULL) + kExpirationTime;
        fInited = true;
}


void
MetadataCache::GrowFile(size_t newSize)
{
        MutexLocker _(fLock);
        off_t oldSize = fStatCache.st_size;
        fStatCache.st_size = max_c((off_t)newSize, fStatCache.st_size);

        if (oldSize != fStatCache.st_size) {
                notify_stat_changed(fInode->GetFileSystem()->DevId(), -1, fInode->ID(),
                        B_STAT_SIZE);
        }
}


status_t
MetadataCache::GetAccess(uid_t uid, uint32* allowed)
{
        ASSERT(allowed != NULL);

        MutexLocker _(fLock);
        AVLTreeMap<uid_t, AccessEntry>::Iterator it = fAccessCache.Find(uid);
        if (!it.HasCurrent())
                return B_ENTRY_NOT_FOUND;

        if (!fForceValid)
                it.CurrentValuePointer()->fForceValid = false;

        if (!it.Current().fForceValid && it.Current().fExpire < time(NULL)) {
                it.Remove();
                return B_ERROR;
        }

        *allowed = it.Current().fAllowed;

        return B_OK;
}


void
MetadataCache::SetAccess(uid_t uid, uint32 allowed)
{
        MutexLocker _(fLock);
        AVLTreeMap<uid_t, AccessEntry>::Iterator it = fAccessCache.Find(uid);
        if (it.HasCurrent())
                it.Remove();

        AccessEntry entry;
        entry.fAllowed = allowed;
        entry.fExpire = time(NULL) + kExpirationTime;
        entry.fForceValid = fForceValid;

        fAccessCache.Insert(uid, entry);
}


status_t
MetadataCache::LockValid()
{
        MutexLocker _(fLock);
        if (fForceValid || fExpire > time(NULL)) {
                fForceValid = true;
                return B_OK;
        }

        return B_ERROR;
}


void
MetadataCache::UnlockValid()
{
        MutexLocker _(fLock);
        fExpire = time(NULL) + kExpirationTime;
        fForceValid = false;
}


void
MetadataCache::Dump(void (*xprintf)(const char*, ...))
{
        if (xprintf != kprintf) {
                status_t status = mutex_trylock(&fLock);
                if (status != B_OK) {
                        xprintf("MetadataCache at %p locked\n", this);
                        return;
                }
        }

        _DumpLocked(xprintf);

        if (xprintf != kprintf)
                mutex_unlock(&fLock);

        return;
}


void
MetadataCache::NotifyChanges(const struct stat* oldStat,
        const struct stat* newStat)
{
        ASSERT(oldStat != NULL);
        ASSERT(newStat != NULL);

        uint32 flags = 0;
        if (oldStat->st_size != newStat->st_size)
                flags |= B_STAT_SIZE;
        if (oldStat->st_mode != newStat->st_mode)
                flags |= B_STAT_MODE;
        if (oldStat->st_uid != newStat->st_uid)
                flags |= B_STAT_UID;
        if (oldStat->st_gid != newStat->st_gid)
                flags |= B_STAT_GID;

        if (memcmp(&oldStat->st_atim, &newStat->st_atim,
                sizeof(struct timespec)) == 0)
                flags |= B_STAT_ACCESS_TIME;

        if (memcmp(&oldStat->st_ctim, &newStat->st_ctim,
                sizeof(struct timespec)) == 0)
                flags |= B_STAT_CHANGE_TIME;

        if (memcmp(&oldStat->st_crtim, &newStat->st_crtim,
                sizeof(struct timespec)) == 0)
                flags |= B_STAT_CREATION_TIME;

        if (memcmp(&oldStat->st_mtim, &newStat->st_mtim,
                sizeof(struct timespec)) == 0)
                flags |= B_STAT_MODIFICATION_TIME;

        notify_stat_changed(fInode->GetFileSystem()->DevId(), -1, fInode->ID(),
                flags);
}


void
MetadataCache::_DumpLocked(void (*xprintf)(const char*, ...)) const
{
        xprintf("MetadataCache at %p for Inode at %p\n", this, fInode);
        xprintf("\tfInited %d, fForceValid %d\n", fInited, fForceValid);
        if (time(NULL) < fExpire)
                xprintf("\tExpires at %" B_PRIdTIME "\n", fExpire);
        else
                xprintf("\tExpired\n");

        xprintf("\tst_mode %" B_PRIo32 "\n", fStatCache.st_mode);
        xprintf("\tst_nlink %" B_PRId32 "\n", fStatCache.st_nlink);
        xprintf("\tst_uid %" B_PRIu32 "\n", fStatCache.st_uid);
        xprintf("\tst_gid %" B_PRIu32 "\n", fStatCache.st_gid);
        xprintf("\tst_size %" B_PRIdOFF "\n", fStatCache.st_size);

        return;
}