xref: /haiku/src/system/kernel/fs/node_monitor.cpp (revision 4f00613311d0bd6b70fa82ce19931c41f071ea4e)
1 /*
2 ** Copyright 2003-2004, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
3 ** Distributed under the terms of the OpenBeOS License.
4 */
5 
6 
7 #include <drivers/node_monitor.h>
8 #include <app/AppDefs.h>
9 
10 #include <fs/node_monitor.h>
11 #include <vfs.h>
12 #include <fd.h>
13 #include <lock.h>
14 #include <khash.h>
15 #include <messaging.h>
16 #include <util/AutoLock.h>
17 #include <util/KMessage.h>
18 #include <util/list.h>
19 
20 #include <malloc.h>
21 #include <stddef.h>
22 
23 
24 #define TRACE_MONITOR 0
25 #if TRACE_MONITOR
26 #	define TRACE(x) dprintf x
27 #else
28 #	define TRACE(x) ;
29 #endif
30 
31 
32 // ToDo: add more fine grained locking - maybe using a ref_count in the
33 //		node_monitor structure?
34 // ToDo: implement watching mounts/unmounts
35 // ToDo: return another error code than B_NO_MEMORY if the team's maximum is hit
36 
37 
38 typedef struct monitor_listener monitor_listener;
39 typedef struct node_monitor node_monitor;
40 
41 struct monitor_listener {
42 	monitor_listener	*next;
43 	monitor_listener	*prev;
44 	list_link			monitor_link;
45 	port_id				port;
46 	uint32				token;
47 	uint32				flags;
48 	node_monitor		*monitor;
49 };
50 
51 struct node_monitor {
52 	node_monitor		*next;
53 	mount_id			device;
54 	vnode_id			node;
55 	struct list			listeners;
56 };
57 
58 struct monitor_hash_key {
59 	mount_id	device;
60 	vnode_id	node;
61 };
62 
63 struct interested_monitor_listener_list {
64 	struct list			*listeners;
65 	monitor_listener	*first_listener;
66 	uint32				flags;
67 };
68 
69 #define MONITORS_HASH_TABLE_SIZE 16
70 
71 static hash_table *gMonitorHash;
72 static mutex gMonitorMutex;
73 
74 
75 static int
76 monitor_compare(void *_monitor, const void *_key)
77 {
78 	node_monitor *monitor = (node_monitor*)_monitor;
79 	const struct monitor_hash_key *key = (const struct monitor_hash_key*)_key;
80 
81 	if (monitor->device == key->device && monitor->node == key->node)
82 		return 0;
83 
84 	return -1;
85 }
86 
87 
88 static uint32
89 monitor_hash(void *_monitor, const void *_key, uint32 range)
90 {
91 	node_monitor *monitor = (node_monitor*)_monitor;
92 	const struct monitor_hash_key *key = (const struct monitor_hash_key*)_key;
93 
94 #define MHASH(device, node) (((uint32)((node) >> 32) + (uint32)(node)) ^ (uint32)(device))
95 
96 	if (monitor != NULL)
97 		return MHASH(monitor->device, monitor->node) % range;
98 
99 	return MHASH(key->device, key->node) % range;
100 #undef MHASH
101 }
102 
103 
104 /** Returns the monitor that matches the specified device/node pair.
105  *	Must be called with monitors lock hold.
106  */
107 
108 static node_monitor *
109 get_monitor_for(mount_id device, vnode_id node)
110 {
111 	struct monitor_hash_key key;
112 	key.device = device;
113 	key.node = node;
114 
115 	return (node_monitor *)hash_lookup(gMonitorHash, &key);
116 }
117 
118 
119 /** Returns the listener that matches the specified port/token pair.
120  *	Must be called with monitors lock hold.
121  */
122 
123 static monitor_listener *
124 get_listener_for(node_monitor *monitor, port_id port, uint32 token)
125 {
126 	monitor_listener *listener = NULL;
127 
128 	while ((listener = (monitor_listener*)list_get_next_item(
129 			&monitor->listeners, listener)) != NULL) {
130 		// does this listener match?
131 		if (listener->port == port && listener->token == token)
132 			return listener;
133 	}
134 
135 	return NULL;
136 }
137 
138 
139 /** Removes the specified node_monitor from the hashtable
140  *	and free it.
141  *	Must be called with monitors lock hold.
142  */
143 
144 static void
145 remove_monitor(node_monitor *monitor)
146 {
147 	hash_remove(gMonitorHash, monitor);
148 	free(monitor);
149 }
150 
151 
152 /** Removes the specified monitor_listener from all lists
153  *	and free it.
154  *	Must be called with monitors lock hold.
155  */
156 
157 static void
158 remove_listener(monitor_listener *listener)
159 {
160 	node_monitor *monitor = listener->monitor;
161 
162 	// remove it from the listener and I/O context lists
163 	list_remove_link(&listener->monitor_link);
164 	list_remove_link(listener);
165 
166 	free(listener);
167 
168 	if (list_is_empty(&monitor->listeners))
169 		remove_monitor(monitor);
170 }
171 
172 
173 static status_t
174 add_node_monitor(io_context *context, mount_id device, vnode_id node,
175 	uint32 flags, port_id port, uint32 token)
176 {
177 	monitor_listener *listener;
178 	node_monitor *monitor;
179 	status_t status = B_OK;
180 
181 	TRACE(("add_node_monitor(dev = %ld, node = %Ld, flags = %ld, port = %ld, token = %ld\n",
182 		device, node, flags, port, token));
183 
184 	mutex_lock(&gMonitorMutex);
185 
186 	monitor = get_monitor_for(device, node);
187 	if (monitor == NULL) {
188 		// check if this team is allowed to have more listeners
189 		if (context->num_monitors >= context->max_monitors) {
190 			// the BeBook says to return B_NO_MEMORY in this case, but
191 			// we should have another one.
192 			status = B_NO_MEMORY;
193 			goto out;
194 		}
195 
196 		// create new monitor
197 		monitor = (node_monitor *)malloc(sizeof(node_monitor));
198 		if (monitor == NULL) {
199 			status = B_NO_MEMORY;
200 			goto out;
201 		}
202 
203 		// initialize monitor
204 		monitor->device = device;
205 		monitor->node = node;
206 		list_init_etc(&monitor->listeners, offsetof(monitor_listener, monitor_link));
207 
208 		hash_insert(gMonitorHash, monitor);
209 	} else {
210 		// check if the listener is already listening, and
211 		// if so, just add the new flags
212 
213 		listener = get_listener_for(monitor, port, token);
214 		if (listener != NULL) {
215 			listener->flags |= flags;
216 			goto out;
217 		}
218 
219 		// check if this team is allowed to have more listeners
220 		if (context->num_monitors >= context->max_monitors) {
221 			// the BeBook says to return B_NO_MEMORY in this case, but
222 			// we should have another one.
223 			status = B_NO_MEMORY;
224 			goto out;
225 		}
226 	}
227 
228 	// add listener
229 
230 	listener = (monitor_listener *)malloc(sizeof(monitor_listener));
231 	if (listener == NULL) {
232 		// no memory for the listener, so remove the monitor as well if needed
233 		if (list_is_empty(&monitor->listeners))
234 			remove_monitor(monitor);
235 
236 		status = B_NO_MEMORY;
237 		goto out;
238 	}
239 
240 	// initialize listener, and add it to the lists
241 	listener->port = port;
242 	listener->token = token;
243 	listener->flags = flags;
244 	listener->monitor = monitor;
245 
246 	list_add_link_to_head(&monitor->listeners, &listener->monitor_link);
247 	list_add_link_to_head(&context->node_monitors, listener);
248 
249 	context->num_monitors++;
250 
251 out:
252 	mutex_unlock(&gMonitorMutex);
253 	return status;
254 }
255 
256 
257 static status_t
258 remove_node_monitor(struct io_context *context, mount_id device, vnode_id node,
259 	uint32 flags, port_id port, uint32 token)
260 {
261 	monitor_listener *listener;
262 	node_monitor *monitor;
263 	status_t status = B_OK;
264 
265 	TRACE(("remove_node_monitor(dev = %ld, node = %Ld, flags = %ld, port = %ld, token = %ld\n",
266 		device, node, flags, port, token));
267 
268 	mutex_lock(&gMonitorMutex);
269 
270 	// get the monitor for this device/node pair
271 	monitor = get_monitor_for(device, node);
272 	if (monitor == NULL) {
273 		status = B_ENTRY_NOT_FOUND;
274 		goto out;
275 	}
276 
277 	// see if it has the listener we are looking for
278 	listener = get_listener_for(monitor, port, token);
279 	if (listener == NULL) {
280 		status = B_ENTRY_NOT_FOUND;
281 		goto out;
282 	}
283 
284 	// no flags means remove all flags
285 	if (flags == B_STOP_WATCHING)
286 		flags = ~0;
287 
288 	listener->flags &= ~flags;
289 
290 	// if there aren't anymore flags, remove this listener
291 	if (listener->flags == B_STOP_WATCHING) {
292 		remove_listener(listener);
293 		context->num_monitors--;
294 	}
295 
296 out:
297 	mutex_unlock(&gMonitorMutex);
298 	return status;
299 }
300 
301 
302 static status_t
303 remove_node_monitors_by_target(struct io_context *context, port_id port, uint32 token)
304 {
305 	monitor_listener *listener = NULL;
306 	int32 count = 0;
307 
308 	while ((listener = (monitor_listener*)list_get_next_item(
309 			&context->node_monitors, listener)) != NULL) {
310 		monitor_listener *removeListener;
311 
312 		if (listener->port != port || listener->token != token)
313 			continue;
314 
315 		listener = (monitor_listener*)list_get_prev_item(
316 			&context->node_monitors, removeListener = listener);
317 			// this line sets the listener one item back, allowing us
318 			// to remove its successor (which is saved in "removeListener")
319 
320 		remove_listener(removeListener);
321 		count++;
322 	}
323 
324 	return count > 0 ? B_OK : B_ENTRY_NOT_FOUND;
325 }
326 
327 
328 //	#pragma mark -
329 //	Exported private kernel API
330 
331 
332 status_t
333 remove_node_monitors(struct io_context *context)
334 {
335 	mutex_lock(&gMonitorMutex);
336 
337 	while (!list_is_empty(&context->node_monitors)) {
338 		// the remove_listener() function will also free the node_monitor
339 		// if it doesn't have any listeners attached anymore
340 		remove_listener(
341 			(monitor_listener*)list_get_first_item(&context->node_monitors));
342 	}
343 
344 	mutex_unlock(&gMonitorMutex);
345 	return B_OK;
346 }
347 
348 
349 status_t
350 node_monitor_init(void)
351 {
352 	gMonitorHash = hash_init(MONITORS_HASH_TABLE_SIZE, offsetof(node_monitor, next),
353 		&monitor_compare, &monitor_hash);
354 	if (gMonitorHash == NULL)
355 			panic("node_monitor_init: error creating mounts hash table\n");
356 
357 	return mutex_init(&gMonitorMutex, "node monitor");
358 }
359 
360 
361 //	#pragma mark -
362 //	Exported public kernel API
363 
364 
365 /**	\brief Subscribes a target to node and/or mount watching.
366  *
367  *	Depending on \a flags, different actions are performed. If flags is \c 0,
368  *	mount watching is requested. \a device and \a node must be \c -1 in this
369  *	case. Otherwise node watching is requested. \a device and \a node must
370  *	refer to a valid node, and \a flags must note contain the flag
371  *	\c B_WATCH_MOUNT, but at least one of the other valid flags.
372  *
373  *	\param device The device the node resides on (node_ref::device). \c -1, if
374  *		   only mount watching is requested.
375  *	\param node The node ID of the node (node_ref::device). \c -1, if
376  *		   only mount watching is requested.
377  *	\param flags A bit mask composed of the values specified in
378  *		   <NodeMonitor.h>.
379  *	\param port The port of the target (a looper port).
380  *	\param handlerToken The token of the target handler. \c -2, if the
381  *		   preferred handler of the looper is the target.
382  *	\return \c B_OK, if everything went fine, another error code otherwise.
383  */
384 
385 status_t
386 start_watching(dev_t device, ino_t node, uint32 flags, port_id port, uint32 token)
387 {
388 	io_context *context = get_current_io_context(true);
389 
390 	return add_node_monitor(context, device, node, flags, port, token);
391 }
392 
393 
394 /**	\brief Unsubscribes a target from watching a node.
395  *	\param device The device the node resides on (node_ref::device).
396  *	\param node The node ID of the node (node_ref::device).
397  *	\param flags Which monitors should be removed (B_STOP_WATCHING for all)
398  *	\param port The port of the target (a looper port).
399  *	\param handlerToken The token of the target handler. \c -2, if the
400  *		   preferred handler of the looper is the target.
401  *	\return \c B_OK, if everything went fine, another error code otherwise.
402  */
403 
404 status_t
405 stop_watching(dev_t device, ino_t node, uint32 flags, port_id port, uint32 token)
406 {
407 	io_context *context = get_current_io_context(true);
408 
409 	return remove_node_monitor(context, device, node, flags, port, token);
410 }
411 
412 
413 /**	\brief Unsubscribes a target from node and mount monitoring.
414  *	\param port The port of the target (a looper port).
415  *	\param handlerToken The token of the target handler. \c -2, if the
416  *		   preferred handler of the looper is the target.
417  *	\return \c B_OK, if everything went fine, another error code otherwise.
418  */
419 
420 status_t
421 stop_notifying(port_id port, uint32 token)
422 {
423 	io_context *context = get_current_io_context(true);
424 
425 	return remove_node_monitors_by_target(context, port, token);
426 }
427 
428 
429 status_t
430 notify_listener(int op, mount_id device, vnode_id parentNode, vnode_id toParentNode,
431 	vnode_id node, const char *name)
432 {
433 	monitor_listener *listener;
434 	node_monitor *monitor;
435 
436 	TRACE(("notify_listener(op = %d, device = %ld, node = %Ld, parent = %Ld, toParent = %Ld"
437 		", name = \"%s\"\n", op, device, node, parentNode, toParentNode, name));
438 
439 	mutex_lock(&gMonitorMutex);
440 
441 	// check the main "node"
442 
443 	if ((op == B_ENTRY_MOVED
444 		|| op == B_ENTRY_REMOVED
445 		|| op == B_STAT_CHANGED
446 		|| op == B_ATTR_CHANGED)
447 		&& (monitor = get_monitor_for(device, node)) != NULL) {
448 		// iterate over all listeners for this monitor, and see
449 		// if we have to send anything
450 		listener = NULL;
451 		while ((listener = (monitor_listener*)list_get_next_item(
452 				&monitor->listeners, listener)) != NULL) {
453 			// do we have a reason to notify this listener?
454 			if (((listener->flags & B_WATCH_NAME) != 0
455 				&& (op == B_ENTRY_MOVED || op == B_ENTRY_REMOVED))
456 				|| ((listener->flags & B_WATCH_STAT) != 0
457 				&& op == B_STAT_CHANGED)
458 				|| ((listener->flags & B_WATCH_ATTR) != 0
459 				&& op == B_ATTR_CHANGED)) {
460 				// then do it!
461 				send_notification(listener->port, listener->token, B_NODE_MONITOR,
462 					op, device, 0, parentNode, toParentNode, node, name);
463 			}
464 		}
465 	}
466 
467 	// check its parent directory
468 
469 	if ((op == B_ENTRY_MOVED
470 		|| op == B_ENTRY_REMOVED
471 		|| op == B_ENTRY_CREATED)
472 		&& (monitor = get_monitor_for(device, parentNode)) != NULL) {
473 		// iterate over all listeners for this monitor, and see
474 		// if we have to send anything
475 		listener = NULL;
476 		while ((listener = (monitor_listener*)list_get_next_item(
477 				&monitor->listeners, listener)) != NULL) {
478 			// do we have a reason to notify this listener?
479 			if ((listener->flags & B_WATCH_DIRECTORY) != 0) {
480 				send_notification(listener->port, listener->token, B_NODE_MONITOR,
481 					op, device, 0, parentNode, toParentNode, node, name);
482 			}
483 		}
484 	}
485 
486 	// check its new target parent directory
487 
488 	if (op == B_ENTRY_MOVED
489 		&& (monitor = get_monitor_for(device, toParentNode)) != NULL) {
490 		// iterate over all listeners for this monitor, and see
491 		// if we have to send anything
492 		listener = NULL;
493 		while ((listener = (monitor_listener*)list_get_next_item(
494 				&monitor->listeners, listener)) != NULL) {
495 			// do we have a reason to notify this listener?
496 			if ((listener->flags & B_WATCH_DIRECTORY) != 0) {
497 				send_notification(listener->port, listener->token, B_NODE_MONITOR,
498 					B_ENTRY_MOVED, device, 0, parentNode, toParentNode, node, name);
499 			}
500 		}
501 	}
502 
503 	mutex_unlock(&gMonitorMutex);
504 	return B_OK;
505 }
506 
507 
508 /**	\brief Given device and node ID and a node monitoring event mask, the
509 		   function checks whether there are listeners interested in any of
510 		   the events for that node and, if so, adds the respective listener
511 		   list to a supplied array of listener lists.
512 
513 	Note, that in general not all of the listeners in an appended list will be
514 	interested in the events, but it is guaranteed that
515 	interested_monitor_listener_list::first_listener is indeed
516 	the first listener in the list, that is interested.
517 
518 	\param device The ID of the mounted FS, the node lives in.
519 	\param node The ID of the node.
520 	\param flags The mask specifying the events occurred for the given node
521 		   (a combination of \c B_WATCH_* constants).
522 	\param interestedListeners An array of listener lists. If there are
523 		   interested listeners for the node, the list will be appended to
524 		   this array.
525 	\param interestedListenerCount The number of elements in the
526 		   \a interestedListeners array. Will be incremented, if a list is
527 		   appended.
528  */
529 static
530 void
531 get_interested_monitor_listeners(mount_id device, vnode_id node,
532 	uint32 flags, interested_monitor_listener_list *interestedListeners,
533 	int32 &interestedListenerCount)
534 {
535 	// get the monitor for the node
536 	node_monitor *monitor = get_monitor_for(device, node);
537 	if (!monitor)
538 		return;
539 
540 	// iterate through the listeners until we find one with matching flags
541 	monitor_listener *listener = NULL;
542 	while ((listener = (monitor_listener*)list_get_next_item(
543 			&monitor->listeners, listener)) != NULL) {
544 		if (listener->flags & flags) {
545 			interested_monitor_listener_list &list
546 				= interestedListeners[interestedListenerCount++];
547 			list.listeners = &monitor->listeners;
548 			list.first_listener = listener;
549 			list.flags = flags;
550 			return;
551 		}
552 	}
553 }
554 
555 
556 /**	\brief Sends a notifcation message to the given listeners.
557 	\param message The message to be sent.
558 	\param interestedListeners An array of listener lists.
559 	\param interestedListenerCount The number of elements in the
560 		   \a interestedListeners array.
561 	\return
562 	- \c B_OK, if everything went fine,
563 	- another error code otherwise.
564  */
565 static
566 status_t
567 send_notification_message(KMessage &message,
568 	interested_monitor_listener_list *interestedListeners,
569 	int32 interestedListenerCount)
570 {
571 	// Since the messaging service supports broadcasting and that is more
572 	// efficient than sending the messages individually, we collect the
573 	// listener targets in an array and send the message to them at once.
574 	const int32 maxTargetCount = 16;
575 	messaging_target targets[maxTargetCount];
576 	int32 targetCount = 0;
577 
578 	// iterate through the lists
579 	interested_monitor_listener_list *list = interestedListeners;
580 	for (int32 i = 0; i < interestedListenerCount; i++, list++) {
581 		// iterate through the listeners
582 		monitor_listener *listener = list->first_listener;
583 		do {
584 			if (listener->flags & list->flags) {
585 				// the listener's flags match: add it to the targets
586 				messaging_target &target = targets[targetCount++];
587 				target.port = listener->port;
588 				target.token = listener->token;
589 
590 				// if the target array is full, send the message
591 				if (targetCount == maxTargetCount) {
592 					status_t error = send_message(&message, targets,
593 						targetCount);
594 					if (error != B_OK)
595 						return error;
596 
597 					targetCount = 0;
598 				}
599 			}
600 		} while ((listener = (monitor_listener*)list_get_next_item(
601 				list->listeners, listener)) != NULL);
602 	}
603 
604 	// if any targets are left (the usual case, unless the target array got
605 	// full early), send the message
606 	if (targetCount > 0)
607 		return send_message(&message, targets, targetCount);
608 
609 	return B_OK;
610 }
611 
612 
613 /**	\brief Notifies all interested listeners that an entry has been created
614 		   or removed.
615 	\param opcode \c B_ENTRY_CREATED or \c B_ENTRY_REMOVED.
616 	\param device The ID of the mounted FS, the entry lives/lived in.
617 	\param directory The entry's parent directory ID.
618 	\param name The entry's name.
619 	\param node The ID of the node the entry refers/referred to.
620 	\return
621 	- \c B_OK, if everything went fine,
622 	- another error code otherwise.
623  */
624 static status_t
625 notify_entry_created_or_removed(int32 opcode, mount_id device,
626 	vnode_id directory, const char *name, vnode_id node)
627 {
628 	if (!name)
629 		return B_BAD_VALUE;
630 
631 	MutexLocker locker(gMonitorMutex);
632 
633 	// get the lists of all interested listeners
634 	interested_monitor_listener_list interestedListeners[3];
635 	int32 interestedListenerCount = 0;
636 	// ... for the node
637 	if (opcode != B_ENTRY_CREATED) {
638 		get_interested_monitor_listeners(device, node, B_WATCH_NAME,
639 			interestedListeners, interestedListenerCount);
640 	}
641 	// ... for the directory
642 	get_interested_monitor_listeners(device, directory, B_WATCH_DIRECTORY,
643 		interestedListeners, interestedListenerCount);
644 
645 	if (interestedListenerCount == 0)
646 		return B_OK;
647 
648 	// there are interested listeners: construct the message and send it
649 	char messageBuffer[1024];
650 	KMessage message;
651 	message.SetTo(messageBuffer, sizeof(messageBuffer), B_NODE_MONITOR);
652 	message.AddInt32("opcode", opcode);
653 	message.AddInt32("device", device);
654 	message.AddInt64("directory", directory);
655 	message.AddInt64("node", node);
656 	message.AddString("name", name);			// for "removed" Haiku only
657 
658 	return send_notification_message(message, interestedListeners,
659 		interestedListenerCount);
660 }
661 
662 
663 /**	\brief Notifies all interested listeners that an entry has been created.
664 	\param device The ID of the mounted FS, the entry lives in.
665 	\param directory The entry's parent directory ID.
666 	\param name The entry's name.
667 	\param node The ID of the node the entry refers to.
668 	\return
669 	- \c B_OK, if everything went fine,
670 	- another error code otherwise.
671  */
672 status_t
673 notify_entry_created(mount_id device, vnode_id directory, const char *name,
674 	vnode_id node)
675 {
676 	return notify_entry_created_or_removed(B_ENTRY_CREATED, device,
677 		directory, name, node);
678 }
679 
680 
681 /**	\brief Notifies all interested listeners that an entry has been removed.
682 	\param device The ID of the mounted FS, the entry lived in.
683 	\param directory The entry's former parent directory ID.
684 	\param name The entry's name.
685 	\param node The ID of the node the entry referred to.
686 	\return
687 	- \c B_OK, if everything went fine,
688 	- another error code otherwise.
689  */
690 status_t
691 notify_entry_removed(mount_id device, vnode_id directory, const char *name,
692 	vnode_id node)
693 {
694 	return notify_entry_created_or_removed(B_ENTRY_REMOVED, device,
695 		directory, name, node);
696 }
697 
698 
699 /**	\brief Notifies all interested listeners that an entry has been moved.
700 	\param device The ID of the mounted FS, the entry lives in.
701 	\param fromDirectory The entry's previous parent directory ID.
702 	\param fromName The entry's previous name.
703 	\param toDirectory The entry's new parent directory ID.
704 	\param toName The entry's new name.
705 	\param node The ID of the node the entry refers to.
706 	\return
707 	- \c B_OK, if everything went fine,
708 	- another error code otherwise.
709  */
710 status_t
711 notify_entry_moved(mount_id device, vnode_id fromDirectory,
712 	const char *fromName, vnode_id toDirectory, const char *toName,
713 	vnode_id node)
714 {
715 	if (!fromName || !toName)
716 		return B_BAD_VALUE;
717 
718 	// If node is a mount point, we need to resolve it to the mounted
719 	// volume's root node.
720 	mount_id nodeDevice = device;
721 	resolve_mount_point_to_volume_root(device, node, &nodeDevice, &node);
722 
723 	MutexLocker locker(gMonitorMutex);
724 
725 	// get the lists of all interested listeners
726 	interested_monitor_listener_list interestedListeners[3];
727 	int32 interestedListenerCount = 0;
728 	// ... for the node
729 	get_interested_monitor_listeners(nodeDevice, node, B_WATCH_NAME,
730 		interestedListeners, interestedListenerCount);
731 	// ... for the source directory
732 	get_interested_monitor_listeners(device, fromDirectory, B_WATCH_DIRECTORY,
733 		interestedListeners, interestedListenerCount);
734 	// ... for the target directory
735 	get_interested_monitor_listeners(device, toDirectory, B_WATCH_DIRECTORY,
736 		interestedListeners, interestedListenerCount);
737 
738 	if (interestedListenerCount == 0)
739 		return B_OK;
740 
741 	// there are interested listeners: construct the message and send it
742 	char messageBuffer[1024];
743 	KMessage message;
744 	message.SetTo(messageBuffer, sizeof(messageBuffer), B_NODE_MONITOR);
745 	message.AddInt32("opcode", B_ENTRY_MOVED);
746 	message.AddInt32("device", device);
747 	message.AddInt64("from directory", fromDirectory);
748 	message.AddInt64("to directory", toDirectory);
749 	message.AddInt32("node device", nodeDevice);	// Haiku only
750 	message.AddInt64("node", node);
751 	message.AddString("from name", fromName);		// Haiku only
752 	message.AddString("name", toName);
753 
754 	return send_notification_message(message, interestedListeners,
755 		interestedListenerCount);
756 }
757 
758 
759 /**	\brief Notifies all interested listeners that a node's stat data have
760 		   changed.
761 	\param device The ID of the mounted FS, the node lives in.
762 	\param node The ID of the node.
763 	\param statFields A bitwise combination of one or more of the \c B_STAT_*
764 		   constants defined in <NodeMonitor.h>, indicating what fields of the
765 		   stat data have changed.
766 	\return
767 	- \c B_OK, if everything went fine,
768 	- another error code otherwise.
769  */
770 status_t
771 notify_stat_changed(mount_id device, vnode_id node, uint32 statFields)
772 {
773 	MutexLocker locker(gMonitorMutex);
774 
775 	// get the lists of all interested listeners
776 	interested_monitor_listener_list interestedListeners[3];
777 	int32 interestedListenerCount = 0;
778 	// ... for the node
779 	get_interested_monitor_listeners(device, node, B_WATCH_STAT,
780 		interestedListeners, interestedListenerCount);
781 
782 	if (interestedListenerCount == 0)
783 		return B_OK;
784 
785 	// there are interested listeners: construct the message and send it
786 	char messageBuffer[1024];
787 	KMessage message;
788 	message.SetTo(messageBuffer, sizeof(messageBuffer), B_NODE_MONITOR);
789 	message.AddInt32("opcode", B_STAT_CHANGED);
790 	message.AddInt32("device", device);
791 	message.AddInt64("node", node);
792 	message.AddInt32("fields", statFields);		// Haiku only
793 
794 	return send_notification_message(message, interestedListeners,
795 		interestedListenerCount);
796 }
797 
798 
799 /**	\brief Notifies all interested listeners that a node attribute has changed.
800 	\param device The ID of the mounted FS, the node lives in.
801 	\param node The ID of the node.
802 	\param attribute The attribute's name.
803 	\param cause Either of \c B_ATTR_CREATED, \c B_ATTR_REMOVED, or
804 		   \c B_ATTR_CHANGED, indicating what exactly happened to the attribute.
805 	\return
806 	- \c B_OK, if everything went fine,
807 	- another error code otherwise.
808  */
809 status_t
810 notify_attribute_changed(mount_id device, vnode_id node, const char *attribute,
811 	int32 cause)
812 {
813 	if (!attribute)
814 		return B_BAD_VALUE;
815 
816 	MutexLocker locker(gMonitorMutex);
817 
818 	// get the lists of all interested listeners
819 	interested_monitor_listener_list interestedListeners[3];
820 	int32 interestedListenerCount = 0;
821 	// ... for the node
822 	get_interested_monitor_listeners(device, node, B_WATCH_ATTR,
823 		interestedListeners, interestedListenerCount);
824 
825 	if (interestedListenerCount == 0)
826 		return B_OK;
827 
828 	// there are interested listeners: construct the message and send it
829 	char messageBuffer[1024];
830 	KMessage message;
831 	message.SetTo(messageBuffer, sizeof(messageBuffer), B_NODE_MONITOR);
832 	message.AddInt32("opcode", B_ATTR_CHANGED);
833 	message.AddInt32("device", device);
834 	message.AddInt64("node", node);
835 	message.AddString("attr", attribute);
836 	message.AddInt32("cause", cause);		// Haiku only
837 
838 	return send_notification_message(message, interestedListeners,
839 		interestedListenerCount);
840 }
841 
842 
843 /**	\brief Notifies the listener of a live query that an entry has been added
844 		   to or removed from the query (for whatever reason).
845 	\param opcode \c B_ENTRY_CREATED or \c B_ENTRY_REMOVED.
846 	\param port The target port of the listener.
847 	\param token The BHandler token of the listener.
848 	\param device The ID of the mounted FS, the entry lives in.
849 	\param directory The entry's parent directory ID.
850 	\param name The entry's name.
851 	\param node The ID of the node the entry refers to.
852 	\return
853 	- \c B_OK, if everything went fine,
854 	- another error code otherwise.
855  */
856 static status_t
857 notify_query_entry_created_or_removed(int32 opcode, port_id port, int32 token,
858 	mount_id device, vnode_id directory, const char *name, vnode_id node)
859 {
860 	if (!name)
861 		return B_BAD_VALUE;
862 
863 	// construct the message
864 	char messageBuffer[1024];
865 	KMessage message;
866 	message.SetTo(messageBuffer, sizeof(messageBuffer), B_QUERY_UPDATE);
867 	message.AddInt32("opcode", opcode);
868 	message.AddInt32("device", device);
869 	message.AddInt64("directory", directory);
870 	message.AddInt64("node", node);
871 	message.AddString("name", name);
872 
873 	// send the message
874 	messaging_target target;
875 	target.port = port;
876 	target.token = token;
877 
878 	return send_message(&message, &target, 1);
879 }
880 
881 
882 /**	\brief Notifies the listener of a live query that an entry has been added
883 		   to the query (for whatever reason).
884 	\param port The target port of the listener.
885 	\param token The BHandler token of the listener.
886 	\param device The ID of the mounted FS, the entry lives in.
887 	\param directory The entry's parent directory ID.
888 	\param name The entry's name.
889 	\param node The ID of the node the entry refers to.
890 	\return
891 	- \c B_OK, if everything went fine,
892 	- another error code otherwise.
893  */
894 status_t
895 notify_query_entry_created(port_id port, int32 token, mount_id device,
896 	vnode_id directory, const char *name, vnode_id node)
897 {
898 	return notify_query_entry_created_or_removed(B_ENTRY_CREATED, port, token,
899 		device, directory, name, node);
900 }
901 
902 
903 /**	\brief Notifies the listener of a live query that an entry has been removed
904 		   from the query (for whatever reason).
905 	\param port The target port of the listener.
906 	\param token The BHandler token of the listener.
907 	\param device The ID of the mounted FS, the entry lives in.
908 	\param directory The entry's parent directory ID.
909 	\param name The entry's name.
910 	\param node The ID of the node the entry refers to.
911 	\return
912 	- \c B_OK, if everything went fine,
913 	- another error code otherwise.
914  */
915 status_t
916 notify_query_entry_removed(port_id port, int32 token,
917 	mount_id device, vnode_id directory, const char *name, vnode_id node)
918 {
919 	return notify_query_entry_created_or_removed(B_ENTRY_REMOVED, port, token,
920 		device, directory, name, node);
921 }
922 
923 
924 
925 //	#pragma mark -
926 //	Userland syscalls
927 
928 
929 status_t
930 _user_stop_notifying(port_id port, uint32 token)
931 {
932 	io_context *context = get_current_io_context(false);
933 
934 	return remove_node_monitors_by_target(context, port, token);
935 }
936 
937 
938 status_t
939 _user_start_watching(dev_t device, ino_t node, uint32 flags, port_id port, uint32 token)
940 {
941 	io_context *context = get_current_io_context(false);
942 
943 	return add_node_monitor(context, device, node, flags, port, token);
944 }
945 
946 
947 status_t
948 _user_stop_watching(dev_t device, ino_t node, uint32 flags, port_id port, uint32 token)
949 {
950 	io_context *context = get_current_io_context(false);
951 
952 	return remove_node_monitor(context, device, node, flags, port, token);
953 }
954 
955