kernel & file_systems: Overhaul query attribute change notifications.

* Introduce notify_query_entry_moved to the fs_interface API.

   axeld added a hook to the query parser (then in BFS) in hrev34317
   to directly handle moves/renames, but just sent notifications for
   it as B_ENTRY_REMOVED + B_ENTRY_CREATED (which is in fact the same
   exact thing BeOS R5 did, I tested). We need to preserve that ABI/API,
   unless flags that didn't exist on BeOS R5 are set.

 * Rework notify_query_attr_changed to notify_query_attribute_changed.
   This method is Haiku-specific (it was added in hrev39131), but it
   wasn't very useful: it only sent the name of the file, not the name
   of the attribute that changed.

   It now sends the name of the attribute along with an "int32 cause",
   same as notify_attribute_changed does.

 * Rename the private flag B_ATTR_CHANGE_NOTIFICATION to B_QUERY_WATCH_ALL.
   This better describes what it does (it's analogous to the B_WATCH_ALL
   node_monitor flag). Probably we should make it public at some point...
   (although probably at a lower value.)

 * Overhaul QueryParser notifications logic to implement WATCH_ALL
   properly, make use of the new and improved notification methods,
   and to deduplify some logic.

This makes it possible to use queries with B_QUERY_WATCH_ALL
in a similar fashion to watching a directory node with B_WATCH_CHILDREN,
avoiding the need to watch every single node individually.
This commit is contained in:
Augustin Cavalier
2025-04-30 17:12:49 -04:00
parent 7998bc4204
commit 7e740f61ac
7 changed files with 248 additions and 130 deletions
+7 -3
View File
@@ -366,12 +366,16 @@ extern status_t notify_attribute_changed(dev_t device, ino_t directory,
extern status_t notify_query_entry_created(port_id port, int32 token,
dev_t device, ino_t directory, const char* name,
ino_t node);
extern status_t notify_query_entry_moved(port_id port, int32 token,
dev_t device, ino_t fromDirectory,
const char* fromName, ino_t toDirectory,
const char* toName, ino_t node);
extern status_t notify_query_entry_removed(port_id port, int32 token,
dev_t device, ino_t directory, const char* name,
ino_t node);
extern status_t notify_query_attr_changed(port_id port, int32 token,
dev_t device, ino_t directory, const char* name,
ino_t node);
extern status_t notify_query_attribute_changed(port_id port, int32 token,
dev_t device, ino_t directory, ino_t node,
const char* attribute, int32 cause);
#ifdef __cplusplus
}
+101 -66
View File
@@ -34,6 +34,7 @@
# include <fs_interface.h>
# include <fs_query.h>
# include <NodeMonitor.h>
# include <TypeConstants.h>
# include <util/SinglyLinkedList.h>
@@ -147,9 +148,13 @@ public:
private:
status_t _GetNextEntry(struct dirent* dirent, size_t size);
void _EvaluateLiveUpdate(Entry* entry, Node* node,
const char* attribute, int32 type,
const uint8* oldKey, size_t oldLength,
const uint8* newKey, size_t newLength,
int32& opcode);
void _SendEntryNotification(Entry* entry,
status_t (*notify)(port_id, int32, dev_t, ino_t,
const char*, ino_t));
int32 opcode, const char* attribute, int32 cause);
private:
Context* fContext;
@@ -1542,14 +1547,12 @@ Query<QueryPolicy>::LiveUpdate(Entry* entry, Node* node, const char* attribute,
if (fPort < 0 || fExpression == NULL || attribute == NULL)
return;
// TODO: check if the attribute is part of the query at all...
// 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 == NULL && fNeedsEntry) {
entry = QueryPolicy::NodeGetFirstReferrer(node);
while (entry) {
while (entry != NULL) {
LiveUpdate(entry, node, attribute, type, oldKey, oldLength, newKey,
newLength);
entry = QueryPolicy::NodeGetNextReferrer(node, entry);
@@ -1557,45 +1560,27 @@ Query<QueryPolicy>::LiveUpdate(Entry* entry, Node* node, const char* attribute,
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);
int32 opcode = -1;
_EvaluateLiveUpdate(entry, node, attribute, type, oldKey, oldLength,
newKey, newLength, opcode);
bool entryCreated = false;
bool stillInQuery = false;
if (oldStatus != MATCH_OK) {
if (newStatus != MATCH_OK) {
// nothing has changed
return;
}
entryCreated = true;
} else if (newStatus != MATCH_OK) {
// entry got removed
entryCreated = false;
} else if ((fFlags & B_ATTR_CHANGE_NOTIFICATION) != 0) {
// The entry stays in the query
stillInQuery = true;
} else
if (opcode <= 0)
return;
// notify query listeners
status_t (*notify)(port_id, int32, dev_t, ino_t, const char*, ino_t);
if (stillInQuery)
notify = notify_query_attr_changed;
else if (entryCreated)
notify = notify_query_entry_created;
else
notify = notify_query_entry_removed;
int32 cause = B_ATTR_CHANGED;
if (opcode == B_ATTR_CHANGED) {
if (oldKey == NULL && newKey != NULL)
cause = B_ATTR_CREATED;
else if (oldKey != NULL && newKey == NULL)
cause = B_ATTR_REMOVED;
}
if (entry != NULL) {
_SendEntryNotification(entry, notify);
_SendEntryNotification(entry, opcode, attribute, cause);
} else {
entry = QueryPolicy::NodeGetFirstReferrer(node);
while (entry) {
_SendEntryNotification(entry, notify);
while (entry != NULL) {
_SendEntryNotification(entry, opcode, attribute, cause);
entry = QueryPolicy::NodeGetNextReferrer(node, entry);
}
}
@@ -1611,31 +1596,66 @@ Query<QueryPolicy>::LiveUpdateRenameMove(Entry* entry, Node* node,
if (fPort < 0 || fExpression == NULL)
return;
// TODO: check if the attribute is part of the query at all...
int32 opcode = -1;
_EvaluateLiveUpdate(entry, node, "name", B_STRING_TYPE,
(const uint8*)oldName, oldLength, (const uint8*)newName, newLength,
opcode);
status_t oldStatus = fExpression->Root()->Match(entry, node, "name",
B_STRING_TYPE, (const uint8*)oldName, oldLength);
status_t newStatus = fExpression->Root()->Match(entry, node, "name",
B_STRING_TYPE, (const uint8*)newName, newLength);
if (oldStatus != MATCH_OK || oldStatus != newStatus)
if (opcode < 0)
return;
// The entry stays in the query, notify query listeners about the rename
// or move
// We send a notification for the given entry, if any, or otherwise for
// all entries referring to the node;
if (entry != NULL) {
_SendEntryNotification(entry, notify_query_entry_removed);
_SendEntryNotification(entry, notify_query_entry_created);
} else {
entry = QueryPolicy::NodeGetFirstReferrer(node);
while (entry) {
_SendEntryNotification(entry, notify_query_entry_removed);
_SendEntryNotification(entry, notify_query_entry_created);
entry = QueryPolicy::NodeGetNextReferrer(node, entry);
if (opcode == 0 || opcode == B_ATTR_CHANGED) {
if ((fFlags & B_QUERY_WATCH_ALL) != 0) {
// In this case, we can send B_ENTRY_MOVED.
notify_query_entry_moved(fPort, fToken, QueryPolicy::ContextGetVolumeID(fContext),
oldDirectoryID, oldName, newDirectoryID, newName,
QueryPolicy::EntryGetNodeID(entry));
} else {
// Just like BeOS, send B_ENTRY_REMOVED and then B_ENTRY_CREATED.
notify_query_entry_removed(fPort, fToken, QueryPolicy::ContextGetVolumeID(fContext),
oldDirectoryID, oldName, QueryPolicy::EntryGetNodeID(entry));
notify_query_entry_created(fPort, fToken, QueryPolicy::ContextGetVolumeID(fContext),
newDirectoryID, newName, QueryPolicy::EntryGetNodeID(entry));
}
return;
}
_SendEntryNotification(entry, opcode, NULL, B_ATTR_CHANGED);
}
template<typename QueryPolicy>
void
Query<QueryPolicy>::_EvaluateLiveUpdate(Entry* entry, Node* node, const char* attribute,
int32 type, const uint8* oldKey, size_t oldLength, const uint8* newKey,
size_t newLength, int32& opcode)
{
if (fPort < 0 || fExpression == NULL)
return;
// TODO: check if the attribute is part of the query at all...
status_t oldStatus = fExpression->Root()->Match(entry, node, attribute,
type, oldKey, oldLength);
status_t newStatus = fExpression->Root()->Match(entry, node, attribute,
type, newKey, newLength);
if (oldStatus != MATCH_OK) {
if (newStatus != MATCH_OK) {
// wasn't in query, and still isn't
return;
}
// entry was added
opcode = B_ENTRY_CREATED;
} else if (newStatus != MATCH_OK) {
// entry was removed
opcode = B_ENTRY_REMOVED;
} else if ((fFlags & B_QUERY_WATCH_ALL) != 0) {
// still in query, all attribute changes watched
opcode = B_ATTR_CHANGED;
} else {
// still in query, no change to notify
opcode = 0;
}
}
@@ -1681,15 +1701,30 @@ Query<QueryPolicy>::_GetNextEntry(struct dirent* dirent, size_t size)
template<typename QueryPolicy>
void
Query<QueryPolicy>::_SendEntryNotification(Entry* entry,
status_t (*notify)(port_id, int32, dev_t, ino_t, const char*, ino_t))
Query<QueryPolicy>::_SendEntryNotification(Entry* entry, int32 opcode,
const char* attribute, int32 cause)
{
NodeHolder nodeHolder;
const char* name = QueryPolicy::EntryGetNameNoCopy(nodeHolder, entry);
if (name != NULL) {
notify(fPort, fToken, QueryPolicy::ContextGetVolumeID(fContext),
QueryPolicy::EntryGetParentID(entry), name,
QueryPolicy::EntryGetNodeID(entry));
switch (opcode) {
case B_ENTRY_CREATED:
case B_ENTRY_REMOVED:
{
NodeHolder nodeHolder;
const char* name = QueryPolicy::EntryGetNameNoCopy(nodeHolder, entry);
if (name != NULL) {
((opcode == B_ENTRY_CREATED) ?
notify_query_entry_created : notify_query_entry_removed)
(fPort, fToken, QueryPolicy::ContextGetVolumeID(fContext),
QueryPolicy::EntryGetParentID(entry), name,
QueryPolicy::EntryGetNodeID(entry));
}
break;
}
case B_ATTR_CHANGED:
notify_query_attribute_changed(fPort, fToken, QueryPolicy::ContextGetVolumeID(fContext),
QueryPolicy::EntryGetParentID(entry), QueryPolicy::EntryGetNodeID(entry),
attribute, cause);
break;
}
}
+2 -1
View File
@@ -964,8 +964,9 @@
#define notify_attribute_changed fssh_notify_attribute_changed
#define notify_query_entry_created fssh_notify_query_entry_created
#define notify_query_entry_moved fssh_notify_query_entry_moved
#define notify_query_entry_removed fssh_notify_query_entry_removed
#define notify_query_attr_changed fssh_notify_query_attr_changed
#define notify_query_attribute_changed fssh_notify_query_attribute_changed
////////////////////////////////////////////////////////////////////////////////
+7 -3
View File
@@ -404,14 +404,18 @@ extern fssh_status_t fssh_notify_query_entry_created(fssh_port_id port,
int32_t token, fssh_mount_id device,
fssh_vnode_id directory, const char *name,
fssh_vnode_id node);
extern fssh_status_t fssh_notify_query_entry_moved(fssh_port_id port, int32_t token,
fssh_mount_id device, fssh_vnode_id fromDirectory,
const char* fromName, fssh_vnode_id toDirectory,
const char* toName, fssh_vnode_id node);
extern fssh_status_t fssh_notify_query_entry_removed(fssh_port_id port,
int32_t token, fssh_mount_id device,
fssh_vnode_id directory, const char *name,
fssh_vnode_id node);
extern fssh_status_t fssh_notify_query_attr_changed(fssh_port_id port,
extern fssh_status_t fssh_notify_query_attribute_changed(fssh_port_id port,
int32_t token, fssh_mount_id device,
fssh_vnode_id directory, const char *name,
fssh_vnode_id node);
fssh_vnode_id directory, fssh_vnode_id node,
const char *attribute, int32_t cause);
#ifdef __cplusplus
}
+4 -4
View File
@@ -2,9 +2,9 @@
#define QUERY_PRIVATE_H
// If an entry is already in a query and a attribute changed
// B_ATTR_CHANGE_NOTIFICATION tells the query to send B_ATTR_CHANGED
// notifications if the entry stays in the query.
#define B_ATTR_CHANGE_NOTIFICATION 0x0000F000
// Send node monitor (B_ATTR_CHANGED, etc.) events for all
// attributes on all entries in the query.
#define B_QUERY_WATCH_ALL 0x0000F000
#endif