/*
 * Copyright 2012-2016 Haiku, Inc. All rights reserved.
 * Distributed under the terms of the MIT License.
 *
 * Authors:
 *		Paweł Dziepak, pdziepak@quarnos.org
 */
 
 
#include "DirectoryCache.h"
 
#include <fs_cache.h>
#include <NodeMonitor.h>
 
#include "Inode.h"
 
 
 
NameCacheEntry::NameCacheEntry(const char* name, ino_t node)
	:
	fNode(node),
	fName(strdup(name))
{
	ASSERT(name != NULL);
}
 
 
NameCacheEntry::NameCacheEntry(const NameCacheEntry& entry)
	:
	fNode(entry.fNode),
	fName(strdup(entry.fName))
{
}
 
 
NameCacheEntry::~NameCacheEntry()
{
	free(const_cast<char*>(fName));
}
 
 
DirectoryCacheSnapshot::DirectoryCacheSnapshot()
{
	mutex_init(&fLock, NULL);
}
 
 
DirectoryCacheSnapshot::DirectoryCacheSnapshot(
	const DirectoryCacheSnapshot& snapshot)
{
	mutex_init(&fLock, NULL);
 
	MutexLocker _(snapshot.fLock);
	NameCacheEntry* entry = snapshot.fEntries.Head();
	NameCacheEntry* new_entry;
	while (entry) {
		new_entry = new NameCacheEntry(*entry);
		if (new_entry == NULL)
			break;
 
		fEntries.Add(new_entry);
 
		entry = snapshot.fEntries.GetNext(entry);
	}
}
 
 
DirectoryCacheSnapshot::~DirectoryCacheSnapshot()
{
	while (!fEntries.IsEmpty()) {
		NameCacheEntry* current = fEntries.RemoveHead();
		delete current;
	}
 
	mutex_destroy(&fLock);
}
 
 
DirectoryCache::DirectoryCache(Inode* inode, bool attr)
	:
	fExpirationTime(inode->fFileSystem->GetConfiguration().fDirectoryCacheTime),
	fDirectoryCache(NULL),
	fInode(inode),
	fAttrDir(attr),
	fTrashed(true)
{
	ASSERT(inode != NULL);
 
	mutex_init(&fLock, NULL);
}
 
 
DirectoryCache::~DirectoryCache()
{
	mutex_destroy(&fLock);
}
 
 
void
DirectoryCache::Reset()
{
	Trash();
	fExpireTime = system_time() + fExpirationTime;
	fTrashed = false;
}
 
 
void
DirectoryCache::Trash()
{
	while (!fNameCache.IsEmpty()) {
		NameCacheEntry* current = fNameCache.RemoveHead();
		entry_cache_remove(fInode->GetFileSystem()->DevId(), fInode->ID(),
			current->fName);
		delete current;
	}
 
	_SetSnapshot(NULL);
 
	fTrashed = true;
}
 
 
status_t
DirectoryCache::AddEntry(const char* name, ino_t node, bool created)
{
	ASSERT(name != NULL);
 
	NameCacheEntry* entry = new(std::nothrow) NameCacheEntry(name, node);
	if (entry == NULL)
		return B_NO_MEMORY;
	if (entry->fName == NULL) {
		delete entry;
		return B_NO_MEMORY;
	}
 
	fNameCache.Add(entry);
 
	if (created && fDirectoryCache != NULL) {
		MutexLocker _(fDirectoryCache->fLock);
		NameCacheEntry* entry = new(std::nothrow) NameCacheEntry(name, node);
		if (entry == NULL)
			return B_NO_MEMORY;
		if (entry->fName == NULL) {
			delete entry;
			return B_NO_MEMORY;
		}
 
		fDirectoryCache->fEntries.Add(entry);
	}
 
	if (!fAttrDir) {
		return entry_cache_add(fInode->GetFileSystem()->DevId(), fInode->ID(),
			name, node);
	}
 
	return B_OK;
}
 
 
void
DirectoryCache::RemoveEntry(const char* name)
{
	ASSERT(name != NULL);
 
	SinglyLinkedList<NameCacheEntry>::Iterator iterator
		= fNameCache.GetIterator();
	NameCacheEntry* previous = NULL;
	NameCacheEntry* current = iterator.Next();
	while (current != NULL) {
		if (strcmp(current->fName, name) == 0) {
			fNameCache.Remove(previous, current);
			delete current;
			break;
		}
 
		previous = current;
		current = iterator.Next();
	}
 
	if (fDirectoryCache != NULL) {
		MutexLocker _(fDirectoryCache->fLock);
		iterator = fDirectoryCache->fEntries.GetIterator();
		previous = NULL;
		current = iterator.Next();
		while (current != NULL) {
			if (strcmp(current->fName, name) == 0) {
				fDirectoryCache->fEntries.Remove(previous, current);
				delete current;
				break;
			}
 
			previous = current;
			current = iterator.Next();
		}
	}
 
	if (!fAttrDir) {
		entry_cache_remove(fInode->GetFileSystem()->DevId(), fInode->ID(),
			name);
	}
}
 
 
void
DirectoryCache::_SetSnapshot(DirectoryCacheSnapshot* snapshot)
{
	if (fDirectoryCache != NULL)
		fDirectoryCache->ReleaseReference();
	fDirectoryCache = snapshot;
}
 
 
status_t
DirectoryCache::_LoadSnapshot(bool trash)
{
	DirectoryCacheSnapshot* oldSnapshot = fDirectoryCache;
	if (oldSnapshot != NULL)
		oldSnapshot->AcquireReference();
 
	if (trash)
		Trash();
 
	DirectoryCacheSnapshot* newSnapshot;
	status_t result = fInode->GetDirSnapshot(&newSnapshot, NULL, &fChange,
		fAttrDir);
	if (result != B_OK) {
		if (oldSnapshot != NULL)
			oldSnapshot->ReleaseReference();
		return result;
	}
	newSnapshot->AcquireReference();
 
	_SetSnapshot(newSnapshot);
	fExpireTime = system_time() + fExpirationTime;
 
	fTrashed = false;
 
	if (oldSnapshot != NULL)
		NotifyChanges(oldSnapshot, newSnapshot);
 
	if (oldSnapshot != NULL)
		oldSnapshot->ReleaseReference();
 
	newSnapshot->ReleaseReference();
	return B_OK;
}
 
 
status_t
DirectoryCache::Revalidate()
{
	if (fExpireTime > system_time())
		return B_OK;
 
	uint64 change;
	if (fInode->GetChangeInfo(&change, fAttrDir) != B_OK) {
		Trash();
		return B_ERROR;
	}
 
	if (change == fChange) {
		fExpireTime = system_time() + fExpirationTime;
		return B_OK;
	}
 
	return _LoadSnapshot(true);
}
 
 
void
DirectoryCache::NotifyChanges(DirectoryCacheSnapshot* oldSnapshot,
	DirectoryCacheSnapshot* newSnapshot)
{
	ASSERT(newSnapshot != NULL);
	ASSERT(oldSnapshot != NULL);
 
	MutexLocker _(newSnapshot->fLock);
 
	SinglyLinkedList<NameCacheEntry>::Iterator oldIt
		= oldSnapshot->fEntries.GetIterator();
	NameCacheEntry* oldCurrent;
 
	SinglyLinkedList<NameCacheEntry>::Iterator newIt
		= newSnapshot->fEntries.GetIterator();
	NameCacheEntry* newCurrent = newIt.Next();
	while (newCurrent != NULL) {
		oldIt = oldSnapshot->fEntries.GetIterator();
		oldCurrent = oldIt.Next();
 
		bool found = false;
		NameCacheEntry* prev = NULL;
		while (oldCurrent != NULL) {
			if (oldCurrent->fNode == newCurrent->fNode
				&& strcmp(oldCurrent->fName, newCurrent->fName) == 0) {
				found = true;
				break;
			}
 
			prev = oldCurrent;
			oldCurrent = oldIt.Next();
		}
 
		if (!found) {
			if (fAttrDir) {
				notify_attribute_changed(fInode->GetFileSystem()->DevId(),
					-1, fInode->ID(), newCurrent->fName, B_ATTR_CREATED);
			} else {
				notify_entry_created(fInode->GetFileSystem()->DevId(),
					fInode->ID(), newCurrent->fName, newCurrent->fNode);
			}
		} else
			oldSnapshot->fEntries.Remove(prev, oldCurrent);
 
		newCurrent = newIt.Next();
	}
 
	oldIt = oldSnapshot->fEntries.GetIterator();
	oldCurrent = oldIt.Next();
 
	while (oldCurrent != NULL) {
		if (fAttrDir) {
			notify_attribute_changed(fInode->GetFileSystem()->DevId(),
				-1, fInode->ID(), oldCurrent->fName, B_ATTR_REMOVED);
		} else {
			notify_entry_removed(fInode->GetFileSystem()->DevId(), fInode->ID(),
				oldCurrent->fName, oldCurrent->fNode);
		}
		oldCurrent = oldIt.Next();
	}
}
 

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