xref: /haiku/src/system/kernel/fs/node_monitor.cpp (revision 1e36cfc2721ef13a187c6f7354dc9cbc485e89d3)
1 /*
2  * Copyright 2003-2006, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
3  * Distributed under the terms of the MIT 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
25 #ifdef 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 /**	\brief Given device and node ID and a node monitoring event mask, the
329  *		   function checks whether there are listeners interested in any of
330  *		   the events for that node and, if so, adds the respective listener
331  *		   list to a supplied array of listener lists.
332  *
333  *	Note, that in general not all of the listeners in an appended list will be
334  *	interested in the events, but it is guaranteed that
335  *	interested_monitor_listener_list::first_listener is indeed
336  *	the first listener in the list, that is interested.
337  *
338  *	\param device The ID of the mounted FS, the node lives in.
339  *	\param node The ID of the node.
340  *	\param flags The mask specifying the events occurred for the given node
341  *		   (a combination of \c B_WATCH_* constants).
342  *	\param interestedListeners An array of listener lists. If there are
343  *		   interested listeners for the node, the list will be appended to
344  *		   this array.
345  *	\param interestedListenerCount The number of elements in the
346  *		   \a interestedListeners array. Will be incremented, if a list is
347  *		   appended.
348  */
349 
350 static void
351 get_interested_monitor_listeners(mount_id device, vnode_id node,
352 	uint32 flags, interested_monitor_listener_list *interestedListeners,
353 	int32 &interestedListenerCount)
354 {
355 	// get the monitor for the node
356 	node_monitor *monitor = get_monitor_for(device, node);
357 	if (!monitor)
358 		return;
359 
360 	// iterate through the listeners until we find one with matching flags
361 	monitor_listener *listener = NULL;
362 	while ((listener = (monitor_listener*)list_get_next_item(
363 			&monitor->listeners, listener)) != NULL) {
364 		if (listener->flags & flags) {
365 			interested_monitor_listener_list &list
366 				= interestedListeners[interestedListenerCount++];
367 			list.listeners = &monitor->listeners;
368 			list.first_listener = listener;
369 			list.flags = flags;
370 			return;
371 		}
372 	}
373 }
374 
375 
376 /**	\brief Sends a notifcation message to the given listeners.
377  *	\param message The message to be sent.
378  *	\param interestedListeners An array of listener lists.
379  *	\param interestedListenerCount The number of elements in the
380  *		   \a interestedListeners array.
381  *	\return
382  *	- \c B_OK, if everything went fine,
383  *	- another error code otherwise.
384  */
385 
386 static status_t
387 send_notification_message(KMessage &message,
388 	interested_monitor_listener_list *interestedListeners,
389 	int32 interestedListenerCount)
390 {
391 	// Since the messaging service supports broadcasting and that is more
392 	// efficient than sending the messages individually, we collect the
393 	// listener targets in an array and send the message to them at once.
394 	const int32 maxTargetCount = 16;
395 	messaging_target targets[maxTargetCount];
396 	int32 targetCount = 0;
397 
398 	// iterate through the lists
399 	interested_monitor_listener_list *list = interestedListeners;
400 	for (int32 i = 0; i < interestedListenerCount; i++, list++) {
401 		// iterate through the listeners
402 		monitor_listener *listener = list->first_listener;
403 		do {
404 			if (listener->flags & list->flags) {
405 				// the listener's flags match: add it to the targets
406 				messaging_target &target = targets[targetCount++];
407 				target.port = listener->port;
408 				target.token = listener->token;
409 
410 				// if the target array is full, send the message
411 				if (targetCount == maxTargetCount) {
412 					status_t error = send_message(&message, targets,
413 						targetCount);
414 					if (error != B_OK)
415 						return error;
416 
417 					targetCount = 0;
418 				}
419 			}
420 		} while ((listener = (monitor_listener*)list_get_next_item(
421 				list->listeners, listener)) != NULL);
422 	}
423 
424 	// if any targets are left (the usual case, unless the target array got
425 	// full early), send the message
426 	if (targetCount > 0)
427 		return send_message(&message, targets, targetCount);
428 
429 	return B_OK;
430 }
431 
432 
433 /**	\brief Notifies all interested listeners that an entry has been created
434  *		   or removed.
435  *	\param opcode \c B_ENTRY_CREATED or \c B_ENTRY_REMOVED.
436  *	\param device The ID of the mounted FS, the entry lives/lived in.
437  *	\param directory The entry's parent directory ID.
438  *	\param name The entry's name.
439  *	\param node The ID of the node the entry refers/referred to.
440  *	\return
441  *	- \c B_OK, if everything went fine,
442  *	- another error code otherwise.
443  */
444 
445 static status_t
446 notify_entry_created_or_removed(int32 opcode, mount_id device,
447 	vnode_id directory, const char *name, vnode_id node)
448 {
449 	if (!name)
450 		return B_BAD_VALUE;
451 
452 	MutexLocker locker(gMonitorMutex);
453 
454 	// get the lists of all interested listeners
455 	interested_monitor_listener_list interestedListeners[3];
456 	int32 interestedListenerCount = 0;
457 	// ... for the node
458 	if (opcode != B_ENTRY_CREATED) {
459 		get_interested_monitor_listeners(device, node, B_WATCH_NAME,
460 			interestedListeners, interestedListenerCount);
461 	}
462 	// ... for the directory
463 	get_interested_monitor_listeners(device, directory, B_WATCH_DIRECTORY,
464 		interestedListeners, interestedListenerCount);
465 
466 	if (interestedListenerCount == 0)
467 		return B_OK;
468 
469 	// there are interested listeners: construct the message and send it
470 	char messageBuffer[1024];
471 	KMessage message;
472 	message.SetTo(messageBuffer, sizeof(messageBuffer), B_NODE_MONITOR);
473 	message.AddInt32("opcode", opcode);
474 	message.AddInt32("device", device);
475 	message.AddInt64("directory", directory);
476 	message.AddInt64("node", node);
477 	message.AddString("name", name);			// for "removed" Haiku only
478 
479 	return send_notification_message(message, interestedListeners,
480 		interestedListenerCount);
481 }
482 
483 
484 /**	\brief Notifies the listener of a live query that an entry has been added
485  *		   to or removed from the query (for whatever reason).
486  *	\param opcode \c B_ENTRY_CREATED or \c B_ENTRY_REMOVED.
487  *	\param port The target port of the listener.
488  *	\param token The BHandler token of the listener.
489  *	\param device The ID of the mounted FS, the entry lives in.
490  *	\param directory The entry's parent directory ID.
491  *	\param name The entry's name.
492  *	\param node The ID of the node the entry refers to.
493  *	\return
494  *	- \c B_OK, if everything went fine,
495  *	- another error code otherwise.
496  */
497 
498 static status_t
499 notify_query_entry_created_or_removed(int32 opcode, port_id port, int32 token,
500 	mount_id device, vnode_id directory, const char *name, vnode_id node)
501 {
502 	if (!name)
503 		return B_BAD_VALUE;
504 
505 	// construct the message
506 	char messageBuffer[1024];
507 	KMessage message;
508 	message.SetTo(messageBuffer, sizeof(messageBuffer), B_QUERY_UPDATE);
509 	message.AddInt32("opcode", opcode);
510 	message.AddInt32("device", device);
511 	message.AddInt64("directory", directory);
512 	message.AddInt64("node", node);
513 	message.AddString("name", name);
514 
515 	// send the message
516 	messaging_target target;
517 	target.port = port;
518 	target.token = token;
519 
520 	return send_message(&message, &target, 1);
521 }
522 
523 
524 //	#pragma mark - private kernel API
525 
526 
527 status_t
528 remove_node_monitors(struct io_context *context)
529 {
530 	mutex_lock(&gMonitorMutex);
531 
532 	while (!list_is_empty(&context->node_monitors)) {
533 		// the remove_listener() function will also free the node_monitor
534 		// if it doesn't have any listeners attached anymore
535 		remove_listener(
536 			(monitor_listener*)list_get_first_item(&context->node_monitors));
537 	}
538 
539 	mutex_unlock(&gMonitorMutex);
540 	return B_OK;
541 }
542 
543 
544 status_t
545 node_monitor_init(void)
546 {
547 	gMonitorHash = hash_init(MONITORS_HASH_TABLE_SIZE, offsetof(node_monitor, next),
548 		&monitor_compare, &monitor_hash);
549 	if (gMonitorHash == NULL)
550 			panic("node_monitor_init: error creating mounts hash table\n");
551 
552 	return mutex_init(&gMonitorMutex, "node monitor");
553 }
554 
555 
556 status_t
557 notify_unmount(mount_id device)
558 {
559 	TRACE(("unmounted device: %ld\n", device));
560 
561 	MutexLocker locker(gMonitorMutex);
562 
563 	// get the lists of all interested listeners
564 	interested_monitor_listener_list interestedListeners[3];
565 	int32 interestedListenerCount = 0;
566 	get_interested_monitor_listeners(-1, -1, B_WATCH_MOUNT,
567 		interestedListeners, interestedListenerCount);
568 
569 	if (interestedListenerCount == 0)
570 		return B_OK;
571 
572 	// there are interested listeners: construct the message and send it
573 	char messageBuffer[96];
574 	KMessage message;
575 	message.SetTo(messageBuffer, sizeof(messageBuffer), B_NODE_MONITOR);
576 	message.AddInt32("opcode", B_DEVICE_UNMOUNTED);
577 	message.AddInt32("device", device);
578 
579 	return send_notification_message(message, interestedListeners,
580 		interestedListenerCount);
581 
582 	return B_OK;
583 }
584 
585 
586 status_t
587 notify_mount(mount_id device, mount_id parentDevice, vnode_id parentDirectory)
588 {
589 	TRACE(("mounted device: %ld, parent %ld:%Ld\n", device, parentDevice,
590 		parentDirectory));
591 
592 	MutexLocker locker(gMonitorMutex);
593 
594 	// get the lists of all interested listeners
595 	interested_monitor_listener_list interestedListeners[3];
596 	int32 interestedListenerCount = 0;
597 	get_interested_monitor_listeners(-1, -1, B_WATCH_MOUNT,
598 		interestedListeners, interestedListenerCount);
599 
600 	if (interestedListenerCount == 0)
601 		return B_OK;
602 
603 	// there are interested listeners: construct the message and send it
604 	char messageBuffer[128];
605 	KMessage message;
606 	message.SetTo(messageBuffer, sizeof(messageBuffer), B_NODE_MONITOR);
607 	message.AddInt32("opcode", B_DEVICE_MOUNTED);
608 	message.AddInt32("new device", device);
609 	message.AddInt32("device", parentDevice);
610 	message.AddInt64("directory", parentDirectory);
611 
612 	return send_notification_message(message, interestedListeners,
613 		interestedListenerCount);
614 
615 	return B_OK;
616 }
617 
618 
619 //	#pragma mark - public kernel API
620 
621 
622 /**	\brief Subscribes a target to node and/or mount watching.
623  *
624  *	Depending on \a flags, different actions are performed. If flags is \c 0,
625  *	mount watching is requested. \a device and \a node must be \c -1 in this
626  *	case. Otherwise node watching is requested. \a device and \a node must
627  *	refer to a valid node, and \a flags must note contain the flag
628  *	\c B_WATCH_MOUNT, but at least one of the other valid flags.
629  *
630  *	\param device The device the node resides on (node_ref::device). \c -1, if
631  *		   only mount watching is requested.
632  *	\param node The node ID of the node (node_ref::device). \c -1, if
633  *		   only mount watching is requested.
634  *	\param flags A bit mask composed of the values specified in
635  *		   <NodeMonitor.h>.
636  *	\param port The port of the target (a looper port).
637  *	\param handlerToken The token of the target handler. \c -2, if the
638  *		   preferred handler of the looper is the target.
639  *	\return \c B_OK, if everything went fine, another error code otherwise.
640  */
641 
642 status_t
643 start_watching(dev_t device, ino_t node, uint32 flags, port_id port, uint32 token)
644 {
645 	io_context *context = get_current_io_context(true);
646 
647 	return add_node_monitor(context, device, node, flags, port, token);
648 }
649 
650 
651 /**	\brief Unsubscribes a target from watching a node.
652  *	\param device The device the node resides on (node_ref::device).
653  *	\param node The node ID of the node (node_ref::device).
654  *	\param flags Which monitors should be removed (B_STOP_WATCHING for all)
655  *	\param port The port of the target (a looper port).
656  *	\param handlerToken The token of the target handler. \c -2, if the
657  *		   preferred handler of the looper is the target.
658  *	\return \c B_OK, if everything went fine, another error code otherwise.
659  */
660 
661 status_t
662 stop_watching(dev_t device, ino_t node, uint32 flags, port_id port, uint32 token)
663 {
664 	io_context *context = get_current_io_context(true);
665 
666 	return remove_node_monitor(context, device, node, flags, port, token);
667 }
668 
669 
670 /**	\brief Unsubscribes a target from node and mount monitoring.
671  *	\param port The port of the target (a looper port).
672  *	\param handlerToken The token of the target handler. \c -2, if the
673  *		   preferred handler of the looper is the target.
674  *	\return \c B_OK, if everything went fine, another error code otherwise.
675  */
676 
677 status_t
678 stop_notifying(port_id port, uint32 token)
679 {
680 	io_context *context = get_current_io_context(true);
681 
682 	return remove_node_monitors_by_target(context, port, token);
683 }
684 
685 
686 status_t
687 notify_listener(int op, mount_id device, vnode_id parentNode, vnode_id toParentNode,
688 	vnode_id node, const char *name)
689 {
690 	TRACE(("notify_listener(op = %d, device = %ld, node = %Ld, parent = %Ld, toParent = %Ld"
691 		", name = \"%s\"\n", op, device, node, parentNode, toParentNode, name));
692 
693 	switch (op) {
694 		case B_ENTRY_CREATED:
695 			return notify_entry_created(device, parentNode, name, node);
696 
697 		case B_ENTRY_REMOVED:
698 			return notify_entry_removed(device, parentNode, name, node);
699 
700 		case B_ENTRY_MOVED:
701 			// no fromName -- use an empty string
702 			return notify_entry_moved(device, parentNode, "", toParentNode,
703 				name, node);
704 
705 		case B_STAT_CHANGED:
706 		{
707 			// no statFields -- consider all stat fields changed
708 			uint32 statFields = B_STAT_MODE | B_STAT_UID | B_STAT_GID
709 				| B_STAT_SIZE | B_STAT_ACCESS_TIME | B_STAT_MODIFICATION_TIME
710 				| B_STAT_CREATION_TIME | B_STAT_CHANGE_TIME;
711 			return notify_stat_changed(device, node, statFields);
712 		}
713 
714 		case B_ATTR_CHANGED:
715 			// no cause -- use B_ATTR_CHANGED
716 			return notify_attribute_changed(device, node, name, B_ATTR_CHANGED);
717 
718 		default:
719 			return B_BAD_VALUE;
720 	}
721 }
722 
723 
724 /**	\brief Notifies all interested listeners that an entry has been created.
725  *	\param device The ID of the mounted FS, the entry lives in.
726  *	\param directory The entry's parent directory ID.
727  *	\param name The entry's name.
728  *	\param node The ID of the node the entry refers to.
729  *	\return
730  *	- \c B_OK, if everything went fine,
731  *	- another error code otherwise.
732  */
733 
734 status_t
735 notify_entry_created(mount_id device, vnode_id directory, const char *name,
736 	vnode_id node)
737 {
738 	return notify_entry_created_or_removed(B_ENTRY_CREATED, device,
739 		directory, name, node);
740 }
741 
742 
743 /**	\brief Notifies all interested listeners that an entry has been removed.
744  *	\param device The ID of the mounted FS, the entry lived in.
745  *	\param directory The entry's former parent directory ID.
746  *	\param name The entry's name.
747  *	\param node The ID of the node the entry referred to.
748  *	\return
749  *	- \c B_OK, if everything went fine,
750  *	- another error code otherwise.
751  */
752 
753 status_t
754 notify_entry_removed(mount_id device, vnode_id directory, const char *name,
755 	vnode_id node)
756 {
757 	return notify_entry_created_or_removed(B_ENTRY_REMOVED, device,
758 		directory, name, node);
759 }
760 
761 
762 /**	\brief Notifies all interested listeners that an entry has been moved.
763  *	\param device The ID of the mounted FS, the entry lives in.
764  *	\param fromDirectory The entry's previous parent directory ID.
765  *	\param fromName The entry's previous name.
766  *	\param toDirectory The entry's new parent directory ID.
767  *	\param toName The entry's new name.
768  *	\param node The ID of the node the entry refers to.
769  *	\return
770  *	- \c B_OK, if everything went fine,
771  *	- another error code otherwise.
772  */
773 
774 status_t
775 notify_entry_moved(mount_id device, vnode_id fromDirectory,
776 	const char *fromName, vnode_id toDirectory, const char *toName,
777 	vnode_id node)
778 {
779 	if (!fromName || !toName)
780 		return B_BAD_VALUE;
781 
782 	// If node is a mount point, we need to resolve it to the mounted
783 	// volume's root node.
784 	mount_id nodeDevice = device;
785 	resolve_mount_point_to_volume_root(device, node, &nodeDevice, &node);
786 
787 	MutexLocker locker(gMonitorMutex);
788 
789 	// get the lists of all interested listeners
790 	interested_monitor_listener_list interestedListeners[3];
791 	int32 interestedListenerCount = 0;
792 	// ... for the node
793 	get_interested_monitor_listeners(nodeDevice, node, B_WATCH_NAME,
794 		interestedListeners, interestedListenerCount);
795 	// ... for the source directory
796 	get_interested_monitor_listeners(device, fromDirectory, B_WATCH_DIRECTORY,
797 		interestedListeners, interestedListenerCount);
798 	// ... for the target directory
799 	if (toDirectory != fromDirectory) {
800 		get_interested_monitor_listeners(device, toDirectory, B_WATCH_DIRECTORY,
801 			interestedListeners, interestedListenerCount);
802 	}
803 
804 	if (interestedListenerCount == 0)
805 		return B_OK;
806 
807 	// there are interested listeners: construct the message and send it
808 	char messageBuffer[1024];
809 	KMessage message;
810 	message.SetTo(messageBuffer, sizeof(messageBuffer), B_NODE_MONITOR);
811 	message.AddInt32("opcode", B_ENTRY_MOVED);
812 	message.AddInt32("device", device);
813 	message.AddInt64("from directory", fromDirectory);
814 	message.AddInt64("to directory", toDirectory);
815 	message.AddInt32("node device", nodeDevice);	// Haiku only
816 	message.AddInt64("node", node);
817 	message.AddString("from name", fromName);		// Haiku only
818 	message.AddString("name", toName);
819 
820 	return send_notification_message(message, interestedListeners,
821 		interestedListenerCount);
822 }
823 
824 
825 /**	\brief Notifies all interested listeners that a node's stat data have
826  *		   changed.
827  *	\param device The ID of the mounted FS, the node lives in.
828  *	\param node The ID of the node.
829  *	\param statFields A bitwise combination of one or more of the \c B_STAT_*
830  *		   constants defined in <NodeMonitor.h>, indicating what fields of the
831  *		   stat data have changed.
832  *	\return
833  *	- \c B_OK, if everything went fine,
834  *	- another error code otherwise.
835  */
836 
837 status_t
838 notify_stat_changed(mount_id device, vnode_id node, uint32 statFields)
839 {
840 	MutexLocker locker(gMonitorMutex);
841 
842 	// get the lists of all interested listeners
843 	interested_monitor_listener_list interestedListeners[3];
844 	int32 interestedListenerCount = 0;
845 	// ... for the node
846 	get_interested_monitor_listeners(device, node, B_WATCH_STAT,
847 		interestedListeners, interestedListenerCount);
848 
849 	if (interestedListenerCount == 0)
850 		return B_OK;
851 
852 	// there are interested listeners: construct the message and send it
853 	char messageBuffer[1024];
854 	KMessage message;
855 	message.SetTo(messageBuffer, sizeof(messageBuffer), B_NODE_MONITOR);
856 	message.AddInt32("opcode", B_STAT_CHANGED);
857 	message.AddInt32("device", device);
858 	message.AddInt64("node", node);
859 	message.AddInt32("fields", statFields);		// Haiku only
860 
861 	return send_notification_message(message, interestedListeners,
862 		interestedListenerCount);
863 }
864 
865 
866 /**	\brief Notifies all interested listeners that a node attribute has changed.
867  *	\param device The ID of the mounted FS, the node lives in.
868  *	\param node The ID of the node.
869  *	\param attribute The attribute's name.
870  *	\param cause Either of \c B_ATTR_CREATED, \c B_ATTR_REMOVED, or
871  *		   \c B_ATTR_CHANGED, indicating what exactly happened to the attribute.
872  *	\return
873  *	- \c B_OK, if everything went fine,
874  *	- another error code otherwise.
875  */
876 
877 status_t
878 notify_attribute_changed(mount_id device, vnode_id node, const char *attribute,
879 	int32 cause)
880 {
881 	if (!attribute)
882 		return B_BAD_VALUE;
883 
884 	MutexLocker locker(gMonitorMutex);
885 
886 	// get the lists of all interested listeners
887 	interested_monitor_listener_list interestedListeners[3];
888 	int32 interestedListenerCount = 0;
889 	// ... for the node
890 	get_interested_monitor_listeners(device, node, B_WATCH_ATTR,
891 		interestedListeners, interestedListenerCount);
892 
893 	if (interestedListenerCount == 0)
894 		return B_OK;
895 
896 	// there are interested listeners: construct the message and send it
897 	char messageBuffer[1024];
898 	KMessage message;
899 	message.SetTo(messageBuffer, sizeof(messageBuffer), B_NODE_MONITOR);
900 	message.AddInt32("opcode", B_ATTR_CHANGED);
901 	message.AddInt32("device", device);
902 	message.AddInt64("node", node);
903 	message.AddString("attr", attribute);
904 	message.AddInt32("cause", cause);		// Haiku only
905 
906 	return send_notification_message(message, interestedListeners,
907 		interestedListenerCount);
908 }
909 
910 
911 /**	\brief Notifies the listener of a live query that an entry has been added
912  *		   to the query (for whatever reason).
913  *	\param port The target port of the listener.
914  *	\param token The BHandler token of the listener.
915  *	\param device The ID of the mounted FS, the entry lives in.
916  *	\param directory The entry's parent directory ID.
917  *	\param name The entry's name.
918  *	\param node The ID of the node the entry refers to.
919  *	\return
920  *	- \c B_OK, if everything went fine,
921  *	- another error code otherwise.
922  */
923 
924 status_t
925 notify_query_entry_created(port_id port, int32 token, mount_id device,
926 	vnode_id directory, const char *name, vnode_id node)
927 {
928 	return notify_query_entry_created_or_removed(B_ENTRY_CREATED, port, token,
929 		device, directory, name, node);
930 }
931 
932 
933 /**	\brief Notifies the listener of a live query that an entry has been removed
934  *		   from the query (for whatever reason).
935  *	\param port The target port of the listener.
936  *	\param token The BHandler token of the listener.
937  *	\param device The ID of the mounted FS, the entry lives in.
938  *	\param directory The entry's parent directory ID.
939  *	\param name The entry's name.
940  *	\param node The ID of the node the entry refers to.
941  *	\return
942  *	- \c B_OK, if everything went fine,
943  *	- another error code otherwise.
944  */
945 
946 status_t
947 notify_query_entry_removed(port_id port, int32 token,
948 	mount_id device, vnode_id directory, const char *name, vnode_id node)
949 {
950 	return notify_query_entry_created_or_removed(B_ENTRY_REMOVED, port, token,
951 		device, directory, name, node);
952 }
953 
954 
955 //	#pragma mark - User syscalls
956 
957 
958 status_t
959 _user_stop_notifying(port_id port, uint32 token)
960 {
961 	io_context *context = get_current_io_context(false);
962 
963 	return remove_node_monitors_by_target(context, port, token);
964 }
965 
966 
967 status_t
968 _user_start_watching(dev_t device, ino_t node, uint32 flags, port_id port, uint32 token)
969 {
970 	io_context *context = get_current_io_context(false);
971 
972 	return add_node_monitor(context, device, node, flags, port, token);
973 }
974 
975 
976 status_t
977 _user_stop_watching(dev_t device, ino_t node, uint32 flags, port_id port, uint32 token)
978 {
979 	io_context *context = get_current_io_context(false);
980 
981 	return remove_node_monitor(context, device, node, flags, port, token);
982 }
983 
984