/*
 * 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, NULL);
}
 
 
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::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);
}
 

V730 Not all members of a class are initialized inside the constructor. Consider inspecting: fStatCache.