* Missing locking initialization in Volume::Mount().

* Added missing locking in the query hooks.
* Added live query support similarly as done in BFS:
  - Volume manages a doubly-linked list of live queries.
  - Volume::UpdateLiveQueries() invoked from several places where it makes
    sense (standard indices, Attribute::WriteAt(), and
    BVolume::NodeAttributeRemoved()) notifies the live Query objects.
  - Adjusted Query to be able to deal with hard links. Unfortunately Tracker
    is a bit broken with respect to hard links, particularly in the query
    windows. E.g. only one entry referring to a node is shown, and the renaming
    method RamFS uses (link new entry, then unlink old one) causes renamed
    entries to fall out of queries, even if they should still be in.
    (Want a bug report for this, Axel? :-P)


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@20203 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Ingo Weinhold 2007-02-22 23:30:39 +00:00
parent b0f302e45b
commit 0d427dec46
11 changed files with 326 additions and 78 deletions

View File

@ -52,19 +52,30 @@ status_t
Attribute::WriteAt(off_t offset, const void *buffer, size_t size,
size_t *bytesWritten)
{
status_t error = B_OK;
if (offset < kMaxIndexKeyLength && size > 0 && fIndex) {
// There is an index and a change will be made within the key.
uint8 oldKey[kMaxIndexKeyLength];
size_t oldLength;
GetKey(oldKey, &oldLength);
error = DataContainer::WriteAt(offset, buffer, size, bytesWritten);
// get the current key for the attribute
uint8 oldKey[kMaxIndexKeyLength];
size_t oldLength;
GetKey(oldKey, &oldLength);
// write the new value
status_t error = DataContainer::WriteAt(offset, buffer, size, bytesWritten);
// If there is an index and a change has been made within the key, notify
// the index.
if (offset < kMaxIndexKeyLength && size > 0 && fIndex)
fIndex->Changed(this, oldKey, oldLength);
} else
error = DataContainer::WriteAt(offset, buffer, size, bytesWritten);
// update live queries
const uint8* newKey;
size_t newLength;
GetKey(&newKey, &newLength);
GetVolume()->UpdateLiveQueries(NULL, fNode, GetName(), fType, oldKey,
oldLength, newKey, newLength);
// node has been changed
if (fNode)
if (fNode && size > 0)
fNode->MarkModified();
return error;
}

View File

@ -156,17 +156,17 @@ Directory::CreateEntry(Node *node, const char *name, Entry **_entry)
if (entry) {
error = entry->InitCheck();
if (error == B_OK) {
// add the entry
error = AddEntry(entry);
// link to the node
error = entry->Link(node);
if (error == B_OK) {
// link to the node
error = entry->Link(node);
// add the entry
error = AddEntry(entry);
if (error == B_OK) {
if (_entry)
*_entry = entry;
} else {
// failure: remove the entry
RemoveEntry(entry);
// failure: unlink the node
entry->Unlink();
}
}
}

View File

@ -157,9 +157,15 @@ LastModifiedIndex::Changed(Node *node, time_t oldModified)
if (iterator->GetCurrentNode() == node)
iterator->NodeRemoved(node);
}
// remove the node
// remove and re-insert the node
fNodes->Remove(it);
error = fNodes->Insert(node);
// udpate live queries
time_t newModified = node->GetMTime();
fVolume->UpdateLiveQueries(NULL, node, GetName(), GetType(),
(const uint8*)&oldModified, sizeof(oldModified),
(const uint8*)&newModified, sizeof(newModified));
}
}
return error;

View File

