From 0d427dec4661085ad0818322b503b30408ee8299 Mon Sep 17 00:00:00 2001 From: Ingo Weinhold Date: Thu, 22 Feb 2007 23:30:39 +0000 Subject: [PATCH] * 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 --- .../r5/src/test/ramfs/Attribute.cpp | 31 ++-- .../r5/src/test/ramfs/Directory.cpp | 12 +- .../r5/src/test/ramfs/LastModifiedIndex.cpp | 8 +- .../r5/src/test/ramfs/NameIndex.cpp | 25 +++- .../userlandfs/r5/src/test/ramfs/NameIndex.h | 3 + .../userlandfs/r5/src/test/ramfs/Query.cpp | 138 +++++++++++++----- .../userlandfs/r5/src/test/ramfs/Query.h | 13 +- .../r5/src/test/ramfs/SizeIndex.cpp | 9 +- .../userlandfs/r5/src/test/ramfs/Volume.cpp | 60 +++++++- .../userlandfs/r5/src/test/ramfs/Volume.h | 19 ++- .../r5/src/test/ramfs/kernel_interface.cpp | 86 +++++++++-- 11 files changed, 326 insertions(+), 78 deletions(-) diff --git a/src/tests/add-ons/kernel/file_systems/userlandfs/r5/src/test/ramfs/Attribute.cpp b/src/tests/add-ons/kernel/file_systems/userlandfs/r5/src/test/ramfs/Attribute.cpp index 01a5bb9692..75f74e4d58 100644 --- a/src/tests/add-ons/kernel/file_systems/userlandfs/r5/src/test/ramfs/Attribute.cpp +++ b/src/tests/add-ons/kernel/file_systems/userlandfs/r5/src/test/ramfs/Attribute.cpp @@ -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; } diff --git a/src/tests/add-ons/kernel/file_systems/userlandfs/r5/src/test/ramfs/Directory.cpp b/src/tests/add-ons/kernel/file_systems/userlandfs/r5/src/test/ramfs/Directory.cpp index 6e7138a7f6..b936bd6082 100644 --- a/src/tests/add-ons/kernel/file_systems/userlandfs/r5/src/test/ramfs/Directory.cpp +++ b/src/tests/add-ons/kernel/file_systems/userlandfs/r5/src/test/ramfs/Directory.cpp @@ -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(); } } } diff --git a/src/tests/add-ons/kernel/file_systems/userlandfs/r5/src/test/ramfs/LastModifiedIndex.cpp b/src/tests/add-ons/kernel/file_systems/userlandfs/r5/src/test/ramfs/LastModifiedIndex.cpp index e731a9e11b..72152b37c1 100644 --- a/src/tests/add-ons/kernel/file_systems/userlandfs/r5/src/test/ramfs/LastModifiedIndex.cpp +++ b/src/tests/add-ons/kernel/file_systems/userlandfs/r5/src/test/ramfs/LastModifiedIndex.cpp @@ -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; diff --git a/src/tests/add-ons/kernel/file_systems/userlandfs/r5/src/test/ramfs/NameIndex.cpp b/src/tests/add-ons/kernel/file_systems/userlandfs/r5/src/test/ramfs/NameIndex.cpp index c84aeb1239..372db75f86 100644 --- a/src/tests/add-ons/kernel/file_systems/userlandfs/r5/src/test/ramfs/NameIndex.cpp +++ b/src/tests/add-ons/kernel/file_systems/userlandfs/r5/src/test/ramfs/NameIndex.cpp @@ -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 diff --git a/src/tests/add-ons/kernel/file_systems/userlandfs/r5/src/test/ramfs/NameIndex.h b/src/tests/add-ons/kernel/file_systems/userlandfs/r5/src/test/ramfs/NameIndex.h index cecc79e974..3503b7c961 100644 --- a/src/tests/add-ons/kernel/file_systems/userlandfs/r5/src/test/ramfs/NameIndex.h +++ b/src/tests/add-ons/kernel/file_systems/userlandfs/r5/src/test/ramfs/NameIndex.h @@ -32,6 +32,9 @@ private: class EntryTree; friend class NameIndexEntryIterator; + void _UpdateLiveQueries(Entry* entry, const char* oldName, + const char* newName); + private: EntryTree *fEntries; }; diff --git a/src/tests/add-ons/kernel/file_systems/userlandfs/r5/src/test/ramfs/Query.cpp b/src/tests/add-ons/kernel/file_systems/userlandfs/r5/src/test/ramfs/Query.cpp index 7f970a5b88..a1cea15070 100644 --- a/src/tests/add-ons/kernel/file_systems/userlandfs/r5/src/test/ramfs/Query.cpp +++ b/src/tests/add-ons/kernel/file_systems/userlandfs/r5/src/test/ramfs/Query.cpp @@ -128,8 +128,6 @@ IndexIterator::GetNextEntry(uint8 *buffer, uint16 *_keyLength, } - - // compare_integral template 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(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); + } + } } diff --git a/src/tests/add-ons/kernel/file_systems/userlandfs/r5/src/test/ramfs/Query.h b/src/tests/add-ons/kernel/file_systems/userlandfs/r5/src/test/ramfs/Query.h index 54b44ad45e..7d909d47f5 100644 --- a/src/tests/add-ons/kernel/file_systems/userlandfs/r5/src/test/ramfs/Query.h +++ b/src/tests/add-ons/kernel/file_systems/userlandfs/r5/src/test/ramfs/Query.h @@ -12,6 +12,7 @@ #include #include +#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 { 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 */ diff --git a/src/tests/add-ons/kernel/file_systems/userlandfs/r5/src/test/ramfs/SizeIndex.cpp b/src/tests/add-ons/kernel/file_systems/userlandfs/r5/src/test/ramfs/SizeIndex.cpp index 13403f549d..f59068a5ce 100644 --- a/src/tests/add-ons/kernel/file_systems/userlandfs/r5/src/test/ramfs/SizeIndex.cpp +++ b/src/tests/add-ons/kernel/file_systems/userlandfs/r5/src/test/ramfs/SizeIndex.cpp @@ -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; diff --git a/src/tests/add-ons/kernel/file_systems/userlandfs/r5/src/test/ramfs/Volume.cpp b/src/tests/add-ons/kernel/file_systems/userlandfs/r5/src/test/ramfs/Volume.cpp index 038d4b14a3..996a446bf1 100644 --- a/src/tests/add-ons/kernel/file_systems/userlandfs/r5/src/test/ramfs/Volume.cpp +++ b/src/tests/add-ons/kernel/file_systems/userlandfs/r5/src/test/ramfs/Volume.cpp @@ -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 _(fQueryLocker); + + if (query) + fQueries.Insert(query); +} + +// RemoveQuery +void +Volume::RemoveQuery(Query *query) +{ + AutoLocker _(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 _(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) diff --git a/src/tests/add-ons/kernel/file_systems/userlandfs/r5/src/test/ramfs/Volume.h b/src/tests/add-ons/kernel/file_systems/userlandfs/r5/src/test/ramfs/Volume.h index f78a325d7c..7e11712ea3 100644 --- a/src/tests/add-ons/kernel/file_systems/userlandfs/r5/src/test/ramfs/Volume.h +++ b/src/tests/add-ons/kernel/file_systems/userlandfs/r5/src/test/ramfs/Volume.h @@ -22,12 +22,14 @@ #ifndef VOLUME_H #define VOLUME_H +#include #include +#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 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; diff --git a/src/tests/add-ons/kernel/file_systems/userlandfs/r5/src/test/ramfs/kernel_interface.cpp b/src/tests/add-ons/kernel/file_systems/userlandfs/r5/src/test/ramfs/kernel_interface.cpp index f68909d6b6..207a9973c0 100644 --- a/src/tests/add-ons/kernel/file_systems/userlandfs/r5/src/test/ramfs/kernel_interface.cpp +++ b/src/tests/add-ons/kernel/file_systems/userlandfs/r5/src/test/ramfs/kernel_interface.cpp @@ -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 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;