* Finally implemented B_WATCH_MOUNT, ie. Tracker now shows newly mounted volumes

(mounting still only works from the Terminal).
* Shuffled functions in node_monitor.cpp around to clearly differentiate between
  private, private kernel, and public kernel functions.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@16575 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Axel Dörfler 2006-03-03 11:48:49 +00:00
parent f1fb383d6f
commit bb674499f8
4 changed files with 348 additions and 273 deletions

View File

@ -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 <OS.h>
#include <fs_interface.h>
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);

View File

@ -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;

View File

@ -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 <drivers/node_monitor.h>
@ -21,8 +21,8 @@
#include <stddef.h>
#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 <NodeMonitor.h>, 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 <NodeMonitor.h>, 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

View File

@ -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);