1 /* 2 * Copyright 2007-2008, Haiku Inc. All Rights Reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Axel Dörfler, axeld@pinc-software.de 7 * Stephan Aßmus <superstippi@gmx.de> 8 */ 9 10 11 #include <PathMonitor.h> 12 13 #include <ObjectList.h> 14 15 #include <Autolock.h> 16 #include <Directory.h> 17 #include <Entry.h> 18 #include <Handler.h> 19 #include <Locker.h> 20 #include <Looper.h> 21 #include <Path.h> 22 #include <String.h> 23 24 #include <map> 25 #include <new> 26 #include <set> 27 28 #undef TRACE 29 //#define TRACE_PATH_MONITOR 30 #ifdef TRACE_PATH_MONITOR 31 # define TRACE(x...) printf(x) 32 #else 33 # define TRACE(x...) ; 34 #endif 35 36 using namespace BPrivate; 37 using namespace std; 38 using std::nothrow; // TODO: Remove this line if the above line is enough. 39 40 // TODO: Use optimizations where stuff is already known to avoid iterating 41 // the watched directory and files set too often. 42 43 #define WATCH_NODE_FLAG_MASK 0x00ff 44 45 namespace BPrivate { 46 47 struct FileEntry { 48 entry_ref ref; 49 ino_t node; 50 }; 51 52 #if __GNUC__ > 3 53 bool operator<(const FileEntry& a, const FileEntry& b); 54 class FileEntryLess : public binary_function<FileEntry, FileEntry, bool> 55 { 56 public: 57 bool operator() (const FileEntry& a, const FileEntry& b) const 58 { 59 return a < b; 60 } 61 }; 62 typedef set<FileEntry, FileEntryLess> FileSet; 63 #else 64 typedef set<FileEntry> FileSet; 65 #endif 66 67 struct WatchedDirectory { 68 node_ref node; 69 bool contained; 70 }; 71 typedef set<WatchedDirectory> DirectorySet; 72 73 74 class PathHandler; 75 typedef map<BString, PathHandler*> HandlerMap; 76 77 struct Watcher { 78 HandlerMap handlers; 79 }; 80 typedef map<BMessenger, Watcher*> WatcherMap; 81 82 class PathHandler : public BHandler { 83 public: 84 PathHandler(const char* path, uint32 flags, BMessenger target, 85 BLooper* looper); 86 virtual ~PathHandler(); 87 88 status_t InitCheck() const; 89 void SetTarget(BMessenger target); 90 void Quit(); 91 92 virtual void MessageReceived(BMessage* message); 93 #ifdef TRACE_PATH_MONITOR 94 void Dump(); 95 #endif 96 97 private: 98 status_t _GetClosest(const char* path, bool updatePath, 99 node_ref& nodeRef); 100 101 bool _WatchRecursively() const; 102 bool _WatchFilesOnly() const; 103 bool _WatchFoldersOnly() const; 104 105 void _EntryCreated(BMessage* message); 106 void _EntryRemoved(BMessage* message); 107 void _EntryMoved(BMessage* message); 108 109 bool _IsContained(const node_ref& nodeRef) const; 110 bool _IsContained(BEntry& entry) const; 111 bool _HasDirectory(const node_ref& nodeRef, 112 bool* _contained = NULL) const; 113 bool _CloserToPath(BEntry& entry) const; 114 115 void _NotifyTarget(BMessage* message) const; 116 void _NotifyTarget(BMessage* message, const node_ref& nodeRef) const; 117 118 status_t _AddDirectory(BEntry& entry); 119 status_t _AddDirectory(node_ref& nodeRef); 120 status_t _RemoveDirectory(const node_ref& nodeRef, ino_t directoryNode); 121 status_t _RemoveDirectory(BEntry& entry, ino_t directoryNode); 122 123 bool _HasFile(const node_ref& nodeRef) const; 124 status_t _AddFile(BEntry& entry); 125 status_t _RemoveFile(const node_ref& nodeRef); 126 status_t _RemoveFile(BEntry& entry); 127 128 BPath fPath; 129 int32 fPathLength; 130 BMessenger fTarget; 131 uint32 fFlags; 132 status_t fStatus; 133 DirectorySet fDirectories; 134 FileSet fFiles; 135 }; 136 137 138 static WatcherMap sWatchers; 139 static BLocker* sLocker = NULL; 140 static BLooper* sLooper = NULL; 141 142 143 static status_t 144 set_entry(const node_ref& nodeRef, const char* name, BEntry& entry) 145 { 146 entry_ref ref; 147 ref.device = nodeRef.device; 148 ref.directory = nodeRef.node; 149 150 status_t status = ref.set_name(name); 151 if (status != B_OK) 152 return status; 153 154 return entry.SetTo(&ref, true); 155 } 156 157 158 bool 159 operator<(const FileEntry& a, const FileEntry& b) 160 { 161 if (a.ref.device == b.ref.device && a.node < b.node) 162 return true; 163 if (a.ref.device < b.ref.device) 164 return true; 165 166 return false; 167 } 168 169 170 bool 171 operator<(const node_ref& a, const node_ref& b) 172 { 173 if (a.device == b.device && a.node < b.node) 174 return true; 175 if (a.device < b.device) 176 return true; 177 178 return false; 179 } 180 181 182 bool 183 operator<(const WatchedDirectory& a, const WatchedDirectory& b) 184 { 185 return a.node < b.node; 186 } 187 188 189 // #pragma mark - 190 191 192 PathHandler::PathHandler(const char* path, uint32 flags, BMessenger target, 193 BLooper* looper) 194 : BHandler(path), 195 fTarget(target), 196 fFlags(flags) 197 { 198 if (path == NULL || !path[0]) { 199 fStatus = B_BAD_VALUE; 200 return; 201 } 202 203 // TODO: support watching not-yet-mounted volumes as well! 204 node_ref nodeRef; 205 fStatus = _GetClosest(path, true, nodeRef); 206 if (fStatus < B_OK) 207 return; 208 209 looper->Lock(); 210 looper->AddHandler(this); 211 looper->Unlock(); 212 213 fStatus = _AddDirectory(nodeRef); 214 215 // TODO: work-around for existing files (should not watch the directory in 216 // this case) 217 BEntry entry(path); 218 if (entry.Exists() && !entry.IsDirectory()) 219 _AddFile(entry); 220 } 221 222 223 PathHandler::~PathHandler() 224 { 225 } 226 227 228 status_t 229 PathHandler::InitCheck() const 230 { 231 return fStatus; 232 } 233 234 235 void 236 PathHandler::Quit() 237 { 238 if (sLooper->Lock()) { 239 stop_watching(this); 240 sLooper->RemoveHandler(this); 241 sLooper->Unlock(); 242 } 243 delete this; 244 } 245 246 247 #ifdef TRACE_PATH_MONITOR 248 void 249 PathHandler::Dump() 250 { 251 printf("WATCHING DIRECTORIES:\n"); 252 DirectorySet::iterator i = fDirectories.begin(); 253 for (; i != fDirectories.end(); i++) { 254 printf(" %ld:%Ld (%s)\n", i->node.device, i->node.node, i->contained 255 ? "contained" : "-"); 256 } 257 258 printf("WATCHING FILES:\n"); 259 260 FileSet::iterator j = fFiles.begin(); 261 for (; j != fFiles.end(); j++) { 262 printf(" %ld:%Ld\n", j->ref.device, j->node); 263 } 264 } 265 #endif 266 267 268 status_t 269 PathHandler::_GetClosest(const char* path, bool updatePath, node_ref& nodeRef) 270 { 271 BPath first(path); 272 BString missing; 273 274 while (true) { 275 // try to find the first part of the path that exists 276 BDirectory directory; 277 status_t status = directory.SetTo(first.Path()); 278 if (status == B_OK) { 279 status = directory.GetNodeRef(&nodeRef); 280 if (status == B_OK) { 281 if (updatePath) { 282 // normalize path 283 status = fPath.SetTo(&directory, NULL, true); 284 if (status == B_OK) { 285 fPath.Append(missing.String()); 286 fPathLength = strlen(fPath.Path()); 287 } 288 } 289 return status; 290 } 291 } 292 293 if (updatePath) { 294 if (missing.Length() > 0) 295 missing.Prepend("/"); 296 missing.Prepend(first.Leaf()); 297 } 298 299 if (first.GetParent(&first) != B_OK) 300 return B_ERROR; 301 } 302 } 303 304 305 bool 306 PathHandler::_WatchRecursively() const 307 { 308 return (fFlags & B_WATCH_RECURSIVELY) != 0; 309 } 310 311 312 bool 313 PathHandler::_WatchFilesOnly() const 314 { 315 return (fFlags & B_WATCH_FILES_ONLY) != 0; 316 } 317 318 319 bool 320 PathHandler::_WatchFoldersOnly() const 321 { 322 return (fFlags & B_WATCH_FOLDERS_ONLY) != 0; 323 } 324 325 326 void 327 PathHandler::_EntryCreated(BMessage* message) 328 { 329 const char* name; 330 node_ref nodeRef; 331 if (message->FindInt32("device", &nodeRef.device) != B_OK 332 || message->FindInt64("directory", &nodeRef.node) != B_OK 333 || message->FindString("name", &name) != B_OK) { 334 TRACE("PathHandler::_EntryCreated() - malformed message!\n"); 335 return; 336 } 337 338 BEntry entry; 339 if (set_entry(nodeRef, name, entry) != B_OK) { 340 TRACE("PathHandler::_EntryCreated() - set_entry failed!\n"); 341 return; 342 } 343 344 bool parentContained = false; 345 bool entryContained = _IsContained(entry); 346 if (entryContained) 347 parentContained = _IsContained(nodeRef); 348 bool notify = entryContained; 349 350 if (entry.IsDirectory()) { 351 // ignore the directory if it's already known 352 if (entry.GetNodeRef(&nodeRef) == B_OK 353 && _HasDirectory(nodeRef)) { 354 TRACE(" WE ALREADY HAVE DIR %s, %ld:%Ld\n", 355 name, nodeRef.device, nodeRef.node); 356 return; 357 } 358 359 // a new directory to watch for us 360 if (!entryContained && !_CloserToPath(entry) 361 || parentContained && !_WatchRecursively() 362 || _AddDirectory(entry) != B_OK 363 || _WatchFilesOnly()) 364 notify = parentContained; 365 // NOTE: entry is now toast after _AddDirectory() was called! 366 // Does not matter right now, but if it's a problem, use the node_ref 367 // version... 368 } else if (entryContained) { 369 TRACE(" NEW ENTRY PARENT CONTAINED: %d\n", parentContained); 370 _AddFile(entry); 371 } 372 373 if (notify && entryContained) { 374 message->AddBool("added", true); 375 // nodeRef is pointing to the parent directory 376 entry.GetNodeRef(&nodeRef); 377 _NotifyTarget(message, nodeRef); 378 } 379 } 380 381 382 void 383 PathHandler::_EntryRemoved(BMessage* message) 384 { 385 node_ref nodeRef; 386 uint64 directoryNode; 387 if (message->FindInt32("device", &nodeRef.device) != B_OK 388 || message->FindInt64("directory", (int64 *)&directoryNode) != B_OK 389 || message->FindInt64("node", &nodeRef.node) != B_OK) 390 return; 391 392 bool contained; 393 if (_HasDirectory(nodeRef, &contained)) { 394 // the directory has been removed, so we remove it as well 395 _RemoveDirectory(nodeRef, directoryNode); 396 if (contained && !_WatchFilesOnly()) { 397 message->AddBool("removed", true); 398 _NotifyTarget(message, nodeRef); 399 } 400 } else if (_HasFile(nodeRef)) { 401 message->AddBool("removed", true); 402 _NotifyTarget(message, nodeRef); 403 _RemoveFile(nodeRef); 404 } 405 } 406 407 408 void 409 PathHandler::_EntryMoved(BMessage* message) 410 { 411 // has the entry been moved into a monitored directory or has 412 // it been removed from one? 413 const char* name; 414 node_ref nodeRef; 415 uint64 fromNode; 416 uint64 node; 417 if (message->FindInt32("device", &nodeRef.device) != B_OK 418 || message->FindInt64("to directory", &nodeRef.node) != B_OK 419 || message->FindInt64("from directory", (int64 *)&fromNode) != B_OK 420 || message->FindInt64("node", (int64 *)&node) != B_OK 421 || message->FindString("name", &name) != B_OK) 422 return; 423 424 BEntry entry; 425 if (set_entry(nodeRef, name, entry) != B_OK) 426 return; 427 428 bool entryContained = _IsContained(entry); 429 bool wasAdded = false; 430 bool wasRemoved = false; 431 bool notify = false; 432 433 bool parentContained; 434 if (_HasDirectory(nodeRef, &parentContained)) { 435 // something has been added to our watched directories 436 437 nodeRef.node = node; 438 TRACE(" ADDED TO PARENT (%d), has entry %d/%d, entry %d %d\n", 439 parentContained, _HasDirectory(nodeRef), _HasFile(nodeRef), 440 entryContained, _CloserToPath(entry)); 441 442 if (entry.IsDirectory()) { 443 if (!_HasDirectory(nodeRef) 444 && (entryContained || _CloserToPath(entry))) { 445 // there is a new directory to watch for us 446 if (entryContained 447 || parentContained && !_WatchRecursively()) { 448 _AddDirectory(entry); 449 // NOTE: entry is toast now! 450 } else if (_GetClosest(fPath.Path(), false, 451 nodeRef) == B_OK) { 452 // the new directory might put us even 453 // closer to the path we are after 454 _AddDirectory(nodeRef); 455 } 456 457 wasAdded = true; 458 notify = entryContained; 459 } 460 if (_WatchFilesOnly()) 461 notify = false; 462 } else if (!_HasFile(nodeRef) && entryContained) { 463 // file has been added 464 wasAdded = true; 465 notify = true; 466 _AddFile(entry); 467 } 468 } else { 469 // and entry has been removed from our directories 470 wasRemoved = true; 471 472 nodeRef.node = node; 473 if (entry.IsDirectory()) { 474 if (_HasDirectory(nodeRef, ¬ify)) 475 _RemoveDirectory(entry, fromNode); 476 if (_WatchFilesOnly()) 477 notify = false; 478 } else { 479 _RemoveFile(entry); 480 notify = true; 481 } 482 } 483 484 if (notify) { 485 if (wasAdded) 486 message->AddBool("added", true); 487 if (wasRemoved) 488 message->AddBool("removed", true); 489 490 _NotifyTarget(message, nodeRef); 491 } 492 } 493 494 495 void 496 PathHandler::MessageReceived(BMessage* message) 497 { 498 switch (message->what) { 499 case B_NODE_MONITOR: 500 { 501 int32 opcode; 502 if (message->FindInt32("opcode", &opcode) != B_OK) 503 return; 504 505 switch (opcode) { 506 case B_ENTRY_CREATED: 507 _EntryCreated(message); 508 break; 509 510 case B_ENTRY_REMOVED: 511 _EntryRemoved(message); 512 break; 513 514 case B_ENTRY_MOVED: 515 _EntryMoved(message); 516 break; 517 518 default: 519 _NotifyTarget(message); 520 break; 521 } 522 break; 523 } 524 525 default: 526 BHandler::MessageReceived(message); 527 break; 528 } 529 530 //#ifdef TRACE_PATH_MONITOR 531 // Dump(); 532 //#endif 533 } 534 535 536 bool 537 PathHandler::_IsContained(const node_ref& nodeRef) const 538 { 539 BDirectory directory(&nodeRef); 540 if (directory.InitCheck() != B_OK) 541 return false; 542 543 BEntry entry; 544 if (directory.GetEntry(&entry) != B_OK) 545 return false; 546 547 return _IsContained(entry); 548 } 549 550 551 bool 552 PathHandler::_IsContained(BEntry& entry) const 553 { 554 BPath path; 555 if (entry.GetPath(&path) != B_OK) 556 return false; 557 558 bool contained = strncmp(path.Path(), fPath.Path(), fPathLength) == 0; 559 if (!contained) 560 return false; 561 562 // Prevent the case that the entry is in another folder which happens 563 // to have the same substring for fPathLength chars, like: 564 // /path/we/are/watching 565 // /path/we/are/watching-not/subfolder/entry 566 // NOTE: We wouldn't be here if path.Path() was shorter than fPathLength, 567 // strncmp() catches that case. 568 const char* last = &path.Path()[fPathLength]; 569 if (last[0] && last[0] != '/') 570 return false; 571 572 return true; 573 } 574 575 576 bool 577 PathHandler::_HasDirectory(const node_ref& nodeRef, 578 bool* _contained /* = NULL */) const 579 { 580 WatchedDirectory directory; 581 directory.node = nodeRef; 582 583 DirectorySet::const_iterator iterator = fDirectories.find(directory); 584 if (iterator == fDirectories.end()) 585 return false; 586 587 if (_contained != NULL) 588 *_contained = iterator->contained; 589 return true; 590 } 591 592 593 bool 594 PathHandler::_CloserToPath(BEntry& entry) const 595 { 596 BPath path; 597 if (entry.GetPath(&path) != B_OK) 598 return false; 599 600 return strncmp(path.Path(), fPath.Path(), strlen(path.Path())) == 0; 601 } 602 603 604 void 605 PathHandler::_NotifyTarget(BMessage* message) const 606 { 607 // NOTE: This version is only used for B_STAT_CHANGED and B_ATTR_CHANGED 608 node_ref nodeRef; 609 if (message->FindInt32("device", &nodeRef.device) != B_OK 610 || message->FindInt64("node", &nodeRef.node) != B_OK) 611 return; 612 _NotifyTarget(message, nodeRef); 613 } 614 615 616 void 617 PathHandler::_NotifyTarget(BMessage* message, const node_ref& nodeRef) const 618 { 619 BMessage update(*message); 620 update.what = B_PATH_MONITOR; 621 622 WatchedDirectory directory; 623 directory.node = nodeRef; 624 625 DirectorySet::const_iterator iterator = fDirectories.find(directory); 626 if (iterator != fDirectories.end()) { 627 if (_WatchFilesOnly()) { 628 // stat or attr notification for a directory 629 return; 630 } 631 BDirectory nodeDirectory(&nodeRef); 632 BEntry entry; 633 if (nodeDirectory.GetEntry(&entry) == B_OK) { 634 BPath path(&entry); 635 update.AddString("path", path.Path()); 636 } 637 } else { 638 if (_WatchFoldersOnly()) { 639 // this is bound to be a notification for a file 640 return; 641 } 642 FileEntry setEntry; 643 setEntry.ref.device = nodeRef.device; 644 setEntry.node = nodeRef.node; 645 // name does not need to be set, since it's not used for comparing 646 FileSet::const_iterator i = fFiles.find(setEntry); 647 if (i != fFiles.end()) { 648 BPath path(&(i->ref)); 649 update.AddString("path", path.Path()); 650 } 651 } 652 653 // This is in case the target is interested in figuring out which 654 // BPathMonitor::StartWatching() call the message is resulting from. 655 update.AddString("watched_path", fPath.Path()); 656 657 fTarget.SendMessage(&update); 658 } 659 660 661 status_t 662 PathHandler::_AddDirectory(BEntry& entry) 663 { 664 WatchedDirectory directory; 665 status_t status = entry.GetNodeRef(&directory.node); 666 if (status != B_OK) 667 return status; 668 669 #ifdef TRACE_PATH_MONITOR 670 { 671 BPath path(&entry); 672 printf(" ADD DIRECTORY %s, %ld:%Ld\n", 673 path.Path(), directory.node.device, directory.node.node); 674 } 675 #endif 676 677 // check if we are already know this directory 678 679 // TODO: It should be possible to ommit this check if we know it 680 // can't be the case (for example when adding subfolders recursively, 681 // although in that case, the API user may still have added this folder 682 // independently, so for now, it should be the safest to perform this 683 // check in all cases.) 684 if (_HasDirectory(directory.node)) 685 return B_OK; 686 687 directory.contained = _IsContained(entry); 688 689 uint32 flags; 690 if (directory.contained) 691 flags = (fFlags & WATCH_NODE_FLAG_MASK) | B_WATCH_DIRECTORY; 692 else 693 flags = B_WATCH_DIRECTORY; 694 695 status = watch_node(&directory.node, flags, this); 696 if (status != B_OK) 697 return status; 698 699 fDirectories.insert(directory); 700 701 if (_WatchRecursively()) { 702 BDirectory dir(&directory.node); 703 while (dir.GetNextEntry(&entry) == B_OK) { 704 if (entry.IsDirectory()) { 705 // and here is the recursion: 706 if (_AddDirectory(entry) != B_OK) 707 break; 708 } else if (!_WatchFoldersOnly()) { 709 if (_AddFile(entry) != B_OK) 710 break; 711 } 712 } 713 } 714 715 #if 0 716 BEntry parent; 717 if (entry.GetParent(&parent) == B_OK 718 && !_IsContained(parent)) { 719 // TODO: remove parent from watched directories 720 } 721 #endif 722 return B_OK; 723 } 724 725 726 status_t 727 PathHandler::_AddDirectory(node_ref& nodeRef) 728 { 729 BDirectory directory(&nodeRef); 730 status_t status = directory.InitCheck(); 731 if (status == B_OK) { 732 BEntry entry; 733 status = directory.GetEntry(&entry); 734 if (status == B_OK) 735 status = _AddDirectory(entry); 736 } 737 738 return status; 739 } 740 741 742 status_t 743 PathHandler::_RemoveDirectory(const node_ref& nodeRef, ino_t directoryNode) 744 { 745 TRACE(" REMOVE DIRECTORY %ld:%Ld\n", nodeRef.device, nodeRef.node); 746 747 WatchedDirectory directory; 748 directory.node = nodeRef; 749 750 DirectorySet::iterator iterator = fDirectories.find(directory); 751 if (iterator == fDirectories.end()) 752 return B_ENTRY_NOT_FOUND; 753 754 watch_node(&directory.node, B_STOP_WATCHING, this); 755 756 node_ref directoryRef; 757 directoryRef.device = nodeRef.device; 758 directoryRef.node = directoryNode; 759 760 if (!_HasDirectory(directoryRef)) { 761 // we don't have the parent directory now, but we'll need it in order 762 // to find this directory again in case it's added again 763 if (_AddDirectory(directoryRef) != B_OK 764 && _GetClosest(fPath.Path(), false, directoryRef) == B_OK) 765 _AddDirectory(directoryRef); 766 } 767 768 fDirectories.erase(iterator); 769 770 // TODO: stop watching subdirectories and their files when in recursive 771 // mode! 772 773 return B_OK; 774 } 775 776 777 status_t 778 PathHandler::_RemoveDirectory(BEntry& entry, ino_t directoryNode) 779 { 780 node_ref nodeRef; 781 status_t status = entry.GetNodeRef(&nodeRef); 782 if (status != B_OK) 783 return status; 784 785 return _RemoveDirectory(nodeRef, directoryNode); 786 } 787 788 789 bool 790 PathHandler::_HasFile(const node_ref& nodeRef) const 791 { 792 FileEntry setEntry; 793 setEntry.ref.device = nodeRef.device; 794 setEntry.node = nodeRef.node; 795 // name does not need to be set, since it's not used for comparing 796 FileSet::const_iterator iterator = fFiles.find(setEntry); 797 return iterator != fFiles.end(); 798 } 799 800 801 status_t 802 PathHandler::_AddFile(BEntry& entry) 803 { 804 if ((fFlags & (WATCH_NODE_FLAG_MASK & ~B_WATCH_DIRECTORY)) == 0) 805 return B_OK; 806 807 #ifdef TRACE_PATH_MONITOR 808 { 809 BPath path(&entry); 810 printf(" ADD FILE %s\n", path.Path()); 811 } 812 #endif 813 814 node_ref nodeRef; 815 status_t status = entry.GetNodeRef(&nodeRef); 816 if (status != B_OK) 817 return status; 818 819 // check if we are already know this file 820 821 // TODO: It should be possible to ommit this check if we know it 822 // can't be the case (for example when adding subfolders recursively, 823 // although in that case, the API user may still have added this file 824 // independently, so for now, it should be the safest to perform this 825 // check in all cases.) 826 if (_HasFile(nodeRef)) 827 return B_OK; 828 829 status = watch_node(&nodeRef, (fFlags & WATCH_NODE_FLAG_MASK), this); 830 if (status != B_OK) 831 return status; 832 833 FileEntry setEntry; 834 entry.GetRef(&setEntry.ref); 835 setEntry.node = nodeRef.node; 836 837 fFiles.insert(setEntry); 838 return B_OK; 839 } 840 841 842 status_t 843 PathHandler::_RemoveFile(const node_ref& nodeRef) 844 { 845 TRACE(" REMOVE FILE %ld:%Ld\n", nodeRef.device, nodeRef.node); 846 847 FileEntry setEntry; 848 setEntry.ref.device = nodeRef.device; 849 setEntry.node = nodeRef.node; 850 // name does not need to be set, since it's not used for comparing 851 FileSet::iterator iterator = fFiles.find(setEntry); 852 if (iterator == fFiles.end()) 853 return B_ENTRY_NOT_FOUND; 854 855 watch_node(&nodeRef, B_STOP_WATCHING, this); 856 fFiles.erase(iterator); 857 return B_OK; 858 } 859 860 861 status_t 862 PathHandler::_RemoveFile(BEntry& entry) 863 { 864 node_ref nodeRef; 865 status_t status = entry.GetNodeRef(&nodeRef); 866 if (status != B_OK) 867 return status; 868 869 return _RemoveFile(nodeRef); 870 } 871 872 873 // #pragma mark - 874 875 876 BPathMonitor::BPathMonitor() 877 { 878 } 879 880 881 BPathMonitor::~BPathMonitor() 882 { 883 } 884 885 886 /*static*/ status_t 887 BPathMonitor::_InitLockerIfNeeded() 888 { 889 static vint32 lock = 0; 890 891 if (sLocker != NULL) 892 return B_OK; 893 894 while (sLocker == NULL) { 895 if (atomic_add(&lock, 1) == 0) { 896 sLocker = new (nothrow) BLocker("path monitor"); 897 if (sLocker == NULL) 898 return B_NO_MEMORY; 899 } 900 snooze(5000); 901 } 902 903 return B_OK; 904 } 905 906 907 /*static*/ status_t 908 BPathMonitor::_InitLooperIfNeeded() 909 { 910 static vint32 lock = 0; 911 912 if (sLooper != NULL) 913 return B_OK; 914 915 while (sLooper == NULL) { 916 if (atomic_add(&lock, 1) == 0) { 917 // first thread initializes the global looper 918 sLooper = new (nothrow) BLooper("PathMonitor looper"); 919 if (sLooper == NULL) 920 return B_NO_MEMORY; 921 thread_id thread = sLooper->Run(); 922 if (thread < B_OK) 923 return (status_t)thread; 924 } 925 snooze(5000); 926 } 927 928 return sLooper->Thread() >= 0 ? B_OK : B_ERROR; 929 } 930 931 932 /*static*/ status_t 933 BPathMonitor::StartWatching(const char* path, uint32 flags, BMessenger target) 934 { 935 status_t status = _InitLockerIfNeeded(); 936 if (status != B_OK) 937 return status; 938 939 // use the global looper for receiving node monitor notifications 940 status = _InitLooperIfNeeded(); 941 if (status < B_OK) 942 return status; 943 944 BAutolock _(sLocker); 945 946 WatcherMap::iterator iterator = sWatchers.find(target); 947 Watcher* watcher = NULL; 948 if (iterator != sWatchers.end()) 949 watcher = iterator->second; 950 951 PathHandler* handler = new (nothrow) PathHandler(path, flags, target, 952 sLooper); 953 if (handler == NULL) 954 return B_NO_MEMORY; 955 status = handler->InitCheck(); 956 if (status < B_OK) 957 return status; 958 959 if (watcher == NULL) { 960 watcher = new (nothrow) BPrivate::Watcher; 961 if (watcher == NULL) 962 return B_NO_MEMORY; 963 sWatchers[target] = watcher; 964 } 965 966 watcher->handlers[path] = handler; 967 return B_OK; 968 } 969 970 971 /*static*/ status_t 972 BPathMonitor::StopWatching(const char* path, BMessenger target) 973 { 974 if (sLocker == NULL) 975 return B_NO_INIT; 976 977 BAutolock _(sLocker); 978 979 WatcherMap::iterator iterator = sWatchers.find(target); 980 if (iterator == sWatchers.end()) 981 return B_BAD_VALUE; 982 983 Watcher* watcher = iterator->second; 984 HandlerMap::iterator i = watcher->handlers.find(path); 985 986 if (i == watcher->handlers.end()) 987 return B_BAD_VALUE; 988 989 PathHandler* handler = i->second; 990 watcher->handlers.erase(i); 991 992 handler->Quit(); 993 994 if (watcher->handlers.empty()) { 995 sWatchers.erase(iterator); 996 delete watcher; 997 } 998 999 return B_OK; 1000 } 1001 1002 1003 /*static*/ status_t 1004 BPathMonitor::StopWatching(BMessenger target) 1005 { 1006 if (sLocker == NULL) 1007 return B_NO_INIT; 1008 1009 BAutolock _(sLocker); 1010 1011 WatcherMap::iterator iterator = sWatchers.find(target); 1012 if (iterator == sWatchers.end()) 1013 return B_BAD_VALUE; 1014 1015 Watcher* watcher = iterator->second; 1016 while (!watcher->handlers.empty()) { 1017 HandlerMap::iterator i = watcher->handlers.begin(); 1018 PathHandler* handler = i->second; 1019 watcher->handlers.erase(i); 1020 1021 handler->Quit(); 1022 } 1023 1024 sWatchers.erase(iterator); 1025 delete watcher; 1026 1027 return B_OK; 1028 } 1029 1030 } // namespace BPrivate 1031