diff --git a/headers/os/drivers/fs_interface.h b/headers/os/drivers/fs_interface.h index 96b69fab04..1720d1e06f 100644 --- a/headers/os/drivers/fs_interface.h +++ b/headers/os/drivers/fs_interface.h @@ -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 } diff --git a/headers/private/file_systems/QueryParser.h b/headers/private/file_systems/QueryParser.h index 6278c52b9e..e6184967a0 100644 --- a/headers/private/file_systems/QueryParser.h +++ b/headers/private/file_systems/QueryParser.h @@ -34,6 +34,7 @@ # include # include +# include # include # include @@ -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::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::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::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 +void +Query::_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::_GetNextEntry(struct dirent* dirent, size_t size) template void -Query::_SendEntryNotification(Entry* entry, - status_t (*notify)(port_id, int32, dev_t, ino_t, const char*, ino_t)) +Query::_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; } } diff --git a/headers/private/fs_shell/fssh_api_wrapper.h b/headers/private/fs_shell/fssh_api_wrapper.h index 88fa0743b8..36753bb049 100644 --- a/headers/private/fs_shell/fssh_api_wrapper.h +++ b/headers/private/fs_shell/fssh_api_wrapper.h @@ -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 //////////////////////////////////////////////////////////////////////////////// diff --git a/headers/private/fs_shell/fssh_fs_interface.h b/headers/private/fs_shell/fssh_fs_interface.h index 7d2d6fcc58..b916d6966d 100644 --- a/headers/private/fs_shell/fssh_fs_interface.h +++ b/headers/private/fs_shell/fssh_fs_interface.h @@ -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 } diff --git a/headers/private/storage/query_private.h b/headers/private/storage/query_private.h index d75fc08eb3..522447515c 100644 --- a/headers/private/storage/query_private.h +++ b/headers/private/storage/query_private.h @@ -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 diff --git a/src/system/kernel/fs/node_monitor.cpp b/src/system/kernel/fs/node_monitor.cpp index c3a3c95b0f..34106761c3 100644 --- a/src/system/kernel/fs/node_monitor.cpp +++ b/src/system/kernel/fs/node_monitor.cpp @@ -221,46 +221,6 @@ class NodeMonitorService : public NotificationService { static NodeMonitorService sNodeMonitorService; -/*! \brief Notifies the listener of a live query that an entry has been added - to or removed from or updated and still in the query (for whatever - reason). - \param opcode \c B_ENTRY_CREATED or \c B_ENTRY_REMOVED or \c B_ATTR_CHANGED. - \param port The target port of the listener. - \param token The BHandler token of the listener. - \param device The ID of the mounted FS, the entry lives in. - \param directory The entry's parent directory ID. - \param name The entry's name. - \param node The ID of the node the entry refers to. - \return - - \c B_OK, if everything went fine, - - another error code otherwise. -*/ -static status_t -notify_query_entry_event(int32 opcode, port_id port, int32 token, - dev_t device, ino_t directory, const char *name, ino_t node) -{ - if (!name) - return B_BAD_VALUE; - - // construct the message - char messageBuffer[1024]; - KMessage message; - message.SetTo(messageBuffer, sizeof(messageBuffer), B_QUERY_UPDATE); - message.AddInt32("opcode", opcode); - message.AddInt32("device", device); - message.AddInt64("directory", directory); - message.AddInt64("node", node); - message.AddString("name", name); - - // send the message - messaging_target target; - target.port = port; - target.token = token; - - return send_message(&message, &target, 1); -} - - // #pragma mark - NodeMonitorService @@ -1083,6 +1043,88 @@ NodeMonitorService::UpdateUserListener(io_context *context, dev_t device, } +static status_t +notify_query_entry_created_or_removed(int32 opcode, port_id port, int32 token, + dev_t device, ino_t directory, const char *name, ino_t node) +{ + if (!name) + return B_BAD_VALUE; + + // construct the message + char messageBuffer[1024]; + KMessage message; + message.SetTo(messageBuffer, sizeof(messageBuffer), B_QUERY_UPDATE); + message.AddInt32("opcode", opcode); + message.AddInt32("device", device); + message.AddInt64("directory", directory); + message.AddInt64("node", node); + message.AddString("name", name); + + // send the message + messaging_target target; + target.port = port; + target.token = token; + + return send_message(&message, &target, 1); +} + + +static status_t +notify_query_attr_changed(port_id port, int32 token, + dev_t device, ino_t directory, ino_t node, const char *attr, int32 cause) +{ + if (!attr) + return B_BAD_VALUE; + + // construct the message + char messageBuffer[1024]; + KMessage message; + message.SetTo(messageBuffer, sizeof(messageBuffer), B_QUERY_UPDATE); + message.AddInt32("opcode", B_ATTR_CHANGED); + message.AddInt32("device", device); + message.AddInt64("directory", directory); + message.AddInt64("node", node); + message.AddString("attr", attr); + message.AddInt32("cause", cause); + + // send the message + messaging_target target; + target.port = port; + target.token = token; + + return send_message(&message, &target, 1); +} + + +static status_t +notify_query_entry_moved(int32 opcode, port_id port, int32 token, + dev_t device, ino_t fromDirectory, const char *fromName, + ino_t toDirectory, const char* toName, ino_t node) +{ + if (!fromName || !toName) + return B_BAD_VALUE; + + // construct the message + char messageBuffer[1024]; + KMessage message; + message.SetTo(messageBuffer, sizeof(messageBuffer), B_QUERY_UPDATE); + message.AddInt32("opcode", opcode); + message.AddInt32("device", device); + message.AddInt64("from directory", fromDirectory); + message.AddInt64("to directory", toDirectory); + message.AddInt64("node", node); + message.AddString("from name", fromName); // Haiku only + message.AddString("name", toName); + + // send the message + messaging_target target; + target.port = port; + target.token = token; + + return send_message(&message, &target, 1); +} + + // #pragma mark - private kernel API @@ -1253,7 +1295,7 @@ status_t notify_query_entry_created(port_id port, int32 token, dev_t device, ino_t directory, const char *name, ino_t node) { - return notify_query_entry_event(B_ENTRY_CREATED, port, token, + return notify_query_entry_created_or_removed(B_ENTRY_CREATED, port, token, device, directory, name, node); } @@ -1274,29 +1316,52 @@ status_t notify_query_entry_removed(port_id port, int32 token, dev_t device, ino_t directory, const char *name, ino_t node) { - return notify_query_entry_event(B_ENTRY_REMOVED, port, token, + return notify_query_entry_created_or_removed(B_ENTRY_REMOVED, port, token, device, directory, name, node); } -/*! \brief Notifies the listener of a live query that an entry has been changed - and is still in the query (for whatever reason). +/*! \brief Notifies the listener of a live query that an entry has been moved. \param port The target port of the listener. \param token The BHandler token of the listener. \param device The ID of the mounted FS, the entry lives in. - \param directory The entry's parent directory ID. - \param name The entry's name. + \param fromDirectory The old parent directory ID. + \param toDirectory The new parent directory ID. + \param fromName The entry's old name. + \param toName The entry's new name. \param node The ID of the node the entry refers to. \return - \c B_OK, if everything went fine, - another error code otherwise. */ status_t -notify_query_attr_changed(port_id port, int32 token, dev_t device, - ino_t directory, const char* name, ino_t node) +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) { - return notify_query_entry_event(B_ATTR_CHANGED, port, token, - device, directory, name, node); + return notify_query_entry_moved(B_ENTRY_MOVED, port, token, + device, fromDirectory, fromName, toDirectory, toName, node); +} + + +/*! \brief Notifies the listener of a live query that an entry has been changed + and is still in the query (for whatever reason). + \param port The target port of the listener. + \param token The BHandler token of the listener. + \param device The ID of the mounted FS, the entry lives in. + \param directory The entry's parent directory ID. + \param name The entry's name. + \param node The ID of the node the entry refers to. + \return + - \c B_OK, if everything went fine, + - another error code otherwise. +*/ +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) +{ + return notify_query_attr_changed(port, token, + device, directory, node, attribute, cause); } diff --git a/src/tools/fs_shell/node_monitor.cpp b/src/tools/fs_shell/node_monitor.cpp index f15aa9ec37..a14be552f1 100644 --- a/src/tools/fs_shell/node_monitor.cpp +++ b/src/tools/fs_shell/node_monitor.cpp @@ -67,9 +67,18 @@ fssh_notify_query_entry_removed(fssh_port_id port, int32_t token, fssh_status_t -fssh_notify_query_attr_changed(fssh_port_id port, int32_t token, - fssh_mount_id device, fssh_vnode_id directory, const char *name, - fssh_vnode_id node) +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) +{ + return FSSH_B_OK; +} + + +fssh_status_t +fssh_notify_query_attribute_changed(fssh_port_id port, int32_t token, + fssh_mount_id device, fssh_vnode_id directory, fssh_vnode_id node, + const char *attribute, int32_t cause) { return FSSH_B_OK; }