@ -139,6 +139,9 @@ NameIndex::Changed(Entry *entry, const char *oldName)
if (foundEntry && *foundEntry == entry) {
fEntries->Remove(it);
error = fEntries->Insert(entry);
// udpate live queries
_UpdateLiveQueries(entry, oldName, entry->GetName());
}
}
return error;
@ -148,16 +151,24 @@ NameIndex::Changed(Entry *entry, const char *oldName)
void
NameIndex::EntryAdded(Entry *entry)
{
if (entry)
if (entry) {
fEntries->Insert(entry);
// udpate live queries
_UpdateLiveQueries(entry, NULL, entry->GetName());
}
}
// EntryRemoved
void
NameIndex::EntryRemoved(Entry *entry)
{
if (entry)
if (entry) {
fEntries->Remove(entry, entry);
// udpate live queries
_UpdateLiveQueries(entry, entry->GetName(), NULL);
}
}
// InternalGetIterator
@ -190,6 +201,16 @@ NameIndex::InternalFind(const uint8 *key, size_t length)
return iterator;
}
// _UpdateLiveQueries
void
NameIndex::_UpdateLiveQueries(Entry* entry, const char* oldName,
const char* newName)
{
fVolume->UpdateLiveQueries(entry, entry->GetNode(), GetName(),
GetType(), (const uint8*)oldName, (oldName ? strlen(oldName) : 0),
(const uint8*)newName, (newName ? strlen(newName) : 0));
}
// NameIndexEntryIterator

View File

@ -32,6 +32,9 @@ private:
class EntryTree;
friend class NameIndexEntryIterator;
void _UpdateLiveQueries(Entry* entry, const char* oldName,
const char* newName);
private:
EntryTree *fEntries;
};

View File

