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 monitor_listener *listener; 691 node_monitor *monitor; 692 693 TRACE(("notify_listener(op = %d, device = %ld, node = %Ld, parent = %Ld, toParent = %Ld" 694 ", name = \"%s\"\n", op, device, node, parentNode, toParentNode, name)); 695 696 mutex_lock(&gMonitorMutex); 697 698 // check the main "node" 699 700 if ((op == B_ENTRY_MOVED 701 || op == B_ENTRY_REMOVED 702 || op == B_STAT_CHANGED 703 || op == B_ATTR_CHANGED) 704 && (monitor = get_monitor_for(device, node)) != NULL) { 705 // iterate over all listeners for this monitor, and see 706 // if we have to send anything 707 listener = NULL; 708 while ((listener = (monitor_listener*)list_get_next_item( 709 &monitor->listeners, listener)) != NULL) { 710 // do we have a reason to notify this listener? 711 if (((listener->flags & B_WATCH_NAME) != 0 712 && (op == B_ENTRY_MOVED || op == B_ENTRY_REMOVED)) 713 || ((listener->flags & B_WATCH_STAT) != 0 714 && op == B_STAT_CHANGED) 715 || ((listener->flags & B_WATCH_ATTR) != 0 716 && op == B_ATTR_CHANGED)) { 717 // then do it! 718 send_notification(listener->port, listener->token, B_NODE_MONITOR, 719 op, device, 0, parentNode, toParentNode, node, name); 720 } 721 } 722 } 723 724 // check its parent directory 725 726 if ((op == B_ENTRY_MOVED 727 || op == B_ENTRY_REMOVED 728 || op == B_ENTRY_CREATED) 729 && (monitor = get_monitor_for(device, parentNode)) != NULL) { 730 // iterate over all listeners for this monitor, and see 731 // if we have to send anything 732 listener = NULL; 733 while ((listener = (monitor_listener*)list_get_next_item( 734 &monitor->listeners, listener)) != NULL) { 735 // do we have a reason to notify this listener? 736 if ((listener->flags & B_WATCH_DIRECTORY) != 0) { 737 send_notification(listener->port, listener->token, B_NODE_MONITOR, 738 op, device, 0, parentNode, toParentNode, node, name); 739 } 740 } 741 } 742 743 // check its new target parent directory 744 745 if (op == B_ENTRY_MOVED 746 && (monitor = get_monitor_for(device, toParentNode)) != NULL) { 747 // iterate over all listeners for this monitor, and see 748 // if we have to send anything 749 listener = NULL; 750 while ((listener = (monitor_listener*)list_get_next_item( 751 &monitor->listeners, listener)) != NULL) { 752 // do we have a reason to notify this listener? 753 if ((listener->flags & B_WATCH_DIRECTORY) != 0) { 754 send_notification(listener->port, listener->token, B_NODE_MONITOR, 755 B_ENTRY_MOVED, device, 0, parentNode, toParentNode, node, name); 756 } 757 } 758 } 759 760 mutex_unlock(&gMonitorMutex); 761 return B_OK; 762 } 763 764 765 /** \brief Notifies all interested listeners that an entry has been created. 766 * \param device The ID of the mounted FS, the entry lives in. 767 * \param directory The entry's parent directory ID. 768 * \param name The entry's name. 769 * \param node The ID of the node the entry refers to. 770 * \return 771 * - \c B_OK, if everything went fine, 772 * - another error code otherwise. 773 */ 774 775 status_t 776 notify_entry_created(mount_id device, vnode_id directory, const char *name, 777 vnode_id node) 778 { 779 return notify_entry_created_or_removed(B_ENTRY_CREATED, device, 780 directory, name, node); 781 } 782 783 784 /** \brief Notifies all interested listeners that an entry has been removed. 785 * \param device The ID of the mounted FS, the entry lived in. 786 * \param directory The entry's former parent directory ID. 787 * \param name The entry's name. 788 * \param node The ID of the node the entry referred to. 789 * \return 790 * - \c B_OK, if everything went fine, 791 * - another error code otherwise. 792 */ 793 794 status_t 795 notify_entry_removed(mount_id device, vnode_id directory, const char *name, 796 vnode_id node) 797 { 798 return notify_entry_created_or_removed(B_ENTRY_REMOVED, device, 799 directory, name, node); 800 } 801 802 803 /** \brief Notifies all interested listeners that an entry has been moved. 804 * \param device The ID of the mounted FS, the entry lives in. 805 * \param fromDirectory The entry's previous parent directory ID. 806 * \param fromName The entry's previous name. 807 * \param toDirectory The entry's new parent directory ID. 808 * \param toName The entry's new name. 809 * \param node The ID of the node the entry refers to. 810 * \return 811 * - \c B_OK, if everything went fine, 812 * - another error code otherwise. 813 */ 814 815 status_t 816 notify_entry_moved(mount_id device, vnode_id fromDirectory, 817 const char *fromName, vnode_id toDirectory, const char *toName, 818 vnode_id node) 819 { 820 if (!fromName || !toName) 821 return B_BAD_VALUE; 822 823 // If node is a mount point, we need to resolve it to the mounted 824 // volume's root node. 825 mount_id nodeDevice = device; 826 resolve_mount_point_to_volume_root(device, node, &nodeDevice, &node); 827 828 MutexLocker locker(gMonitorMutex); 829 830 // get the lists of all interested listeners 831 interested_monitor_listener_list interestedListeners[3]; 832 int32 interestedListenerCount = 0; 833 // ... for the node 834 get_interested_monitor_listeners(nodeDevice, node, B_WATCH_NAME, 835 interestedListeners, interestedListenerCount); 836 // ... for the source directory 837 get_interested_monitor_listeners(device, fromDirectory, B_WATCH_DIRECTORY, 838 interestedListeners, interestedListenerCount); 839 // ... for the target directory 840 get_interested_monitor_listeners(device, toDirectory, B_WATCH_DIRECTORY, 841 interestedListeners, interestedListenerCount); 842 843 if (interestedListenerCount == 0) 844 return B_OK; 845 846 // there are interested listeners: construct the message and send it 847 char messageBuffer[1024]; 848 KMessage message; 849 message.SetTo(messageBuffer, sizeof(messageBuffer), B_NODE_MONITOR); 850 message.AddInt32("opcode", B_ENTRY_MOVED); 851 message.AddInt32("device", device); 852 message.AddInt64("from directory", fromDirectory); 853 message.AddInt64("to directory", toDirectory); 854 message.AddInt32("node device", nodeDevice); // Haiku only 855 message.AddInt64("node", node); 856 message.AddString("from name", fromName); // Haiku only 857 message.AddString("name", toName); 858 859 return send_notification_message(message, interestedListeners, 860 interestedListenerCount); 861 } 862 863 864 /** \brief Notifies all interested listeners that a node's stat data have 865 * changed. 866 * \param device The ID of the mounted FS, the node lives in. 867 * \param node The ID of the node. 868 * \param statFields A bitwise combination of one or more of the \c B_STAT_* 869 * constants defined in <NodeMonitor.h>, indicating what fields of the 870 * stat data have changed. 871 * \return 872 * - \c B_OK, if everything went fine, 873 * - another error code otherwise. 874 */ 875 876 status_t 877 notify_stat_changed(mount_id device, vnode_id node, uint32 statFields) 878 { 879 MutexLocker locker(gMonitorMutex); 880 881 // get the lists of all interested listeners 882 interested_monitor_listener_list interestedListeners[3]; 883 int32 interestedListenerCount = 0; 884 // ... for the node 885 get_interested_monitor_listeners(device, node, B_WATCH_STAT, 886 interestedListeners, interestedListenerCount); 887 888 if (interestedListenerCount == 0) 889 return B_OK; 890 891 // there are interested listeners: construct the message and send it 892 char messageBuffer[1024]; 893 KMessage message; 894 message.SetTo(messageBuffer, sizeof(messageBuffer), B_NODE_MONITOR); 895 message.AddInt32("opcode", B_STAT_CHANGED); 896 message.AddInt32("device", device); 897 message.AddInt64("node", node); 898 message.AddInt32("fields", statFields); // Haiku only 899 900 return send_notification_message(message, interestedListeners, 901 interestedListenerCount); 902 } 903 904 905 /** \brief Notifies all interested listeners that a node attribute has changed. 906 * \param device The ID of the mounted FS, the node lives in. 907 * \param node The ID of the node. 908 * \param attribute The attribute's name. 909 * \param cause Either of \c B_ATTR_CREATED, \c B_ATTR_REMOVED, or 910 * \c B_ATTR_CHANGED, indicating what exactly happened to the attribute. 911 * \return 912 * - \c B_OK, if everything went fine, 913 * - another error code otherwise. 914 */ 915 916 status_t 917 notify_attribute_changed(mount_id device, vnode_id node, const char *attribute, 918 int32 cause) 919 { 920 if (!attribute) 921 return B_BAD_VALUE; 922 923 MutexLocker locker(gMonitorMutex); 924 925 // get the lists of all interested listeners 926 interested_monitor_listener_list interestedListeners[3]; 927 int32 interestedListenerCount = 0; 928 // ... for the node 929 get_interested_monitor_listeners(device, node, B_WATCH_ATTR, 930 interestedListeners, interestedListenerCount); 931 932 if (interestedListenerCount == 0) 933 return B_OK; 934 935 // there are interested listeners: construct the message and send it 936 char messageBuffer[1024]; 937 KMessage message; 938 message.SetTo(messageBuffer, sizeof(messageBuffer), B_NODE_MONITOR); 939 message.AddInt32("opcode", B_ATTR_CHANGED); 940 message.AddInt32("device", device); 941 message.AddInt64("node", node); 942 message.AddString("attr", attribute); 943 message.AddInt32("cause", cause); // Haiku only 944 945 return send_notification_message(message, interestedListeners, 946 interestedListenerCount); 947 } 948 949 950 /** \brief Notifies the listener of a live query that an entry has been added 951 * to the query (for whatever reason). 952 * \param port The target port of the listener. 953 * \param token The BHandler token of the listener. 954 * \param device The ID of the mounted FS, the entry lives in. 955 * \param directory The entry's parent directory ID. 956 * \param name The entry's name. 957 * \param node The ID of the node the entry refers to. 958 * \return 959 * - \c B_OK, if everything went fine, 960 * - another error code otherwise. 961 */ 962 963 status_t 964 notify_query_entry_created(port_id port, int32 token, mount_id device, 965 vnode_id directory, const char *name, vnode_id node) 966 { 967 return notify_query_entry_created_or_removed(B_ENTRY_CREATED, port, token, 968 device, directory, name, node); 969 } 970 971 972 /** \brief Notifies the listener of a live query that an entry has been removed 973 * from the query (for whatever reason). 974 * \param port The target port of the listener. 975 * \param token The BHandler token of the listener. 976 * \param device The ID of the mounted FS, the entry lives in. 977 * \param directory The entry's parent directory ID. 978 * \param name The entry's name. 979 * \param node The ID of the node the entry refers to. 980 * \return 981 * - \c B_OK, if everything went fine, 982 * - another error code otherwise. 983 */ 984 985 status_t 986 notify_query_entry_removed(port_id port, int32 token, 987 mount_id device, vnode_id directory, const char *name, vnode_id node) 988 { 989 return notify_query_entry_created_or_removed(B_ENTRY_REMOVED, port, token, 990 device, directory, name, node); 991 } 992 993 994 // #pragma mark - User syscalls 995 996 997 status_t 998 _user_stop_notifying(port_id port, uint32 token) 999 { 1000 io_context *context = get_current_io_context(false); 1001 1002 return remove_node_monitors_by_target(context, port, token); 1003 } 1004 1005 1006 status_t 1007 _user_start_watching(dev_t device, ino_t node, uint32 flags, port_id port, uint32 token) 1008 { 1009 io_context *context = get_current_io_context(false); 1010 1011 return add_node_monitor(context, device, node, flags, port, token); 1012 } 1013 1014 1015 status_t 1016 _user_stop_watching(dev_t device, ino_t node, uint32 flags, port_id port, uint32 token) 1017 { 1018 io_context *context = get_current_io_context(false); 1019 1020 return remove_node_monitor(context, device, node, flags, port, token); 1021 } 1022 1023