* 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
@@ -52,19 +52,30 @@ status_t
Attribute::WriteAt(off_t offset, const void *buffer, size_t size, Attribute::WriteAt(off_t offset, const void *buffer, size_t size,
size_t *bytesWritten) size_t *bytesWritten)
{ {
status_t error = B_OK; // get the current key for the attribute
if (offset < kMaxIndexKeyLength && size > 0 && fIndex) { uint8 oldKey[kMaxIndexKeyLength];
// There is an index and a change will be made within the key. size_t oldLength;
uint8 oldKey[kMaxIndexKeyLength]; GetKey(oldKey, &oldLength);
size_t oldLength;
GetKey(oldKey, &oldLength); // write the new value
error = DataContainer::WriteAt(offset, buffer, size, bytesWritten); 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); 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 // node has been changed
if (fNode) if (fNode && size > 0)
fNode->MarkModified(); fNode->MarkModified();
return error; return error;
} }
@@ -156,17 +156,17 @@ Directory::CreateEntry(Node *node, const char *name, Entry **_entry)
if (entry) { if (entry) {
error = entry->InitCheck(); error = entry->InitCheck();
if (error == B_OK) { if (error == B_OK) {
// add the entry // link to the node
error = AddEntry(entry); error = entry->Link(node);
if (error == B_OK) { if (error == B_OK) {
// link to the node // add the entry
error = entry->Link(node); error = AddEntry(entry);
if (error == B_OK) { if (error == B_OK) {
if (_entry) if (_entry)
*_entry = entry; *_entry = entry;
} else { } else {
// failure: remove the entry // failure: unlink the node
RemoveEntry(entry); entry->Unlink();
} }
} }
} }
@@ -157,9 +157,15 @@ LastModifiedIndex::Changed(Node *node, time_t oldModified)
if (iterator->GetCurrentNode() == node) if (iterator->GetCurrentNode() == node)
iterator->NodeRemoved(node); iterator->NodeRemoved(node);
} }
// remove the node // remove and re-insert the node
fNodes->Remove(it); fNodes->Remove(it);
error = fNodes->Insert(node); 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; return error;
@@ -139,6 +139,9 @@ NameIndex::Changed(Entry *entry, const char *oldName)
if (foundEntry && *foundEntry == entry) { if (foundEntry && *foundEntry == entry) {
fEntries->Remove(it); fEntries->Remove(it);
error = fEntries->Insert(entry); error = fEntries->Insert(entry);
// udpate live queries
_UpdateLiveQueries(entry, oldName, entry->GetName());
} }
} }
return error; return error;
@@ -148,16 +151,24 @@ NameIndex::Changed(Entry *entry, const char *oldName)
void void
NameIndex::EntryAdded(Entry *entry) NameIndex::EntryAdded(Entry *entry)
{ {
if (entry) if (entry) {
fEntries->Insert(entry); fEntries->Insert(entry);
// udpate live queries
_UpdateLiveQueries(entry, NULL, entry->GetName());
}
} }
// EntryRemoved // EntryRemoved
void void
NameIndex::EntryRemoved(Entry *entry) NameIndex::EntryRemoved(Entry *entry)
{ {
if (entry) if (entry) {
fEntries->Remove(entry, entry); fEntries->Remove(entry, entry);
// udpate live queries
_UpdateLiveQueries(entry, entry->GetName(), NULL);
}
} }
// InternalGetIterator // InternalGetIterator
@@ -190,6 +201,16 @@ NameIndex::InternalFind(const uint8 *key, size_t length)
return iterator; 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 // NameIndexEntryIterator
@@ -32,6 +32,9 @@ private:
class EntryTree; class EntryTree;
friend class NameIndexEntryIterator; friend class NameIndexEntryIterator;
void _UpdateLiveQueries(Entry* entry, const char* oldName,
const char* newName);
private: private:
EntryTree *fEntries; EntryTree *fEntries;
}; };
@@ -128,8 +128,6 @@ IndexIterator::GetNextEntry(uint8 *buffer, uint16 *_keyLength,
} }
// compare_integral // compare_integral
template<typename Key> template<typename Key>
static inline static inline
@@ -258,8 +256,9 @@ class Term {
void SetParent(Term *parent) { fParent = parent; } void SetParent(Term *parent) { fParent = parent; }
Term *Parent() const { return fParent; } Term *Parent() const { return fParent; }
virtual status_t Match(Entry *entry,const char *attribute = NULL,int32 type = 0, virtual status_t Match(Entry *entry, Node* node,
const uint8 *key = NULL,size_t size = 0) = 0; const char *attribute = NULL, int32 type = 0,
const uint8 *key = NULL, size_t size = 0) = 0;
virtual void Complement() = 0; virtual void Complement() = 0;
virtual void CalculateScore(IndexWrapper &index) = 0; virtual void CalculateScore(IndexWrapper &index) = 0;
@@ -267,6 +266,8 @@ class Term {
virtual status_t InitCheck() = 0; virtual status_t InitCheck() = 0;
virtual bool NeedsEntry() = 0;
#ifdef DEBUG #ifdef DEBUG
virtual void PrintToStream() = 0; virtual void PrintToStream() = 0;
#endif #endif
@@ -295,8 +296,9 @@ class Equation : public Term {
status_t ParseQuotedString(char **_start, char **_end); status_t ParseQuotedString(char **_start, char **_end);
char *CopyString(char *start, char *end); char *CopyString(char *start, char *end);
virtual status_t Match(Entry *entry, const char *attribute = NULL, int32 type = 0, virtual status_t Match(Entry *entry, Node* node,
const uint8 *key = NULL, size_t size = 0); const char *attribute = NULL, int32 type = 0,
const uint8 *key = NULL, size_t size = 0);
virtual void Complement(); virtual void Complement();
status_t PrepareQuery(Volume *volume, IndexWrapper &index, IndexIterator **iterator, status_t PrepareQuery(Volume *volume, IndexWrapper &index, IndexIterator **iterator,
@@ -307,6 +309,8 @@ class Equation : public Term {
virtual void CalculateScore(IndexWrapper &index); virtual void CalculateScore(IndexWrapper &index);
virtual int32 Score() const { return fScore; } virtual int32 Score() const { return fScore; }
virtual bool NeedsEntry();
#ifdef DEBUG #ifdef DEBUG
virtual void PrintToStream(); virtual void PrintToStream();
#endif #endif
@@ -340,7 +344,8 @@ class Operator : public Term {
Term *Left() const { return fLeft; } Term *Left() const { return fLeft; }
Term *Right() const { return fRight; } 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); const uint8 *key = NULL, size_t size = 0);
virtual void Complement(); virtual void Complement();
@@ -349,6 +354,8 @@ class Operator : public Term {
virtual status_t InitCheck(); virtual status_t InitCheck();
virtual bool NeedsEntry();
//Term *Copy() const; //Term *Copy() const;
#ifdef DEBUG #ifdef DEBUG
virtual void PrintToStream(); virtual void PrintToStream();
@@ -940,7 +947,8 @@ Equation::MatchEmptyString()
*/ */
status_t 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 // get a pointer to the attribute in question
union value value; 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 // first, check if we are matching for a live query and use that value
if (attributeName != NULL && !strcmp(fAttribute, attributeName)) { if (attributeName != NULL && !strcmp(fAttribute, attributeName)) {
if (key == NULL) { 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 MatchEmptyString();
}
return NO_MATCH; return NO_MATCH;
} }
buffer = const_cast<uint8 *>(key); buffer = const_cast<uint8 *>(key);
} else if (!strcmp(fAttribute, "name")) { } else if (!strcmp(fAttribute, "name")) {
// if not, check for "fake" attributes, "name", "size", "last_modified", // if not, check for "fake" attributes, "name", "size", "last_modified",
if (!entry)
return B_ERROR;
buffer = (uint8 *)entry->GetName(); buffer = (uint8 *)entry->GetName();
if (buffer == NULL) if (buffer == NULL)
return B_ERROR; return B_ERROR;
@@ -964,18 +981,18 @@ Equation::Match(Entry *entry, const char *attributeName, int32 type, const uint8
type = B_STRING_TYPE; type = B_STRING_TYPE;
size = strlen((const char *)buffer); size = strlen((const char *)buffer);
} else if (!strcmp(fAttribute,"size")) { } else if (!strcmp(fAttribute,"size")) {
value.Int64 = entry->GetNode()->GetSize(); value.Int64 = node->GetSize();
buffer = (uint8 *)&value; buffer = (uint8 *)&value;
type = B_INT64_TYPE; type = B_INT64_TYPE;
} else if (!strcmp(fAttribute,"last_modified")) { } else if (!strcmp(fAttribute,"last_modified")) {
value.Int32 = entry->GetNode()->GetMTime(); value.Int32 = node->GetMTime();
buffer = (uint8 *)&value; buffer = (uint8 *)&value;
type = B_INT32_TYPE; type = B_INT32_TYPE;
} else { } else {
// then for attributes // then for attributes
Attribute *attribute = NULL; Attribute *attribute = NULL;
if (entry->GetNode()->FindAttribute(fAttribute, &attribute) == B_OK) { if (node->FindAttribute(fAttribute, &attribute) == B_OK) {
attribute->GetKey(&buffer, &size); attribute->GetKey(&buffer, &size);
type = attribute->GetType(); type = attribute->GetType();
} else } else
@@ -1151,7 +1168,7 @@ Equation::GetNextMatching(Volume *volume, IndexIterator *iterator,
status = MATCH_OK; status = MATCH_OK;
if (!fHasIndex) if (!fHasIndex)
status = Match(entry); status = Match(entry, entry->GetNode());
while (term != NULL && status == MATCH_OK) { while (term != NULL && status == MATCH_OK) {
Operator *parent = (Operator *)term->Parent(); 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)); FATAL(("&&-operator has only one child... (parent = %p)\n", parent));
break; break;
} }
status = other->Match(entry); status = other->Match(entry, entry->GetNode());
if (status < 0) { if (status < 0) {
REPORT_ERROR(status); REPORT_ERROR(status);
status = NO_MATCH; status = NO_MATCH;
@@ -1196,6 +1213,13 @@ Equation::GetNextMatching(Volume *volume, IndexIterator *iterator,
} }
bool
Equation::NeedsEntry()
{
return strcmp(fAttribute, "name") == 0;
}
// #pragma mark - // #pragma mark -
@@ -1219,22 +1243,24 @@ Operator::~Operator()
status_t 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) { 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) if (status != MATCH_OK)
return status; return status;
return fRight->Match(entry, attribute, type, key, size); return fRight->Match(entry, node, attribute, type, key, size);
} else { } else {
// choose the term with the better score for OP_OR // choose the term with the better score for OP_OR
if (fRight->Score() > fLeft->Score()) { 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) if (status != NO_MATCH)
return status; 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 #if 0
Term * Term *
Operator::Copy() const Operator::Copy() const
@@ -1529,7 +1562,8 @@ Query::Query(Volume *volume, Expression *expression, uint32 flags)
fIterator(NULL), fIterator(NULL),
fIndex(volume), fIndex(volume),
fFlags(flags), fFlags(flags),
fPort(-1) fPort(-1),
fNeedsEntry(false)
{ {
// if the expression has a valid root pointer, the whole tree has // 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 // 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); fExpression->Root()->CalculateScore(fIndex);
fIndex.Unset(); fIndex.Unset();
fNeedsEntry = fExpression->Root()->NeedsEntry();
Rewind(); Rewind();
if (fFlags & B_LIVE_QUERY) if (fFlags & B_LIVE_QUERY)
@@ -1639,16 +1675,35 @@ Query::SetLiveMode(port_id port, int32 token)
void void
Query::LiveUpdate(Entry *entry, const char *attribute, int32 type, const uint8 *oldKey, Query::LiveUpdate(Entry *entry, Node* node, const char *attribute, int32 type,
size_t oldLength, const uint8 *newKey, size_t newLength) 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; return;
// ToDo: check if the attribute is part of the query at all... // ToDo: check if the attribute is part of the query at all...
status_t oldStatus = fExpression->Root()->Match(entry, attribute, type, oldKey, oldLength); // If no entry has been supplied, but the we need one for the evaluation
status_t newStatus = fExpression->Root()->Match(entry, attribute, type, newKey, newLength); // (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; int32 op;
if (oldStatus == MATCH_OK && newStatus == MATCH_OK) { 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")) if (oldKey == NULL || strcmp(attribute,"name"))
return; return;
send_notification(fPort, fToken, B_QUERY_UPDATE, B_ENTRY_REMOVED, fVolume->GetID(), 0, if (entry) {
entry->GetParent()->GetID(), 0, entry->GetNode()->GetID(), (const char *)oldKey); // 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; op = B_ENTRY_CREATED;
} else if (oldStatus != MATCH_OK && newStatus != MATCH_OK) { } else if (oldStatus != MATCH_OK && newStatus != MATCH_OK) {
// nothing has changed // nothing has changed
@@ -1667,12 +1728,21 @@ Query::LiveUpdate(Entry *entry, const char *attribute, int32 type, const uint8 *
else else
op = B_ENTRY_CREATED; op = B_ENTRY_CREATED;
// if "value" is NULL, send_notification() crashes... // We send a notification for the given entry, if any, or otherwise for
const char *value = (const char *)newKey; // all entries referring to the node;
if (type != B_STRING_TYPE || value == NULL) if (entry) {
value = ""; 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(),
send_notification(fPort, fToken, B_QUERY_UPDATE, op, fVolume->GetID(), 0, 0, entry->GetParent()->GetID(), 0, entry->GetNode()->GetID(),
entry->GetParent()->GetID(), 0, entry->GetNode()->GetID(), value); 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);
}
}
} }
@@ -12,6 +12,7 @@
#include <OS.h> #include <OS.h>
#include <SupportDefs.h> #include <SupportDefs.h>
#include "DLList.h"
#include "Index.h" #include "Index.h"
#include "Stack.h" #include "Stack.h"
#include "ramfs.h" #include "ramfs.h"
@@ -19,6 +20,7 @@
class Entry; class Entry;
class Equation; class Equation;
class IndexIterator; class IndexIterator;
class Node;
class Query; class Query;
class Term; class Term;
class Volume; class Volume;
@@ -90,7 +92,7 @@ class Expression {
Term *fTerm; Term *fTerm;
}; };
class Query { class Query : public DLListLinkImpl<Query> {
public: public:
Query(Volume *volume, Expression *expression, uint32 flags); Query(Volume *volume, Expression *expression, uint32 flags);
~Query(); ~Query();
@@ -99,11 +101,15 @@ class Query {
status_t GetNextEntry(struct dirent *, size_t size); status_t GetNextEntry(struct dirent *, size_t size);
void SetLiveMode(port_id port, int32 token); void SetLiveMode(port_id port, int32 token);
void LiveUpdate(Entry *entry, const char *attribute, int32 type, void LiveUpdate(Entry *entry, Node* node, const char *attribute,
const uint8 *oldKey, size_t oldLength, const uint8 *newKey, size_t newLength); int32 type, const uint8 *oldKey, size_t oldLength,
const uint8 *newKey, size_t newLength);
Expression *GetExpression() const { return fExpression; } Expression *GetExpression() const { return fExpression; }
private:
// void SendNotification(Entry* entry)
private: private:
Volume *fVolume; Volume *fVolume;
Expression *fExpression; Expression *fExpression;
@@ -115,6 +121,7 @@ class Query {
uint32 fFlags; uint32 fFlags;
port_id fPort; port_id fPort;
int32 fToken; int32 fToken;
bool fNeedsEntry;
}; };
#endif /* QUERY_H */ #endif /* QUERY_H */
@@ -155,9 +155,16 @@ SizeIndex::Changed(Node *node, off_t oldSize)
if (iterator->GetCurrentNode() == node) if (iterator->GetCurrentNode() == node)
iterator->NodeRemoved(node); iterator->NodeRemoved(node);
} }
// remove the node
// remove and re-insert the node
fNodes->Remove(it); fNodes->Remove(it);
error = fNodes->Insert(node); 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; return error;
@@ -33,6 +33,7 @@
#include "Entry.h" #include "Entry.h"
#include "EntryListener.h" #include "EntryListener.h"
#include "IndexDirectory.h" #include "IndexDirectory.h"
#include "Locking.h"
#include "Misc.h" #include "Misc.h"
#include "NameIndex.h" #include "NameIndex.h"
#include "Node.h" #include "Node.h"
@@ -140,8 +141,9 @@ Volume::Volume()
fIndexDirectory(NULL), fIndexDirectory(NULL),
fRootDirectory(NULL), fRootDirectory(NULL),
fName(kDefaultVolumeName), fName(kDefaultVolumeName),
fLocker(), fLocker("volume"),
fIteratorLocker(), fIteratorLocker("iterators"),
fQueryLocker("queries"),
fNodeListeners(NULL), fNodeListeners(NULL),
fAnyNodeListeners(), fAnyNodeListeners(),
fEntryListeners(NULL), fEntryListeners(NULL),
@@ -165,6 +167,15 @@ status_t
Volume::Mount(nspace_id id) Volume::Mount(nspace_id id)
{ {
Unmount(); 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; status_t error = B_OK;
fID = id; fID = id;
// create a block allocator // create a block allocator
@@ -659,6 +670,15 @@ Volume::NodeAttributeRemoved(vnode_id id, Attribute *attribute)
index->Removed(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; return error;
} }
@@ -712,6 +732,42 @@ Volume::FindAttributeIndex(const char *name, uint32 type)
? fIndexDirectory->FindAttributeIndex(name, type) : NULL); ? 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 // AllocateBlock
status_t status_t
Volume::AllocateBlock(size_t size, BlockReference **block) Volume::AllocateBlock(size_t size, BlockReference **block)
@@ -22,12 +22,14 @@
#ifndef VOLUME_H #ifndef VOLUME_H
#define VOLUME_H #define VOLUME_H
#include <fsproto.h>
#include <SupportDefs.h> #include <SupportDefs.h>
#include "DLList.h"
#include "Entry.h" #include "Entry.h"
#include "fsproto.h"
#include "List.h" #include "List.h"
#include "Locker.h" #include "Locker.h"
#include "Query.h"
#include "String.h" #include "String.h"
class AllocationInfo; class AllocationInfo;
@@ -48,7 +50,6 @@ class NodeAttributeTable;
class NodeListener; class NodeListener;
class NodeListenerTree; class NodeListenerTree;
class NodeTable; class NodeTable;
class Query;
class SizeIndex; class SizeIndex;
const vnode_id kRootParentID = 0; const vnode_id kRootParentID = 0;
@@ -146,11 +147,11 @@ public:
AttributeIndex *FindAttributeIndex(const char *name, uint32 type); AttributeIndex *FindAttributeIndex(const char *name, uint32 type);
// queries // queries
// TODO: Implement. void AddQuery(Query *query);
// status_t AddQuery(Query */*query*/) { return B_ERROR; } void RemoveQuery(Query *query);
// status_t RemoveQuery(Query */*query*/) { return B_ERROR; } void UpdateLiveQueries(Entry *entry, Node* node, const char *attribute,
status_t AddQuery(Query */*query*/) { return B_OK; } int32 type, const uint8 *oldKey, size_t oldLength,
status_t RemoveQuery(Query */*query*/) { return B_OK; } const uint8 *newKey, size_t newLength);
vnode_id NextNodeID() { return fNextNodeID++; } vnode_id NextNodeID() { return fNextNodeID++; }
@@ -173,6 +174,8 @@ public:
void IteratorUnlock(); void IteratorUnlock();
private: private:
typedef DLList<Query> QueryList;
nspace_id fID; nspace_id fID;
vnode_id fNextNodeID; vnode_id fNextNodeID;
NodeTable *fNodeTable; NodeTable *fNodeTable;
@@ -183,10 +186,12 @@ private:
String fName; String fName;
Locker fLocker; Locker fLocker;
Locker fIteratorLocker; Locker fIteratorLocker;
Locker fQueryLocker;
NodeListenerTree *fNodeListeners; NodeListenerTree *fNodeListeners;
NodeListenerList fAnyNodeListeners; NodeListenerList fAnyNodeListeners;
EntryListenerTree *fEntryListeners; EntryListenerTree *fEntryListeners;
EntryListenerList fAnyEntryListeners; EntryListenerList fAnyEntryListeners;
QueryList fQueries;
BlockAllocator *fBlockAllocator; BlockAllocator *fBlockAllocator;
off_t fBlockSize; off_t fBlockSize;
off_t fAllocatedBlocks; off_t fAllocatedBlocks;
@@ -43,6 +43,7 @@
#include "AllocationInfo.h" #include "AllocationInfo.h"
#include "AttributeIndex.h" #include "AttributeIndex.h"
#include "AttributeIterator.h" #include "AttributeIterator.h"
#include "AutoDeleter.h"
#include "Debug.h" #include "Debug.h"
#include "Directory.h" #include "Directory.h"
#include "Entry.h" #include "Entry.h"
@@ -249,6 +250,10 @@ notify_if_stat_changed(Volume *volume, Node *node)
} }
} }
// #pragma mark - FS
// ramfs_mount // ramfs_mount
static static
int int
@@ -323,6 +328,10 @@ ramfs_sync(void */*_ns*/)
return B_OK; return B_OK;
} }
// #pragma mark - VNodes
// ramfs_read_vnode // ramfs_read_vnode
static static
int int
@@ -375,6 +384,10 @@ FUNCTION(("node: %Ld\n", ((Node*)_node)->GetID()));
RETURN_ERROR(error); RETURN_ERROR(error);
} }
// #pragma mark - Nodes
// ramfs_walk // ramfs_walk
static static
int int
@@ -562,6 +575,10 @@ ramfs_write_stat(void *ns, void *_node, struct stat *st, long mask)
RETURN_ERROR(error); RETURN_ERROR(error);
} }
// #pragma mark - Files
// FileCookie // FileCookie
class FileCookie { class FileCookie {
public: public:
@@ -841,6 +858,11 @@ ramfs_access(void *ns, void *_node, int mode)
RETURN_ERROR(error); RETURN_ERROR(error);
} }
// #pragma mark - Directories
// ramfs_rename // ramfs_rename
static static
int int
@@ -1330,7 +1352,11 @@ ramfs_free_dir_cookie(void */*ns*/, void */*_node*/, void *_cookie)
delete cookie; delete cookie;
return B_OK; return B_OK;
} }
// #pragma mark - FS Stats
// ramfs_read_fs_stat // ramfs_read_fs_stat
static static
int int
@@ -1354,6 +1380,7 @@ ramfs_read_fs_stat(void *ns, struct fs_info *info)
return B_OK; return B_OK;
} }
// ramfs_write_fs_stat // ramfs_write_fs_stat
static static
int int
@@ -1370,6 +1397,10 @@ ramfs_write_fs_stat(void *ns, struct fs_info *info, long mask)
RETURN_ERROR(error); RETURN_ERROR(error);
} }
// #pragma mark - Symlinks
// ramfs_symlink // ramfs_symlink
static static
int int
@@ -1455,6 +1486,10 @@ ramfs_read_link(void *ns, void *_node, char *buffer, size_t *bufferSize)
RETURN_ERROR(error); RETURN_ERROR(error);
} }
// #pragma mark - Attributes
// ramfs_open_attrdir // ramfs_open_attrdir
static static
int int
@@ -1703,6 +1738,10 @@ ramfs_stat_attr(void *ns, void *_node, const char *name,
RETURN_ERROR(error); RETURN_ERROR(error);
} }
// #pragma mark - Indices
// IndexDirCookie // IndexDirCookie
class IndexDirCookie { class IndexDirCookie {
public: 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. // Query implementation by Axel Dörfler. Slightly adjusted.
//
// TODO: Locking!
// ramfs_open_query // ramfs_open_query
int int
@@ -1922,21 +1961,30 @@ ramfs_open_query(void *ns, const char *queryString, ulong flags, port_id port,
Volume *volume = (Volume *)ns; 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); Expression *expression = new Expression((char *)queryString);
if (expression == NULL) if (expression == NULL)
RETURN_ERROR(B_NO_MEMORY); RETURN_ERROR(B_NO_MEMORY);
ObjectDeleter<Expression> expressionDeleter(expression);
if (expression->InitCheck() < B_OK) { if (expression->InitCheck() < B_OK) {
FATAL(("Could not parse query, stopped at: \"%s\"\n", expression->Position())); WARN(("Could not parse query, stopped at: \"%s\"\n",
delete expression; expression->Position()));
RETURN_ERROR(B_BAD_VALUE); RETURN_ERROR(B_BAD_VALUE);
} }
// create the query
Query *query = new Query(volume, expression, flags); Query *query = new Query(volume, expression, flags);
if (query == NULL) { if (query == NULL)
delete expression;
RETURN_ERROR(B_NO_MEMORY); 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) if (flags & B_LIVE_QUERY)
query->SetLiveMode(port, token); query->SetLiveMode(port, token);
@@ -1956,12 +2004,19 @@ ramfs_close_query(void */*ns*/, void */*cookie*/)
// ramfs_free_query_cookie // ramfs_free_query_cookie
int int
ramfs_free_query_cookie(void */*ns*/, void */*node*/, void *cookie) ramfs_free_query_cookie(void *ns, void */*node*/, void *cookie)
{ {
FUNCTION_START(); FUNCTION_START();
if (cookie == NULL) if (ns == NULL || cookie == NULL)
RETURN_ERROR(B_BAD_VALUE); 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; Query *query = (Query *)cookie;
Expression *expression = query->GetExpression(); Expression *expression = query->GetExpression();
delete query; delete query;
@@ -1972,14 +2027,21 @@ ramfs_free_query_cookie(void */*ns*/, void */*node*/, void *cookie)
// ramfs_read_query // ramfs_read_query
int 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) struct dirent *buffer, size_t bufferSize)
{ {
FUNCTION_START(); FUNCTION_START();
Query *query = (Query *)cookie; Query *query = (Query *)cookie;
if (query == NULL) if (ns == NULL || query == NULL)
RETURN_ERROR(B_BAD_VALUE); 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); status_t status = query->GetNextEntry(buffer, bufferSize);
if (status == B_OK) if (status == B_OK)
*count = 1; *count = 1;