@ -128,8 +128,6 @@ IndexIterator::GetNextEntry(uint8 *buffer, uint16 *_keyLength,
}
// compare_integral
template<typename Key>
static inline
@ -258,8 +256,9 @@ class Term {
void SetParent(Term *parent) { fParent = parent; }
Term *Parent() const { return fParent; }
virtual status_t Match(Entry *entry,const char *attribute = NULL,int32 type = 0,
const uint8 *key = NULL,size_t size = 0) = 0;
virtual status_t Match(Entry *entry, Node* node,
const char *attribute = NULL, int32 type = 0,
const uint8 *key = NULL, size_t size = 0) = 0;
virtual void Complement() = 0;
virtual void CalculateScore(IndexWrapper &index) = 0;
@ -267,6 +266,8 @@ class Term {
virtual status_t InitCheck() = 0;
virtual bool NeedsEntry() = 0;
#ifdef DEBUG
virtual void PrintToStream() = 0;
#endif
@ -295,8 +296,9 @@ class Equation : public Term {
status_t ParseQuotedString(char **_start, char **_end);
char *CopyString(char *start, char *end);
virtual status_t Match(Entry *entry, const char *attribute = NULL, int32 type = 0,
const uint8 *key = NULL, size_t size = 0);
virtual status_t Match(Entry *entry, Node* node,
const char *attribute = NULL, int32 type = 0,
const uint8 *key = NULL, size_t size = 0);
virtual void Complement();
status_t PrepareQuery(Volume *volume, IndexWrapper &index, IndexIterator **iterator,
@ -307,6 +309,8 @@ class Equation : public Term {
virtual void CalculateScore(IndexWrapper &index);
virtual int32 Score() const { return fScore; }
virtual bool NeedsEntry();
#ifdef DEBUG
virtual void PrintToStream();
#endif
@ -340,7 +344,8 @@ class Operator : public Term {
Term *Left() const { return fLeft; }
Term *Right() const { return fRight; }
virtual status_t Match(Entry *entry, const char *attribute = NULL, int32 type = 0,
virtual status_t Match(Entry *entry, Node* node,
const char *attribute = NULL, int32 type = 0,
const uint8 *key = NULL, size_t size = 0);
virtual void Complement();
@ -349,6 +354,8 @@ class Operator : public Term {
virtual status_t InitCheck();
virtual bool NeedsEntry();
//Term *Copy() const;
#ifdef DEBUG
virtual void PrintToStream();
@ -940,7 +947,8 @@ Equation::MatchEmptyString()
*/
status_t
Equation::Match(Entry *entry, const char *attributeName, int32 type, const uint8 *key, size_t size)
Equation::Match(Entry *entry, Node* node, const char *attributeName, int32 type,
const uint8 *key, size_t size)
{
// get a pointer to the attribute in question
union value value;
@ -949,14 +957,23 @@ Equation::Match(Entry *entry, const char *attributeName, int32 type, const uint8
// first, check if we are matching for a live query and use that value
if (attributeName != NULL && !strcmp(fAttribute, attributeName)) {
if (key == NULL) {
if (type == B_STRING_TYPE)
if (type == B_STRING_TYPE) {
// special case: a NULL "name" means the entry has been removed
// or not yet been added -- we refuse to match, whatever the
// pattern
if (!strcmp(fAttribute, "name"))
return NO_MATCH;
return MatchEmptyString();
}
return NO_MATCH;
}
buffer = const_cast<uint8 *>(key);
} else if (!strcmp(fAttribute, "name")) {
// if not, check for "fake" attributes, "name", "size", "last_modified",
if (!entry)
return B_ERROR;
buffer = (uint8 *)entry->GetName();
if (buffer == NULL)
return B_ERROR;
@ -964,18 +981,18 @@ Equation::Match(Entry *entry, const char *attributeName, int32 type, const uint8
type = B_STRING_TYPE;
size = strlen((const char *)buffer);
} else if (!strcmp(fAttribute,"size")) {
value.Int64 = entry->GetNode()->GetSize();
value.Int64 = node->GetSize();
buffer = (uint8 *)&value;
type = B_INT64_TYPE;
} else if (!strcmp(fAttribute,"last_modified")) {
value.Int32 = entry->GetNode()->GetMTime();
value.Int32 = node->GetMTime();
buffer = (uint8 *)&value;
type = B_INT32_TYPE;
} else {
// then for attributes
Attribute *attribute = NULL;
if (entry->GetNode()->FindAttribute(fAttribute, &attribute) == B_OK) {
if (node->FindAttribute(fAttribute, &attribute) == B_OK) {
attribute->GetKey(&buffer, &size);
type = attribute->GetType();
} else
@ -1151,7 +1168,7 @@ Equation::GetNextMatching(Volume *volume, IndexIterator *iterator,
status = MATCH_OK;
if (!fHasIndex)
status = Match(entry);
status = Match(entry, entry->GetNode());
while (term != NULL && status == MATCH_OK) {
Operator *parent = (Operator *)term->Parent();
@ -1168,7 +1185,7 @@ Equation::GetNextMatching(Volume *volume, IndexIterator *iterator,
FATAL(("&&-operator has only one child... (parent = %p)\n", parent));
break;
}
status = other->Match(entry);
status = other->Match(entry, entry->GetNode());
if (status < 0) {
REPORT_ERROR(status);
status = NO_MATCH;
@ -1196,6 +1213,13 @@ Equation::GetNextMatching(Volume *volume, IndexIterator *iterator,
}
bool
Equation::NeedsEntry()
{
return strcmp(fAttribute, "name") == 0;
}
// #pragma mark -
@ -1219,22 +1243,24 @@ Operator::~Operator()
status_t
Operator::Match(Entry *entry, const char *attribute, int32 type, const uint8 *key, size_t size)
Operator::Match(Entry *entry, Node* node, const char *attribute,
int32 type, const uint8 *key, size_t size)
{
if (fOp == OP_AND) {
status_t status = fLeft->Match(entry, attribute, type, key, size);
status_t status = fLeft->Match(entry, node, attribute, type, key, size);
if (status != MATCH_OK)
return status;
return fRight->Match(entry, attribute, type, key, size);
return fRight->Match(entry, node, attribute, type, key, size);
} else {
// choose the term with the better score for OP_OR
if (fRight->Score() > fLeft->Score()) {
status_t status = fRight->Match(entry, attribute, type, key, size);
status_t status = fRight->Match(entry, node, attribute, type, key,
size);
if (status != NO_MATCH)
return status;
}
return fLeft->Match(entry, attribute, type, key, size);
return fLeft->Match(entry, node, attribute, type, key, size);
}
}
@ -1291,6 +1317,13 @@ Operator::InitCheck()
}
bool
Operator::NeedsEntry()
{
return ((fLeft && fLeft->NeedsEntry()) || (fRight && fRight->NeedsEntry()));
}
#if 0
Term *
Operator::Copy() const
@ -1529,7 +1562,8 @@ Query::Query(Volume *volume, Expression *expression, uint32 flags)
fIterator(NULL),
fIndex(volume),
fFlags(flags),
fPort(-1)
fPort(-1),
fNeedsEntry(false)
{
// if the expression has a valid root pointer, the whole tree has
// already passed the sanity check, so that we don't have to check
@ -1541,6 +1575,8 @@ Query::Query(Volume *volume, Expression *expression, uint32 flags)
fExpression->Root()->CalculateScore(fIndex);
fIndex.Unset();
fNeedsEntry = fExpression->Root()->NeedsEntry();
Rewind();
if (fFlags & B_LIVE_QUERY)
@ -1639,16 +1675,35 @@ Query::SetLiveMode(port_id port, int32 token)
void
Query::LiveUpdate(Entry *entry, const char *attribute, int32 type, const uint8 *oldKey,
size_t oldLength, const uint8 *newKey, size_t newLength)
Query::LiveUpdate(Entry *entry, Node* node, const char *attribute, int32 type,
const uint8 *oldKey, size_t oldLength, const uint8 *newKey,
size_t newLength)
{
if (fPort < 0 || fExpression == NULL || attribute == NULL)
PRINT(("%p->Query::LiveUpdate(%p, %p, \"%s\", 0x%lx, %p, %lu, %p, %lu)\n",
this, entry, node, attribute, type, oldKey, oldLength, newKey, newLength));
if (fPort < 0 || fExpression == NULL || node == NULL || attribute == NULL)
return;
// ToDo: check if the attribute is part of the query at all...
status_t oldStatus = fExpression->Root()->Match(entry, attribute, type, oldKey, oldLength);
status_t newStatus = fExpression->Root()->Match(entry, attribute, type, newKey, newLength);
// If no entry has been supplied, but the we need one for the evaluation
// (i.e. the "name" attribute is used), we invoke ourselves for all entries
// referring to the given node.
if (!entry && fNeedsEntry) {
entry = node->GetFirstReferrer();
while (entry) {
LiveUpdate(entry, node, attribute, type, oldKey, oldLength, newKey,
newLength);
entry = node->GetNextReferrer(entry);
}
return;
}
status_t oldStatus = fExpression->Root()->Match(entry, node, attribute,
type, oldKey, oldLength);
status_t newStatus = fExpression->Root()->Match(entry, node, attribute,
type, newKey, newLength);
PRINT((" oldStatus: 0x%lx, newStatus: 0x%lx\n", oldStatus, newStatus));
int32 op;
if (oldStatus == MATCH_OK && newStatus == MATCH_OK) {
@ -1656,8 +1711,14 @@ Query::LiveUpdate(Entry *entry, const char *attribute, int32 type, const uint8 *
if (oldKey == NULL || strcmp(attribute,"name"))
return;
send_notification(fPort, fToken, B_QUERY_UPDATE, B_ENTRY_REMOVED, fVolume->GetID(), 0,
entry->GetParent()->GetID(), 0, entry->GetNode()->GetID(), (const char *)oldKey);
if (entry) {
// entry should actually always be given, when the changed
// attribute is the entry name
PRINT(("send_notification(): old: B_ENTRY_REMOVED\n"));
send_notification(fPort, fToken, B_QUERY_UPDATE, B_ENTRY_REMOVED,
fVolume->GetID(), 0, entry->GetParent()->GetID(), 0,
entry->GetNode()->GetID(), (const char *)oldKey);
}
op = B_ENTRY_CREATED;
} else if (oldStatus != MATCH_OK && newStatus != MATCH_OK) {
// nothing has changed
@ -1667,12 +1728,21 @@ Query::LiveUpdate(Entry *entry, const char *attribute, int32 type, const uint8 *
else
op = B_ENTRY_CREATED;
// if "value" is NULL, send_notification() crashes...
const char *value = (const char *)newKey;
if (type != B_STRING_TYPE || value == NULL)
value = "";
send_notification(fPort, fToken, B_QUERY_UPDATE, op, fVolume->GetID(), 0,
entry->GetParent()->GetID(), 0, entry->GetNode()->GetID(), value);
// We send a notification for the given entry, if any, or otherwise for
// all entries referring to the node;
if (entry) {
PRINT(("send_notification(): new: %s\n", (op == B_ENTRY_REMOVED ? "B_ENTRY_REMOVED" : "B_ENTRY_CREATED")));
send_notification(fPort, fToken, B_QUERY_UPDATE, op, fVolume->GetID(),
0, entry->GetParent()->GetID(), 0, entry->GetNode()->GetID(),
entry->GetName());
} else {
entry = node->GetFirstReferrer();
while (entry) {
send_notification(fPort, fToken, B_QUERY_UPDATE, op,
fVolume->GetID(), 0, entry->GetParent()->GetID(), 0,
entry->GetNode()->GetID(), entry->GetName());
entry = node->GetNextReferrer(entry);
}
}
}

View File

@ -12,6 +12,7 @@
#include <OS.h>
#include <SupportDefs.h>
#include "DLList.h"
#include "Index.h"
#include "Stack.h"
#include "ramfs.h"
@ -19,6 +20,7 @@
class Entry;
class Equation;
class IndexIterator;
class Node;
class Query;
class Term;
class Volume;
@ -90,7 +92,7 @@ class Expression {
Term *fTerm;
};
class Query {
class Query : public DLListLinkImpl<Query> {
public:
Query(Volume *volume, Expression *expression, uint32 flags);
~Query();
@ -99,11 +101,15 @@ class Query {
status_t GetNextEntry(struct dirent *, size_t size);
void SetLiveMode(port_id port, int32 token);
void LiveUpdate(Entry *entry, const char *attribute, int32 type,
const uint8 *oldKey, size_t oldLength, const uint8 *newKey, size_t newLength);
void LiveUpdate(Entry *entry, Node* node, const char *attribute,
int32 type, const uint8 *oldKey, size_t oldLength,
const uint8 *newKey, size_t newLength);
Expression *GetExpression() const { return fExpression; }
private:
// void SendNotification(Entry* entry)
private:
Volume *fVolume;
Expression *fExpression;
@ -115,6 +121,7 @@ class Query {
uint32 fFlags;
port_id fPort;
int32 fToken;
bool fNeedsEntry;
};
#endif /* QUERY_H */

View File

@ -155,9 +155,16 @@ SizeIndex::Changed(Node *node, off_t oldSize)
if (iterator->GetCurrentNode() == node)
iterator->NodeRemoved(node);
}
// remove the node
// remove and re-insert the node
fNodes->Remove(it);
error = fNodes->Insert(node);
// udpate live queries
off_t newSize = node->GetSize();
fVolume->UpdateLiveQueries(NULL, node, GetName(), GetType(),
(const uint8*)&oldSize, sizeof(oldSize), (const uint8*)&newSize,
sizeof(newSize));
}
}
return error;

View File

@ -33,6 +33,7 @@
#include "Entry.h"
#include "EntryListener.h"
#include "IndexDirectory.h"
#include "Locking.h"
#include "Misc.h"
#include "NameIndex.h"
#include "Node.h"
@ -140,8 +141,9 @@ Volume::Volume()
fIndexDirectory(NULL),
fRootDirectory(NULL),
fName(kDefaultVolumeName),
fLocker(),
fIteratorLocker(),
fLocker("volume"),
fIteratorLocker("iterators"),
fQueryLocker("queries"),
fNodeListeners(NULL),
fAnyNodeListeners(),
fEntryListeners(NULL),
@ -165,6 +167,15 @@ status_t
Volume::Mount(nspace_id id)
{
Unmount();
// check the locker's semaphores
if (fLocker.Sem() < 0)
return fLocker.Sem();
if (fIteratorLocker.Sem() < 0)
return fIteratorLocker.Sem();
if (fQueryLocker.Sem() < 0)
return fQueryLocker.Sem();
status_t error = B_OK;
fID = id;
// create a block allocator
@ -659,6 +670,15 @@ Volume::NodeAttributeRemoved(vnode_id id, Attribute *attribute)
index->Removed(attribute);
}
}
// update live queries
if (error == B_OK && attribute->GetNode()) {
const uint8* oldKey;
size_t oldLength;
attribute->GetKey(&oldKey, &oldLength);
UpdateLiveQueries(NULL, attribute->GetNode(), attribute->GetName(),
attribute->GetType(), oldKey, oldLength, NULL, 0);
}
}
return error;
}
@ -712,6 +732,42 @@ Volume::FindAttributeIndex(const char *name, uint32 type)
? fIndexDirectory->FindAttributeIndex(name, type) : NULL);
}
// AddQuery
void
Volume::AddQuery(Query *query)
{
AutoLocker<Locker> _(fQueryLocker);
if (query)
fQueries.Insert(query);
}
// RemoveQuery
void
Volume::RemoveQuery(Query *query)
{
AutoLocker<Locker> _(fQueryLocker);
if (query)
fQueries.Remove(query);
}
// UpdateLiveQueries
void
Volume::UpdateLiveQueries(Entry *entry, Node* node, const char *attribute,
int32 type, const uint8 *oldKey, size_t oldLength, const uint8 *newKey,
size_t newLength)
{
AutoLocker<Locker> _(fQueryLocker);
for (Query* query = fQueries.GetFirst();
query;
query = fQueries.GetNext(query)) {
query->LiveUpdate(entry, node, attribute, type, oldKey, oldLength,
newKey, newLength);
}
}
// AllocateBlock
status_t
Volume::AllocateBlock(size_t size, BlockReference **block)

View File

@ -22,12 +22,14 @@
#ifndef VOLUME_H
#define VOLUME_H
#include <fsproto.h>
#include <SupportDefs.h>
#include "DLList.h"
#include "Entry.h"
#include "fsproto.h"
#include "List.h"
#include "Locker.h"
#include "Query.h"
#include "String.h"
class AllocationInfo;
@ -48,7 +50,6 @@ class NodeAttributeTable;
class NodeListener;
class NodeListenerTree;
class NodeTable;
class Query;
class SizeIndex;
const vnode_id kRootParentID = 0;
@ -146,11 +147,11 @@ public:
AttributeIndex *FindAttributeIndex(const char *name, uint32 type);
// queries
// TODO: Implement.
// status_t AddQuery(Query */*query*/) { return B_ERROR; }
// status_t RemoveQuery(Query */*query*/) { return B_ERROR; }
status_t AddQuery(Query */*query*/) { return B_OK; }
status_t RemoveQuery(Query */*query*/) { return B_OK; }
void AddQuery(Query *query);
void RemoveQuery(Query *query);
void UpdateLiveQueries(Entry *entry, Node* node, const char *attribute,
int32 type, const uint8 *oldKey, size_t oldLength,
const uint8 *newKey, size_t newLength);
vnode_id NextNodeID() { return fNextNodeID++; }
@ -173,6 +174,8 @@ public:
void IteratorUnlock();
private:
typedef DLList<Query> QueryList;
nspace_id fID;
vnode_id fNextNodeID;
NodeTable *fNodeTable;
@ -183,10 +186,12 @@ private:
String fName;
Locker fLocker;
Locker fIteratorLocker;
Locker fQueryLocker;
NodeListenerTree *fNodeListeners;
NodeListenerList fAnyNodeListeners;
EntryListenerTree *fEntryListeners;
EntryListenerList fAnyEntryListeners;
QueryList fQueries;
BlockAllocator *fBlockAllocator;
off_t fBlockSize;
off_t fAllocatedBlocks;

View File

@ -43,6 +43,7 @@
#include "AllocationInfo.h"
#include "AttributeIndex.h"
#include "AttributeIterator.h"
#include "AutoDeleter.h"
#include "Debug.h"
#include "Directory.h"
#include "Entry.h"
@ -249,6 +250,10 @@ notify_if_stat_changed(Volume *volume, Node *node)
}
}
// #pragma mark - FS
// ramfs_mount
static
int
@ -323,6 +328,10 @@ ramfs_sync(void */*_ns*/)
return B_OK;
}
// #pragma mark - VNodes
// ramfs_read_vnode
static
int
@ -375,6 +384,10 @@ FUNCTION(("node: %Ld\n", ((Node*)_node)->GetID()));
RETURN_ERROR(error);
}
// #pragma mark - Nodes
// ramfs_walk
static
int
@ -562,6 +575,10 @@ ramfs_write_stat(void *ns, void *_node, struct stat *st, long mask)
RETURN_ERROR(error);
}
// #pragma mark - Files
// FileCookie
class FileCookie {
public:
@ -841,6 +858,11 @@ ramfs_access(void *ns, void *_node, int mode)
RETURN_ERROR(error);
}
// #pragma mark - Directories
// ramfs_rename
static
int
@ -1330,7 +1352,11 @@ ramfs_free_dir_cookie(void */*ns*/, void */*_node*/, void *_cookie)
delete cookie;
return B_OK;
}
// #pragma mark - FS Stats
// ramfs_read_fs_stat
static
int
@ -1354,6 +1380,7 @@ ramfs_read_fs_stat(void *ns, struct fs_info *info)
return B_OK;
}
// ramfs_write_fs_stat
static
int
@ -1370,6 +1397,10 @@ ramfs_write_fs_stat(void *ns, struct fs_info *info, long mask)
RETURN_ERROR(error);
}
// #pragma mark - Symlinks
// ramfs_symlink
static
int
@ -1455,6 +1486,10 @@ ramfs_read_link(void *ns, void *_node, char *buffer, size_t *bufferSize)
RETURN_ERROR(error);
}
// #pragma mark - Attributes
// ramfs_open_attrdir
static
int
@ -1703,6 +1738,10 @@ ramfs_stat_attr(void *ns, void *_node, const char *name,
RETURN_ERROR(error);
}
// #pragma mark - Indices
// IndexDirCookie
class IndexDirCookie {
public:
@ -1905,9 +1944,9 @@ ramfs_stat_index(void *ns, const char *name, struct index_info *indexInfo)
}
// #pragma mark - Queries
// Query implementation by Axel Dörfler. Slightly adjusted.
//
// TODO: Locking!
// ramfs_open_query
int
@ -1922,21 +1961,30 @@ ramfs_open_query(void *ns, const char *queryString, ulong flags, port_id port,
Volume *volume = (Volume *)ns;
// lock the volume
VolumeReadLocker locker(volume);
if (!locker.IsLocked())
RETURN_ERROR(B_ERROR);
// parse the query expression
Expression *expression = new Expression((char *)queryString);
if (expression == NULL)
RETURN_ERROR(B_NO_MEMORY);
ObjectDeleter<Expression> expressionDeleter(expression);
if (expression->InitCheck() < B_OK) {
FATAL(("Could not parse query, stopped at: \"%s\"\n", expression->Position()));
delete expression;
WARN(("Could not parse query, stopped at: \"%s\"\n",
expression->Position()));
RETURN_ERROR(B_BAD_VALUE);
}
// create the query
Query *query = new Query(volume, expression, flags);
if (query == NULL) {
delete expression;
if (query == NULL)
RETURN_ERROR(B_NO_MEMORY);
}
expressionDeleter.Detach();
// TODO: The Query references an Index, but nothing prevents the Index
// from being deleted, while the Query is in existence.
if (flags & B_LIVE_QUERY)
query->SetLiveMode(port, token);
@ -1956,12 +2004,19 @@ ramfs_close_query(void */*ns*/, void */*cookie*/)
// ramfs_free_query_cookie
int
ramfs_free_query_cookie(void */*ns*/, void */*node*/, void *cookie)
ramfs_free_query_cookie(void *ns, void */*node*/, void *cookie)
{
FUNCTION_START();
if (cookie == NULL)
if (ns == NULL || cookie == NULL)
RETURN_ERROR(B_BAD_VALUE);
Volume *volume = (Volume *)ns;
// lock the volume
VolumeReadLocker locker(volume);
if (!locker.IsLocked())
RETURN_ERROR(B_ERROR);
Query *query = (Query *)cookie;
Expression *expression = query->GetExpression();
delete query;
@ -1972,14 +2027,21 @@ ramfs_free_query_cookie(void */*ns*/, void */*node*/, void *cookie)
// ramfs_read_query
int
ramfs_read_query(void */*ns*/, void *cookie, long *count,
ramfs_read_query(void *ns, void *cookie, long *count,
struct dirent *buffer, size_t bufferSize)
{
FUNCTION_START();
Query *query = (Query *)cookie;
if (query == NULL)
if (ns == NULL || query == NULL)
RETURN_ERROR(B_BAD_VALUE);
Volume *volume = (Volume *)ns;
// lock the volume
VolumeReadLocker locker(volume);
if (!locker.IsLocked())
RETURN_ERROR(B_ERROR);
status_t status = query->GetNextEntry(buffer, bufferSize);
if (status == B_OK)
*count = 1;