#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <vm/vm_page.h>
#include "DebugSupport.h"
#include "Directory.h"
#include "DirectoryEntryTable.h"
#include "Entry.h"
#include "EntryListener.h"
#include "IndexDirectory.h"
#include "Locking.h"
#include "Misc.h"
#include "NameIndex.h"
#include "Node.h"
#include "NodeListener.h"
#include "NodeTable.h"
#include "TwoKeyAVLTree.h"
#include "Volume.h"
static const char *kDefaultVolumeName = "RAMFS";
class NodeListenerGetPrimaryKey {
public:
inline Node *operator()(const NodeListenerValue &a)
{
return a.node;
}
inline Node *operator()(const NodeListenerValue &a) const
{
return a.node;
}
};
class NodeListenerGetSecondaryKey {
public:
inline NodeListener *operator()(const NodeListenerValue &a)
{
return a.listener;
}
inline NodeListener *operator()(const NodeListenerValue &a) const
{
return a.listener;
}
};
typedef TwoKeyAVLTree<NodeListenerValue, Node*,
TwoKeyAVLTreeStandardCompare<Node*>,
NodeListenerGetPrimaryKey, NodeListener*,
TwoKeyAVLTreeStandardCompare<NodeListener*>,
NodeListenerGetSecondaryKey > _NodeListenerTree;
class NodeListenerTree : public _NodeListenerTree {};
class EntryListenerGetPrimaryKey {
public:
inline Entry *operator()(const EntryListenerValue &a)
{
return a.entry;
}
inline Entry *operator()(const EntryListenerValue &a) const
{
return a.entry;
}
};
class EntryListenerGetSecondaryKey {
public:
inline EntryListener *operator()(const EntryListenerValue &a)
{
return a.listener;
}
inline EntryListener *operator()(const EntryListenerValue &a) const
{
return a.listener;
}
};
typedef TwoKeyAVLTree<EntryListenerValue, Entry*,
TwoKeyAVLTreeStandardCompare<Entry*>,
EntryListenerGetPrimaryKey, EntryListener*,
TwoKeyAVLTreeStandardCompare<EntryListener*>,
EntryListenerGetSecondaryKey > _EntryListenerTree;
class EntryListenerTree : public _EntryListenerTree {};
\class Volume
\brief Represents a volume.
*/
Volume::Volume(fs_volume* volume)
:
fVolume(volume),
fName(kDefaultVolumeName),
fNextNodeID(kRootParentID + 1),
fNodeTable(NULL),
fDirectoryEntryTable(NULL),
fIndexDirectory(NULL),
fRootDirectory(NULL),
fNodeListeners(NULL),
fAnyNodeListeners(),
fEntryListeners(NULL),
fAnyEntryListeners(),
fAccessTime(0),
fMounted(false)
{
rw_lock_init(&fLocker, "ramfs volume");
recursive_lock_init(&fListenersLock, "ramfs listeners");
recursive_lock_init(&fIteratorLock, "ramfs iterators");
recursive_lock_init(&fAttributeIteratorLock, "ramfs attribute iterators");
recursive_lock_init(&fQueryLocker, "ramfs queries");
}
Volume::~Volume()
{
Unmount();
recursive_lock_destroy(&fAttributeIteratorLock);
recursive_lock_destroy(&fIteratorLock);
recursive_lock_destroy(&fQueryLocker);
recursive_lock_destroy(&fListenersLock);
rw_lock_destroy(&fLocker);
}
status_t
Volume::Mount(uint32 flags)
{
Unmount();
status_t error = B_OK;
if (error == B_OK) {
fNodeListeners = new(nothrow) NodeListenerTree;
if (!fNodeListeners)
error = B_NO_MEMORY;
}
if (error == B_OK) {
fEntryListeners = new(nothrow) EntryListenerTree;
if (!fEntryListeners)
error = B_NO_MEMORY;
}
if (error == B_OK) {
fNodeTable = new(nothrow) NodeTable;
if (fNodeTable)
error = fNodeTable->InitCheck();
else
SET_ERROR(error, B_NO_MEMORY);
}
if (error == B_OK) {
fDirectoryEntryTable = new(nothrow) DirectoryEntryTable;
if (fDirectoryEntryTable)
error = fDirectoryEntryTable->InitCheck();
else
SET_ERROR(error, B_NO_MEMORY);
}
if (error == B_OK) {
fIndexDirectory = new(nothrow) IndexDirectory(this);
if (!fIndexDirectory)
SET_ERROR(error, B_NO_MEMORY);
}
if (error == B_OK) {
fRootDirectory = new(nothrow) Directory(this);
if (fRootDirectory) {
fRootDirectory->SetMode(
S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
error = PublishVNode(fRootDirectory);
} else
SET_ERROR(error, B_NO_MEMORY);
}
if (error == B_OK)
fMounted = true;
else
Unmount();
RETURN_ERROR(error);
}
status_t
Volume::Unmount()
{
fMounted = false;
if (fRootDirectory) {
delete fRootDirectory;
fRootDirectory = NULL;
}
if (fIndexDirectory) {
delete fIndexDirectory;
fIndexDirectory = NULL;
}
if (fEntryListeners) {
delete fEntryListeners;
fEntryListeners = NULL;
}
if (fNodeListeners) {
delete fNodeListeners;
fNodeListeners = NULL;
}
if (fDirectoryEntryTable) {
delete fDirectoryEntryTable;
fDirectoryEntryTable = NULL;
}
if (fNodeTable) {
delete fNodeTable;
fNodeTable = NULL;
}
return B_OK;
}
off_t
Volume::CountBlocks() const
{
return 0;
}
off_t
Volume::CountFreeBlocks() const
{
return vm_page_num_free_pages();
}
status_t
Volume::SetName(const char *name)
{
status_t error = (name ? B_OK : B_BAD_VALUE);
if (error == B_OK) {
if (!fName.SetTo(name))
SET_ERROR(error, B_NO_MEMORY);
}
return error;
}
const char *
Volume::GetName() const
{
return fName.GetString();
}
status_t
Volume::NewVNode(Node *node)
{
status_t error = NodeAdded(node);
if (error == B_OK) {
error = new_vnode(FSVolume(), node->GetID(), node, &gRamFSVnodeOps);
if (error != B_OK)
NodeRemoved(node);
}
return error;
}
status_t
Volume::PublishVNode(Node *node)
{
status_t error = NodeAdded(node);
if (error == B_OK) {
error = publish_vnode(FSVolume(), node->GetID(), node, &gRamFSVnodeOps,
node->GetMode(), 0);
if (error != B_OK)
NodeRemoved(node);
}
return error;
}
status_t
Volume::GetVNode(ino_t id, Node **node)
{
return (fMounted ? get_vnode(FSVolume(), id, (void**)node) : B_BAD_VALUE);
}
status_t
Volume::GetVNode(Node *node)
{
Node *dummy = NULL;
status_t error = (fMounted ? GetVNode(node->GetID(), &dummy)
: B_BAD_VALUE );
if (error == B_OK && dummy != node) {
FATAL("Two Nodes have the same ID: %" B_PRIdINO "!\n", node->GetID());
PutVNode(dummy);
error = B_ERROR;
}
return error;
}
status_t
Volume::PutVNode(ino_t id)
{
return (fMounted ? put_vnode(FSVolume(), id) : B_BAD_VALUE);
}
status_t
Volume::PutVNode(Node *node)
{
return (fMounted ? put_vnode(FSVolume(), node->GetID()) : B_BAD_VALUE);
}
status_t
Volume::RemoveVNode(Node *node)
{
if (fMounted)
return remove_vnode(FSVolume(), node->GetID());
status_t error = NodeRemoved(node);
if (error == B_OK)
delete node;
return error;
}
status_t
Volume::UnremoveVNode(Node *node)
{
return (fMounted ? unremove_vnode(FSVolume(), node->GetID()) : B_BAD_VALUE);
}
status_t
Volume::NodeAdded(Node *node)
{
status_t error = (node ? B_OK : B_BAD_VALUE);
if (error == B_OK) {
error = fNodeTable->AddNode(node);
if (error == B_OK) {
NodeListenerTree::Iterator it;
if (fNodeListeners->FindFirst(node, &it)) {
for (NodeListenerValue *value = it.GetCurrent();
value && value->node == node;
value = it.GetNext()) {
if (value->flags & NODE_LISTEN_ADDED)
value->listener->NodeAdded(node);
}
}
int32 count = fAnyNodeListeners.CountItems();
for (int32 i = 0; i < count; i++) {
const NodeListenerValue &value = fAnyNodeListeners.ItemAt(i);
if (value.flags & NODE_LISTEN_ADDED)
value.listener->NodeAdded(node);
}
}
}
return error;
}
status_t
Volume::NodeRemoved(Node *node)
{
status_t error = (node ? B_OK : B_BAD_VALUE);
if (error == B_OK) {
error = fNodeTable->RemoveNode(node);
if (error == B_OK) {
NodeListenerTree::Iterator it;
if (fNodeListeners->FindFirst(node, &it)) {
for (NodeListenerValue *value = it.GetCurrent();
value && value->node == node;
value = it.GetNext()) {
if (value->flags & NODE_LISTEN_REMOVED)
value->listener->NodeRemoved(node);
}
}
int32 count = fAnyNodeListeners.CountItems();
for (int32 i = 0; i < count; i++) {
const NodeListenerValue &value = fAnyNodeListeners.ItemAt(i);
if (value.flags & NODE_LISTEN_REMOVED)
value.listener->NodeRemoved(node);
}
}
}
return error;
}
\note The method does not initialize the parent ID for non-directory nodes.
\param id ID of the node to be found.
\param node pointer to a pre-allocated Node* to be set to the found node.
\return \c B_OK, if everything went fine.
*/
status_t
Volume::FindNode(ino_t id, Node **node)
{
status_t error = (node ? B_OK : B_BAD_VALUE);
if (error == B_OK) {
*node = fNodeTable->GetNode(id);
if (!*node)
error = B_ENTRY_NOT_FOUND;
}
return error;
}
status_t
Volume::AddNodeListener(NodeListener *listener, Node *node, uint32 flags)
{
if (!listener || (!node && !(flags & NODE_LISTEN_ANY_NODE))
|| !(flags & NODE_LISTEN_ALL)) {
return B_BAD_VALUE;
}
RecursiveLocker locker(fListenersLock);
status_t error = B_OK;
NodeListenerValue value(listener, node, flags);
if (flags & NODE_LISTEN_ANY_NODE) {
if (!fAnyNodeListeners.AddItem(value))
error = B_NO_MEMORY;
} else
error = fNodeListeners->Insert(value);
return error;
}
status_t
Volume::RemoveNodeListener(NodeListener *listener, Node *node)
{
if (!listener)
return B_BAD_VALUE;
RecursiveLocker locker(fListenersLock);
status_t error = B_OK;
if (node)
error = fNodeListeners->Remove(node, listener);
else {
NodeListenerValue value(listener, node, 0);
if (!fAnyNodeListeners.RemoveItem(value))
error = B_ENTRY_NOT_FOUND;
}
return error;
}
status_t
Volume::EntryAdded(ino_t id, Entry *entry)
{
status_t error = (entry ? B_OK : B_BAD_VALUE);
if (error == B_OK) {
error = fDirectoryEntryTable->AddEntry(id, entry);
if (error == B_OK) {
EntryListenerTree::Iterator it;
if (fEntryListeners->FindFirst(entry, &it)) {
for (EntryListenerValue *value = it.GetCurrent();
value && value->entry == entry;
value = it.GetNext()) {
if (value->flags & ENTRY_LISTEN_ADDED)
value->listener->EntryAdded(entry);
}
}
int32 count = fAnyEntryListeners.CountItems();
for (int32 i = 0; i < count; i++) {
const EntryListenerValue &value = fAnyEntryListeners.ItemAt(i);
if (value.flags & ENTRY_LISTEN_ADDED)
value.listener->EntryAdded(entry);
}
}
}
return error;
}
status_t
Volume::EntryRemoved(ino_t id, Entry *entry)
{
status_t error = (entry ? B_OK : B_BAD_VALUE);
if (error == B_OK) {
error = fDirectoryEntryTable->RemoveEntry(id, entry);
if (error == B_OK) {
EntryListenerTree::Iterator it;
if (fEntryListeners->FindFirst(entry, &it)) {
for (EntryListenerValue *value = it.GetCurrent();
value && value->entry == entry;
value = it.GetNext()) {
if (value->flags & ENTRY_LISTEN_REMOVED)
value->listener->EntryRemoved(entry);
}
}
int32 count = fAnyEntryListeners.CountItems();
for (int32 i = 0; i < count; i++) {
const EntryListenerValue &value = fAnyEntryListeners.ItemAt(i);
if (value.flags & ENTRY_LISTEN_REMOVED)
value.listener->EntryRemoved(entry);
}
}
}
return error;
}
status_t
Volume::FindEntry(ino_t id, const char *name, Entry **entry)
{
status_t error = (entry ? B_OK : B_BAD_VALUE);
if (error == B_OK) {
*entry = fDirectoryEntryTable->GetEntry(id, name);
if (!*entry)
error = B_ENTRY_NOT_FOUND;
}
return error;
}
status_t
Volume::AddEntryListener(EntryListener *listener, Entry *entry, uint32 flags)
{
if (!listener || (!entry && !(flags & ENTRY_LISTEN_ANY_ENTRY))
|| !(flags & ENTRY_LISTEN_ALL)) {
return B_BAD_VALUE;
}
RecursiveLocker locker(fListenersLock);
status_t error = B_OK;
EntryListenerValue value(listener, entry, flags);
if (flags & ENTRY_LISTEN_ANY_ENTRY) {
if (!fAnyEntryListeners.AddItem(value))
error = B_NO_MEMORY;
} else
error = fEntryListeners->Insert(value);
return error;
}
status_t
Volume::RemoveEntryListener(EntryListener *listener, Entry *entry)
{
if (!listener)
return B_BAD_VALUE;
RecursiveLocker locker(fListenersLock);
status_t error = B_OK;
if (entry)
error = fEntryListeners->Remove(entry, listener);
else {
EntryListenerValue value(listener, entry, 0);
if (!fAnyEntryListeners.RemoveItem(value))
error = B_ENTRY_NOT_FOUND;
}
return error;
}
status_t
Volume::NodeAttributeAdded(ino_t id, Attribute *attribute)
{
status_t error = (attribute ? B_OK : B_BAD_VALUE);
if (error == B_OK) {
if (error == B_OK) {
if (AttributeIndex *index = FindAttributeIndex(
attribute->GetName(), attribute->GetType())) {
index->Added(attribute);
}
}
}
return error;
}
status_t
Volume::NodeAttributeRemoved(ino_t id, Attribute *attribute)
{
if (attribute == NULL)
return B_BAD_VALUE;
if (AttributeIndex *index = attribute->GetIndex())
index->Removed(attribute);
if (attribute->GetNode() != NULL) {
uint8 oldKey[kMaxIndexKeyLength];
size_t oldLength;
attribute->GetKey(oldKey, &oldLength);
UpdateLiveQueries(NULL, attribute->GetNode(), attribute->GetName(),
attribute->GetType(), oldKey, oldLength, NULL, 0);
}
return B_OK;
}
NameIndex *
Volume::GetNameIndex() const
{
return (fIndexDirectory ? fIndexDirectory->GetNameIndex() : NULL);
}
LastModifiedIndex *
Volume::GetLastModifiedIndex() const
{
return (fIndexDirectory ? fIndexDirectory->GetLastModifiedIndex() : NULL);
}
SizeIndex *
Volume::GetSizeIndex() const
{
return (fIndexDirectory ? fIndexDirectory->GetSizeIndex() : NULL);
}
Index *
Volume::FindIndex(const char *name)
{
return (fIndexDirectory ? fIndexDirectory->FindIndex(name) : NULL);
}
AttributeIndex *
Volume::FindAttributeIndex(const char *name, uint32 type)
{
return (fIndexDirectory
? fIndexDirectory->FindAttributeIndex(name, type) : NULL);
}
void
Volume::AddQuery(Query *query)
{
RecursiveLocker _(fQueryLocker);
if (query)
fQueries.Insert(query);
}
void
Volume::RemoveQuery(Query *query)
{
RecursiveLocker _(fQueryLocker);
if (query)
fQueries.Remove(query);
}
void
Volume::UpdateLiveQueries(Entry *entry, Node* node, const char *attribute,
int32 type, const uint8 *oldKey, size_t oldLength, const uint8 *newKey,
size_t newLength)
{
RecursiveLocker _(fQueryLocker);
for (Query* query = fQueries.First();
query;
query = fQueries.GetNext(query)) {
query->LiveUpdate(entry, node, attribute, type, oldKey, oldLength,
newKey, newLength);
}
}
void
Volume::GetAllocationInfo(AllocationInfo &info)
{
info.AddOtherAllocation(sizeof(NodeTable));
fNodeTable->GetAllocationInfo(info);
info.AddOtherAllocation(sizeof(DirectoryEntryTable));
fDirectoryEntryTable->GetAllocationInfo(info);
fRootDirectory->GetAllocationInfo(info);
info.AddStringAllocation(fName.GetLength());
}
bool
Volume::ReadLock()
{
bool ok = rw_lock_read_lock(&fLocker) == B_OK;
if (ok && fLocker.owner_count > 1)
fAccessTime = system_time();
return ok;
}
void
Volume::ReadUnlock()
{
rw_lock_read_unlock(&fLocker);
}
bool
Volume::WriteLock()
{
bool ok = rw_lock_write_lock(&fLocker) == B_OK;
if (ok && fLocker.owner_count > 1)
fAccessTime = system_time();
return ok;
}
void
Volume::WriteUnlock()
{
rw_lock_write_unlock(&fLocker);
}