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...) debug_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, bool notify = false); 119 status_t _AddDirectory(node_ref& nodeRef, bool notify = false); 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, bool notify = false); 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 TRACE("PathHandler: %s\n", path); 210 211 looper->Lock(); 212 looper->AddHandler(this); 213 looper->Unlock(); 214 215 fStatus = _AddDirectory(nodeRef); 216 217 // TODO: work-around for existing files (should not watch the directory in 218 // this case) 219 BEntry entry(path); 220 if (entry.Exists() && !entry.IsDirectory()) 221 _AddFile(entry); 222 } 223 224 225 PathHandler::~PathHandler() 226 { 227 } 228 229 230 status_t 231 PathHandler::InitCheck() const 232 { 233 return fStatus; 234 } 235 236 237 void 238 PathHandler::Quit() 239 { 240 if (sLooper->Lock()) { 241 stop_watching(this); 242 sLooper->RemoveHandler(this); 243 sLooper->Unlock(); 244 } 245 delete this; 246 } 247 248 249 #ifdef TRACE_PATH_MONITOR 250 void 251 PathHandler::Dump() 252 { 253 TRACE("WATCHING DIRECTORIES:\n"); 254 DirectorySet::iterator i = fDirectories.begin(); 255 for (; i != fDirectories.end(); i++) { 256 TRACE(" %ld:%Ld (%s)\n", i->node.device, i->node.node, i->contained 257 ? "contained" : "-"); 258 } 259 260 TRACE("WATCHING FILES:\n"); 261 262 FileSet::iterator j = fFiles.begin(); 263 for (; j != fFiles.end(); j++) { 264 TRACE(" %ld:%Ld\n", j->ref.device, j->node); 265 } 266 } 267 #endif 268 269 270 status_t 271 PathHandler::_GetClosest(const char* path, bool updatePath, node_ref& nodeRef) 272 { 273 BPath first(path); 274 BString missing; 275 276 while (true) { 277 // try to find the first part of the path that exists 278 BDirectory directory; 279 status_t status = directory.SetTo(first.Path()); 280 if (status == B_OK) { 281 status = directory.GetNodeRef(&nodeRef); 282 if (status == B_OK) { 283 if (updatePath) { 284 // normalize path 285 status = fPath.SetTo(&directory, NULL, true); 286 if (status == B_OK) { 287 fPath.Append(missing.String()); 288 fPathLength = strlen(fPath.Path()); 289 } 290 } 291 return status; 292 } 293 } 294 295 if (updatePath) { 296 if (missing.Length() > 0) 297 missing.Prepend("/"); 298 missing.Prepend(first.Leaf()); 299 } 300 301 if (first.GetParent(&first) != B_OK) 302 return B_ERROR; 303 } 304 } 305 306 307 bool 308 PathHandler::_WatchRecursively() const 309 { 310 return (fFlags & B_WATCH_RECURSIVELY) != 0; 311 } 312 313 314 bool 315 PathHandler::_WatchFilesOnly() const 316 { 317 return (fFlags & B_WATCH_FILES_ONLY) != 0; 318 } 319 320 321 bool 322 PathHandler::_WatchFoldersOnly() const 323 { 324 return (fFlags & B_WATCH_FOLDERS_ONLY) != 0; 325 } 326 327 328 void 329 PathHandler::_EntryCreated(BMessage* message) 330 { 331 const char* name; 332 node_ref nodeRef; 333 if (message->FindInt32("device", &nodeRef.device) != B_OK 334 || message->FindInt64("directory", &nodeRef.node) != B_OK 335 || message->FindString("name", &name) != B_OK) { 336 TRACE("PathHandler::_EntryCreated() - malformed message!\n"); 337 return; 338 } 339 340 BEntry entry; 341 if (set_entry(nodeRef, name, entry) != B_OK) { 342 TRACE("PathHandler::_EntryCreated() - set_entry failed!\n"); 343 return; 344 } 345 346 bool parentContained = false; 347 bool entryContained = _IsContained(entry); 348 if (entryContained) 349 parentContained = _IsContained(nodeRef); 350 bool notify = entryContained; 351 352 if (entry.IsDirectory()) { 353 // ignore the directory if it's already known 354 if (entry.GetNodeRef(&nodeRef) == B_OK 355 && _HasDirectory(nodeRef)) { 356 TRACE(" WE ALREADY HAVE DIR %s, %ld:%Ld\n", 357 name, nodeRef.device, nodeRef.node); 358 return; 359 } 360 361 // a new directory to watch for us 362 if ((!entryContained && !_CloserToPath(entry)) 363 || (parentContained && !_WatchRecursively()) 364 || _AddDirectory(entry, true) != B_OK 365 || _WatchFilesOnly()) 366 notify = parentContained; 367 // NOTE: entry is now toast after _AddDirectory() was called! 368 // Does not matter right now, but if it's a problem, use the node_ref 369 // version... 370 } else if (entryContained) { 371 TRACE(" NEW ENTRY PARENT CONTAINED: %d\n", parentContained); 372 _AddFile(entry); 373 } 374 375 if (notify && entryContained) { 376 message->AddBool("added", true); 377 // nodeRef is pointing to the parent directory 378 entry.GetNodeRef(&nodeRef); 379 _NotifyTarget(message, nodeRef); 380 } 381 } 382 383 384 void 385 PathHandler::_EntryRemoved(BMessage* message) 386 { 387 node_ref nodeRef; 388 uint64 directoryNode; 389 if (message->FindInt32("device", &nodeRef.device) != B_OK 390 || message->FindInt64("directory", (int64 *)&directoryNode) != B_OK 391 || message->FindInt64("node", &nodeRef.node) != B_OK) 392 return; 393 394 bool contained; 395 if (_HasDirectory(nodeRef, &contained)) { 396 // the directory has been removed, so we remove it as well 397 _RemoveDirectory(nodeRef, directoryNode); 398 if (contained && !_WatchFilesOnly()) { 399 message->AddBool("removed", true); 400 _NotifyTarget(message, nodeRef); 401 } 402 } else if (_HasFile(nodeRef)) { 403 message->AddBool("removed", true); 404 _NotifyTarget(message, nodeRef); 405 _RemoveFile(nodeRef); 406 } 407 } 408 409 410 void 411 PathHandler::_EntryMoved(BMessage* message) 412 { 413 // has the entry been moved into a monitored directory or has 414 // it been removed from one? 415 const char* name; 416 node_ref nodeRef; 417 uint64 fromNode; 418 uint64 node; 419 if (message->FindInt32("device", &nodeRef.device) != B_OK 420 || message->FindInt64("to directory", &nodeRef.node) != B_OK 421 || message->FindInt64("from directory", (int64 *)&fromNode) != B_OK 422 || message->FindInt64("node", (int64 *)&node) != B_OK 423 || message->FindString("name", &name) != B_OK) 424 return; 425 426 BEntry entry; 427 if (set_entry(nodeRef, name, entry) != B_OK) 428 return; 429 430 bool entryContained = _IsContained(entry); 431 bool wasAdded = false; 432 bool wasRemoved = false; 433 bool notify = false; 434 435 bool parentContained; 436 if (_HasDirectory(nodeRef, &parentContained)) { 437 // something has been added to our watched directories 438 439 nodeRef.node = node; 440 TRACE(" ADDED TO PARENT (%d), has entry %d/%d, entry %d %d\n", 441 parentContained, _HasDirectory(nodeRef), _HasFile(nodeRef), 442 entryContained, _CloserToPath(entry)); 443 444 if (entry.IsDirectory()) { 445 if (!_HasDirectory(nodeRef) 446 && (entryContained || _CloserToPath(entry))) { 447 // there is a new directory to watch for us 448 if (entryContained 449 || (parentContained && !_WatchRecursively())) { 450 _AddDirectory(entry, true); 451 // NOTE: entry is toast now! 452 } else if (_GetClosest(fPath.Path(), false, 453 nodeRef) == B_OK) { 454 // the new directory might put us even 455 // closer to the path we are after 456 _AddDirectory(nodeRef, true); 457 } 458 459 wasAdded = true; 460 notify = entryContained; 461 } 462 if (_WatchFilesOnly()) 463 notify = false; 464 } else if (!_HasFile(nodeRef) && entryContained) { 465 // file has been added 466 wasAdded = true; 467 notify = true; 468 _AddFile(entry); 469 } 470 } else { 471 // and entry has been removed from our directories 472 wasRemoved = true; 473 474 nodeRef.node = node; 475 if (entry.IsDirectory()) { 476 if (_HasDirectory(nodeRef, ¬ify)) 477 _RemoveDirectory(entry, fromNode); 478 if (_WatchFilesOnly()) 479 notify = false; 480 } else { 481 _RemoveFile(entry); 482 notify = true; 483 } 484 } 485 486 if (notify) { 487 if (wasAdded) 488 message->AddBool("added", true); 489 if (wasRemoved) 490 message->AddBool("removed", true); 491 492 _NotifyTarget(message, nodeRef); 493 } 494 } 495 496 497 void 498 PathHandler::MessageReceived(BMessage* message) 499 { 500 switch (message->what) { 501 case B_NODE_MONITOR: 502 { 503 int32 opcode; 504 if (message->FindInt32("opcode", &opcode) != B_OK) 505 return; 506 507 switch (opcode) { 508 case B_ENTRY_CREATED: 509 _EntryCreated(message); 510 break; 511 512 case B_ENTRY_REMOVED: 513 _EntryRemoved(message); 514 break; 515 516 case B_ENTRY_MOVED: 517 _EntryMoved(message); 518 break; 519 520 default: 521 _NotifyTarget(message); 522 break; 523 } 524 break; 525 } 526 527 default: 528 BHandler::MessageReceived(message); 529 break; 530 } 531 532 //#ifdef TRACE_PATH_MONITOR 533 // Dump(); 534 //#endif 535 } 536 537 538 bool 539 PathHandler::_IsContained(const node_ref& nodeRef) const 540 { 541 BDirectory directory(&nodeRef); 542 if (directory.InitCheck() != B_OK) 543 return false; 544 545 BEntry entry; 546 if (directory.GetEntry(&entry) != B_OK) 547 return false; 548 549 return _IsContained(entry); 550 } 551 552 553 bool 554 PathHandler::_IsContained(BEntry& entry) const 555 { 556 BPath path; 557 if (entry.GetPath(&path) != B_OK) 558 return false; 559 560 bool contained = strncmp(path.Path(), fPath.Path(), fPathLength) == 0; 561 if (!contained) 562 return false; 563 564 // Prevent the case that the entry is in another folder which happens 565 // to have the same substring for fPathLength chars, like: 566 // /path/we/are/watching 567 // /path/we/are/watching-not/subfolder/entry 568 // NOTE: We wouldn't be here if path.Path() was shorter than fPathLength, 569 // strncmp() catches that case. 570 const char* last = &path.Path()[fPathLength]; 571 if (last[0] && last[0] != '/') 572 return false; 573 574 return true; 575 } 576 577 578 bool 579 PathHandler::_HasDirectory(const node_ref& nodeRef, 580 bool* _contained /* = NULL */) const 581 { 582 WatchedDirectory directory; 583 directory.node = nodeRef; 584 585 DirectorySet::const_iterator iterator = fDirectories.find(directory); 586 if (iterator == fDirectories.end()) 587 return false; 588 589 if (_contained != NULL) 590 *_contained = iterator->contained; 591 return true; 592 } 593 594 595 bool 596 PathHandler::_CloserToPath(BEntry& entry) const 597 { 598 BPath path; 599 if (entry.GetPath(&path) != B_OK) 600 return false; 601 602 return strncmp(path.Path(), fPath.Path(), strlen(path.Path())) == 0; 603 } 604 605 606 void 607 PathHandler::_NotifyTarget(BMessage* message) const 608 { 609 // NOTE: This version is only used for B_STAT_CHANGED and B_ATTR_CHANGED 610 node_ref nodeRef; 611 if (message->FindInt32("device", &nodeRef.device) != B_OK 612 || message->FindInt64("node", &nodeRef.node) != B_OK) 613 return; 614 _NotifyTarget(message, nodeRef); 615 } 616 617 618 void 619 PathHandler::_NotifyTarget(BMessage* message, const node_ref& nodeRef) const 620 { 621 BMessage update(*message); 622 update.what = B_PATH_MONITOR; 623 624 TRACE("_NotifyTarget(): node ref %ld.%Ld\n", nodeRef.device, nodeRef.node); 625 626 WatchedDirectory directory; 627 directory.node = nodeRef; 628 629 DirectorySet::const_iterator iterator = fDirectories.find(directory); 630 if (iterator != fDirectories.end()) { 631 if (_WatchFilesOnly()) { 632 // stat or attr notification for a directory 633 return; 634 } 635 BDirectory nodeDirectory(&nodeRef); 636 BEntry entry; 637 if (nodeDirectory.GetEntry(&entry) == B_OK) { 638 BPath path(&entry); 639 update.AddString("path", path.Path()); 640 } 641 } else { 642 if (_WatchFoldersOnly()) { 643 // this is bound to be a notification for a file 644 return; 645 } 646 FileEntry setEntry; 647 setEntry.ref.device = nodeRef.device; 648 setEntry.node = nodeRef.node; 649 // name does not need to be set, since it's not used for comparing 650 FileSet::const_iterator i = fFiles.find(setEntry); 651 if (i != fFiles.end()) { 652 BPath path(&(i->ref)); 653 update.AddString("path", path.Path()); 654 } 655 } 656 657 // This is in case the target is interested in figuring out which 658 // BPathMonitor::StartWatching() call the message is resulting from. 659 update.AddString("watched_path", fPath.Path()); 660 661 fTarget.SendMessage(&update); 662 } 663 664 665 status_t 666 PathHandler::_AddDirectory(BEntry& entry, bool notify) 667 { 668 WatchedDirectory directory; 669 status_t status = entry.GetNodeRef(&directory.node); 670 if (status != B_OK) 671 return status; 672 673 #ifdef TRACE_PATH_MONITOR 674 { 675 BPath path(&entry); 676 TRACE(" ADD DIRECTORY %s, %ld:%Ld\n", 677 path.Path(), directory.node.device, directory.node.node); 678 } 679 #endif 680 681 // check if we are already know this directory 682 683 // TODO: It should be possible to ommit this check if we know it 684 // can't be the case (for example when adding subfolders recursively, 685 // although in that case, the API user may still have added this folder 686 // independently, so for now, it should be the safest to perform this 687 // check in all cases.) 688 if (_HasDirectory(directory.node)) 689 return B_OK; 690 691 directory.contained = _IsContained(entry); 692 693 uint32 flags; 694 if (directory.contained) 695 flags = (fFlags & WATCH_NODE_FLAG_MASK) | B_WATCH_DIRECTORY; 696 else 697 flags = B_WATCH_DIRECTORY; 698 699 status = watch_node(&directory.node, flags, this); 700 if (status != B_OK) 701 return status; 702 703 fDirectories.insert(directory); 704 705 if (_WatchRecursively()) { 706 BDirectory dir(&directory.node); 707 while (dir.GetNextEntry(&entry) == B_OK) { 708 if (entry.IsDirectory()) { 709 // and here is the recursion: 710 if (_AddDirectory(entry, notify) != B_OK) 711 break; 712 } else if (!_WatchFoldersOnly()) { 713 if (_AddFile(entry, notify) != B_OK) 714 break; 715 } 716 } 717 } 718 719 #if 0 720 BEntry parent; 721 if (entry.GetParent(&parent) == B_OK 722 && !_IsContained(parent)) { 723 // TODO: remove parent from watched directories 724 } 725 #endif 726 return B_OK; 727 } 728 729 730 status_t 731 PathHandler::_AddDirectory(node_ref& nodeRef, bool notify) 732 { 733 BDirectory directory(&nodeRef); 734 status_t status = directory.InitCheck(); 735 if (status == B_OK) { 736 BEntry entry; 737 status = directory.GetEntry(&entry); 738 if (status == B_OK) 739 status = _AddDirectory(entry, notify); 740 } 741 742 return status; 743 } 744 745 746 status_t 747 PathHandler::_RemoveDirectory(const node_ref& nodeRef, ino_t directoryNode) 748 { 749 TRACE(" REMOVE DIRECTORY %ld:%Ld\n", nodeRef.device, nodeRef.node); 750 751 WatchedDirectory directory; 752 directory.node = nodeRef; 753 754 DirectorySet::iterator iterator = fDirectories.find(directory); 755 if (iterator == fDirectories.end()) 756 return B_ENTRY_NOT_FOUND; 757 758 watch_node(&directory.node, B_STOP_WATCHING, this); 759 760 node_ref directoryRef; 761 directoryRef.device = nodeRef.device; 762 directoryRef.node = directoryNode; 763 764 if (!_HasDirectory(directoryRef)) { 765 // we don't have the parent directory now, but we'll need it in order 766 // to find this directory again in case it's added again 767 if (_AddDirectory(directoryRef) != B_OK 768 && _GetClosest(fPath.Path(), false, directoryRef) == B_OK) 769 _AddDirectory(directoryRef); 770 } 771 772 fDirectories.erase(iterator); 773 774 // TODO: stop watching subdirectories and their files when in recursive 775 // mode! 776 777 return B_OK; 778 } 779 780 781 status_t 782 PathHandler::_RemoveDirectory(BEntry& entry, ino_t directoryNode) 783 { 784 node_ref nodeRef; 785 status_t status = entry.GetNodeRef(&nodeRef); 786 if (status != B_OK) 787 return status; 788 789 return _RemoveDirectory(nodeRef, directoryNode); 790 } 791 792 793 bool 794 PathHandler::_HasFile(const node_ref& nodeRef) const 795 { 796 FileEntry setEntry; 797 setEntry.ref.device = nodeRef.device; 798 setEntry.node = nodeRef.node; 799 // name does not need to be set, since it's not used for comparing 800 FileSet::const_iterator iterator = fFiles.find(setEntry); 801 return iterator != fFiles.end(); 802 } 803 804 805 status_t 806 PathHandler::_AddFile(BEntry& entry, bool notify) 807 { 808 if ((fFlags & (WATCH_NODE_FLAG_MASK & ~B_WATCH_DIRECTORY)) == 0) 809 return B_OK; 810 811 #ifdef TRACE_PATH_MONITOR 812 { 813 BPath path(&entry); 814 TRACE(" ADD FILE %s\n", path.Path()); 815 } 816 #endif 817 818 node_ref nodeRef; 819 status_t status = entry.GetNodeRef(&nodeRef); 820 if (status != B_OK) 821 return status; 822 823 // check if we already know this file 824 825 // TODO: It should be possible to omit this check if we know it 826 // can't be the case (for example when adding subfolders recursively, 827 // although in that case, the API user may still have added this file 828 // independently, so for now, it should be the safest to perform this 829 // check in all cases.) 830 if (_HasFile(nodeRef)) 831 return B_OK; 832 833 status = watch_node(&nodeRef, (fFlags & WATCH_NODE_FLAG_MASK), this); 834 if (status != B_OK) 835 return status; 836 837 FileEntry setEntry; 838 entry.GetRef(&setEntry.ref); 839 setEntry.node = nodeRef.node; 840 841 fFiles.insert(setEntry); 842 843 if (notify && _WatchFilesOnly()) { 844 // We also notify our target about new files if it's only interested 845 // in files; it won't be notified about new directories, so it cannot 846 // know when to search for them. 847 BMessage update; 848 update.AddInt32("opcode", B_ENTRY_CREATED); 849 update.AddInt32("device", nodeRef.device); 850 update.AddInt64("directory", setEntry.ref.directory); 851 update.AddString("name", setEntry.ref.name); 852 update.AddBool("added", true); 853 854 _NotifyTarget(&update, nodeRef); 855 } 856 857 return B_OK; 858 } 859 860 861 status_t 862 PathHandler::_RemoveFile(const node_ref& nodeRef) 863 { 864 TRACE(" REMOVE FILE %ld:%Ld\n", nodeRef.device, nodeRef.node); 865 866 FileEntry setEntry; 867 setEntry.ref.device = nodeRef.device; 868 setEntry.node = nodeRef.node; 869 // name does not need to be set, since it's not used for comparing 870 FileSet::iterator iterator = fFiles.find(setEntry); 871 if (iterator == fFiles.end()) 872 return B_ENTRY_NOT_FOUND; 873 874 watch_node(&nodeRef, B_STOP_WATCHING, this); 875 fFiles.erase(iterator); 876 return B_OK; 877 } 878 879 880 status_t 881 PathHandler::_RemoveFile(BEntry& entry) 882 { 883 node_ref nodeRef; 884 status_t status = entry.GetNodeRef(&nodeRef); 885 if (status != B_OK) 886 return status; 887 888 return _RemoveFile(nodeRef); 889 } 890 891 892 // #pragma mark - 893 894 895 BPathMonitor::BPathMonitor() 896 { 897 } 898 899 900 BPathMonitor::~BPathMonitor() 901 { 902 } 903 904 905 /*static*/ status_t 906 BPathMonitor::_InitLockerIfNeeded() 907 { 908 static vint32 lock = 0; 909 910 if (sLocker != NULL) 911 return B_OK; 912 913 while (sLocker == NULL) { 914 if (atomic_add(&lock, 1) == 0) { 915 sLocker = new (nothrow) BLocker("path monitor"); 916 TRACE("Create PathMonitor locker\n"); 917 if (sLocker == NULL) 918 return B_NO_MEMORY; 919 } 920 snooze(5000); 921 } 922 923 return B_OK; 924 } 925 926 927 /*static*/ status_t 928 BPathMonitor::_InitLooperIfNeeded() 929 { 930 static vint32 lock = 0; 931 932 if (sLooper != NULL) 933 return B_OK; 934 935 while (sLooper == NULL) { 936 if (atomic_add(&lock, 1) == 0) { 937 // first thread initializes the global looper 938 sLooper = new (nothrow) BLooper("PathMonitor looper"); 939 TRACE("Start PathMonitor looper\n"); 940 if (sLooper == NULL) 941 return B_NO_MEMORY; 942 thread_id thread = sLooper->Run(); 943 if (thread < B_OK) 944 return (status_t)thread; 945 } 946 snooze(5000); 947 } 948 949 return sLooper->Thread() >= 0 ? B_OK : B_ERROR; 950 } 951 952 953 /*static*/ status_t 954 BPathMonitor::StartWatching(const char* path, uint32 flags, BMessenger target) 955 { 956 TRACE("StartWatching(%s)\n", path); 957 958 status_t status = _InitLockerIfNeeded(); 959 if (status != B_OK) 960 return status; 961 962 // use the global looper for receiving node monitor notifications 963 status = _InitLooperIfNeeded(); 964 if (status < B_OK) 965 return status; 966 967 BAutolock _(sLocker); 968 969 WatcherMap::iterator iterator = sWatchers.find(target); 970 Watcher* watcher = NULL; 971 if (iterator != sWatchers.end()) 972 watcher = iterator->second; 973 974 PathHandler* handler = new (nothrow) PathHandler(path, flags, target, 975 sLooper); 976 if (handler == NULL) 977 return B_NO_MEMORY; 978 status = handler->InitCheck(); 979 if (status < B_OK) { 980 delete handler; 981 return status; 982 } 983 984 if (watcher == NULL) { 985 watcher = new (nothrow) BPrivate::Watcher; 986 if (watcher == NULL) { 987 delete handler; 988 return B_NO_MEMORY; 989 } 990 sWatchers[target] = watcher; 991 } 992 993 watcher->handlers[path] = handler; 994 return B_OK; 995 } 996 997 998 /*static*/ status_t 999 BPathMonitor::StopWatching(const char* path, BMessenger target) 1000 { 1001 if (sLocker == NULL) 1002 return B_NO_INIT; 1003 1004 TRACE("StopWatching(%s)\n", path); 1005 1006 BAutolock _(sLocker); 1007 1008 WatcherMap::iterator iterator = sWatchers.find(target); 1009 if (iterator == sWatchers.end()) 1010 return B_BAD_VALUE; 1011 1012 Watcher* watcher = iterator->second; 1013 HandlerMap::iterator i = watcher->handlers.find(path); 1014 1015 if (i == watcher->handlers.end()) 1016 return B_BAD_VALUE; 1017 1018 PathHandler* handler = i->second; 1019 watcher->handlers.erase(i); 1020 1021 handler->Quit(); 1022 1023 if (watcher->handlers.empty()) { 1024 sWatchers.erase(iterator); 1025 delete watcher; 1026 } 1027 1028 return B_OK; 1029 } 1030 1031 1032 /*static*/ status_t 1033 BPathMonitor::StopWatching(BMessenger target) 1034 { 1035 if (sLocker == NULL) 1036 return B_NO_INIT; 1037 1038 BAutolock _(sLocker); 1039 1040 WatcherMap::iterator iterator = sWatchers.find(target); 1041 if (iterator == sWatchers.end()) 1042 return B_BAD_VALUE; 1043 1044 Watcher* watcher = iterator->second; 1045 while (!watcher->handlers.empty()) { 1046 HandlerMap::iterator i = watcher->handlers.begin(); 1047 PathHandler* handler = i->second; 1048 watcher->handlers.erase(i); 1049 1050 handler->Quit(); 1051 } 1052 1053 sWatchers.erase(iterator); 1054 delete watcher; 1055 1056 return B_OK; 1057 } 1058 1059 } // namespace BPrivate 1060