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