2d690920ac
git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@12360 a95241bf-73f2-0310-859d-f6bbb57e9c96
955 lines
28 KiB
C++
955 lines
28 KiB
C++
/*
|
|
** Copyright 2003-2004, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
|
|
** Distributed under the terms of the OpenBeOS License.
|
|
*/
|
|
|
|
|
|
#include <drivers/node_monitor.h>
|
|
#include <app/AppDefs.h>
|
|
|
|
#include <fs/node_monitor.h>
|
|
#include <vfs.h>
|
|
#include <fd.h>
|
|
#include <lock.h>
|
|
#include <khash.h>
|
|
#include <messaging.h>
|
|
#include <util/AutoLock.h>
|
|
#include <util/KMessage.h>
|
|
#include <util/list.h>
|
|
|
|
#include <malloc.h>
|
|
#include <stddef.h>
|
|
|
|
|
|
#define TRACE_MONITOR 0
|
|
#if TRACE_MONITOR
|
|
# define TRACE(x) dprintf x
|
|
#else
|
|
# define TRACE(x) ;
|
|
#endif
|
|
|
|
|
|
// ToDo: add more fine grained locking - maybe using a ref_count in the
|
|
// node_monitor structure?
|
|
// ToDo: implement watching mounts/unmounts
|
|
// ToDo: return another error code than B_NO_MEMORY if the team's maximum is hit
|
|
|
|
|
|
typedef struct monitor_listener monitor_listener;
|
|
typedef struct node_monitor node_monitor;
|
|
|
|
struct monitor_listener {
|
|
monitor_listener *next;
|
|
monitor_listener *prev;
|
|
list_link monitor_link;
|
|
port_id port;
|
|
uint32 token;
|
|
uint32 flags;
|
|
node_monitor *monitor;
|
|
};
|
|
|
|
struct node_monitor {
|
|
node_monitor *next;
|
|
mount_id device;
|
|
vnode_id node;
|
|
struct list listeners;
|
|
};
|
|
|
|
struct monitor_hash_key {
|
|
mount_id device;
|
|
vnode_id node;
|
|
};
|
|
|
|
struct interested_monitor_listener_list {
|
|
struct list *listeners;
|
|
monitor_listener *first_listener;
|
|
uint32 flags;
|
|
};
|
|
|
|
#define MONITORS_HASH_TABLE_SIZE 16
|
|
|
|
static hash_table *gMonitorHash;
|
|
static mutex gMonitorMutex;
|
|
|
|
|
|
static int
|
|
monitor_compare(void *_monitor, const void *_key)
|
|
{
|
|
node_monitor *monitor = (node_monitor*)_monitor;
|
|
const struct monitor_hash_key *key = (const struct monitor_hash_key*)_key;
|
|
|
|
if (monitor->device == key->device && monitor->node == key->node)
|
|
return 0;
|
|
|
|
return -1;
|
|
}
|
|
|
|
|
|
static uint32
|
|
monitor_hash(void *_monitor, const void *_key, uint32 range)
|
|
{
|
|
node_monitor *monitor = (node_monitor*)_monitor;
|
|
const struct monitor_hash_key *key = (const struct monitor_hash_key*)_key;
|
|
|
|
#define MHASH(device, node) (((uint32)((node) >> 32) + (uint32)(node)) ^ (uint32)(device))
|
|
|
|
if (monitor != NULL)
|
|
return (MHASH(monitor->device, monitor->node) % range);
|
|
|
|
return (MHASH(key->device, key->node) % range);
|
|
#undef MHASH
|
|
}
|
|
|
|
|
|
/** Returns the monitor that matches the specified device/node pair.
|
|
* Must be called with monitors lock hold.
|
|
*/
|
|
|
|
static node_monitor *
|
|
get_monitor_for(mount_id device, vnode_id node)
|
|
{
|
|
struct monitor_hash_key key;
|
|
key.device = device;
|
|
key.node = node;
|
|
|
|
return (node_monitor *)hash_lookup(gMonitorHash, &key);
|
|
}
|
|
|
|
|
|
/** Returns the listener that matches the specified port/token pair.
|
|
* Must be called with monitors lock hold.
|
|
*/
|
|
|
|
static monitor_listener *
|
|
get_listener_for(node_monitor *monitor, port_id port, uint32 token)
|
|
{
|
|
monitor_listener *listener = NULL;
|
|
|
|
while ((listener = (monitor_listener*)list_get_next_item(
|
|
&monitor->listeners, listener)) != NULL) {
|
|
// does this listener match?
|
|
if (listener->port == port && listener->token == token)
|
|
return listener;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/** Removes the specified node_monitor from the hashtable
|
|
* and free it.
|
|
* Must be called with monitors lock hold.
|
|
*/
|
|
|
|
static void
|
|
remove_monitor(node_monitor *monitor)
|
|
{
|
|
hash_remove(gMonitorHash, monitor);
|
|
free(monitor);
|
|
}
|
|
|
|
|
|
/** Removes the specified monitor_listener from all lists
|
|
* and free it.
|
|
* Must be called with monitors lock hold.
|
|
*/
|
|
|
|
static void
|
|
remove_listener(monitor_listener *listener)
|
|
{
|
|
node_monitor *monitor = listener->monitor;
|
|
|
|
// remove it from the listener and I/O context lists
|
|
list_remove_link(&listener->monitor_link);
|
|
list_remove_link(listener);
|
|
|
|
free(listener);
|
|
|
|
if (list_is_empty(&monitor->listeners))
|
|
remove_monitor(monitor);
|
|
}
|
|
|
|
|
|
static status_t
|
|
add_node_monitor(io_context *context, mount_id device, vnode_id node,
|
|
uint32 flags, port_id port, uint32 token)
|
|
{
|
|
monitor_listener *listener;
|
|
node_monitor *monitor;
|
|
status_t status = B_OK;
|
|
|
|
TRACE(("add_node_monitor(dev = %ld, node = %Ld, flags = %ld, port = %ld, token = %ld\n",
|
|
device, node, flags, port, token));
|
|
|
|
mutex_lock(&gMonitorMutex);
|
|
|
|
monitor = get_monitor_for(device, node);
|
|
if (monitor == NULL) {
|
|
// check if this team is allowed to have more listeners
|
|
if (context->num_monitors >= context->max_monitors) {
|
|
// the BeBook says to return B_NO_MEMORY in this case, but
|
|
// we should have another one.
|
|
status = B_NO_MEMORY;
|
|
goto out;
|
|
}
|
|
|
|
// create new monitor
|
|
monitor = (node_monitor *)malloc(sizeof(node_monitor));
|
|
if (monitor == NULL) {
|
|
status = B_NO_MEMORY;
|
|
goto out;
|
|
}
|
|
|
|
// initialize monitor
|
|
monitor->device = device;
|
|
monitor->node = node;
|
|
list_init_etc(&monitor->listeners, offsetof(monitor_listener, monitor_link));
|
|
|
|
hash_insert(gMonitorHash, monitor);
|
|
} else {
|
|
// check if the listener is already listening, and
|
|
// if so, just add the new flags
|
|
|
|
listener = get_listener_for(monitor, port, token);
|
|
if (listener != NULL) {
|
|
listener->flags |= flags;
|
|
goto out;
|
|
}
|
|
|
|
// check if this team is allowed to have more listeners
|
|
if (context->num_monitors >= context->max_monitors) {
|
|
// the BeBook says to return B_NO_MEMORY in this case, but
|
|
// we should have another one.
|
|
status = B_NO_MEMORY;
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
// add listener
|
|
|
|
listener = (monitor_listener *)malloc(sizeof(monitor_listener));
|
|
if (listener == NULL) {
|
|
// no memory for the listener, so remove the monitor as well if needed
|
|
if (list_is_empty(&monitor->listeners))
|
|
remove_monitor(monitor);
|
|
|
|
status = B_NO_MEMORY;
|
|
goto out;
|
|
}
|
|
|
|
// initialize listener, and add it to the lists
|
|
listener->port = port;
|
|
listener->token = token;
|
|
listener->flags = flags;
|
|
listener->monitor = monitor;
|
|
|
|
list_add_link_to_head(&monitor->listeners, &listener->monitor_link);
|
|
list_add_link_to_head(&context->node_monitors, listener);
|
|
|
|
context->num_monitors++;
|
|
|
|
out:
|
|
mutex_unlock(&gMonitorMutex);
|
|
return status;
|
|
}
|
|
|
|
|
|
static status_t
|
|
remove_node_monitor(struct io_context *context, mount_id device, vnode_id node,
|
|
uint32 flags, port_id port, uint32 token)
|
|
{
|
|
monitor_listener *listener;
|
|
node_monitor *monitor;
|
|
status_t status = B_OK;
|
|
|
|
TRACE(("remove_node_monitor(dev = %ld, node = %Ld, flags = %ld, port = %ld, token = %ld\n",
|
|
device, node, flags, port, token));
|
|
|
|
mutex_lock(&gMonitorMutex);
|
|
|
|
// get the monitor for this device/node pair
|
|
monitor = get_monitor_for(device, node);
|
|
if (monitor == NULL) {
|
|
status = B_ENTRY_NOT_FOUND;
|
|
goto out;
|
|
}
|
|
|
|
// see if it has the listener we are looking for
|
|
listener = get_listener_for(monitor, port, token);
|
|
if (listener == NULL) {
|
|
status = B_ENTRY_NOT_FOUND;
|
|
goto out;
|
|
}
|
|
|
|
// no flags means remove all flags
|
|
if (flags == B_STOP_WATCHING)
|
|
flags = ~0;
|
|
|
|
listener->flags &= ~flags;
|
|
|
|
// if there aren't anymore flags, remove this listener
|
|
if (listener->flags == B_STOP_WATCHING) {
|
|
remove_listener(listener);
|
|
context->num_monitors--;
|
|
}
|
|
|
|
out:
|
|
mutex_unlock(&gMonitorMutex);
|
|
return status;
|
|
}
|
|
|
|
|
|
static status_t
|
|
remove_node_monitors_by_target(struct io_context *context, port_id port, uint32 token)
|
|
{
|
|
monitor_listener *listener = NULL;
|
|
int32 count = 0;
|
|
|
|
while ((listener = (monitor_listener*)list_get_next_item(
|
|
&context->node_monitors, listener)) != NULL) {
|
|
monitor_listener *removeListener;
|
|
|
|
if (listener->port != port || listener->token != token)
|
|
continue;
|
|
|
|
listener = (monitor_listener*)list_get_prev_item(
|
|
&context->node_monitors, removeListener = listener);
|
|
// this line sets the listener one item back, allowing us
|
|
// to remove its successor (which is saved in "removeListener")
|
|
|
|
remove_listener(removeListener);
|
|
count++;
|
|
}
|
|
|
|
return count > 0 ? B_OK : B_ENTRY_NOT_FOUND;
|
|
}
|
|
|
|
|
|
// #pragma mark -
|
|
// Exported private kernel API
|
|
|
|
|
|
status_t
|
|
remove_node_monitors(struct io_context *context)
|
|
{
|
|
mutex_lock(&gMonitorMutex);
|
|
|
|
while (!list_is_empty(&context->node_monitors)) {
|
|
// the remove_listener() function will also free the node_monitor
|
|
// if it doesn't have any listeners attached anymore
|
|
remove_listener(
|
|
(monitor_listener*)list_get_first_item(&context->node_monitors));
|
|
}
|
|
|
|
mutex_unlock(&gMonitorMutex);
|
|
return B_OK;
|
|
}
|
|
|
|
|
|
status_t
|
|
node_monitor_init(void)
|
|
{
|
|
gMonitorHash = hash_init(MONITORS_HASH_TABLE_SIZE, offsetof(node_monitor, next),
|
|
&monitor_compare, &monitor_hash);
|
|
if (gMonitorHash == NULL)
|
|
panic("node_monitor_init: error creating mounts hash table\n");
|
|
|
|
return mutex_init(&gMonitorMutex, "node monitor");
|
|
}
|
|
|
|
|
|
// #pragma mark -
|
|
// Exported public kernel API
|
|
|
|
|
|
/** \brief Subscribes a target to node and/or mount watching.
|
|
*
|
|
* Depending on \a flags, different actions are performed. If flags is \c 0,
|
|
* mount watching is requested. \a device and \a node must be \c -1 in this
|
|
* case. Otherwise node watching is requested. \a device and \a node must
|
|
* refer to a valid node, and \a flags must note contain the flag
|
|
* \c B_WATCH_MOUNT, but at least one of the other valid flags.
|
|
*
|
|
* \param device The device the node resides on (node_ref::device). \c -1, if
|
|
* only mount watching is requested.
|
|
* \param node The node ID of the node (node_ref::device). \c -1, if
|
|
* only mount watching is requested.
|
|
* \param flags A bit mask composed of the values specified in
|
|
* <NodeMonitor.h>.
|
|
* \param port The port of the target (a looper port).
|
|
* \param handlerToken The token of the target handler. \c -2, if the
|
|
* preferred handler of the looper is the target.
|
|
* \return \c B_OK, if everything went fine, another error code otherwise.
|
|
*/
|
|
|
|
status_t
|
|
start_watching(dev_t device, ino_t node, uint32 flags, port_id port, uint32 token)
|
|
{
|
|
io_context *context = get_current_io_context(true);
|
|
|
|
return add_node_monitor(context, device, node, flags, port, token);
|
|
}
|
|
|
|
|
|
/** \brief Unsubscribes a target from watching a node.
|
|
* \param device The device the node resides on (node_ref::device).
|
|
* \param node The node ID of the node (node_ref::device).
|
|
* \param flags Which monitors should be removed (B_STOP_WATCHING for all)
|
|
* \param port The port of the target (a looper port).
|
|
* \param handlerToken The token of the target handler. \c -2, if the
|
|
* preferred handler of the looper is the target.
|
|
* \return \c B_OK, if everything went fine, another error code otherwise.
|
|
*/
|
|
|
|
status_t
|
|
stop_watching(dev_t device, ino_t node, uint32 flags, port_id port, uint32 token)
|
|
{
|
|
io_context *context = get_current_io_context(true);
|
|
|
|
return remove_node_monitor(context, device, node, flags, port, token);
|
|
}
|
|
|
|
|
|
/** \brief Unsubscribes a target from node and mount monitoring.
|
|
* \param port The port of the target (a looper port).
|
|
* \param handlerToken The token of the target handler. \c -2, if the
|
|
* preferred handler of the looper is the target.
|
|
* \return \c B_OK, if everything went fine, another error code otherwise.
|
|
*/
|
|
|
|
status_t
|
|
stop_notifying(port_id port, uint32 token)
|
|
{
|
|
io_context *context = get_current_io_context(true);
|
|
|
|
return remove_node_monitors_by_target(context, port, token);
|
|
}
|
|
|
|
|
|
status_t
|
|
notify_listener(int op, mount_id device, vnode_id parentNode, vnode_id toParentNode,
|
|
vnode_id node, const char *name)
|
|
{
|
|
monitor_listener *listener;
|
|
node_monitor *monitor;
|
|
|
|
TRACE(("notify_listener(op = %d, device = %ld, node = %Ld, parent = %Ld, toParent = %Ld"
|
|
", name = \"%s\"\n", op, device, node, parentNode, toParentNode, name));
|
|
|
|
mutex_lock(&gMonitorMutex);
|
|
|
|
// check the main "node"
|
|
|
|
if ((op == B_ENTRY_MOVED
|
|
|| op == B_ENTRY_REMOVED
|
|
|| op == B_STAT_CHANGED
|
|
|| op == B_ATTR_CHANGED)
|
|
&& (monitor = get_monitor_for(device, node)) != NULL) {
|
|
// iterate over all listeners for this monitor, and see
|
|
// if we have to send anything
|
|
listener = NULL;
|
|
while ((listener = (monitor_listener*)list_get_next_item(
|
|
&monitor->listeners, listener)) != NULL) {
|
|
// do we have a reason to notify this listener?
|
|
if (((listener->flags & B_WATCH_NAME) != 0
|
|
&& (op == B_ENTRY_MOVED || op == B_ENTRY_REMOVED))
|
|
|| ((listener->flags & B_WATCH_STAT) != 0
|
|
&& op == B_STAT_CHANGED)
|
|
|| ((listener->flags & B_WATCH_ATTR) != 0
|
|
&& op == B_ATTR_CHANGED)) {
|
|
// then do it!
|
|
send_notification(listener->port, listener->token, B_NODE_MONITOR,
|
|
op, device, 0, parentNode, toParentNode, node, name);
|
|
}
|
|
}
|
|
}
|
|
|
|
// check its parent directory
|
|
|
|
if ((op == B_ENTRY_MOVED
|
|
|| op == B_ENTRY_REMOVED
|
|
|| op == B_ENTRY_CREATED)
|
|
&& (monitor = get_monitor_for(device, parentNode)) != NULL) {
|
|
// iterate over all listeners for this monitor, and see
|
|
// if we have to send anything
|
|
listener = NULL;
|
|
while ((listener = (monitor_listener*)list_get_next_item(
|
|
&monitor->listeners, listener)) != NULL) {
|
|
// do we have a reason to notify this listener?
|
|
if ((listener->flags & B_WATCH_DIRECTORY) != 0) {
|
|
send_notification(listener->port, listener->token, B_NODE_MONITOR,
|
|
op, device, 0, parentNode, toParentNode, node, name);
|
|
}
|
|
}
|
|
}
|
|
|
|
// check its new target parent directory
|
|
|
|
if (op == B_ENTRY_MOVED
|
|
&& (monitor = get_monitor_for(device, toParentNode)) != NULL) {
|
|
// iterate over all listeners for this monitor, and see
|
|
// if we have to send anything
|
|
listener = NULL;
|
|
while ((listener = (monitor_listener*)list_get_next_item(
|
|
&monitor->listeners, listener)) != NULL) {
|
|
// do we have a reason to notify this listener?
|
|
if ((listener->flags & B_WATCH_DIRECTORY) != 0) {
|
|
send_notification(listener->port, listener->token, B_NODE_MONITOR,
|
|
B_ENTRY_MOVED, device, 0, parentNode, toParentNode, node, name);
|
|
}
|
|
}
|
|
}
|
|
|
|
mutex_unlock(&gMonitorMutex);
|
|
return B_OK;
|
|
}
|
|
|
|
|
|
/** \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.
|
|
*/
|
|
status_t
|
|
notify_entry_created(mount_id device, vnode_id directory, const char *name,
|
|
vnode_id node)
|
|
{
|
|
return notify_entry_created_or_removed(B_ENTRY_CREATED, device,
|
|
directory, name, node);
|
|
}
|
|
|
|
|
|
/** \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.
|
|
*/
|
|
status_t
|
|
notify_entry_removed(mount_id device, vnode_id directory, const char *name,
|
|
vnode_id node)
|
|
{
|
|
return notify_entry_created_or_removed(B_ENTRY_REMOVED, device,
|
|
directory, name, node);
|
|
}
|
|
|
|
|
|
/** \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.
|
|
*/
|
|
status_t
|
|
notify_entry_moved(mount_id device, vnode_id fromDirectory,
|
|
const char *fromName, vnode_id toDirectory, const char *toName,
|
|
vnode_id node)
|
|
{
|
|
if (!fromName || !toName)
|
|
return B_BAD_VALUE;
|
|
|
|
// If node is a mount point, we need to resolve it to the mounted
|
|
// volume's root node.
|
|
mount_id nodeDevice = device;
|
|
resolve_mount_point_to_volume_root(device, node, &nodeDevice, &node);
|
|
|
|
MutexLocker locker(gMonitorMutex);
|
|
|
|
// get the lists of all interested listeners
|
|
interested_monitor_listener_list interestedListeners[3];
|
|
int32 interestedListenerCount = 0;
|
|
// ... for the node
|
|
get_interested_monitor_listeners(nodeDevice, node, B_WATCH_NAME,
|
|
interestedListeners, interestedListenerCount);
|
|
// ... for the source directory
|
|
get_interested_monitor_listeners(device, fromDirectory, B_WATCH_DIRECTORY,
|
|
interestedListeners, interestedListenerCount);
|
|
// ... for the target directory
|
|
get_interested_monitor_listeners(device, toDirectory, 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", B_ENTRY_MOVED);
|
|
message.AddInt32("device", device);
|
|
message.AddInt64("from directory", fromDirectory);
|
|
message.AddInt64("to directory", toDirectory);
|
|
message.AddInt32("node device", nodeDevice); // Haiku only
|
|
message.AddInt64("node", node);
|
|
message.AddString("from name", fromName); // Haiku only
|
|
message.AddString("name", toName);
|
|
|
|
return send_notification_message(message, interestedListeners,
|
|
interestedListenerCount);
|
|
}
|
|
|
|
|
|
/** \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.
|
|
*/
|
|
status_t
|
|
notify_stat_changed(mount_id device, vnode_id node, uint32 statFields)
|
|
{
|
|
MutexLocker locker(gMonitorMutex);
|
|
|
|
// get the lists of all interested listeners
|
|
interested_monitor_listener_list interestedListeners[3];
|
|
int32 interestedListenerCount = 0;
|
|
// ... for the node
|
|
get_interested_monitor_listeners(device, node, B_WATCH_STAT,
|
|
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", B_STAT_CHANGED);
|
|
message.AddInt32("device", device);
|
|
message.AddInt64("node", node);
|
|
message.AddInt32("fields", statFields); // Haiku only
|
|
|
|
return send_notification_message(message, interestedListeners,
|
|
interestedListenerCount);
|
|
}
|
|
|
|
|
|
/** \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.
|
|
*/
|
|
status_t
|
|
notify_attribute_changed(mount_id device, vnode_id node, const char *attribute,
|
|
int32 cause)
|
|
{
|
|
if (!attribute)
|
|
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
|
|
get_interested_monitor_listeners(device, node, B_WATCH_ATTR,
|
|
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", B_ATTR_CHANGED);
|
|
message.AddInt32("device", device);
|
|
message.AddInt64("node", node);
|
|
message.AddString("attr", attribute);
|
|
message.AddInt32("cause", cause); // 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);
|
|
}
|
|
|
|
|
|
/** \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)
|
|
{
|
|
return notify_query_entry_created_or_removed(B_ENTRY_CREATED, port, token,
|
|
device, directory, name, node);
|
|
}
|
|
|
|
|
|
/** \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.
|
|
*/
|
|
status_t
|
|
notify_query_entry_removed(port_id port, int32 token,
|
|
mount_id device, vnode_id directory, const char *name, vnode_id node)
|
|
{
|
|
return notify_query_entry_created_or_removed(B_ENTRY_REMOVED, port, token,
|
|
device, directory, name, node);
|
|
}
|
|
|
|
|
|
|
|
// #pragma mark -
|
|
// Userland syscalls
|
|
|
|
|
|
status_t
|
|
_user_stop_notifying(port_id port, uint32 token)
|
|
{
|
|
io_context *context = get_current_io_context(false);
|
|
|
|
return remove_node_monitors_by_target(context, port, token);
|
|
}
|
|
|
|
|
|
status_t
|
|
_user_start_watching(dev_t device, ino_t node, uint32 flags, port_id port, uint32 token)
|
|
{
|
|
io_context *context = get_current_io_context(false);
|
|
|
|
return add_node_monitor(context, device, node, flags, port, token);
|
|
}
|
|
|
|
|
|
status_t
|
|
_user_stop_watching(dev_t device, ino_t node, uint32 flags, port_id port, uint32 token)
|
|
{
|
|
io_context *context = get_current_io_context(false);
|
|
|
|
return remove_node_monitor(context, device, node, flags, port, token);
|
|
}
|
|
|