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. 5 * 6 * Distributed under the terms of the MIT License. 7 */ 8 9 10 #include <fs/node_monitor.h> 11 12 #include <stddef.h> 13 #include <stdlib.h> 14 15 #include <AppDefs.h> 16 #include <NodeMonitor.h> 17 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> 27 28 #include "node_monitor_private.h" 29 #include "Vnode.h" 30 31 32 //#define TRACE_MONITOR 33 #ifdef TRACE_MONITOR 34 # define TRACE(x) dprintf x 35 #else 36 # define TRACE(x) ; 37 #endif 38 39 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 43 44 45 typedef struct monitor_listener monitor_listener; 46 typedef struct node_monitor node_monitor; 47 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; 54 }; 55 56 typedef DoublyLinkedList<monitor_listener, DoublyLinkedListMemberGetLink< 57 monitor_listener, &monitor_listener::monitor_link> > MonitorListenerList; 58 59 struct node_monitor { 60 node_monitor* hash_link; 61 dev_t device; 62 ino_t node; 63 MonitorListenerList listeners; 64 }; 65 66 struct interested_monitor_listener_list { 67 MonitorListenerList::Iterator iterator; 68 uint32 flags; 69 }; 70 71 static UserMessagingMessageSender sNodeMonitorSender; 72 73 class UserNodeListener : public UserMessagingListener { 74 public: 75 UserNodeListener(port_id port, int32 token) 76 : UserMessagingListener(sNodeMonitorSender, port, token) 77 { 78 } 79 80 bool operator==(const NotificationListener& _other) const 81 { 82 const UserNodeListener* other 83 = dynamic_cast<const UserNodeListener*>(&_other); 84 return other != NULL && other->Port() == Port() 85 && other->Token() == Token(); 86 } 87 }; 88 89 class NodeMonitorService : public NotificationService { 90 public: 91 NodeMonitorService(); 92 virtual ~NodeMonitorService(); 93 94 status_t InitCheck(); 95 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); 108 109 status_t RemoveListeners(io_context *context); 110 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); 117 118 status_t AddListener(io_context *context, dev_t device, ino_t node, 119 uint32 flags, NotificationListener ¬ificationListener); 120 status_t RemoveListener(io_context *context, dev_t device, ino_t node, 121 NotificationListener ¬ificationListener); 122 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); 127 128 virtual const char* Name() { return "node monitor"; } 129 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 ¬ificationListener); 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); 159 160 struct monitor_hash_key { 161 dev_t device; 162 ino_t node; 163 }; 164 165 struct HashDefinition { 166 typedef monitor_hash_key* KeyType; 167 typedef node_monitor ValueType; 168 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); } 173 174 bool Compare(monitor_hash_key* key, node_monitor *monitor) const 175 { 176 return key->device == monitor->device 177 && key->node == monitor->node; 178 } 179 180 node_monitor*& GetLink(node_monitor* monitor) const 181 { return monitor->hash_link; } 182 183 uint32 _Hash(dev_t device, ino_t node) const 184 { 185 return ((uint32)(node >> 32) + (uint32)node) ^ (uint32)device; 186 } 187 }; 188 189 typedef BOpenHashTable<HashDefinition> MonitorHash; 190 191 struct VolumeHashDefinition { 192 typedef dev_t KeyType; 193 typedef node_monitor ValueType; 194 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); } 199 200 bool Compare(dev_t key, node_monitor *monitor) const 201 { 202 return key == monitor->device; 203 } 204 205 node_monitor*& GetLink(node_monitor* monitor) const 206 { return monitor->hash_link; } 207 208 uint32 _Hash(dev_t device) const 209 { 210 return (uint32)(device >> 16) + (uint16)device; 211 } 212 }; 213 214 typedef BOpenHashTable<VolumeHashDefinition> VolumeMonitorHash; 215 216 MonitorHash fMonitors; 217 VolumeMonitorHash fVolumeMonitors; 218 recursive_lock fRecursiveLock; 219 }; 220 221 static NodeMonitorService sNodeMonitorService; 222 223 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. 237 */ 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) 241 { 242 if (!name) 243 return B_BAD_VALUE; 244 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); 254 255 // send the message 256 messaging_target target; 257 target.port = port; 258 target.token = token; 259 260 return send_message(&message, &target, 1); 261 } 262 263 264 // #pragma mark - NodeMonitorService 265 266 267 NodeMonitorService::NodeMonitorService() 268 { 269 recursive_lock_init(&fRecursiveLock, "node monitor"); 270 } 271 272 273 NodeMonitorService::~NodeMonitorService() 274 { 275 recursive_lock_destroy(&fRecursiveLock); 276 } 277 278 279 status_t 280 NodeMonitorService::InitCheck() 281 { 282 return B_OK; 283 } 284 285 286 /*! Removes the specified node_monitor from the hashtable 287 and free it. 288 Must be called with monitors lock hold. 289 */ 290 void 291 NodeMonitorService::_RemoveMonitor(node_monitor *monitor, uint32 flags) 292 { 293 if ((flags & B_WATCH_VOLUME) != 0) 294 fVolumeMonitors.Remove(monitor); 295 else 296 fMonitors.Remove(monitor); 297 delete monitor; 298 } 299 300 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) 306 { 307 TRACE(("%s(dev = %ld, node = %Ld, listener = %p\n", 308 __PRETTY_FUNCTION__, device, node, ¬ificationListener)); 309 310 RecursiveLocker _(fRecursiveLock); 311 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; 316 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; 322 323 _RemoveListener(listener); 324 context->num_monitors--; 325 326 return B_OK; 327 } 328 329 330 /*! Removes the specified monitor_listener from all lists 331 and free it. 332 Must be called with monitors lock hold. 333 */ 334 void 335 NodeMonitorService::_RemoveListener(monitor_listener *listener) 336 { 337 uint32 flags = listener->flags; 338 node_monitor *monitor = listener->monitor; 339 340 // remove it from the listener and I/O context lists 341 monitor->listeners.Remove(listener); 342 list_remove_link(listener); 343 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; 348 } 349 350 delete listener; 351 352 if (monitor->listeners.IsEmpty()) 353 _RemoveMonitor(monitor, flags); 354 } 355 356 357 /*! Returns the monitor that matches the specified device/node pair. 358 Must be called with monitors lock hold. 359 */ 360 node_monitor * 361 NodeMonitorService::_MonitorFor(dev_t device, ino_t node, bool isVolumeListener) 362 { 363 if (isVolumeListener) 364 return fVolumeMonitors.Lookup(device); 365 366 struct monitor_hash_key key; 367 key.device = device; 368 key.node = node; 369 370 return fMonitors.Lookup(&key); 371 } 372 373 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. 377 */ 378 status_t 379 NodeMonitorService::_GetMonitor(io_context *context, dev_t device, ino_t node, 380 bool addIfNecessary, node_monitor** _monitor, bool isVolumeListener) 381 { 382 node_monitor* monitor = _MonitorFor(device, node, isVolumeListener); 383 if (monitor != NULL) { 384 *_monitor = monitor; 385 return B_OK; 386 } 387 if (!addIfNecessary) 388 return B_BAD_VALUE; 389 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; 395 } 396 397 // create new monitor 398 monitor = new(std::nothrow) node_monitor; 399 if (monitor == NULL) 400 return B_NO_MEMORY; 401 402 // initialize monitor 403 monitor->device = device; 404 monitor->node = node; 405 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; 414 } 415 416 *_monitor = monitor; 417 return B_OK; 418 } 419 420 421 /*! Returns the listener that matches the specified port/token pair. 422 Must be called with monitors lock hold. 423 */ 424 monitor_listener* 425 NodeMonitorService::_MonitorListenerFor(node_monitor* monitor, 426 NotificationListener& notificationListener) 427 { 428 MonitorListenerList::Iterator iterator 429 = monitor->listeners.GetIterator(); 430 431 while (monitor_listener* listener = iterator.Next()) { 432 // does this listener match? 433 if (*listener->listener == notificationListener) 434 return listener; 435 } 436 437 return NULL; 438 } 439 440 441 status_t 442 NodeMonitorService::_AddMonitorListener(io_context *context, 443 node_monitor* monitor, uint32 flags, 444 NotificationListener& notificationListener) 445 { 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); 451 452 return B_NO_MEMORY; 453 } 454 455 // initialize listener, and add it to the lists 456 listener->listener = ¬ificationListener; 457 listener->flags = flags; 458 listener->monitor = monitor; 459 460 monitor->listeners.Add(listener); 461 list_add_link_to_head(&context->node_monitors, listener); 462 463 context->num_monitors++; 464 return B_OK; 465 } 466 467 468 status_t 469 NodeMonitorService::AddListener(io_context *context, dev_t device, ino_t node, 470 uint32 flags, NotificationListener& notificationListener) 471 { 472 TRACE(("%s(dev = %ld, node = %Ld, flags = %ld, listener = %p\n", 473 __PRETTY_FUNCTION__, device, node, flags, ¬ificationListener)); 474 475 RecursiveLocker _(fRecursiveLock); 476 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; 482 483 // add listener 484 485 return _AddMonitorListener(context, monitor, flags, notificationListener); 486 } 487 488 489 status_t 490 NodeMonitorService::_UpdateListener(io_context *context, dev_t device, 491 ino_t node, uint32 flags, bool addFlags, 492 NotificationListener& notificationListener) 493 { 494 TRACE(("%s(dev = %ld, node = %Ld, flags = %ld, listener = %p\n", 495 __PRETTY_FUNCTION__, device, node, flags, ¬ificationListener)); 496 497 RecursiveLocker _(fRecursiveLock); 498 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; 504 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; 513 } 514 } 515 516 return B_BAD_VALUE; 517 } 518 519 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. 524 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. 529 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. 540 */ 541 void 542 NodeMonitorService::_GetInterestedMonitorListeners(dev_t device, ino_t node, 543 uint32 flags, interested_monitor_listener_list *interestedListeners, 544 int32 &interestedListenerCount) 545 { 546 // get the monitor for the node 547 node_monitor *monitor = _MonitorFor(device, node, false); 548 if (monitor == NULL) 549 return; 550 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; 560 } 561 } 562 } 563 564 565 void 566 NodeMonitorService::_GetInterestedVolumeListeners(dev_t device, uint32 flags, 567 interested_monitor_listener_list *interestedListeners, 568 int32 &interestedListenerCount) 569 { 570 // get the monitor for the node 571 node_monitor *monitor = _MonitorFor(device, -1, true); 572 if (monitor == NULL) 573 return; 574 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; 584 } 585 } 586 } 587 588 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. 597 */ 598 status_t 599 NodeMonitorService::_SendNotificationMessage(KMessage &message, 600 interested_monitor_listener_list *interestedListeners, 601 int32 interestedListenerCount) 602 { 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); 613 } 614 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); 623 } 624 625 return B_OK; 626 } 627 628 629 /*! \brief Resolves the device/directory node pair to the node it's covered 630 by, if any. 631 */ 632 void 633 NodeMonitorService::_ResolveMountPoint(dev_t device, ino_t directory, 634 dev_t& parentDevice, ino_t& parentDirectory) 635 { 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); 642 } 643 if (status != B_OK) 644 dprintf("Resolving mount point %ld:%lld failed!\n", device, directory); 645 } 646 647 648 /*! \brief Notifies all interested listeners that an entry has been created 649 or removed. 650 \param opcode \c B_ENTRY_CREATED or \c B_ENTRY_REMOVED. 651 \param device The ID of the mounted FS, the entry lives/lived in. 652 \param directory The entry's parent directory ID. 653 \param name The entry's name. 654 \param node The ID of the node the entry refers/referred to. 655 \return 656 - \c B_OK, if everything went fine, 657 - another error code otherwise. 658 */ 659 status_t 660 NodeMonitorService::NotifyEntryCreatedOrRemoved(int32 opcode, dev_t device, 661 ino_t directory, const char *name, ino_t node) 662 { 663 if (!name) 664 return B_BAD_VALUE; 665 666 RecursiveLocker locker(fRecursiveLock); 667 668 // get the lists of all interested listeners 669 interested_monitor_listener_list interestedListeners[3]; 670 int32 interestedListenerCount = 0; 671 // ... for the volume 672 _GetInterestedVolumeListeners(device, B_WATCH_NAME, 673 interestedListeners, interestedListenerCount); 674 // ... for the node 675 if (opcode != B_ENTRY_CREATED) { 676 _GetInterestedMonitorListeners(device, node, B_WATCH_NAME, 677 interestedListeners, interestedListenerCount); 678 } 679 // ... for the directory 680 _GetInterestedMonitorListeners(device, directory, B_WATCH_DIRECTORY, 681 interestedListeners, interestedListenerCount); 682 683 if (interestedListenerCount == 0) 684 return B_OK; 685 686 // there are interested listeners: construct the message and send it 687 char messageBuffer[1024]; 688 KMessage message; 689 message.SetTo(messageBuffer, sizeof(messageBuffer), B_NODE_MONITOR); 690 message.AddInt32("opcode", opcode); 691 message.AddInt32("device", device); 692 message.AddInt64("directory", directory); 693 message.AddInt64("node", node); 694 message.AddString("name", name); // for "removed" Haiku only 695 696 return _SendNotificationMessage(message, interestedListeners, 697 interestedListenerCount); 698 } 699 700 701 inline status_t 702 NodeMonitorService::NotifyEntryMoved(dev_t device, ino_t fromDirectory, 703 const char *fromName, ino_t toDirectory, const char *toName, 704 ino_t node) 705 { 706 if (!fromName || !toName) 707 return B_BAD_VALUE; 708 709 // If node is a mount point, we need to resolve it to the mounted 710 // volume's root node. 711 dev_t nodeDevice = device; 712 vfs_resolve_vnode_to_covering_vnode(device, node, &nodeDevice, &node); 713 714 RecursiveLocker locker(fRecursiveLock); 715 716 // get the lists of all interested listeners 717 interested_monitor_listener_list interestedListeners[4]; 718 int32 interestedListenerCount = 0; 719 // ... for the volume 720 _GetInterestedVolumeListeners(device, B_WATCH_NAME, 721 interestedListeners, interestedListenerCount); 722 // ... for the node 723 _GetInterestedMonitorListeners(nodeDevice, node, B_WATCH_NAME, 724 interestedListeners, interestedListenerCount); 725 // ... for the source directory 726 _GetInterestedMonitorListeners(device, fromDirectory, B_WATCH_DIRECTORY, 727 interestedListeners, interestedListenerCount); 728 // ... for the target directory 729 if (toDirectory != fromDirectory) { 730 _GetInterestedMonitorListeners(device, toDirectory, B_WATCH_DIRECTORY, 731 interestedListeners, interestedListenerCount); 732 } 733 734 if (interestedListenerCount == 0) 735 return B_OK; 736 737 // there are interested listeners: construct the message and send it 738 char messageBuffer[1024]; 739 KMessage message; 740 message.SetTo(messageBuffer, sizeof(messageBuffer), B_NODE_MONITOR); 741 message.AddInt32("opcode", B_ENTRY_MOVED); 742 message.AddInt32("device", device); 743 message.AddInt64("from directory", fromDirectory); 744 message.AddInt64("to directory", toDirectory); 745 message.AddInt32("node device", nodeDevice); // Haiku only 746 message.AddInt64("node", node); 747 message.AddString("from name", fromName); // Haiku only 748 message.AddString("name", toName); 749 750 return _SendNotificationMessage(message, interestedListeners, 751 interestedListenerCount); 752 } 753 754 755 inline status_t 756 NodeMonitorService::NotifyStatChanged(dev_t device, ino_t directory, ino_t node, 757 uint32 statFields) 758 { 759 RecursiveLocker locker(fRecursiveLock); 760 761 // get the lists of all interested listeners depending on whether its an 762 // interim update or not 763 interested_monitor_listener_list interestedListeners[3]; 764 int32 interestedListenerCount = 0; 765 uint32 watchFlag = (statFields & B_STAT_INTERIM_UPDATE) != 0 766 ? B_WATCH_INTERIM_STAT : B_WATCH_STAT; 767 768 // ... for the volume 769 _GetInterestedVolumeListeners(device, watchFlag, interestedListeners, 770 interestedListenerCount); 771 // ... for the directory 772 if (directory > 0) { 773 dev_t parentDevice = device; 774 ino_t parentDirectory = directory; 775 if (directory == node) { 776 // This is a mount point -- get its file system parent 777 _ResolveMountPoint(device, directory, parentDevice, 778 parentDirectory); 779 } 780 _GetInterestedMonitorListeners(parentDevice, parentDirectory, 781 B_WATCH_CHILDREN | watchFlag, 782 interestedListeners, interestedListenerCount); 783 } 784 // ... and for the node 785 _GetInterestedMonitorListeners(device, node, watchFlag, 786 interestedListeners, interestedListenerCount); 787 788 if (interestedListenerCount == 0) 789 return B_OK; 790 791 // there are interested listeners: construct the message and send it 792 char messageBuffer[1024]; 793 KMessage message; 794 message.SetTo(messageBuffer, sizeof(messageBuffer), B_NODE_MONITOR); 795 message.AddInt32("opcode", B_STAT_CHANGED); 796 message.AddInt32("device", device); 797 message.AddInt64("node", node); 798 message.AddInt32("fields", statFields); // Haiku only 799 800 return _SendNotificationMessage(message, interestedListeners, 801 interestedListenerCount); 802 } 803 804 805 /*! \brief Notifies all interested listeners that a node attribute has changed. 806 \param device The ID of the mounted FS, the node lives in. 807 \param node The ID of the node. 808 \param attribute The attribute's name. 809 \param cause Either of \c B_ATTR_CREATED, \c B_ATTR_REMOVED, or 810 \c B_ATTR_CHANGED, indicating what exactly happened to the attribute. 811 \return 812 - \c B_OK, if everything went fine, 813 - another error code otherwise. 814 */ 815 status_t 816 NodeMonitorService::NotifyAttributeChanged(dev_t device, ino_t directory, 817 ino_t node, const char *attribute, int32 cause) 818 { 819 if (!attribute) 820 return B_BAD_VALUE; 821 822 RecursiveLocker locker(fRecursiveLock); 823 824 // get the lists of all interested listeners 825 interested_monitor_listener_list interestedListeners[3]; 826 int32 interestedListenerCount = 0; 827 // ... for the volume 828 _GetInterestedVolumeListeners(device, B_WATCH_ATTR, 829 interestedListeners, interestedListenerCount); 830 // ... for the directory 831 if (directory > 0) { 832 dev_t parentDevice = device; 833 ino_t parentDirectory = directory; 834 if (directory == node) { 835 // This is a mount point -- get its file system parent 836 _ResolveMountPoint(device, directory, parentDevice, 837 parentDirectory); 838 } 839 _GetInterestedMonitorListeners(parentDevice, parentDirectory, 840 B_WATCH_CHILDREN | B_WATCH_ATTR, 841 interestedListeners, interestedListenerCount); 842 } 843 // ... for the node 844 _GetInterestedMonitorListeners(device, node, B_WATCH_ATTR, 845 interestedListeners, interestedListenerCount); 846 847 if (interestedListenerCount == 0) 848 return B_OK; 849 850 // there are interested listeners: construct the message and send it 851 char messageBuffer[1024]; 852 KMessage message; 853 message.SetTo(messageBuffer, sizeof(messageBuffer), B_NODE_MONITOR); 854 message.AddInt32("opcode", B_ATTR_CHANGED); 855 message.AddInt32("device", device); 856 if (directory >= 0) 857 message.AddInt64("directory", directory); 858 message.AddInt64("node", node); 859 message.AddString("attr", attribute); 860 message.AddInt32("cause", cause); // Haiku only 861 862 return _SendNotificationMessage(message, interestedListeners, 863 interestedListenerCount); 864 } 865 866 867 inline status_t 868 NodeMonitorService::NotifyUnmount(dev_t device) 869 { 870 TRACE(("unmounted device: %ld\n", device)); 871 872 RecursiveLocker locker(fRecursiveLock); 873 874 // get the lists of all interested listeners 875 interested_monitor_listener_list interestedListeners[3]; 876 int32 interestedListenerCount = 0; 877 _GetInterestedMonitorListeners(-1, -1, B_WATCH_MOUNT, 878 interestedListeners, interestedListenerCount); 879 880 if (interestedListenerCount == 0) 881 return B_OK; 882 883 // there are interested listeners: construct the message and send it 884 char messageBuffer[96]; 885 KMessage message; 886 message.SetTo(messageBuffer, sizeof(messageBuffer), B_NODE_MONITOR); 887 message.AddInt32("opcode", B_DEVICE_UNMOUNTED); 888 message.AddInt32("device", device); 889 890 return _SendNotificationMessage(message, interestedListeners, 891 interestedListenerCount); 892 } 893 894 895 inline status_t 896 NodeMonitorService::NotifyMount(dev_t device, dev_t parentDevice, 897 ino_t parentDirectory) 898 { 899 TRACE(("mounted device: %ld, parent %ld:%Ld\n", device, parentDevice, 900 parentDirectory)); 901 902 RecursiveLocker locker(fRecursiveLock); 903 904 // get the lists of all interested listeners 905 interested_monitor_listener_list interestedListeners[3]; 906 int32 interestedListenerCount = 0; 907 _GetInterestedMonitorListeners(-1, -1, B_WATCH_MOUNT, 908 interestedListeners, interestedListenerCount); 909 910 if (interestedListenerCount == 0) 911 return B_OK; 912 913 // there are interested listeners: construct the message and send it 914 char messageBuffer[128]; 915 KMessage message; 916 message.SetTo(messageBuffer, sizeof(messageBuffer), B_NODE_MONITOR); 917 message.AddInt32("opcode", B_DEVICE_MOUNTED); 918 message.AddInt32("new device", device); 919 message.AddInt32("device", parentDevice); 920 message.AddInt64("directory", parentDirectory); 921 922 return _SendNotificationMessage(message, interestedListeners, 923 interestedListenerCount); 924 } 925 926 927 inline status_t 928 NodeMonitorService::RemoveListeners(io_context *context) 929 { 930 RecursiveLocker locker(fRecursiveLock); 931 932 while (!list_is_empty(&context->node_monitors)) { 933 // the _RemoveListener() method will also free the node_monitor 934 // if it doesn't have any listeners attached anymore 935 _RemoveListener( 936 (monitor_listener*)list_get_first_item(&context->node_monitors)); 937 } 938 939 return B_OK; 940 } 941 942 943 status_t 944 NodeMonitorService::AddListener(const KMessage* eventSpecifier, 945 NotificationListener& listener) 946 { 947 if (eventSpecifier == NULL) 948 return B_BAD_VALUE; 949 950 io_context *context = get_current_io_context( 951 eventSpecifier->GetBool("kernel", true)); 952 953 dev_t device = eventSpecifier->GetInt32("device", -1); 954 ino_t node = eventSpecifier->GetInt64("node", -1); 955 uint32 flags = eventSpecifier->GetInt32("flags", 0); 956 957 return AddListener(context, device, node, flags, listener); 958 } 959 960 961 status_t 962 NodeMonitorService::UpdateListener(const KMessage* eventSpecifier, 963 NotificationListener& listener) 964 { 965 if (eventSpecifier == NULL) 966 return B_BAD_VALUE; 967 968 io_context *context = get_current_io_context( 969 eventSpecifier->GetBool("kernel", true)); 970 971 dev_t device = eventSpecifier->GetInt32("device", -1); 972 ino_t node = eventSpecifier->GetInt64("node", -1); 973 uint32 flags = eventSpecifier->GetInt32("flags", 0); 974 bool addFlags = eventSpecifier->GetBool("add flags", false); 975 976 return _UpdateListener(context, device, node, flags, addFlags, listener); 977 } 978 979 980 status_t 981 NodeMonitorService::RemoveListener(const KMessage* eventSpecifier, 982 NotificationListener& listener) 983 { 984 if (eventSpecifier == NULL) 985 return B_BAD_VALUE; 986 987 io_context *context = get_current_io_context( 988 eventSpecifier->GetBool("kernel", true)); 989 990 dev_t device = eventSpecifier->GetInt32("device", -1); 991 ino_t node = eventSpecifier->GetInt64("node", -1); 992 993 return RemoveListener(context, device, node, listener); 994 } 995 996 997 status_t 998 NodeMonitorService::RemoveListener(io_context *context, dev_t device, 999 ino_t node, NotificationListener& notificationListener) 1000 { 1001 TRACE(("%s(dev = %ld, node = %Ld, listener = %p\n", 1002 __PRETTY_FUNCTION__, device, node, ¬ificationListener)); 1003 1004 RecursiveLocker _(fRecursiveLock); 1005 1006 if (_RemoveListener(context, device, node, notificationListener, false) 1007 == B_OK) 1008 return B_OK; 1009 1010 return _RemoveListener(context, device, node, notificationListener, true); 1011 } 1012 1013 1014 inline status_t 1015 NodeMonitorService::RemoveUserListeners(struct io_context *context, 1016 port_id port, uint32 token) 1017 { 1018 UserNodeListener userListener(port, token); 1019 monitor_listener *listener = NULL; 1020 int32 count = 0; 1021 1022 RecursiveLocker _(fRecursiveLock); 1023 1024 while ((listener = (monitor_listener*)list_get_next_item( 1025 &context->node_monitors, listener)) != NULL) { 1026 monitor_listener *removeListener; 1027 1028 if (*listener->listener != userListener) 1029 continue; 1030 1031 listener = (monitor_listener*)list_get_prev_item( 1032 &context->node_monitors, removeListener = listener); 1033 // this line sets the listener one item back, allowing us 1034 // to remove its successor (which is saved in "removeListener") 1035 1036 _RemoveListener(removeListener); 1037 context->num_monitors--; 1038 count++; 1039 } 1040 1041 return count > 0 ? B_OK : B_ENTRY_NOT_FOUND; 1042 } 1043 1044 1045 status_t 1046 NodeMonitorService::UpdateUserListener(io_context *context, dev_t device, 1047 ino_t node, uint32 flags, UserNodeListener& userListener) 1048 { 1049 TRACE(("%s(dev = %ld, node = %Ld, flags = %ld, listener = %p\n", 1050 __PRETTY_FUNCTION__, device, node, flags, &userListener)); 1051 1052 RecursiveLocker _(fRecursiveLock); 1053 1054 node_monitor *monitor; 1055 status_t status = _GetMonitor(context, device, node, true, &monitor, 1056 (flags & B_WATCH_VOLUME) != 0); 1057 if (status < B_OK) 1058 return status; 1059 1060 MonitorListenerList::Iterator iterator = monitor->listeners.GetIterator(); 1061 while (monitor_listener* listener = iterator.Next()) { 1062 if (*listener->listener == userListener) { 1063 listener->flags |= flags; 1064 return B_OK; 1065 } 1066 } 1067 1068 UserNodeListener* copiedListener = new(std::nothrow) UserNodeListener( 1069 userListener); 1070 if (copiedListener == NULL) { 1071 if (monitor->listeners.IsEmpty()) 1072 _RemoveMonitor(monitor, flags); 1073 return B_NO_MEMORY; 1074 } 1075 1076 status = _AddMonitorListener(context, monitor, flags, *copiedListener); 1077 if (status != B_OK) 1078 delete copiedListener; 1079 1080 return status; 1081 } 1082 1083 1084 // #pragma mark - private kernel API 1085 1086 1087 status_t 1088 remove_node_monitors(struct io_context *context) 1089 { 1090 return sNodeMonitorService.RemoveListeners(context); 1091 } 1092 1093 1094 status_t 1095 node_monitor_init(void) 1096 { 1097 new(&sNodeMonitorSender) UserMessagingMessageSender(); 1098 new(&sNodeMonitorService) NodeMonitorService(); 1099 1100 if (sNodeMonitorService.InitCheck() < B_OK) 1101 panic("initializing node monitor failed\n"); 1102 1103 return B_OK; 1104 } 1105 1106 1107 status_t 1108 notify_unmount(dev_t device) 1109 { 1110 return sNodeMonitorService.NotifyUnmount(device); 1111 } 1112 1113 1114 status_t 1115 notify_mount(dev_t device, dev_t parentDevice, ino_t parentDirectory) 1116 { 1117 return sNodeMonitorService.NotifyMount(device, parentDevice, 1118 parentDirectory); 1119 } 1120 1121 1122 status_t 1123 remove_node_listener(dev_t device, ino_t node, NotificationListener& listener) 1124 { 1125 return sNodeMonitorService.RemoveListener(get_current_io_context(true), 1126 device, node, listener); 1127 } 1128 1129 1130 status_t 1131 add_node_listener(dev_t device, ino_t node, uint32 flags, 1132 NotificationListener& listener) 1133 { 1134 return sNodeMonitorService.AddListener(get_current_io_context(true), 1135 device, node, flags, listener); 1136 } 1137 1138 1139 // #pragma mark - public kernel API 1140 1141 1142 /*! \brief Notifies all interested listeners that an entry has been created. 1143 \param device The ID of the mounted FS, the entry lives in. 1144 \param directory The entry's parent directory ID. 1145 \param name The entry's name. 1146 \param node The ID of the node the entry refers to. 1147 \return 1148 - \c B_OK, if everything went fine, 1149 - another error code otherwise. 1150 */ 1151 status_t 1152 notify_entry_created(dev_t device, ino_t directory, const char *name, 1153 ino_t node) 1154 { 1155 return sNodeMonitorService.NotifyEntryCreatedOrRemoved(B_ENTRY_CREATED, 1156 device, directory, name, node); 1157 } 1158 1159 1160 /*! \brief Notifies all interested listeners that an entry has been removed. 1161 \param device The ID of the mounted FS, the entry lived in. 1162 \param directory The entry's former parent directory ID. 1163 \param name The entry's name. 1164 \param node The ID of the node the entry referred to. 1165 \return 1166 - \c B_OK, if everything went fine, 1167 - another error code otherwise. 1168 */ 1169 status_t 1170 notify_entry_removed(dev_t device, ino_t directory, const char *name, 1171 ino_t node) 1172 { 1173 return sNodeMonitorService.NotifyEntryCreatedOrRemoved(B_ENTRY_REMOVED, 1174 device, directory, name, node); 1175 } 1176 1177 1178 /*! \brief Notifies all interested listeners that an entry has been moved. 1179 \param device The ID of the mounted FS, the entry lives in. 1180 \param fromDirectory The entry's previous parent directory ID. 1181 \param fromName The entry's previous name. 1182 \param toDirectory The entry's new parent directory ID. 1183 \param toName The entry's new name. 1184 \param node The ID of the node the entry refers to. 1185 \return 1186 - \c B_OK, if everything went fine, 1187 - another error code otherwise. 1188 */ 1189 status_t 1190 notify_entry_moved(dev_t device, ino_t fromDirectory, 1191 const char *fromName, ino_t toDirectory, const char *toName, 1192 ino_t node) 1193 { 1194 return sNodeMonitorService.NotifyEntryMoved(device, fromDirectory, 1195 fromName, toDirectory, toName, node); 1196 } 1197 1198 1199 /*! \brief Notifies all interested listeners that a node's stat data have 1200 changed. 1201 \param device The ID of the mounted FS, the node lives in. 1202 \param node The ID of the node. 1203 \param statFields A bitwise combination of one or more of the \c B_STAT_* 1204 constants defined in <NodeMonitor.h>, indicating what fields of the 1205 stat data have changed. 1206 \return 1207 - \c B_OK, if everything went fine, 1208 - another error code otherwise. 1209 */ 1210 status_t 1211 notify_stat_changed(dev_t device, ino_t directory, ino_t node, 1212 uint32 statFields) 1213 { 1214 return sNodeMonitorService.NotifyStatChanged(device, directory, node, 1215 statFields); 1216 } 1217 1218 1219 /*! \brief Notifies all interested listeners that a node attribute has changed. 1220 \param device The ID of the mounted FS, the node lives in. 1221 \param node The ID of the node. 1222 \param attribute The attribute's name. 1223 \param cause Either of \c B_ATTR_CREATED, \c B_ATTR_REMOVED, or 1224 \c B_ATTR_CHANGED, indicating what exactly happened to the attribute. 1225 \return 1226 - \c B_OK, if everything went fine, 1227 - another error code otherwise. 1228 */ 1229 status_t 1230 notify_attribute_changed(dev_t device, ino_t directory, ino_t node, 1231 const char *attribute, int32 cause) 1232 { 1233 return sNodeMonitorService.NotifyAttributeChanged(device, directory, node, 1234 attribute, cause); 1235 } 1236 1237 1238 /*! \brief Notifies the listener of a live query that an entry has been added 1239 to the query (for whatever reason). 1240 \param port The target port of the listener. 1241 \param token The BHandler token of the listener. 1242 \param device The ID of the mounted FS, the entry lives in. 1243 \param directory The entry's parent directory ID. 1244 \param name The entry's name. 1245 \param node The ID of the node the entry refers to. 1246 \return 1247 - \c B_OK, if everything went fine, 1248 - another error code otherwise. 1249 */ 1250 status_t 1251 notify_query_entry_created(port_id port, int32 token, dev_t device, 1252 ino_t directory, const char *name, ino_t node) 1253 { 1254 return notify_query_entry_event(B_ENTRY_CREATED, port, token, 1255 device, directory, name, node); 1256 } 1257 1258 1259 /*! \brief Notifies the listener of a live query that an entry has been removed 1260 from the query (for whatever reason). 1261 \param port The target port of the listener. 1262 \param token The BHandler token of the listener. 1263 \param device The ID of the mounted FS, the entry lives in. 1264 \param directory The entry's parent directory ID. 1265 \param name The entry's name. 1266 \param node The ID of the node the entry refers to. 1267 \return 1268 - \c B_OK, if everything went fine, 1269 - another error code otherwise. 1270 */ 1271 status_t 1272 notify_query_entry_removed(port_id port, int32 token, dev_t device, 1273 ino_t directory, const char *name, ino_t node) 1274 { 1275 return notify_query_entry_event(B_ENTRY_REMOVED, port, token, 1276 device, directory, name, node); 1277 } 1278 1279 1280 /*! \brief Notifies the listener of a live query that an entry has been changed 1281 and is still in the query (for whatever reason). 1282 \param port The target port of the listener. 1283 \param token The BHandler token of the listener. 1284 \param device The ID of the mounted FS, the entry lives in. 1285 \param directory The entry's parent directory ID. 1286 \param name The entry's name. 1287 \param node The ID of the node the entry refers to. 1288 \return 1289 - \c B_OK, if everything went fine, 1290 - another error code otherwise. 1291 */ 1292 status_t 1293 notify_query_attr_changed(port_id port, int32 token, dev_t device, 1294 ino_t directory, const char* name, ino_t node) 1295 { 1296 return notify_query_entry_event(B_ATTR_CHANGED, port, token, 1297 device, directory, name, node); 1298 } 1299 1300 1301 // #pragma mark - User syscalls 1302 1303 1304 // TODO: We should verify that the port specified in the syscalls does actually 1305 // belong to the calling team. The situation is complicated by the fact that a 1306 // port can be transferred to another team. Consequently we should remove all 1307 // associated monitor listeners when a port is transferred/deleted. 1308 1309 1310 status_t 1311 _user_stop_notifying(port_id port, uint32 token) 1312 { 1313 io_context *context = get_current_io_context(false); 1314 1315 return sNodeMonitorService.RemoveUserListeners(context, port, token); 1316 } 1317 1318 1319 status_t 1320 _user_start_watching(dev_t device, ino_t node, uint32 flags, port_id port, 1321 uint32 token) 1322 { 1323 io_context *context = get_current_io_context(false); 1324 1325 UserNodeListener listener(port, token); 1326 return sNodeMonitorService.UpdateUserListener(context, device, node, flags, 1327 listener); 1328 } 1329 1330 1331 status_t 1332 _user_stop_watching(dev_t device, ino_t node, port_id port, uint32 token) 1333 { 1334 io_context *context = get_current_io_context(false); 1335 1336 UserNodeListener listener(port, token); 1337 return sNodeMonitorService.RemoveListener(context, device, node, 1338 listener); 1339 } 1340 1341