diff --git a/headers/private/kernel/fs/node_monitor.h b/headers/private/kernel/fs/node_monitor.h index ab4d0bc937..23a7545c6c 100644 --- a/headers/private/kernel/fs/node_monitor.h +++ b/headers/private/kernel/fs/node_monitor.h @@ -1,12 +1,12 @@ +/* + * Copyright 2003-2006, Axel Dörfler, axeld@pinc-software.de. All rights reserved. + * Distributed under the terms of the MIT License. + */ #ifndef _KERNEL_NODE_MONITOR_H #define _KERNEL_NODE_MONITOR_H -/* -** Copyright 2003-2004, Axel Dörfler, axeld@pinc-software.de. All rights reserved. -** Distributed under the terms of the OpenBeOS License. -*/ -#include +#include struct io_context; @@ -18,6 +18,9 @@ extern "C" { // private kernel API extern status_t remove_node_monitors(struct io_context *context); extern status_t node_monitor_init(void); +extern status_t notify_unmount(mount_id device); +extern status_t notify_mount(mount_id device, mount_id parentDevice, + vnode_id parentDirectory); // user-space exported calls extern status_t _user_stop_notifying(port_id port, uint32 token); diff --git a/src/kits/storage/NodeMonitor.cpp b/src/kits/storage/NodeMonitor.cpp index c5fc06126d..53d3b9c4a8 100644 --- a/src/kits/storage/NodeMonitor.cpp +++ b/src/kits/storage/NodeMonitor.cpp @@ -62,8 +62,8 @@ watch_node(const node_ref *node, uint32 flags, BMessenger target) // subscribe to... // mount watching if (flags & B_WATCH_MOUNT) { - status_t status = _kern_start_watching((dev_t)-1, (ino_t)-1, 0, port, - token); + status_t status = _kern_start_watching((dev_t)-1, (ino_t)-1, + B_WATCH_MOUNT, port, token); if (status < B_OK) return status; diff --git a/src/system/kernel/fs/node_monitor.cpp b/src/system/kernel/fs/node_monitor.cpp index 143ab6f928..37f07f8520 100644 --- a/src/system/kernel/fs/node_monitor.cpp +++ b/src/system/kernel/fs/node_monitor.cpp @@ -1,7 +1,7 @@ -/* -** Copyright 2003-2004, Axel Dörfler, axeld@pinc-software.de. All rights reserved. -** Distributed under the terms of the OpenBeOS License. -*/ +/* + * Copyright 2003-2006, Axel Dörfler, axeld@pinc-software.de. All rights reserved. + * Distributed under the terms of the MIT License. + */ #include @@ -21,8 +21,8 @@ #include -#define TRACE_MONITOR 0 -#if TRACE_MONITOR +//#define TRACE_MONITOR +#ifdef TRACE_MONITOR # define TRACE(x) dprintf x #else # define TRACE(x) ; @@ -325,8 +325,203 @@ remove_node_monitors_by_target(struct io_context *context, port_id port, uint32 } -// #pragma mark - -// Exported private kernel API +/** \brief Given device and node ID and a node monitoring event mask, the + * function checks whether there are listeners interested in any of + * the events for that node and, if so, adds the respective listener + * list to a supplied array of listener lists. + * + * Note, that in general not all of the listeners in an appended list will be + * interested in the events, but it is guaranteed that + * interested_monitor_listener_list::first_listener is indeed + * the first listener in the list, that is interested. + * + * \param device The ID of the mounted FS, the node lives in. + * \param node The ID of the node. + * \param flags The mask specifying the events occurred for the given node + * (a combination of \c B_WATCH_* constants). + * \param interestedListeners An array of listener lists. If there are + * interested listeners for the node, the list will be appended to + * this array. + * \param interestedListenerCount The number of elements in the + * \a interestedListeners array. Will be incremented, if a list is + * appended. + */ + +static void +get_interested_monitor_listeners(mount_id device, vnode_id node, + uint32 flags, interested_monitor_listener_list *interestedListeners, + int32 &interestedListenerCount) +{ + // get the monitor for the node + node_monitor *monitor = get_monitor_for(device, node); + if (!monitor) + return; + + // iterate through the listeners until we find one with matching flags + monitor_listener *listener = NULL; + while ((listener = (monitor_listener*)list_get_next_item( + &monitor->listeners, listener)) != NULL) { + if (listener->flags & flags) { + interested_monitor_listener_list &list + = interestedListeners[interestedListenerCount++]; + list.listeners = &monitor->listeners; + list.first_listener = listener; + list.flags = flags; + return; + } + } +} + + +/** \brief Sends a notifcation message to the given listeners. + * \param message The message to be sent. + * \param interestedListeners An array of listener lists. + * \param interestedListenerCount The number of elements in the + * \a interestedListeners array. + * \return + * - \c B_OK, if everything went fine, + * - another error code otherwise. + */ + +static status_t +send_notification_message(KMessage &message, + interested_monitor_listener_list *interestedListeners, + int32 interestedListenerCount) +{ + // Since the messaging service supports broadcasting and that is more + // efficient than sending the messages individually, we collect the + // listener targets in an array and send the message to them at once. + const int32 maxTargetCount = 16; + messaging_target targets[maxTargetCount]; + int32 targetCount = 0; + + // iterate through the lists + interested_monitor_listener_list *list = interestedListeners; + for (int32 i = 0; i < interestedListenerCount; i++, list++) { + // iterate through the listeners + monitor_listener *listener = list->first_listener; + do { + if (listener->flags & list->flags) { + // the listener's flags match: add it to the targets + messaging_target &target = targets[targetCount++]; + target.port = listener->port; + target.token = listener->token; + + // if the target array is full, send the message + if (targetCount == maxTargetCount) { + status_t error = send_message(&message, targets, + targetCount); + if (error != B_OK) + return error; + + targetCount = 0; + } + } + } while ((listener = (monitor_listener*)list_get_next_item( + list->listeners, listener)) != NULL); + } + + // if any targets are left (the usual case, unless the target array got + // full early), send the message + if (targetCount > 0) + return send_message(&message, targets, targetCount); + + return B_OK; +} + + +/** \brief Notifies all interested listeners that an entry has been created + * or removed. + * \param opcode \c B_ENTRY_CREATED or \c B_ENTRY_REMOVED. + * \param device The ID of the mounted FS, the entry lives/lived 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/referred to. + * \return + * - \c B_OK, if everything went fine, + * - another error code otherwise. + */ + +static status_t +notify_entry_created_or_removed(int32 opcode, mount_id device, + vnode_id directory, const char *name, vnode_id node) +{ + if (!name) + return B_BAD_VALUE; + + MutexLocker locker(gMonitorMutex); + + // get the lists of all interested listeners + interested_monitor_listener_list interestedListeners[3]; + int32 interestedListenerCount = 0; + // ... for the node + if (opcode != B_ENTRY_CREATED) { + get_interested_monitor_listeners(device, node, B_WATCH_NAME, + interestedListeners, interestedListenerCount); + } + // ... for the directory + get_interested_monitor_listeners(device, directory, B_WATCH_DIRECTORY, + interestedListeners, interestedListenerCount); + + if (interestedListenerCount == 0) + return B_OK; + + // there are interested listeners: construct the message and send it + char messageBuffer[1024]; + KMessage message; + message.SetTo(messageBuffer, sizeof(messageBuffer), B_NODE_MONITOR); + message.AddInt32("opcode", opcode); + message.AddInt32("device", device); + message.AddInt64("directory", directory); + message.AddInt64("node", node); + message.AddString("name", name); // for "removed" Haiku only + + return send_notification_message(message, interestedListeners, + interestedListenerCount); +} + + +/** \brief Notifies the listener of a live query that an entry has been added + * to or removed from the query (for whatever reason). + * \param opcode \c B_ENTRY_CREATED or \c B_ENTRY_REMOVED. + * \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_created_or_removed(int32 opcode, port_id port, int32 token, + mount_id device, vnode_id directory, const char *name, vnode_id 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 - private kernel API status_t @@ -358,8 +553,70 @@ node_monitor_init(void) } -// #pragma mark - -// Exported public kernel API +status_t +notify_unmount(mount_id device) +{ + TRACE(("unmounted device: %ld\n", device)); + + MutexLocker locker(gMonitorMutex); + + // get the lists of all interested listeners + interested_monitor_listener_list interestedListeners[3]; + int32 interestedListenerCount = 0; + get_interested_monitor_listeners(-1, -1, B_WATCH_MOUNT, + interestedListeners, interestedListenerCount); + + if (interestedListenerCount == 0) + return B_OK; + + // there are interested listeners: construct the message and send it + char messageBuffer[96]; + KMessage message; + message.SetTo(messageBuffer, sizeof(messageBuffer), B_NODE_MONITOR); + message.AddInt32("opcode", B_DEVICE_UNMOUNTED); + message.AddInt32("device", device); + + return send_notification_message(message, interestedListeners, + interestedListenerCount); + + return B_OK; +} + + +status_t +notify_mount(mount_id device, mount_id parentDevice, vnode_id parentDirectory) +{ + TRACE(("mounted device: %ld, parent %ld:%Ld\n", device, parentDevice, + parentDirectory)); + + MutexLocker locker(gMonitorMutex); + + // get the lists of all interested listeners + interested_monitor_listener_list interestedListeners[3]; + int32 interestedListenerCount = 0; + get_interested_monitor_listeners(-1, -1, B_WATCH_MOUNT, + interestedListeners, interestedListenerCount); + + if (interestedListenerCount == 0) + return B_OK; + + // there are interested listeners: construct the message and send it + char messageBuffer[128]; + KMessage message; + message.SetTo(messageBuffer, sizeof(messageBuffer), B_NODE_MONITOR); + message.AddInt32("opcode", B_DEVICE_MOUNTED); + message.AddInt32("new device", device); + message.AddInt32("device", parentDevice); + message.AddInt64("directory", parentDirectory); + + return send_notification_message(message, interestedListeners, + interestedListenerCount); + + return B_OK; +} + + +// #pragma mark - public kernel API /** \brief Subscribes a target to node and/or mount watching. @@ -505,170 +762,16 @@ notify_listener(int op, mount_id device, vnode_id parentNode, vnode_id toParentN } -/** \brief Given device and node ID and a node monitoring event mask, the - function checks whether there are listeners interested in any of - the events for that node and, if so, adds the respective listener - list to a supplied array of listener lists. - - Note, that in general not all of the listeners in an appended list will be - interested in the events, but it is guaranteed that - interested_monitor_listener_list::first_listener is indeed - the first listener in the list, that is interested. - - \param device The ID of the mounted FS, the node lives in. - \param node The ID of the node. - \param flags The mask specifying the events occurred for the given node - (a combination of \c B_WATCH_* constants). - \param interestedListeners An array of listener lists. If there are - interested listeners for the node, the list will be appended to - this array. - \param interestedListenerCount The number of elements in the - \a interestedListeners array. Will be incremented, if a list is - appended. - */ -static -void -get_interested_monitor_listeners(mount_id device, vnode_id node, - uint32 flags, interested_monitor_listener_list *interestedListeners, - int32 &interestedListenerCount) -{ - // get the monitor for the node - node_monitor *monitor = get_monitor_for(device, node); - if (!monitor) - return; - - // iterate through the listeners until we find one with matching flags - monitor_listener *listener = NULL; - while ((listener = (monitor_listener*)list_get_next_item( - &monitor->listeners, listener)) != NULL) { - if (listener->flags & flags) { - interested_monitor_listener_list &list - = interestedListeners[interestedListenerCount++]; - list.listeners = &monitor->listeners; - list.first_listener = listener; - list.flags = flags; - return; - } - } -} - - -/** \brief Sends a notifcation message to the given listeners. - \param message The message to be sent. - \param interestedListeners An array of listener lists. - \param interestedListenerCount The number of elements in the - \a interestedListeners array. - \return - - \c B_OK, if everything went fine, - - another error code otherwise. - */ -static -status_t -send_notification_message(KMessage &message, - interested_monitor_listener_list *interestedListeners, - int32 interestedListenerCount) -{ - // Since the messaging service supports broadcasting and that is more - // efficient than sending the messages individually, we collect the - // listener targets in an array and send the message to them at once. - const int32 maxTargetCount = 16; - messaging_target targets[maxTargetCount]; - int32 targetCount = 0; - - // iterate through the lists - interested_monitor_listener_list *list = interestedListeners; - for (int32 i = 0; i < interestedListenerCount; i++, list++) { - // iterate through the listeners - monitor_listener *listener = list->first_listener; - do { - if (listener->flags & list->flags) { - // the listener's flags match: add it to the targets - messaging_target &target = targets[targetCount++]; - target.port = listener->port; - target.token = listener->token; - - // if the target array is full, send the message - if (targetCount == maxTargetCount) { - status_t error = send_message(&message, targets, - targetCount); - if (error != B_OK) - return error; - - targetCount = 0; - } - } - } while ((listener = (monitor_listener*)list_get_next_item( - list->listeners, listener)) != NULL); - } - - // if any targets are left (the usual case, unless the target array got - // full early), send the message - if (targetCount > 0) - return send_message(&message, targets, targetCount); - - return B_OK; -} - - -/** \brief Notifies all interested listeners that an entry has been created - or removed. - \param opcode \c B_ENTRY_CREATED or \c B_ENTRY_REMOVED. - \param device The ID of the mounted FS, the entry lives/lived 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/referred to. - \return - - \c B_OK, if everything went fine, - - another error code otherwise. - */ -static status_t -notify_entry_created_or_removed(int32 opcode, mount_id device, - vnode_id directory, const char *name, vnode_id node) -{ - if (!name) - return B_BAD_VALUE; - - MutexLocker locker(gMonitorMutex); - - // get the lists of all interested listeners - interested_monitor_listener_list interestedListeners[3]; - int32 interestedListenerCount = 0; - // ... for the node - if (opcode != B_ENTRY_CREATED) { - get_interested_monitor_listeners(device, node, B_WATCH_NAME, - interestedListeners, interestedListenerCount); - } - // ... for the directory - get_interested_monitor_listeners(device, directory, B_WATCH_DIRECTORY, - interestedListeners, interestedListenerCount); - - if (interestedListenerCount == 0) - return B_OK; - - // there are interested listeners: construct the message and send it - char messageBuffer[1024]; - KMessage message; - message.SetTo(messageBuffer, sizeof(messageBuffer), B_NODE_MONITOR); - message.AddInt32("opcode", opcode); - message.AddInt32("device", device); - message.AddInt64("directory", directory); - message.AddInt64("node", node); - message.AddString("name", name); // for "removed" Haiku only - - return send_notification_message(message, interestedListeners, - interestedListenerCount); -} - - /** \brief Notifies all interested listeners that an entry has been created. - \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. + * \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_entry_created(mount_id device, vnode_id directory, const char *name, vnode_id node) @@ -679,14 +782,15 @@ notify_entry_created(mount_id device, vnode_id directory, const char *name, /** \brief Notifies all interested listeners that an entry has been removed. - \param device The ID of the mounted FS, the entry lived in. - \param directory The entry's former parent directory ID. - \param name The entry's name. - \param node The ID of the node the entry referred to. - \return - - \c B_OK, if everything went fine, - - another error code otherwise. + * \param device The ID of the mounted FS, the entry lived in. + * \param directory The entry's former parent directory ID. + * \param name The entry's name. + * \param node The ID of the node the entry referred to. + * \return + * - \c B_OK, if everything went fine, + * - another error code otherwise. */ + status_t notify_entry_removed(mount_id device, vnode_id directory, const char *name, vnode_id node) @@ -697,16 +801,17 @@ notify_entry_removed(mount_id device, vnode_id directory, const char *name, /** \brief Notifies all interested listeners that an entry has been moved. - \param device The ID of the mounted FS, the entry lives in. - \param fromDirectory The entry's previous parent directory ID. - \param fromName The entry's previous name. - \param toDirectory The entry's new parent directory ID. - \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. + * \param device The ID of the mounted FS, the entry lives in. + * \param fromDirectory The entry's previous parent directory ID. + * \param fromName The entry's previous name. + * \param toDirectory The entry's new parent directory ID. + * \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_entry_moved(mount_id device, vnode_id fromDirectory, const char *fromName, vnode_id toDirectory, const char *toName, @@ -757,16 +862,17 @@ notify_entry_moved(mount_id device, vnode_id fromDirectory, /** \brief Notifies all interested listeners that a node's stat data have - changed. - \param device The ID of the mounted FS, the node lives in. - \param node The ID of the node. - \param statFields A bitwise combination of one or more of the \c B_STAT_* - constants defined in , indicating what fields of the - stat data have changed. - \return - - \c B_OK, if everything went fine, - - another error code otherwise. + * changed. + * \param device The ID of the mounted FS, the node lives in. + * \param node The ID of the node. + * \param statFields A bitwise combination of one or more of the \c B_STAT_* + * constants defined in , indicating what fields of the + * stat data have changed. + * \return + * - \c B_OK, if everything went fine, + * - another error code otherwise. */ + status_t notify_stat_changed(mount_id device, vnode_id node, uint32 statFields) { @@ -797,15 +903,16 @@ notify_stat_changed(mount_id device, vnode_id node, uint32 statFields) /** \brief Notifies all interested listeners that a node attribute has changed. - \param device The ID of the mounted FS, the node lives in. - \param node The ID of the node. - \param attribute The attribute's name. - \param cause Either of \c B_ATTR_CREATED, \c B_ATTR_REMOVED, or - \c B_ATTR_CHANGED, indicating what exactly happened to the attribute. - \return - - \c B_OK, if everything went fine, - - another error code otherwise. + * \param device The ID of the mounted FS, the node lives in. + * \param node The ID of the node. + * \param attribute The attribute's name. + * \param cause Either of \c B_ATTR_CREATED, \c B_ATTR_REMOVED, or + * \c B_ATTR_CHANGED, indicating what exactly happened to the attribute. + * \return + * - \c B_OK, if everything went fine, + * - another error code otherwise. */ + status_t notify_attribute_changed(mount_id device, vnode_id node, const char *attribute, int32 cause) @@ -841,56 +948,18 @@ notify_attribute_changed(mount_id device, vnode_id node, const char *attribute, /** \brief Notifies the listener of a live query that an entry has been added - to or removed from the query (for whatever reason). - \param opcode \c B_ENTRY_CREATED or \c B_ENTRY_REMOVED. - \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. + * to 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. */ -static status_t -notify_query_entry_created_or_removed(int32 opcode, port_id port, int32 token, - mount_id device, vnode_id directory, const char *name, vnode_id 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); -} - - -/** \brief Notifies the listener of a live query that an entry has been added - to 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_entry_created(port_id port, int32 token, mount_id device, vnode_id directory, const char *name, vnode_id node) @@ -901,17 +970,18 @@ notify_query_entry_created(port_id port, int32 token, mount_id device, /** \brief Notifies the listener of a live query that an entry has been removed - from 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. + * from 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_entry_removed(port_id port, int32 token, mount_id device, vnode_id directory, const char *name, vnode_id node) @@ -921,9 +991,7 @@ notify_query_entry_removed(port_id port, int32 token, } - -// #pragma mark - -// Userland syscalls +// #pragma mark - User syscalls status_t diff --git a/src/system/kernel/fs/vfs.cpp b/src/system/kernel/fs/vfs.cpp index 427c3b9bf1..cb6c14257d 100644 --- a/src/system/kernel/fs/vfs.cpp +++ b/src/system/kernel/fs/vfs.cpp @@ -5061,6 +5061,9 @@ fs_mount(char *path, const char *device, const char *fsName, uint32 flags, fileDeviceDeleter.id = -1; } + notify_mount(mount->id, mount->covers_vnode ? mount->covers_vnode->device : -1, + mount->covers_vnode ? mount->covers_vnode->id : -1); + return mount->id; err7: @@ -5295,6 +5298,7 @@ fs_unmount(char *path, uint32 flags, bool kernel) mountOpLocker.Unlock(); FS_MOUNT_CALL(mount, unmount)(mount->cookie); + notify_unmount(mount->id); // release the file system put_file_system(mount->fs);