vfs: check userland buffers before reading them.
[haiku.git] / src / system / kernel / fs / node_monitor.cpp
blobf37081d9176405b930dc4508d83f076b8bf36c55
1 /*
2 * Copyright 2003-2016, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
3 * Copyright 2005-2008, Ingo Weinhold, bonefish@users.sf.net.
4 * Copyright 2010, Clemens Zeidler, haiku@clemens-zeidler.de.
6 * Distributed under the terms of the MIT License.
7 */
10 #include <fs/node_monitor.h>
12 #include <stddef.h>
13 #include <stdlib.h>
15 #include <AppDefs.h>
16 #include <NodeMonitor.h>
18 #include <fd.h>
19 #include <lock.h>
20 #include <messaging.h>
21 #include <Notifications.h>
22 #include <vfs.h>
23 #include <util/AutoLock.h>
24 #include <util/DoublyLinkedList.h>
25 #include <util/KMessage.h>
26 #include <util/list.h>
28 #include "node_monitor_private.h"
29 #include "Vnode.h"
32 //#define TRACE_MONITOR
33 #ifdef TRACE_MONITOR
34 # define TRACE(x) dprintf x
35 #else
36 # define TRACE(x) ;
37 #endif
40 // ToDo: add more fine grained locking - maybe using a ref_count in the
41 // node_monitor structure?
42 // ToDo: return another error code than B_NO_MEMORY if the team's maximum is hit
45 typedef struct monitor_listener monitor_listener;
46 typedef struct node_monitor node_monitor;
48 struct monitor_listener {
49 list_link context_link;
50 DoublyLinkedListLink<monitor_listener> monitor_link;
51 NotificationListener *listener;
52 uint32 flags;
53 node_monitor *monitor;
56 typedef DoublyLinkedList<monitor_listener, DoublyLinkedListMemberGetLink<
57 monitor_listener, &monitor_listener::monitor_link> > MonitorListenerList;
59 struct node_monitor {
60 node_monitor* hash_link;
61 dev_t device;
62 ino_t node;
63 MonitorListenerList listeners;
66 struct interested_monitor_listener_list {
67 MonitorListenerList::Iterator iterator;
68 uint32 flags;
71 static UserMessagingMessageSender sNodeMonitorSender;
73 class UserNodeListener : public UserMessagingListener {
74 public:
75 UserNodeListener(port_id port, int32 token)
76 : UserMessagingListener(sNodeMonitorSender, port, token)
80 bool operator==(const NotificationListener& _other) const
82 const UserNodeListener* other
83 = dynamic_cast<const UserNodeListener*>(&_other);
84 return other != NULL && other->Port() == Port()
85 && other->Token() == Token();
89 class NodeMonitorService : public NotificationService {
90 public:
91 NodeMonitorService();
92 virtual ~NodeMonitorService();
94 status_t InitCheck();
96 status_t NotifyEntryCreatedOrRemoved(int32 opcode, dev_t device,
97 ino_t directory, const char *name, ino_t node);
98 status_t NotifyEntryMoved(dev_t device, ino_t fromDirectory,
99 const char *fromName, ino_t toDirectory, const char *toName,
100 ino_t node);
101 status_t NotifyStatChanged(dev_t device, ino_t directory, ino_t node,
102 uint32 statFields);
103 status_t NotifyAttributeChanged(dev_t device, ino_t directory,
104 ino_t node, const char *attribute, int32 cause);
105 status_t NotifyUnmount(dev_t device);
106 status_t NotifyMount(dev_t device, dev_t parentDevice,
107 ino_t parentDirectory);
109 status_t RemoveListeners(io_context *context);
111 status_t AddListener(const KMessage *eventSpecifier,
112 NotificationListener &listener);
113 status_t UpdateListener(const KMessage *eventSpecifier,
114 NotificationListener &listener);
115 status_t RemoveListener(const KMessage *eventSpecifier,
116 NotificationListener &listener);
118 status_t AddListener(io_context *context, dev_t device, ino_t node,
119 uint32 flags, NotificationListener &notificationListener);
120 status_t RemoveListener(io_context *context, dev_t device, ino_t node,
121 NotificationListener &notificationListener);
123 status_t RemoveUserListeners(struct io_context *context,
124 port_id port, uint32 token);
125 status_t UpdateUserListener(io_context *context, dev_t device,
126 ino_t node, uint32 flags, UserNodeListener &userListener);
128 virtual const char* Name() { return "node monitor"; }
130 private:
131 void _RemoveMonitor(node_monitor *monitor, uint32 flags);
132 status_t _RemoveListener(io_context *context, dev_t device, ino_t node,
133 NotificationListener& notificationListener, bool isVolumeListener);
134 void _RemoveListener(monitor_listener *listener);
135 node_monitor *_MonitorFor(dev_t device, ino_t node,
136 bool isVolumeListener);
137 status_t _GetMonitor(io_context *context, dev_t device, ino_t node,
138 bool addIfNecessary, node_monitor **_monitor,
139 bool isVolumeListener);
140 monitor_listener *_MonitorListenerFor(node_monitor* monitor,
141 NotificationListener& notificationListener);
142 status_t _AddMonitorListener(io_context *context,
143 node_monitor* monitor, uint32 flags,
144 NotificationListener& notificationListener);
145 status_t _UpdateListener(io_context *context, dev_t device, ino_t node,
146 uint32 flags, bool addFlags,
147 NotificationListener &notificationListener);
148 void _GetInterestedMonitorListeners(dev_t device, ino_t node,
149 uint32 flags, interested_monitor_listener_list *interestedListeners,
150 int32 &interestedListenerCount);
151 void _GetInterestedVolumeListeners(dev_t device, uint32 flags,
152 interested_monitor_listener_list *interestedListeners,
153 int32 &interestedListenerCount);
154 status_t _SendNotificationMessage(KMessage &message,
155 interested_monitor_listener_list *interestedListeners,
156 int32 interestedListenerCount);
157 void _ResolveMountPoint(dev_t device, ino_t directory,
158 dev_t& parentDevice, ino_t& parentDirectory);
160 struct monitor_hash_key {
161 dev_t device;
162 ino_t node;
165 struct HashDefinition {
166 typedef monitor_hash_key* KeyType;
167 typedef node_monitor ValueType;
169 size_t HashKey(monitor_hash_key* key) const
170 { return _Hash(key->device, key->node); }
171 size_t Hash(node_monitor *monitor) const
172 { return _Hash(monitor->device, monitor->node); }
174 bool Compare(monitor_hash_key* key, node_monitor *monitor) const
176 return key->device == monitor->device
177 && key->node == monitor->node;
180 node_monitor*& GetLink(node_monitor* monitor) const
181 { return monitor->hash_link; }
183 uint32 _Hash(dev_t device, ino_t node) const
185 return ((uint32)(node >> 32) + (uint32)node) ^ (uint32)device;
189 typedef BOpenHashTable<HashDefinition> MonitorHash;
191 struct VolumeHashDefinition {
192 typedef dev_t KeyType;
193 typedef node_monitor ValueType;
195 size_t HashKey(dev_t key) const
196 { return _Hash(key); }
197 size_t Hash(node_monitor *monitor) const
198 { return _Hash(monitor->device); }
200 bool Compare(dev_t key, node_monitor *monitor) const
202 return key == monitor->device;
205 node_monitor*& GetLink(node_monitor* monitor) const
206 { return monitor->hash_link; }
208 uint32 _Hash(dev_t device) const
210 return (uint32)(device >> 16) + (uint16)device;
214 typedef BOpenHashTable<VolumeHashDefinition> VolumeMonitorHash;
216 MonitorHash fMonitors;
217 VolumeMonitorHash fVolumeMonitors;
218 recursive_lock fRecursiveLock;
221 static NodeMonitorService sNodeMonitorService;
224 /*! \brief Notifies the listener of a live query that an entry has been added
225 to or removed from or updated and still in the query (for whatever
226 reason).
227 \param opcode \c B_ENTRY_CREATED or \c B_ENTRY_REMOVED or \c B_ATTR_CHANGED.
228 \param port The target port of the listener.
229 \param token The BHandler token of the listener.
230 \param device The ID of the mounted FS, the entry lives in.
231 \param directory The entry's parent directory ID.
232 \param name The entry's name.
233 \param node The ID of the node the entry refers to.
234 \return
235 - \c B_OK, if everything went fine,
236 - another error code otherwise.
238 static status_t
239 notify_query_entry_event(int32 opcode, port_id port, int32 token,
240 dev_t device, ino_t directory, const char *name, ino_t node)
242 if (!name)
243 return B_BAD_VALUE;
245 // construct the message
246 char messageBuffer[1024];
247 KMessage message;
248 message.SetTo(messageBuffer, sizeof(messageBuffer), B_QUERY_UPDATE);
249 message.AddInt32("opcode", opcode);
250 message.AddInt32("device", device);
251 message.AddInt64("directory", directory);
252 message.AddInt64("node", node);
253 message.AddString("name", name);
255 // send the message
256 messaging_target target;
257 target.port = port;
258 target.token = token;
260 return send_message(&message, &target, 1);
264 // #pragma mark - NodeMonitorService
267 NodeMonitorService::NodeMonitorService()
269 recursive_lock_init(&fRecursiveLock, "node monitor");
273 NodeMonitorService::~NodeMonitorService()
275 recursive_lock_destroy(&fRecursiveLock);
279 status_t
280 NodeMonitorService::InitCheck()
282 return B_OK;
286 /*! Removes the specified node_monitor from the hashtable
287 and free it.
288 Must be called with monitors lock hold.
290 void
291 NodeMonitorService::_RemoveMonitor(node_monitor *monitor, uint32 flags)
293 if ((flags & B_WATCH_VOLUME) != 0)
294 fVolumeMonitors.Remove(monitor);
295 else
296 fMonitors.Remove(monitor);
297 delete monitor;
301 //! Helper function for the RemoveListener function.
302 status_t
303 NodeMonitorService::_RemoveListener(io_context *context, dev_t device,
304 ino_t node, NotificationListener& notificationListener,
305 bool isVolumeListener)
307 TRACE(("%s(dev = %ld, node = %Ld, listener = %p\n",
308 __PRETTY_FUNCTION__, device, node, &notificationListener));
310 RecursiveLocker _(fRecursiveLock);
312 // get the monitor for this device/node pair
313 node_monitor *monitor = _MonitorFor(device, node, isVolumeListener);
314 if (monitor == NULL)
315 return B_BAD_VALUE;
317 // see if it has the listener we are looking for
318 monitor_listener* listener = _MonitorListenerFor(monitor,
319 notificationListener);
320 if (listener == NULL)
321 return B_BAD_VALUE;
323 _RemoveListener(listener);
324 context->num_monitors--;
326 return B_OK;
330 /*! Removes the specified monitor_listener from all lists
331 and free it.
332 Must be called with monitors lock hold.
334 void
335 NodeMonitorService::_RemoveListener(monitor_listener *listener)
337 uint32 flags = listener->flags;
338 node_monitor *monitor = listener->monitor;
340 // remove it from the listener and I/O context lists
341 monitor->listeners.Remove(listener);
342 list_remove_link(listener);
344 if (dynamic_cast<UserNodeListener*>(listener->listener) != NULL) {
345 // This is a listener we copied ourselves in UpdateUserListener(),
346 // so we have to delete it here.
347 delete listener->listener;
350 delete listener;
352 if (monitor->listeners.IsEmpty())
353 _RemoveMonitor(monitor, flags);
357 /*! Returns the monitor that matches the specified device/node pair.
358 Must be called with monitors lock hold.
360 node_monitor *
361 NodeMonitorService::_MonitorFor(dev_t device, ino_t node, bool isVolumeListener)
363 if (isVolumeListener)
364 return fVolumeMonitors.Lookup(device);
366 struct monitor_hash_key key;
367 key.device = device;
368 key.node = node;
370 return fMonitors.Lookup(&key);
374 /*! Returns the monitor that matches the specified device/node pair.
375 If the monitor does not exist yet, it will be created.
376 Must be called with monitors lock hold.
378 status_t
379 NodeMonitorService::_GetMonitor(io_context *context, dev_t device, ino_t node,
380 bool addIfNecessary, node_monitor** _monitor, bool isVolumeListener)
382 node_monitor* monitor = _MonitorFor(device, node, isVolumeListener);
383 if (monitor != NULL) {
384 *_monitor = monitor;
385 return B_OK;
387 if (!addIfNecessary)
388 return B_BAD_VALUE;
390 // check if this team is allowed to have more listeners
391 if (context->num_monitors >= context->max_monitors) {
392 // the BeBook says to return B_NO_MEMORY in this case, but
393 // we should have another one.
394 return B_NO_MEMORY;
397 // create new monitor
398 monitor = new(std::nothrow) node_monitor;
399 if (monitor == NULL)
400 return B_NO_MEMORY;
402 // initialize monitor
403 monitor->device = device;
404 monitor->node = node;
406 status_t status;
407 if (isVolumeListener)
408 status = fVolumeMonitors.Insert(monitor);
409 else
410 status = fMonitors.Insert(monitor);
411 if (status < B_OK) {
412 delete monitor;
413 return B_NO_MEMORY;
416 *_monitor = monitor;
417 return B_OK;
421 /*! Returns the listener that matches the specified port/token pair.
422 Must be called with monitors lock hold.
424 monitor_listener*
425 NodeMonitorService::_MonitorListenerFor(node_monitor* monitor,
426 NotificationListener& notificationListener)
428 MonitorListenerList::Iterator iterator
429 = monitor->listeners.GetIterator();
431 while (monitor_listener* listener = iterator.Next()) {
432 // does this listener match?
433 if (*listener->listener == notificationListener)
434 return listener;
437 return NULL;
441 status_t
442 NodeMonitorService::_AddMonitorListener(io_context *context,
443 node_monitor* monitor, uint32 flags,
444 NotificationListener& notificationListener)
446 monitor_listener *listener = new(std::nothrow) monitor_listener;
447 if (listener == NULL) {
448 // no memory for the listener, so remove the monitor as well if needed
449 if (monitor->listeners.IsEmpty())
450 _RemoveMonitor(monitor, flags);
452 return B_NO_MEMORY;
455 // initialize listener, and add it to the lists
456 listener->listener = &notificationListener;
457 listener->flags = flags;
458 listener->monitor = monitor;
460 monitor->listeners.Add(listener);
461 list_add_link_to_head(&context->node_monitors, listener);
463 context->num_monitors++;
464 return B_OK;
468 status_t
469 NodeMonitorService::AddListener(io_context *context, dev_t device, ino_t node,
470 uint32 flags, NotificationListener& notificationListener)
472 TRACE(("%s(dev = %ld, node = %Ld, flags = %ld, listener = %p\n",
473 __PRETTY_FUNCTION__, device, node, flags, &notificationListener));
475 RecursiveLocker _(fRecursiveLock);
477 node_monitor *monitor;
478 status_t status = _GetMonitor(context, device, node, true, &monitor,
479 (flags & B_WATCH_VOLUME) != 0);
480 if (status < B_OK)
481 return status;
483 // add listener
485 return _AddMonitorListener(context, monitor, flags, notificationListener);
489 status_t
490 NodeMonitorService::_UpdateListener(io_context *context, dev_t device,
491 ino_t node, uint32 flags, bool addFlags,
492 NotificationListener& notificationListener)
494 TRACE(("%s(dev = %ld, node = %Ld, flags = %ld, listener = %p\n",
495 __PRETTY_FUNCTION__, device, node, flags, &notificationListener));
497 RecursiveLocker _(fRecursiveLock);
499 node_monitor *monitor;
500 status_t status = _GetMonitor(context, device, node, false, &monitor,
501 (flags & B_WATCH_VOLUME) != 0);
502 if (status < B_OK)
503 return status;
505 MonitorListenerList::Iterator iterator = monitor->listeners.GetIterator();
506 while (monitor_listener* listener = iterator.Next()) {
507 if (*listener->listener == notificationListener) {
508 if (addFlags)
509 listener->flags |= flags;
510 else
511 listener->flags = flags;
512 return B_OK;
516 return B_BAD_VALUE;
520 /*! \brief Given device and node ID and a node monitoring event mask, the
521 function checks whether there are listeners interested in any of
522 the events for that node and, if so, adds the respective listener
523 list to a supplied array of listener lists.
525 Note, that in general not all of the listeners in an appended list will be
526 interested in the events, but it is guaranteed that
527 interested_monitor_listener_list::first_listener is indeed
528 the first listener in the list, that is interested.
530 \param device The ID of the mounted FS, the node lives in.
531 \param node The ID of the node.
532 \param flags The mask specifying the events occurred for the given node
533 (a combination of \c B_WATCH_* constants).
534 \param interestedListeners An array of listener lists. If there are
535 interested listeners for the node, the list will be appended to
536 this array.
537 \param interestedListenerCount The number of elements in the
538 \a interestedListeners array. Will be incremented, if a list is
539 appended.
541 void
542 NodeMonitorService::_GetInterestedMonitorListeners(dev_t device, ino_t node,
543 uint32 flags, interested_monitor_listener_list *interestedListeners,
544 int32 &interestedListenerCount)
546 // get the monitor for the node
547 node_monitor *monitor = _MonitorFor(device, node, false);
548 if (monitor == NULL)
549 return;
551 // iterate through the listeners until we find one with matching flags
552 MonitorListenerList::Iterator iterator = monitor->listeners.GetIterator();
553 while (monitor_listener *listener = iterator.Next()) {
554 if ((listener->flags & flags) == flags) {
555 interested_monitor_listener_list &list
556 = interestedListeners[interestedListenerCount++];
557 list.iterator = iterator;
558 list.flags = flags;
559 return;
565 void
566 NodeMonitorService::_GetInterestedVolumeListeners(dev_t device, uint32 flags,
567 interested_monitor_listener_list *interestedListeners,
568 int32 &interestedListenerCount)
570 // get the monitor for the node
571 node_monitor *monitor = _MonitorFor(device, -1, true);
572 if (monitor == NULL)
573 return;
575 // iterate through the listeners until we find one with matching flags
576 MonitorListenerList::Iterator iterator = monitor->listeners.GetIterator();
577 while (monitor_listener *listener = iterator.Next()) {
578 if ((listener->flags & flags) == flags) {
579 interested_monitor_listener_list &list
580 = interestedListeners[interestedListenerCount++];
581 list.iterator = iterator;
582 list.flags = flags;
583 return;
589 /*! \brief Sends a notifcation message to the given listeners.
590 \param message The message to be sent.
591 \param interestedListeners An array of listener lists.
592 \param interestedListenerCount The number of elements in the
593 \a interestedListeners array.
594 \return
595 - \c B_OK, if everything went fine,
596 - another error code otherwise.
598 status_t
599 NodeMonitorService::_SendNotificationMessage(KMessage &message,
600 interested_monitor_listener_list *interestedListeners,
601 int32 interestedListenerCount)
603 // iterate through the lists
604 interested_monitor_listener_list *list = interestedListeners;
605 for (int32 i = 0; i < interestedListenerCount; i++, list++) {
606 // iterate through the listeners
607 MonitorListenerList::Iterator iterator = list->iterator;
608 do {
609 monitor_listener *listener = iterator.Current();
610 if (listener->flags & list->flags)
611 listener->listener->EventOccurred(*this, &message);
612 } while (iterator.Next() != NULL);
615 list = interestedListeners;
616 for (int32 i = 0; i < interestedListenerCount; i++, list++) {
617 // iterate through the listeners
618 do {
619 monitor_listener *listener = list->iterator.Current();
620 if (listener->flags & list->flags)
621 listener->listener->AllListenersNotified(*this);
622 } while (list->iterator.Next() != NULL);
625 return B_OK;
629 /*! \brief Resolves the device/directory node pair to the node it's covered
630 by, if any.
632 void
633 NodeMonitorService::_ResolveMountPoint(dev_t device, ino_t directory,
634 dev_t& parentDevice, ino_t& parentDirectory)
636 struct vnode* vnode;
637 status_t status = vfs_get_vnode(device, directory, true, &vnode);
638 if (status == B_OK) {
639 if (vnode->covers != NULL)
640 status = vfs_resolve_parent(vnode, &parentDevice, &parentDirectory);
641 vfs_put_vnode(vnode);
643 if (status != B_OK) {
644 dprintf("Resolving mount point %" B_PRIdDEV ":%" B_PRIdINO " failed!"
645 "\n", device, directory);
650 /*! \brief Notifies all interested listeners that an entry has been created
651 or removed.
652 \param opcode \c B_ENTRY_CREATED or \c B_ENTRY_REMOVED.
653 \param device The ID of the mounted FS, the entry lives/lived in.
654 \param directory The entry's parent directory ID.
655 \param name The entry's name.
656 \param node The ID of the node the entry refers/referred to.
657 \return
658 - \c B_OK, if everything went fine,
659 - another error code otherwise.
661 status_t
662 NodeMonitorService::NotifyEntryCreatedOrRemoved(int32 opcode, dev_t device,
663 ino_t directory, const char *name, ino_t node)
665 if (!name)
666 return B_BAD_VALUE;
668 RecursiveLocker locker(fRecursiveLock);
670 // get the lists of all interested listeners
671 interested_monitor_listener_list interestedListeners[3];
672 int32 interestedListenerCount = 0;
673 // ... for the volume
674 _GetInterestedVolumeListeners(device, B_WATCH_NAME,
675 interestedListeners, interestedListenerCount);
676 // ... for the node
677 if (opcode != B_ENTRY_CREATED) {
678 _GetInterestedMonitorListeners(device, node, B_WATCH_NAME,
679 interestedListeners, interestedListenerCount);
681 // ... for the directory
682 _GetInterestedMonitorListeners(device, directory, B_WATCH_DIRECTORY,
683 interestedListeners, interestedListenerCount);
685 if (interestedListenerCount == 0)
686 return B_OK;
688 // there are interested listeners: construct the message and send it
689 char messageBuffer[1024];
690 KMessage message;
691 message.SetTo(messageBuffer, sizeof(messageBuffer), B_NODE_MONITOR);
692 message.AddInt32("opcode", opcode);
693 message.AddInt32("device", device);
694 message.AddInt64("directory", directory);
695 message.AddInt64("node", node);
696 message.AddString("name", name); // for "removed" Haiku only
698 return _SendNotificationMessage(message, interestedListeners,
699 interestedListenerCount);
703 inline status_t
704 NodeMonitorService::NotifyEntryMoved(dev_t device, ino_t fromDirectory,
705 const char *fromName, ino_t toDirectory, const char *toName,
706 ino_t node)
708 if (!fromName || !toName)
709 return B_BAD_VALUE;
711 // If node is a mount point, we need to resolve it to the mounted
712 // volume's root node.
713 dev_t nodeDevice = device;
714 vfs_resolve_vnode_to_covering_vnode(device, node, &nodeDevice, &node);
716 RecursiveLocker locker(fRecursiveLock);
718 // get the lists of all interested listeners
719 interested_monitor_listener_list interestedListeners[4];
720 int32 interestedListenerCount = 0;
721 // ... for the volume
722 _GetInterestedVolumeListeners(device, B_WATCH_NAME,
723 interestedListeners, interestedListenerCount);
724 // ... for the node
725 _GetInterestedMonitorListeners(nodeDevice, node, B_WATCH_NAME,
726 interestedListeners, interestedListenerCount);
727 // ... for the source directory
728 _GetInterestedMonitorListeners(device, fromDirectory, B_WATCH_DIRECTORY,
729 interestedListeners, interestedListenerCount);
730 // ... for the target directory
731 if (toDirectory != fromDirectory) {
732 _GetInterestedMonitorListeners(device, toDirectory, B_WATCH_DIRECTORY,
733 interestedListeners, interestedListenerCount);
736 if (interestedListenerCount == 0)
737 return B_OK;
739 // there are interested listeners: construct the message and send it
740 char messageBuffer[1024];
741 KMessage message;
742 message.SetTo(messageBuffer, sizeof(messageBuffer), B_NODE_MONITOR);
743 message.AddInt32("opcode", B_ENTRY_MOVED);
744 message.AddInt32("device", device);
745 message.AddInt64("from directory", fromDirectory);
746 message.AddInt64("to directory", toDirectory);
747 message.AddInt32("node device", nodeDevice); // Haiku only
748 message.AddInt64("node", node);
749 message.AddString("from name", fromName); // Haiku only
750 message.AddString("name", toName);
752 return _SendNotificationMessage(message, interestedListeners,
753 interestedListenerCount);
757 inline status_t
758 NodeMonitorService::NotifyStatChanged(dev_t device, ino_t directory, ino_t node,
759 uint32 statFields)
761 RecursiveLocker locker(fRecursiveLock);
763 // get the lists of all interested listeners depending on whether its an
764 // interim update or not
765 interested_monitor_listener_list interestedListeners[3];
766 int32 interestedListenerCount = 0;
767 uint32 watchFlag = (statFields & B_STAT_INTERIM_UPDATE) != 0
768 ? B_WATCH_INTERIM_STAT : B_WATCH_STAT;
770 // ... for the volume
771 _GetInterestedVolumeListeners(device, watchFlag, interestedListeners,
772 interestedListenerCount);
773 // ... for the directory
774 if (directory > 0) {
775 dev_t parentDevice = device;
776 ino_t parentDirectory = directory;
777 if (directory == node) {
778 // This is a mount point -- get its file system parent
779 _ResolveMountPoint(device, directory, parentDevice,
780 parentDirectory);
782 _GetInterestedMonitorListeners(parentDevice, parentDirectory,
783 B_WATCH_CHILDREN | watchFlag,
784 interestedListeners, interestedListenerCount);
786 // ... and for the node
787 _GetInterestedMonitorListeners(device, node, watchFlag,
788 interestedListeners, interestedListenerCount);
790 if (interestedListenerCount == 0)
791 return B_OK;
793 // there are interested listeners: construct the message and send it
794 char messageBuffer[1024];
795 KMessage message;
796 message.SetTo(messageBuffer, sizeof(messageBuffer), B_NODE_MONITOR);
797 message.AddInt32("opcode", B_STAT_CHANGED);
798 message.AddInt32("device", device);
799 message.AddInt64("node", node);
800 message.AddInt32("fields", statFields); // Haiku only
802 return _SendNotificationMessage(message, interestedListeners,
803 interestedListenerCount);
807 /*! \brief Notifies all interested listeners that a node attribute has changed.
808 \param device The ID of the mounted FS, the node lives in.
809 \param node The ID of the node.
810 \param attribute The attribute's name.
811 \param cause Either of \c B_ATTR_CREATED, \c B_ATTR_REMOVED, or
812 \c B_ATTR_CHANGED, indicating what exactly happened to the attribute.
813 \return
814 - \c B_OK, if everything went fine,
815 - another error code otherwise.
817 status_t
818 NodeMonitorService::NotifyAttributeChanged(dev_t device, ino_t directory,
819 ino_t node, const char *attribute, int32 cause)
821 if (!attribute)
822 return B_BAD_VALUE;
824 RecursiveLocker locker(fRecursiveLock);
826 // get the lists of all interested listeners
827 interested_monitor_listener_list interestedListeners[3];
828 int32 interestedListenerCount = 0;
829 // ... for the volume
830 _GetInterestedVolumeListeners(device, B_WATCH_ATTR,
831 interestedListeners, interestedListenerCount);
832 // ... for the directory
833 if (directory > 0) {
834 dev_t parentDevice = device;
835 ino_t parentDirectory = directory;
836 if (directory == node) {
837 // This is a mount point -- get its file system parent
838 _ResolveMountPoint(device, directory, parentDevice,
839 parentDirectory);
841 _GetInterestedMonitorListeners(parentDevice, parentDirectory,
842 B_WATCH_CHILDREN | B_WATCH_ATTR,
843 interestedListeners, interestedListenerCount);
845 // ... for the node
846 _GetInterestedMonitorListeners(device, node, B_WATCH_ATTR,
847 interestedListeners, interestedListenerCount);
849 if (interestedListenerCount == 0)
850 return B_OK;
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_ATTR_CHANGED);
857 message.AddInt32("device", device);
858 if (directory >= 0)
859 message.AddInt64("directory", directory);
860 message.AddInt64("node", node);
861 message.AddString("attr", attribute);
862 message.AddInt32("cause", cause); // Haiku only
864 return _SendNotificationMessage(message, interestedListeners,
865 interestedListenerCount);
869 inline status_t
870 NodeMonitorService::NotifyUnmount(dev_t device)
872 TRACE(("unmounted device: %ld\n", device));
874 RecursiveLocker locker(fRecursiveLock);
876 // get the lists of all interested listeners
877 interested_monitor_listener_list interestedListeners[3];
878 int32 interestedListenerCount = 0;
879 _GetInterestedMonitorListeners(-1, -1, B_WATCH_MOUNT,
880 interestedListeners, interestedListenerCount);
882 if (interestedListenerCount == 0)
883 return B_OK;
885 // there are interested listeners: construct the message and send it
886 char messageBuffer[96];
887 KMessage message;
888 message.SetTo(messageBuffer, sizeof(messageBuffer), B_NODE_MONITOR);
889 message.AddInt32("opcode", B_DEVICE_UNMOUNTED);
890 message.AddInt32("device", device);
892 return _SendNotificationMessage(message, interestedListeners,
893 interestedListenerCount);
897 inline status_t
898 NodeMonitorService::NotifyMount(dev_t device, dev_t parentDevice,
899 ino_t parentDirectory)
901 TRACE(("mounted device: %ld, parent %ld:%Ld\n", device, parentDevice,
902 parentDirectory));
904 RecursiveLocker locker(fRecursiveLock);
906 // get the lists of all interested listeners
907 interested_monitor_listener_list interestedListeners[3];
908 int32 interestedListenerCount = 0;
909 _GetInterestedMonitorListeners(-1, -1, B_WATCH_MOUNT,
910 interestedListeners, interestedListenerCount);
912 if (interestedListenerCount == 0)
913 return B_OK;
915 // there are interested listeners: construct the message and send it
916 char messageBuffer[128];
917 KMessage message;
918 message.SetTo(messageBuffer, sizeof(messageBuffer), B_NODE_MONITOR);
919 message.AddInt32("opcode", B_DEVICE_MOUNTED);
920 message.AddInt32("new device", device);
921 message.AddInt32("device", parentDevice);
922 message.AddInt64("directory", parentDirectory);
924 return _SendNotificationMessage(message, interestedListeners,
925 interestedListenerCount);
929 inline status_t
930 NodeMonitorService::RemoveListeners(io_context *context)
932 RecursiveLocker locker(fRecursiveLock);
934 while (!list_is_empty(&context->node_monitors)) {
935 // the _RemoveListener() method will also free the node_monitor
936 // if it doesn't have any listeners attached anymore
937 _RemoveListener(
938 (monitor_listener*)list_get_first_item(&context->node_monitors));
941 return B_OK;
945 status_t
946 NodeMonitorService::AddListener(const KMessage* eventSpecifier,
947 NotificationListener& listener)
949 if (eventSpecifier == NULL)
950 return B_BAD_VALUE;
952 io_context *context = get_current_io_context(
953 eventSpecifier->GetBool("kernel", true));
955 dev_t device = eventSpecifier->GetInt32("device", -1);
956 ino_t node = eventSpecifier->GetInt64("node", -1);
957 uint32 flags = eventSpecifier->GetInt32("flags", 0);
959 return AddListener(context, device, node, flags, listener);
963 status_t
964 NodeMonitorService::UpdateListener(const KMessage* eventSpecifier,
965 NotificationListener& listener)
967 if (eventSpecifier == NULL)
968 return B_BAD_VALUE;
970 io_context *context = get_current_io_context(
971 eventSpecifier->GetBool("kernel", true));
973 dev_t device = eventSpecifier->GetInt32("device", -1);
974 ino_t node = eventSpecifier->GetInt64("node", -1);
975 uint32 flags = eventSpecifier->GetInt32("flags", 0);
976 bool addFlags = eventSpecifier->GetBool("add flags", false);
978 return _UpdateListener(context, device, node, flags, addFlags, listener);
982 status_t
983 NodeMonitorService::RemoveListener(const KMessage* eventSpecifier,
984 NotificationListener& listener)
986 if (eventSpecifier == NULL)
987 return B_BAD_VALUE;
989 io_context *context = get_current_io_context(
990 eventSpecifier->GetBool("kernel", true));
992 dev_t device = eventSpecifier->GetInt32("device", -1);
993 ino_t node = eventSpecifier->GetInt64("node", -1);
995 return RemoveListener(context, device, node, listener);
999 status_t
1000 NodeMonitorService::RemoveListener(io_context *context, dev_t device,
1001 ino_t node, NotificationListener& notificationListener)
1003 TRACE(("%s(dev = %ld, node = %Ld, listener = %p\n",
1004 __PRETTY_FUNCTION__, device, node, &notificationListener));
1006 RecursiveLocker _(fRecursiveLock);
1008 if (_RemoveListener(context, device, node, notificationListener, false)
1009 == B_OK)
1010 return B_OK;
1012 return _RemoveListener(context, device, node, notificationListener, true);
1016 inline status_t
1017 NodeMonitorService::RemoveUserListeners(struct io_context *context,
1018 port_id port, uint32 token)
1020 UserNodeListener userListener(port, token);
1021 monitor_listener *listener = NULL;
1022 int32 count = 0;
1024 RecursiveLocker _(fRecursiveLock);
1026 while ((listener = (monitor_listener*)list_get_next_item(
1027 &context->node_monitors, listener)) != NULL) {
1028 monitor_listener *removeListener;
1030 if (*listener->listener != userListener)
1031 continue;
1033 listener = (monitor_listener*)list_get_prev_item(
1034 &context->node_monitors, removeListener = listener);
1035 // this line sets the listener one item back, allowing us
1036 // to remove its successor (which is saved in "removeListener")
1038 _RemoveListener(removeListener);
1039 context->num_monitors--;
1040 count++;
1043 return count > 0 ? B_OK : B_ENTRY_NOT_FOUND;
1047 status_t
1048 NodeMonitorService::UpdateUserListener(io_context *context, dev_t device,
1049 ino_t node, uint32 flags, UserNodeListener& userListener)
1051 TRACE(("%s(dev = %ld, node = %Ld, flags = %ld, listener = %p\n",
1052 __PRETTY_FUNCTION__, device, node, flags, &userListener));
1054 RecursiveLocker _(fRecursiveLock);
1056 node_monitor *monitor;
1057 status_t status = _GetMonitor(context, device, node, true, &monitor,
1058 (flags & B_WATCH_VOLUME) != 0);
1059 if (status < B_OK)
1060 return status;
1062 MonitorListenerList::Iterator iterator = monitor->listeners.GetIterator();
1063 while (monitor_listener* listener = iterator.Next()) {
1064 if (*listener->listener == userListener) {
1065 listener->flags |= flags;
1066 return B_OK;
1070 UserNodeListener* copiedListener = new(std::nothrow) UserNodeListener(
1071 userListener);
1072 if (copiedListener == NULL) {
1073 if (monitor->listeners.IsEmpty())
1074 _RemoveMonitor(monitor, flags);
1075 return B_NO_MEMORY;
1078 status = _AddMonitorListener(context, monitor, flags, *copiedListener);
1079 if (status != B_OK)
1080 delete copiedListener;
1082 return status;
1086 // #pragma mark - private kernel API
1089 status_t
1090 remove_node_monitors(struct io_context *context)
1092 return sNodeMonitorService.RemoveListeners(context);
1096 status_t
1097 node_monitor_init(void)
1099 new(&sNodeMonitorSender) UserMessagingMessageSender();
1100 new(&sNodeMonitorService) NodeMonitorService();
1102 if (sNodeMonitorService.InitCheck() < B_OK)
1103 panic("initializing node monitor failed\n");
1105 return B_OK;
1109 status_t
1110 notify_unmount(dev_t device)
1112 return sNodeMonitorService.NotifyUnmount(device);
1116 status_t
1117 notify_mount(dev_t device, dev_t parentDevice, ino_t parentDirectory)
1119 return sNodeMonitorService.NotifyMount(device, parentDevice,
1120 parentDirectory);
1124 status_t
1125 remove_node_listener(dev_t device, ino_t node, NotificationListener& listener)
1127 return sNodeMonitorService.RemoveListener(get_current_io_context(true),
1128 device, node, listener);
1132 status_t
1133 add_node_listener(dev_t device, ino_t node, uint32 flags,
1134 NotificationListener& listener)
1136 return sNodeMonitorService.AddListener(get_current_io_context(true),
1137 device, node, flags, listener);
1141 // #pragma mark - public kernel API
1144 /*! \brief Notifies all interested listeners that an entry has been created.
1145 \param device The ID of the mounted FS, the entry lives in.
1146 \param directory The entry's parent directory ID.
1147 \param name The entry's name.
1148 \param node The ID of the node the entry refers to.
1149 \return
1150 - \c B_OK, if everything went fine,
1151 - another error code otherwise.
1153 status_t
1154 notify_entry_created(dev_t device, ino_t directory, const char *name,
1155 ino_t node)
1157 return sNodeMonitorService.NotifyEntryCreatedOrRemoved(B_ENTRY_CREATED,
1158 device, directory, name, node);
1162 /*! \brief Notifies all interested listeners that an entry has been removed.
1163 \param device The ID of the mounted FS, the entry lived in.
1164 \param directory The entry's former parent directory ID.
1165 \param name The entry's name.
1166 \param node The ID of the node the entry referred to.
1167 \return
1168 - \c B_OK, if everything went fine,
1169 - another error code otherwise.
1171 status_t
1172 notify_entry_removed(dev_t device, ino_t directory, const char *name,
1173 ino_t node)
1175 return sNodeMonitorService.NotifyEntryCreatedOrRemoved(B_ENTRY_REMOVED,
1176 device, directory, name, node);
1180 /*! \brief Notifies all interested listeners that an entry has been moved.
1181 \param device The ID of the mounted FS, the entry lives in.
1182 \param fromDirectory The entry's previous parent directory ID.
1183 \param fromName The entry's previous name.
1184 \param toDirectory The entry's new parent directory ID.
1185 \param toName The entry's new name.
1186 \param node The ID of the node the entry refers to.
1187 \return
1188 - \c B_OK, if everything went fine,
1189 - another error code otherwise.
1191 status_t
1192 notify_entry_moved(dev_t device, ino_t fromDirectory,
1193 const char *fromName, ino_t toDirectory, const char *toName,
1194 ino_t node)
1196 return sNodeMonitorService.NotifyEntryMoved(device, fromDirectory,
1197 fromName, toDirectory, toName, node);
1201 /*! \brief Notifies all interested listeners that a node's stat data have
1202 changed.
1203 \param device The ID of the mounted FS, the node lives in.
1204 \param node The ID of the node.
1205 \param statFields A bitwise combination of one or more of the \c B_STAT_*
1206 constants defined in <NodeMonitor.h>, indicating what fields of the
1207 stat data have changed.
1208 \return
1209 - \c B_OK, if everything went fine,
1210 - another error code otherwise.
1212 status_t
1213 notify_stat_changed(dev_t device, ino_t directory, ino_t node,
1214 uint32 statFields)
1216 return sNodeMonitorService.NotifyStatChanged(device, directory, node,
1217 statFields);
1221 /*! \brief Notifies all interested listeners that a node attribute has changed.
1222 \param device The ID of the mounted FS, the node lives in.
1223 \param node The ID of the node.
1224 \param attribute The attribute's name.
1225 \param cause Either of \c B_ATTR_CREATED, \c B_ATTR_REMOVED, or
1226 \c B_ATTR_CHANGED, indicating what exactly happened to the attribute.
1227 \return
1228 - \c B_OK, if everything went fine,
1229 - another error code otherwise.
1231 status_t
1232 notify_attribute_changed(dev_t device, ino_t directory, ino_t node,
1233 const char *attribute, int32 cause)
1235 return sNodeMonitorService.NotifyAttributeChanged(device, directory, node,
1236 attribute, cause);
1240 /*! \brief Notifies the listener of a live query that an entry has been added
1241 to the query (for whatever reason).
1242 \param port The target port of the listener.
1243 \param token The BHandler token of the listener.
1244 \param device The ID of the mounted FS, the entry lives in.
1245 \param directory The entry's parent directory ID.
1246 \param name The entry's name.
1247 \param node The ID of the node the entry refers to.
1248 \return
1249 - \c B_OK, if everything went fine,
1250 - another error code otherwise.
1252 status_t
1253 notify_query_entry_created(port_id port, int32 token, dev_t device,
1254 ino_t directory, const char *name, ino_t node)
1256 return notify_query_entry_event(B_ENTRY_CREATED, port, token,
1257 device, directory, name, node);
1261 /*! \brief Notifies the listener of a live query that an entry has been removed
1262 from the query (for whatever reason).
1263 \param port The target port of the listener.
1264 \param token The BHandler token of the listener.
1265 \param device The ID of the mounted FS, the entry lives in.
1266 \param directory The entry's parent directory ID.
1267 \param name The entry's name.
1268 \param node The ID of the node the entry refers to.
1269 \return
1270 - \c B_OK, if everything went fine,
1271 - another error code otherwise.
1273 status_t
1274 notify_query_entry_removed(port_id port, int32 token, dev_t device,
1275 ino_t directory, const char *name, ino_t node)
1277 return notify_query_entry_event(B_ENTRY_REMOVED, port, token,
1278 device, directory, name, node);
1282 /*! \brief Notifies the listener of a live query that an entry has been changed
1283 and is still in the query (for whatever reason).
1284 \param port The target port of the listener.
1285 \param token The BHandler token of the listener.
1286 \param device The ID of the mounted FS, the entry lives in.
1287 \param directory The entry's parent directory ID.
1288 \param name The entry's name.
1289 \param node The ID of the node the entry refers to.
1290 \return
1291 - \c B_OK, if everything went fine,
1292 - another error code otherwise.
1294 status_t
1295 notify_query_attr_changed(port_id port, int32 token, dev_t device,
1296 ino_t directory, const char* name, ino_t node)
1298 return notify_query_entry_event(B_ATTR_CHANGED, port, token,
1299 device, directory, name, node);
1303 // #pragma mark - User syscalls
1306 // TODO: We should verify that the port specified in the syscalls does actually
1307 // belong to the calling team. The situation is complicated by the fact that a
1308 // port can be transferred to another team. Consequently we should remove all
1309 // associated monitor listeners when a port is transferred/deleted.
1312 status_t
1313 _user_stop_notifying(port_id port, uint32 token)
1315 io_context *context = get_current_io_context(false);
1317 return sNodeMonitorService.RemoveUserListeners(context, port, token);
1321 status_t
1322 _user_start_watching(dev_t device, ino_t node, uint32 flags, port_id port,
1323 uint32 token)
1325 io_context *context = get_current_io_context(false);
1327 UserNodeListener listener(port, token);
1328 return sNodeMonitorService.UpdateUserListener(context, device, node, flags,
1329 listener);
1333 status_t
1334 _user_stop_watching(dev_t device, ino_t node, port_id port, uint32 token)
1336 io_context *context = get_current_io_context(false);
1338 UserNodeListener listener(port, token);
1339 return sNodeMonitorService.RemoveListener(context, device, node,
1340 listener);