1 /* 2 * Copyright 2007-2013, 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 * Ingo Weinhold, ingo_weinhold@gmx.de 9 */ 10 11 12 #include <PathMonitor.h> 13 14 #include <pthread.h> 15 #include <stdio.h> 16 17 #include <map> 18 #include <new> 19 #include <set> 20 21 #include <Autolock.h> 22 #include <Directory.h> 23 #include <Entry.h> 24 #include <Handler.h> 25 #include <Locker.h> 26 #include <Looper.h> 27 #include <Path.h> 28 #include <String.h> 29 30 #include <AutoDeleter.h> 31 #include <NotOwningEntryRef.h> 32 #include <ObjectList.h> 33 #include <util/OpenHashTable.h> 34 #include <util/SinglyLinkedList.h> 35 36 37 #undef TRACE 38 //#define TRACE_PATH_MONITOR 39 #ifdef TRACE_PATH_MONITOR 40 # define TRACE(...) debug_printf("BPathMonitor: " __VA_ARGS__) 41 #else 42 # define TRACE(...) ; 43 #endif 44 45 46 // TODO: Support symlink components in the path. 47 // TODO: Support mounting/unmounting of volumes in path components and within 48 // the watched path tree. 49 50 51 #define WATCH_NODE_FLAG_MASK 0x00ff 52 53 54 namespace { 55 56 57 struct Directory; 58 struct Node; 59 struct WatcherHashDefinition; 60 typedef BOpenHashTable<WatcherHashDefinition> WatcherMap; 61 62 63 static pthread_once_t sInitOnce = PTHREAD_ONCE_INIT; 64 static WatcherMap* sWatchers = NULL; 65 static BLocker* sLocker = NULL; 66 static BLooper* sLooper = NULL; 67 static BPathMonitor::BWatchingInterface* sDefaultWatchingInterface = NULL; 68 static BPathMonitor::BWatchingInterface* sWatchingInterface = NULL; 69 70 71 // #pragma mark - 72 73 74 /*! Returns empty path, if either \a parent or \a subPath is empty or an 75 allocation fails. 76 */ 77 static BString 78 make_path(const BString& parent, const char* subPath) 79 { 80 BString path = parent; 81 int32 length = path.Length(); 82 if (length == 0 || subPath[0] == '\0') 83 return BString(); 84 85 if (parent.ByteAt(length - 1) != '/') { 86 path << '/'; 87 if (path.Length() < ++length) 88 return BString(); 89 } 90 91 path << subPath; 92 if (path.Length() <= length) 93 return BString(); 94 return path; 95 } 96 97 98 // #pragma mark - Ancestor 99 100 101 class Ancestor { 102 public: 103 Ancestor(Ancestor* parent, const BString& path, size_t pathComponentOffset) 104 : 105 fParent(parent), 106 fChild(NULL), 107 fPath(path), 108 fEntryRef(-1, -1, fPath.String() + pathComponentOffset), 109 fNodeRef(), 110 fWatchingFlags(0), 111 fIsDirectory(false) 112 { 113 if (pathComponentOffset == 0) { 114 // must be "/" 115 fEntryRef.SetTo(-1, -1, "."); 116 } 117 118 if (fParent != NULL) 119 fParent->fChild = this; 120 } 121 122 Ancestor* Parent() const 123 { 124 return fParent; 125 } 126 127 Ancestor* Child() const 128 { 129 return fChild; 130 } 131 132 const BString& Path() const 133 { 134 return fPath; 135 } 136 137 const char* Name() const 138 { 139 return fEntryRef.name; 140 } 141 142 bool Exists() const 143 { 144 return fNodeRef.device >= 0; 145 } 146 147 const NotOwningEntryRef& EntryRef() const 148 { 149 return fEntryRef; 150 } 151 152 const node_ref& NodeRef() const 153 { 154 return fNodeRef; 155 } 156 157 bool IsDirectory() const 158 { 159 return fIsDirectory; 160 } 161 162 status_t StartWatching(uint32 pathFlags, BHandler* target) 163 { 164 // init entry ref 165 BEntry entry; 166 status_t error = entry.SetTo(fPath); 167 if (error != B_OK) 168 return error; 169 170 entry_ref entryRef; 171 error = entry.GetRef(&entryRef); 172 if (error != B_OK) 173 return error; 174 175 fEntryRef.device = entryRef.device; 176 fEntryRef.directory = entryRef.directory; 177 178 // init node ref 179 struct stat st; 180 error = entry.GetStat(&st); 181 if (error != B_OK) 182 return error == B_ENTRY_NOT_FOUND ? B_OK : error; 183 184 fNodeRef = node_ref(st.st_dev, st.st_ino); 185 fIsDirectory = S_ISDIR(st.st_mode); 186 187 // start watching 188 uint32 flags = fChild == NULL ? pathFlags : B_WATCH_DIRECTORY; 189 // In theory B_WATCH_NAME would suffice for all existing ancestors, 190 // plus B_WATCH_DIRECTORY for the parent of the first not existing 191 // ancestor. In practice this complicates the transitions when an 192 // ancestor is created/removed/moved. 193 if (flags != 0) { 194 error = sWatchingInterface->WatchNode(&fNodeRef, flags, target); 195 TRACE(" started to watch ancestor %p (\"%s\", %#" B_PRIx32 196 ") -> %s\n", this, Name(), flags, strerror(error)); 197 if (error != B_OK) 198 return error; 199 } 200 201 fWatchingFlags = flags; 202 return B_OK; 203 } 204 205 void StopWatching(BHandler* target) 206 { 207 // stop watching 208 if (fWatchingFlags != 0) { 209 sWatchingInterface->WatchNode(&fNodeRef, B_STOP_WATCHING, target); 210 fWatchingFlags = 0; 211 } 212 213 // uninitialize node and entry ref 214 fIsDirectory = false; 215 fNodeRef = node_ref(); 216 fEntryRef.SetDirectoryNodeRef(node_ref()); 217 } 218 219 Ancestor*& HashNext() 220 { 221 return fHashNext; 222 } 223 224 private: 225 Ancestor* fParent; 226 Ancestor* fChild; 227 Ancestor* fHashNext; 228 BString fPath; 229 NotOwningEntryRef fEntryRef; 230 node_ref fNodeRef; 231 uint32 fWatchingFlags; 232 bool fIsDirectory; 233 }; 234 235 236 // #pragma mark - AncestorMap 237 238 239 struct AncestorHashDefinition { 240 typedef node_ref KeyType; 241 typedef Ancestor ValueType; 242 243 size_t HashKey(const node_ref& key) const 244 { 245 return size_t(key.device ^ key.node); 246 } 247 248 size_t Hash(Ancestor* value) const 249 { 250 return HashKey(value->NodeRef()); 251 } 252 253 bool Compare(const node_ref& key, Ancestor* value) const 254 { 255 return key == value->NodeRef(); 256 } 257 258 Ancestor*& GetLink(Ancestor* value) const 259 { 260 return value->HashNext(); 261 } 262 }; 263 264 265 typedef BOpenHashTable<AncestorHashDefinition> AncestorMap; 266 267 268 // #pragma mark - Entry 269 270 271 class Entry : public SinglyLinkedListLinkImpl<Entry> { 272 public: 273 Entry(Directory* parent, const BString& name, ::Node* node) 274 : 275 fParent(parent), 276 fName(name), 277 fNode(node) 278 { 279 } 280 281 Directory* Parent() const 282 { 283 return fParent; 284 } 285 286 const BString& Name() const 287 { 288 return fName; 289 } 290 291 ::Node* Node() const 292 { 293 return fNode; 294 } 295 296 void SetNode(::Node* node) 297 { 298 fNode = node; 299 } 300 301 inline NotOwningEntryRef EntryRef() const; 302 303 Entry*& HashNext() 304 { 305 return fHashNext; 306 } 307 308 private: 309 Directory* fParent; 310 BString fName; 311 ::Node* fNode; 312 Entry* fHashNext; 313 }; 314 315 typedef SinglyLinkedList<Entry> EntryList; 316 317 318 // EntryMap 319 320 321 struct EntryHashDefinition { 322 typedef const char* KeyType; 323 typedef Entry ValueType; 324 325 size_t HashKey(const char* key) const 326 { 327 return BString::HashValue(key); 328 } 329 330 size_t Hash(Entry* value) const 331 { 332 return value->Name().HashValue(); 333 } 334 335 bool Compare(const char* key, Entry* value) const 336 { 337 return value->Name() == key; 338 } 339 340 Entry*& GetLink(Entry* value) const 341 { 342 return value->HashNext(); 343 } 344 }; 345 346 347 typedef BOpenHashTable<EntryHashDefinition> EntryMap; 348 349 350 // #pragma mark - Node 351 352 353 class Node { 354 public: 355 Node(const node_ref& nodeRef) 356 : 357 fNodeRef(nodeRef) 358 { 359 } 360 361 virtual ~Node() 362 { 363 } 364 365 virtual bool IsDirectory() const 366 { 367 return false; 368 } 369 370 virtual Directory* ToDirectory() 371 { 372 return NULL; 373 } 374 375 const node_ref& NodeRef() const 376 { 377 return fNodeRef; 378 } 379 380 const EntryList& Entries() const 381 { 382 return fEntries; 383 } 384 385 bool HasEntries() const 386 { 387 return !fEntries.IsEmpty(); 388 } 389 390 Entry* FirstNodeEntry() const 391 { 392 return fEntries.Head(); 393 } 394 395 bool IsOnlyNodeEntry(Entry* entry) const 396 { 397 return entry == fEntries.Head() && fEntries.GetNext(entry) == NULL; 398 } 399 400 void AddNodeEntry(Entry* entry) 401 { 402 fEntries.Add(entry); 403 } 404 405 void RemoveNodeEntry(Entry* entry) 406 { 407 fEntries.Remove(entry); 408 } 409 410 Node*& HashNext() 411 { 412 return fHashNext; 413 } 414 415 private: 416 node_ref fNodeRef; 417 EntryList fEntries; 418 Node* fHashNext; 419 }; 420 421 422 struct NodeHashDefinition { 423 typedef node_ref KeyType; 424 typedef Node ValueType; 425 426 size_t HashKey(const node_ref& key) const 427 { 428 return size_t(key.device ^ key.node); 429 } 430 431 size_t Hash(Node* value) const 432 { 433 return HashKey(value->NodeRef()); 434 } 435 436 bool Compare(const node_ref& key, Node* value) const 437 { 438 return key == value->NodeRef(); 439 } 440 441 Node*& GetLink(Node* value) const 442 { 443 return value->HashNext(); 444 } 445 }; 446 447 448 typedef BOpenHashTable<NodeHashDefinition> NodeMap; 449 450 451 // #pragma mark - Directory 452 453 454 class Directory : public Node { 455 public: 456 static Directory* Create(const node_ref& nodeRef) 457 { 458 Directory* directory = new(std::nothrow) Directory(nodeRef); 459 if (directory == NULL || directory->fEntries.Init() != B_OK) { 460 delete directory; 461 return NULL; 462 } 463 464 return directory; 465 } 466 467 virtual bool IsDirectory() const 468 { 469 return true; 470 } 471 472 virtual Directory* ToDirectory() 473 { 474 return this; 475 } 476 477 Entry* FindEntry(const char* name) const 478 { 479 return fEntries.Lookup(name); 480 } 481 482 Entry* CreateEntry(const BString& name, Node* node) 483 { 484 Entry* entry = new(std::nothrow) Entry(this, name, node); 485 if (entry == NULL || entry->Name().IsEmpty()) { 486 delete entry; 487 return NULL; 488 } 489 490 AddEntry(entry); 491 return entry; 492 } 493 494 void AddEntry(Entry* entry) 495 { 496 fEntries.Insert(entry); 497 } 498 499 void RemoveEntry(Entry* entry) 500 { 501 fEntries.Remove(entry); 502 } 503 504 EntryMap::Iterator GetEntryIterator() const 505 { 506 return fEntries.GetIterator(); 507 } 508 509 Entry* RemoveAllEntries() 510 { 511 return fEntries.Clear(true); 512 } 513 514 private: 515 Directory(const node_ref& nodeRef) 516 : 517 Node(nodeRef) 518 { 519 } 520 521 private: 522 EntryMap fEntries; 523 }; 524 525 526 // #pragma mark - Entry 527 528 529 inline NotOwningEntryRef 530 Entry::EntryRef() const 531 { 532 return NotOwningEntryRef(fParent->NodeRef(), fName); 533 } 534 535 536 // #pragma mark - PathHandler 537 538 539 class PathHandler : public BHandler { 540 public: 541 PathHandler(const char* path, uint32 flags, 542 const BMessenger& target, BLooper* looper); 543 virtual ~PathHandler(); 544 545 status_t InitCheck() const; 546 void Quit(); 547 548 const BString& OriginalPath() const 549 { return fOriginalPath; } 550 uint32 Flags() const { return fFlags; } 551 552 virtual void MessageReceived(BMessage* message); 553 554 PathHandler*& HashNext() { return fHashNext; } 555 556 private: 557 status_t _CreateAncestors(); 558 status_t _StartWatchingAncestors(Ancestor* ancestor, 559 bool notify); 560 void _StopWatchingAncestors(Ancestor* ancestor, 561 bool notify); 562 563 void _EntryCreated(BMessage* message); 564 void _EntryRemoved(BMessage* message); 565 void _EntryMoved(BMessage* message); 566 void _NodeChanged(BMessage* message); 567 568 bool _EntryCreated(const NotOwningEntryRef& entryRef, 569 const node_ref& nodeRef, bool isDirectory, 570 bool dryRun, bool notify, Entry** _entry); 571 bool _EntryRemoved(const NotOwningEntryRef& entryRef, 572 const node_ref& nodeRef, bool dryRun, 573 bool notify, Entry** _keepEntry); 574 575 bool _CheckDuplicateEntryNotification(int32 opcode, 576 const entry_ref& toEntryRef, 577 const node_ref& nodeRef, 578 const entry_ref* fromEntryRef = NULL); 579 void _UnsetDuplicateEntryNotification(); 580 581 Ancestor* _GetAncestor(const node_ref& nodeRef) const; 582 583 status_t _AddNode(const node_ref& nodeRef, 584 bool isDirectory, bool notify, 585 Entry* entry = NULL, Node** _node = NULL); 586 void _DeleteNode(Node* node, bool notify); 587 Node* _GetNode(const node_ref& nodeRef) const; 588 589 status_t _AddEntryIfNeeded(Directory* directory, 590 const char* name, const node_ref& nodeRef, 591 bool isDirectory, bool notify, 592 Entry** _entry = NULL); 593 void _DeleteEntry(Entry* entry, bool notify); 594 void _DeleteEntryAlreadyRemovedFromParent( 595 Entry* entry, bool notify); 596 597 void _NotifyFilesCreatedOrRemoved(Entry* entry, 598 int32 opcode) const; 599 void _NotifyEntryCreatedOrRemoved(Entry* entry, 600 int32 opcode) const; 601 void _NotifyEntryCreatedOrRemoved( 602 const entry_ref& entryRef, 603 const node_ref& nodeRef, const char* path, 604 bool isDirectory, int32 opcode) const; 605 void _NotifyEntryMoved(const entry_ref& fromEntryRef, 606 const entry_ref& toEntryRef, 607 const node_ref& nodeRef, 608 const char* fromPath, const char* path, 609 bool isDirectory, bool wasAdded, 610 bool wasRemoved) const; 611 void _NotifyTarget(BMessage& message, 612 const char* path) const; 613 614 BString _NodePath(const Node* node) const; 615 BString _EntryPath(const Entry* entry) const; 616 617 618 bool _WatchRecursively() const; 619 bool _WatchFilesOnly() const; 620 bool _WatchDirectoriesOnly() const; 621 622 private: 623 BMessenger fTarget; 624 uint32 fFlags; 625 status_t fStatus; 626 BString fOriginalPath; 627 BString fPath; 628 Ancestor* fRoot; 629 Ancestor* fBaseAncestor; 630 Node* fBaseNode; 631 AncestorMap fAncestors; 632 NodeMap fNodes; 633 PathHandler* fHashNext; 634 int32 fDuplicateEntryNotificationOpcode; 635 node_ref fDuplicateEntryNotificationNodeRef; 636 entry_ref fDuplicateEntryNotificationToEntryRef; 637 entry_ref fDuplicateEntryNotificationFromEntryRef; 638 }; 639 640 641 struct PathHandlerHashDefinition { 642 typedef const char* KeyType; 643 typedef PathHandler ValueType; 644 645 size_t HashKey(const char* key) const 646 { 647 return BString::HashValue(key); 648 } 649 650 size_t Hash(PathHandler* value) const 651 { 652 return value->OriginalPath().HashValue(); 653 } 654 655 bool Compare(const char* key, PathHandler* value) const 656 { 657 return key == value->OriginalPath(); 658 } 659 660 PathHandler*& GetLink(PathHandler* value) const 661 { 662 return value->HashNext(); 663 } 664 }; 665 666 667 typedef BOpenHashTable<PathHandlerHashDefinition> PathHandlerMap; 668 669 670 // #pragma mark - Watcher 671 672 673 struct Watcher : public PathHandlerMap { 674 static Watcher* Create(const BMessenger& target) 675 { 676 Watcher* watcher = new(std::nothrow) Watcher(target); 677 if (watcher == NULL || watcher->Init() != B_OK) { 678 delete watcher; 679 return NULL; 680 } 681 return watcher; 682 } 683 684 const BMessenger& Target() const 685 { 686 return fTarget; 687 } 688 689 Watcher*& HashNext() 690 { 691 return fHashNext; 692 } 693 694 private: 695 Watcher(const BMessenger& target) 696 : 697 fTarget(target) 698 { 699 } 700 701 private: 702 BMessenger fTarget; 703 Watcher* fHashNext; 704 }; 705 706 707 struct WatcherHashDefinition { 708 typedef BMessenger KeyType; 709 typedef Watcher ValueType; 710 711 size_t HashKey(const BMessenger& key) const 712 { 713 return key.HashValue(); 714 } 715 716 size_t Hash(Watcher* value) const 717 { 718 return HashKey(value->Target()); 719 } 720 721 bool Compare(const BMessenger& key, Watcher* value) const 722 { 723 return key == value->Target(); 724 } 725 726 Watcher*& GetLink(Watcher* value) const 727 { 728 return value->HashNext(); 729 } 730 }; 731 732 733 // #pragma mark - PathHandler 734 735 736 PathHandler::PathHandler(const char* path, uint32 flags, 737 const BMessenger& target, BLooper* looper) 738 : 739 BHandler(path), 740 fTarget(target), 741 fFlags(flags), 742 fStatus(B_OK), 743 fOriginalPath(path), 744 fPath(), 745 fRoot(NULL), 746 fBaseAncestor(NULL), 747 fBaseNode(NULL), 748 fAncestors(), 749 fNodes() 750 { 751 TRACE("%p->PathHandler::PathHandler(\"%s\", %#" B_PRIx32 ")\n", this, path, 752 flags); 753 754 _UnsetDuplicateEntryNotification(); 755 756 fStatus = fAncestors.Init(); 757 if (fStatus != B_OK) 758 return; 759 760 fStatus = fNodes.Init(); 761 if (fStatus != B_OK) 762 return; 763 764 // normalize the flags 765 if ((fFlags & B_WATCH_RECURSIVELY) != 0) { 766 // We add B_WATCH_NAME and B_WATCH_DIRECTORY as needed, so clear them 767 // here. 768 fFlags &= ~uint32(B_WATCH_NAME | B_WATCH_DIRECTORY); 769 } else { 770 // The B_WATCH_*_ONLY flags are only valid for the recursive mode. 771 // B_WATCH_NAME is implied (we watch the parent directory). 772 fFlags &= ~uint32(B_WATCH_FILES_ONLY | B_WATCH_DIRECTORIES_ONLY 773 | B_WATCH_NAME); 774 } 775 776 // Normalize the path a bit. We can't use BPath, as it may really normalize 777 // the path, i.e. resolve symlinks and such, which may cause us to monitor 778 // the wrong path. We want some normalization, though: 779 // * relative -> absolute path 780 // * fold duplicate '/'s 781 // * omit "." components 782 // * fail when encountering ".." components 783 784 // make absolute 785 BString normalizedPath; 786 if (path[0] == '/') { 787 normalizedPath = "/"; 788 path++; 789 } else 790 normalizedPath = BPath(".").Path(); 791 if (normalizedPath.IsEmpty()) { 792 fStatus = B_NO_MEMORY; 793 return; 794 } 795 796 // parse path components 797 const char* pathEnd = path + strlen(path); 798 for (;;) { 799 // skip '/'s 800 while (path[0] == '/') 801 path++; 802 if (path == pathEnd) 803 break; 804 805 const char* componentEnd = strchr(path, '/'); 806 if (componentEnd == NULL) 807 componentEnd = pathEnd; 808 size_t componentLength = componentEnd - path; 809 810 // handle ".' and ".." 811 if (path[0] == '.') { 812 if (componentLength == 1) { 813 path = componentEnd; 814 continue; 815 } 816 if (componentLength == 2 && path[1] == '.') { 817 fStatus = B_BAD_VALUE; 818 return; 819 } 820 } 821 822 int32 normalizedPathLength = normalizedPath.Length(); 823 if (normalizedPath.ByteAt(normalizedPathLength - 1) != '/') { 824 normalizedPath << '/'; 825 normalizedPathLength++; 826 } 827 normalizedPath.Append(path, componentEnd - path); 828 normalizedPathLength += int32(componentEnd - path); 829 830 if (normalizedPath.Length() != normalizedPathLength) { 831 fStatus = B_NO_MEMORY; 832 return; 833 } 834 835 path = componentEnd; 836 } 837 838 fPath = normalizedPath; 839 840 // Create the Ancestor objects -- they correspond to the path components and 841 // are used for watching changes that affect the entries on the path. 842 fStatus = _CreateAncestors(); 843 if (fStatus != B_OK) 844 return; 845 846 // add ourselves to the looper 847 if (!looper->Lock()) 848 debugger("PathHandler: failed to lock the looper"); 849 looper->AddHandler(this); 850 looper->Unlock(); 851 852 // start watching 853 fStatus = _StartWatchingAncestors(fRoot, false); 854 if (fStatus != B_OK) 855 return; 856 } 857 858 859 PathHandler::~PathHandler() 860 { 861 TRACE("%p->PathHandler::~PathHandler(\"%s\", %#" B_PRIx32 ")\n", this, 862 fPath.String(), fFlags); 863 864 if (fBaseNode != NULL) 865 _DeleteNode(fBaseNode, false); 866 867 while (fRoot != NULL) { 868 Ancestor* nextAncestor = fRoot->Child(); 869 delete fRoot; 870 fRoot = nextAncestor; 871 } 872 } 873 874 875 status_t 876 PathHandler::InitCheck() const 877 { 878 return fStatus; 879 } 880 881 882 void 883 PathHandler::Quit() 884 { 885 if (sLooper->Lock()) { 886 TRACE("%p->PathHandler::Quit()\n", this); 887 sWatchingInterface->StopWatching(this); 888 sLooper->RemoveHandler(this); 889 sLooper->Unlock(); 890 } else 891 TRACE("%p->PathHandler::Quit(): failed to lock looper\n", this); 892 delete this; 893 } 894 895 896 void 897 PathHandler::MessageReceived(BMessage* message) 898 { 899 switch (message->what) { 900 case B_NODE_MONITOR: 901 { 902 int32 opcode; 903 if (message->FindInt32("opcode", &opcode) != B_OK) 904 return; 905 906 switch (opcode) { 907 case B_ENTRY_CREATED: 908 _EntryCreated(message); 909 break; 910 911 case B_ENTRY_REMOVED: 912 _EntryRemoved(message); 913 break; 914 915 case B_ENTRY_MOVED: 916 _EntryMoved(message); 917 break; 918 919 default: 920 _UnsetDuplicateEntryNotification(); 921 _NodeChanged(message); 922 break; 923 } 924 925 break; 926 } 927 928 default: 929 BHandler::MessageReceived(message); 930 break; 931 } 932 } 933 934 935 status_t 936 PathHandler::_CreateAncestors() 937 { 938 TRACE("%p->PathHandler::_CreateAncestors()\n", this); 939 940 // create the Ancestor objects 941 const char* path = fPath.String(); 942 const char* pathEnd = path + fPath.Length(); 943 const char* component = path; 944 945 Ancestor* ancestor = NULL; 946 947 while (component < pathEnd) { 948 const char* componentEnd = component == path 949 ? component + 1 : strchr(component, '/'); 950 if (componentEnd == NULL) 951 componentEnd = pathEnd; 952 953 BString ancestorPath(path, componentEnd - path); 954 if (ancestorPath.IsEmpty()) 955 return B_NO_MEMORY; 956 957 ancestor = new(std::nothrow) Ancestor(ancestor, ancestorPath, 958 component - path); 959 TRACE(" created ancestor %p (\"%s\" / \"%s\")\n", ancestor, 960 ancestor->Path().String(), ancestor->Name()); 961 if (ancestor == NULL) 962 return B_NO_MEMORY; 963 964 if (fRoot == NULL) 965 fRoot = ancestor; 966 967 component = componentEnd[0] == '/' ? componentEnd + 1 : componentEnd; 968 } 969 970 fBaseAncestor = ancestor; 971 972 return B_OK; 973 } 974 975 976 status_t 977 PathHandler::_StartWatchingAncestors(Ancestor* startAncestor, bool notify) 978 { 979 TRACE("%p->PathHandler::_StartWatchingAncestors(%p, %d)\n", this, 980 startAncestor, notify); 981 982 // The watch flags for the path (if it exists). Recursively implies 983 // directory, since we need to watch the entries. 984 uint32 watchFlags = (fFlags & WATCH_NODE_FLAG_MASK) 985 | (_WatchRecursively() ? B_WATCH_DIRECTORY : 0); 986 987 for (Ancestor* ancestor = startAncestor; ancestor != NULL; 988 ancestor = ancestor->Child()) { 989 status_t error = ancestor->StartWatching(watchFlags, this); 990 if (error != B_OK) 991 return error; 992 993 if (!ancestor->Exists()) { 994 TRACE(" -> ancestor doesn't exist\n"); 995 break; 996 } 997 998 fAncestors.Insert(ancestor); 999 } 1000 1001 if (!fBaseAncestor->Exists()) 1002 return B_OK; 1003 1004 if (notify) { 1005 _NotifyEntryCreatedOrRemoved(fBaseAncestor->EntryRef(), 1006 fBaseAncestor->NodeRef(), fPath, fBaseAncestor->IsDirectory(), 1007 B_ENTRY_CREATED); 1008 } 1009 1010 if (!_WatchRecursively()) 1011 return B_OK; 1012 1013 status_t error = _AddNode(fBaseAncestor->NodeRef(), 1014 fBaseAncestor->IsDirectory(), notify && _WatchFilesOnly(), NULL, 1015 &fBaseNode); 1016 if (error != B_OK) 1017 return error; 1018 1019 return B_OK; 1020 } 1021 1022 1023 void 1024 PathHandler::_StopWatchingAncestors(Ancestor* ancestor, bool notify) 1025 { 1026 // stop watching the tree below path 1027 if (fBaseNode != NULL) { 1028 _DeleteNode(fBaseNode, notify && _WatchFilesOnly()); 1029 fBaseNode = NULL; 1030 } 1031 1032 if (notify && fBaseAncestor->Exists() 1033 && (fBaseAncestor->IsDirectory() 1034 ? !_WatchFilesOnly() : !_WatchDirectoriesOnly())) { 1035 _NotifyEntryCreatedOrRemoved(fBaseAncestor->EntryRef(), 1036 fBaseAncestor->NodeRef(), fPath, fBaseAncestor->IsDirectory(), 1037 B_ENTRY_REMOVED); 1038 } 1039 1040 // stop watching the ancestors and uninitialize their entries 1041 for (; ancestor != NULL; ancestor = ancestor->Child()) { 1042 if (ancestor->Exists()) 1043 fAncestors.Remove(ancestor); 1044 ancestor->StopWatching(this); 1045 } 1046 } 1047 1048 1049 void 1050 PathHandler::_EntryCreated(BMessage* message) 1051 { 1052 // TODO: Unless we're watching files only, we might want to forward (some 1053 // of) the messages that don't agree with our model, since our client 1054 // maintains its model at a different time and the notification might be 1055 // necessary to keep it up-to-date. E.g. consider the following case: 1056 // 1. a directory is created 1057 // 2. a file is created in the directory 1058 // 3. the file is removed from the directory 1059 // If we get the notification after 1. and before 2., we pass it on to the 1060 // client, which may get it after 2. and before 3., thus seeing the file. 1061 // If we then get the entry-created notification after 3., we don't see the 1062 // file anymore and ignore the notification as well as the following 1063 // entry-removed notification. That is the client will never know that the 1064 // file has been removed. This can only happen in recursive mode. Otherwise 1065 // (and with B_WATCH_DIRECTORY) we just pass on all notifications. 1066 // A possible solution could be to just create a zombie entry and pass on 1067 // the entry-created notification. We wouldn't be able to adhere to the 1068 // B_WATCH_FILES_ONLY/B_WATCH_DIRECTORIES_ONLY flags, but that should be 1069 // acceptable. Either the client hasn't seen the entry either -- then it 1070 // doesn't matter -- or it likely has ignored a not matching entry anyway. 1071 1072 NotOwningEntryRef entryRef; 1073 node_ref nodeRef; 1074 1075 if (message->FindInt32("device", &nodeRef.device) != B_OK 1076 || message->FindInt64("node", &nodeRef.node) != B_OK 1077 || message->FindInt64("directory", &entryRef.directory) != B_OK 1078 || message->FindString("name", (const char**)&entryRef.name) != B_OK) { 1079 return; 1080 } 1081 entryRef.device = nodeRef.device; 1082 1083 if (_CheckDuplicateEntryNotification(B_ENTRY_CREATED, entryRef, nodeRef)) 1084 return; 1085 1086 TRACE("%p->PathHandler::_EntryCreated(): entry: %" B_PRIdDEV ":%" B_PRIdINO 1087 ":\"%s\", node: %" B_PRIdDEV ":%" B_PRIdINO "\n", this, entryRef.device, 1088 entryRef.directory, entryRef.name, nodeRef.device, nodeRef.node); 1089 1090 BEntry entry; 1091 struct stat st; 1092 if (entry.SetTo(&entryRef) != B_OK || entry.GetStat(&st) != B_OK 1093 || nodeRef != node_ref(st.st_dev, st.st_ino)) { 1094 return; 1095 } 1096 1097 _EntryCreated(entryRef, nodeRef, S_ISDIR(st.st_mode), false, true, NULL); 1098 } 1099 1100 1101 void 1102 PathHandler::_EntryRemoved(BMessage* message) 1103 { 1104 NotOwningEntryRef entryRef; 1105 node_ref nodeRef; 1106 1107 if (message->FindInt32("device", &nodeRef.device) != B_OK 1108 || message->FindInt64("node", &nodeRef.node) != B_OK 1109 || message->FindInt64("directory", &entryRef.directory) != B_OK 1110 || message->FindString("name", (const char**)&entryRef.name) != B_OK) { 1111 return; 1112 } 1113 entryRef.device = nodeRef.device; 1114 1115 if (_CheckDuplicateEntryNotification(B_ENTRY_REMOVED, entryRef, nodeRef)) 1116 return; 1117 1118 TRACE("%p->PathHandler::_EntryRemoved(): entry: %" B_PRIdDEV ":%" B_PRIdINO 1119 ":\"%s\", node: %" B_PRIdDEV ":%" B_PRIdINO "\n", this, entryRef.device, 1120 entryRef.directory, entryRef.name, nodeRef.device, nodeRef.node); 1121 1122 _EntryRemoved(entryRef, nodeRef, false, true, NULL); 1123 } 1124 1125 1126 void 1127 PathHandler::_EntryMoved(BMessage* message) 1128 { 1129 NotOwningEntryRef fromEntryRef; 1130 NotOwningEntryRef toEntryRef; 1131 node_ref nodeRef; 1132 1133 if (message->FindInt32("node device", &nodeRef.device) != B_OK 1134 || message->FindInt64("node", &nodeRef.node) != B_OK 1135 || message->FindInt32("device", &fromEntryRef.device) != B_OK 1136 || message->FindInt64("from directory", &fromEntryRef.directory) != B_OK 1137 || message->FindInt64("to directory", &toEntryRef.directory) != B_OK 1138 || message->FindString("from name", (const char**)&fromEntryRef.name) 1139 != B_OK 1140 || message->FindString("name", (const char**)&toEntryRef.name) 1141 != B_OK) { 1142 return; 1143 } 1144 toEntryRef.device = fromEntryRef.device; 1145 1146 if (_CheckDuplicateEntryNotification(B_ENTRY_MOVED, toEntryRef, nodeRef, 1147 &fromEntryRef)) { 1148 return; 1149 } 1150 1151 TRACE("%p->PathHandler::_EntryMoved(): entry: %" B_PRIdDEV ":%" B_PRIdINO 1152 ":\"%s\" -> %" B_PRIdDEV ":%" B_PRIdINO ":\"%s\", node: %" B_PRIdDEV 1153 ":%" B_PRIdINO "\n", this, fromEntryRef.device, fromEntryRef.directory, 1154 fromEntryRef.name, toEntryRef.device, toEntryRef.directory, 1155 toEntryRef.name, nodeRef.device, nodeRef.node); 1156 1157 BEntry entry; 1158 struct stat st; 1159 if (entry.SetTo(&toEntryRef) != B_OK || entry.GetStat(&st) != B_OK 1160 || nodeRef != node_ref(st.st_dev, st.st_ino)) { 1161 _EntryRemoved(fromEntryRef, nodeRef, false, true, NULL); 1162 return; 1163 } 1164 bool isDirectory = S_ISDIR(st.st_mode); 1165 1166 Ancestor* fromAncestor = _GetAncestor(fromEntryRef.DirectoryNodeRef()); 1167 Ancestor* toAncestor = _GetAncestor(toEntryRef.DirectoryNodeRef()); 1168 1169 if (_WatchRecursively()) { 1170 Node* fromDirectoryNode = _GetNode(fromEntryRef.DirectoryNodeRef()); 1171 Node* toDirectoryNode = _GetNode(toEntryRef.DirectoryNodeRef()); 1172 if (fromDirectoryNode != NULL || toDirectoryNode != NULL) { 1173 // Check whether _EntryRemoved()/_EntryCreated() can handle the 1174 // respective entry regularly (i.e. don't encounter an out-of-sync 1175 // issue) or don't need to be called at all (entry outside the 1176 // monitored tree). 1177 if ((fromDirectoryNode == NULL 1178 || _EntryRemoved(fromEntryRef, nodeRef, true, false, NULL)) 1179 && (toDirectoryNode == NULL 1180 || _EntryCreated(toEntryRef, nodeRef, isDirectory, true, 1181 false, NULL))) { 1182 // The entries can be handled regularly. We delegate the work to 1183 // _EntryRemoved() and _EntryCreated() and only handle the 1184 // notification ourselves. 1185 1186 // handle removed 1187 Entry* removedEntry = NULL; 1188 if (fromDirectoryNode != NULL) { 1189 _EntryRemoved(fromEntryRef, nodeRef, false, false, 1190 &removedEntry); 1191 } 1192 1193 // handle created 1194 Entry* createdEntry = NULL; 1195 if (toDirectoryNode != NULL) { 1196 _EntryCreated(toEntryRef, nodeRef, isDirectory, false, 1197 false, &createdEntry); 1198 } 1199 1200 // notify 1201 if (_WatchFilesOnly() && isDirectory) { 1202 // recursively iterate through the removed and created 1203 // hierarchy and send notifications for the files 1204 if (removedEntry != NULL) { 1205 _NotifyFilesCreatedOrRemoved(removedEntry, 1206 B_ENTRY_REMOVED); 1207 } 1208 1209 if (createdEntry != NULL) { 1210 _NotifyFilesCreatedOrRemoved(createdEntry, 1211 B_ENTRY_CREATED); 1212 } 1213 } else { 1214 BString fromPath; 1215 if (fromDirectoryNode != NULL) { 1216 fromPath = make_path(_NodePath(fromDirectoryNode), 1217 fromEntryRef.name); 1218 } 1219 1220 BString path; 1221 if (toDirectoryNode != NULL) { 1222 path = make_path(_NodePath(toDirectoryNode), 1223 toEntryRef.name); 1224 } 1225 1226 _NotifyEntryMoved(fromEntryRef, toEntryRef, nodeRef, 1227 fromPath, path, isDirectory, fromDirectoryNode == NULL, 1228 toDirectoryNode == NULL); 1229 } 1230 1231 if (removedEntry != NULL) 1232 _DeleteEntry(removedEntry, false); 1233 } else { 1234 // The entries can't be handled regularly. We delegate all the 1235 // work to _EntryRemoved() and _EntryCreated(). This will 1236 // generate separate entry-removed and entry-created 1237 // notifications. 1238 1239 // handle removed 1240 if (fromDirectoryNode != NULL) 1241 _EntryRemoved(fromEntryRef, nodeRef, false, true, NULL); 1242 1243 // handle created 1244 if (toDirectoryNode != NULL) { 1245 _EntryCreated(toEntryRef, nodeRef, isDirectory, false, true, 1246 NULL); 1247 } 1248 } 1249 1250 return; 1251 } 1252 1253 if (fromAncestor == fBaseAncestor || toAncestor == fBaseAncestor) { 1254 // That should never happen, as we should have found a matching 1255 // directory node in this case. 1256 #ifdef DEBUG 1257 debugger("path ancestor exists, but doesn't have a directory"); 1258 // Could actually be an out-of-memory situation, if we simply failed 1259 // to create the directory earlier. 1260 #endif 1261 _StopWatchingAncestors(fRoot, false); 1262 _StartWatchingAncestors(fRoot, false); 1263 return; 1264 } 1265 } else { 1266 // Non-recursive mode: This notification is only of interest to us, if 1267 // it is either a move into/within/out of the path and B_WATCH_DIRECTORY 1268 // is set, or an ancestor might be affected. 1269 if (fromAncestor == NULL && toAncestor == NULL) 1270 return; 1271 1272 if (fromAncestor == fBaseAncestor || toAncestor == fBaseAncestor) { 1273 if ((fFlags & B_WATCH_DIRECTORY) != 0) { 1274 BString fromPath; 1275 if (fromAncestor == fBaseAncestor) 1276 fromPath = make_path(fPath, fromEntryRef.name); 1277 1278 BString path; 1279 if (toAncestor == fBaseAncestor) 1280 path = make_path(fPath, toEntryRef.name); 1281 1282 _NotifyEntryMoved(fromEntryRef, toEntryRef, nodeRef, 1283 fromPath, path, isDirectory, fromAncestor == NULL, 1284 toAncestor == NULL); 1285 } 1286 return; 1287 } 1288 } 1289 1290 if (fromAncestor == NULL && toAncestor == NULL) 1291 return; 1292 1293 if (fromAncestor == NULL) { 1294 _EntryCreated(toEntryRef, nodeRef, isDirectory, false, true, NULL); 1295 return; 1296 } 1297 1298 if (toAncestor == NULL) { 1299 _EntryRemoved(fromEntryRef, nodeRef, false, true, NULL); 1300 return; 1301 } 1302 1303 // An entry was moved in a true ancestor directory or between true ancestor 1304 // directories. Unless the moved entry was or becomes our base ancestor, we 1305 // let _EntryRemoved() and _EntryCreated() handle it. 1306 bool fromIsBase = fromAncestor == fBaseAncestor->Parent() 1307 && strcmp(fromEntryRef.name, fBaseAncestor->Name()) == 0; 1308 bool toIsBase = toAncestor == fBaseAncestor->Parent() 1309 && strcmp(toEntryRef.name, fBaseAncestor->Name()) == 0; 1310 if (fromIsBase || toIsBase) { 1311 // This might be a duplicate notification. Check whether our model 1312 // already reflects the change. Otherwise stop/start watching the base 1313 // ancestor as required. 1314 bool notifyFilesRecursively = _WatchFilesOnly() && isDirectory; 1315 if (fromIsBase) { 1316 if (!fBaseAncestor->Exists()) 1317 return; 1318 _StopWatchingAncestors(fBaseAncestor, notifyFilesRecursively); 1319 } else { 1320 if (fBaseAncestor->Exists()) { 1321 if (fBaseAncestor->NodeRef() == nodeRef 1322 && isDirectory == fBaseAncestor->IsDirectory()) { 1323 return; 1324 } 1325 1326 // We're out of sync with reality. 1327 _StopWatchingAncestors(fBaseAncestor, true); 1328 _StartWatchingAncestors(fBaseAncestor, true); 1329 return; 1330 } 1331 1332 _StartWatchingAncestors(fBaseAncestor, notifyFilesRecursively); 1333 } 1334 1335 if (!notifyFilesRecursively) { 1336 _NotifyEntryMoved(fromEntryRef, toEntryRef, nodeRef, 1337 fromIsBase ? fPath.String() : NULL, 1338 toIsBase ? fPath.String() : NULL, 1339 isDirectory, toIsBase, fromIsBase); 1340 } 1341 return; 1342 } 1343 1344 _EntryRemoved(fromEntryRef, nodeRef, false, true, NULL); 1345 _EntryCreated(toEntryRef, nodeRef, isDirectory, false, true, NULL); 1346 } 1347 1348 1349 void 1350 PathHandler::_NodeChanged(BMessage* message) 1351 { 1352 node_ref nodeRef; 1353 1354 if (message->FindInt32("device", &nodeRef.device) != B_OK 1355 || message->FindInt64("node", &nodeRef.node) != B_OK) { 1356 return; 1357 } 1358 1359 TRACE("%p->PathHandler::_NodeChanged(): node: %" B_PRIdDEV ":%" B_PRIdINO 1360 ", %s%s\n", this, nodeRef.device, nodeRef.node, 1361 message->GetInt32("opcode", B_STAT_CHANGED) == B_ATTR_CHANGED 1362 ? "attribute: " : "stat", 1363 message->GetInt32("opcode", B_STAT_CHANGED) == B_ATTR_CHANGED 1364 ? message->GetString("attr", "") : ""); 1365 1366 bool isDirectory = false; 1367 BString path; 1368 if (Ancestor* ancestor = _GetAncestor(nodeRef)) { 1369 if (ancestor != fBaseAncestor) 1370 return; 1371 isDirectory = ancestor->IsDirectory(); 1372 path = fPath; 1373 } else if (Node* node = _GetNode(nodeRef)) { 1374 isDirectory = node->IsDirectory(); 1375 path = _NodePath(node); 1376 } else 1377 return; 1378 1379 if (isDirectory ? _WatchFilesOnly() : _WatchDirectoriesOnly()) 1380 return; 1381 1382 _NotifyTarget(*message, path); 1383 } 1384 1385 1386 bool 1387 PathHandler::_EntryCreated(const NotOwningEntryRef& entryRef, 1388 const node_ref& nodeRef, bool isDirectory, bool dryRun, bool notify, 1389 Entry** _entry) 1390 { 1391 if (_entry != NULL) 1392 *_entry = NULL; 1393 1394 Ancestor* ancestor = _GetAncestor(nodeRef); 1395 if (ancestor != NULL) { 1396 if (isDirectory == ancestor->IsDirectory() 1397 && entryRef == ancestor->EntryRef()) { 1398 // just a duplicate notification 1399 TRACE(" -> we already know the ancestor\n"); 1400 return true; 1401 } 1402 1403 struct stat ancestorStat; 1404 if (BEntry(&ancestor->EntryRef()).GetStat(&ancestorStat) == B_OK 1405 && node_ref(ancestorStat.st_dev, ancestorStat.st_ino) 1406 == ancestor->NodeRef() 1407 && S_ISDIR(ancestorStat.st_mode) == ancestor->IsDirectory()) { 1408 // Our information for the ancestor is up-to-date, so ignore the 1409 // notification. 1410 TRACE(" -> we know a different ancestor, but our info is " 1411 "up-to-date\n"); 1412 return true; 1413 } 1414 1415 // We're out of sync with reality. 1416 TRACE(" -> ancestor mismatch -> resyncing\n"); 1417 if (!dryRun) { 1418 _StopWatchingAncestors(ancestor, true); 1419 _StartWatchingAncestors(ancestor, true); 1420 } 1421 return false; 1422 } 1423 1424 ancestor = _GetAncestor(entryRef.DirectoryNodeRef()); 1425 if (ancestor != NULL) { 1426 if (ancestor != fBaseAncestor) { 1427 // The directory is a true ancestor -- the notification is only of 1428 // interest, if the entry matches the child ancestor. 1429 Ancestor* childAncestor = ancestor->Child(); 1430 if (strcmp(entryRef.name, childAncestor->Name()) != 0) { 1431 TRACE(" -> not an ancestor entry we're interested in " 1432 "(\"%s\")\n", childAncestor->Name()); 1433 return true; 1434 } 1435 1436 if (!dryRun) { 1437 if (childAncestor->Exists()) { 1438 TRACE(" ancestor entry mismatch -> resyncing\n"); 1439 // We're out of sync with reality -- the new entry refers to 1440 // a different node. 1441 _StopWatchingAncestors(childAncestor, true); 1442 } 1443 1444 TRACE(" -> starting to watch newly appeared ancestor\n"); 1445 _StartWatchingAncestors(childAncestor, true); 1446 } 1447 return false; 1448 } 1449 1450 // The directory is our path. If watching recursively, just fall 1451 // through. Otherwise, we want to pass on the notification, if directory 1452 // watching is enabled. 1453 if (!_WatchRecursively()) { 1454 if ((fFlags & B_WATCH_DIRECTORY) != 0) { 1455 _NotifyEntryCreatedOrRemoved(entryRef, nodeRef, 1456 make_path(fPath, entryRef.name), isDirectory, 1457 B_ENTRY_CREATED); 1458 } 1459 return true; 1460 } 1461 } 1462 1463 if (!_WatchRecursively()) { 1464 // That shouldn't happen, since we only watch the ancestors in this 1465 // case. 1466 return true; 1467 } 1468 1469 Node* directoryNode = _GetNode(entryRef.DirectoryNodeRef()); 1470 if (directoryNode == NULL) 1471 return true; 1472 1473 Directory* directory = directoryNode->ToDirectory(); 1474 if (directory == NULL) { 1475 // We're out of sync with reality. 1476 if (!dryRun) { 1477 if (Entry* nodeEntry = directory->FirstNodeEntry()) { 1478 // remove the entry that is in the way and re-add the proper 1479 // entry 1480 NotOwningEntryRef directoryEntryRef = nodeEntry->EntryRef(); 1481 BString directoryName = nodeEntry->Name(); 1482 _DeleteEntry(nodeEntry, true); 1483 _EntryCreated(directoryEntryRef, entryRef.DirectoryNodeRef(), 1484 true, false, notify, NULL); 1485 } else { 1486 // It's either the base node or something's severely fishy. 1487 // Resync the whole path. 1488 _StopWatchingAncestors(fBaseAncestor, true); 1489 _StartWatchingAncestors(fBaseAncestor, true); 1490 } 1491 } 1492 1493 return false; 1494 } 1495 1496 // Check, if there's a colliding entry. 1497 if (Entry* nodeEntry = directory->FindEntry(entryRef.name)) { 1498 Node* entryNode = nodeEntry->Node(); 1499 if (entryNode->NodeRef() == nodeRef) 1500 return true; 1501 1502 // We're out of sync with reality -- the new entry refers to a different 1503 // node. 1504 _DeleteEntry(nodeEntry, true); 1505 } 1506 1507 if (dryRun) 1508 return true; 1509 1510 _AddEntryIfNeeded(directory, entryRef.name, nodeRef, isDirectory, notify, 1511 _entry); 1512 return true; 1513 } 1514 1515 1516 bool 1517 PathHandler::_EntryRemoved(const NotOwningEntryRef& entryRef, 1518 const node_ref& nodeRef, bool dryRun, bool notify, Entry** _keepEntry) 1519 { 1520 if (_keepEntry != NULL) 1521 *_keepEntry = NULL; 1522 1523 Ancestor* ancestor = _GetAncestor(nodeRef); 1524 if (ancestor != NULL) { 1525 // The node is an ancestor. If this is a true match, stop watching the 1526 // ancestor. 1527 if (!ancestor->Exists()) 1528 return true; 1529 1530 if (entryRef != ancestor->EntryRef()) { 1531 // We might be out of sync with reality -- the new entry refers to a 1532 // different node. 1533 struct stat ancestorStat; 1534 if (BEntry(&ancestor->EntryRef()).GetStat(&ancestorStat) != B_OK) { 1535 if (!dryRun) 1536 _StopWatchingAncestors(ancestor, true); 1537 return false; 1538 } 1539 1540 if (node_ref(ancestorStat.st_dev, ancestorStat.st_ino) 1541 != ancestor->NodeRef() 1542 || S_ISDIR(ancestorStat.st_mode) != ancestor->IsDirectory()) { 1543 if (!dryRun) { 1544 _StopWatchingAncestors(ancestor, true); 1545 _StartWatchingAncestors(ancestor, true); 1546 } 1547 return false; 1548 } 1549 return true; 1550 } 1551 1552 if (!dryRun) 1553 _StopWatchingAncestors(ancestor, true); 1554 return false; 1555 } 1556 1557 ancestor = _GetAncestor(entryRef.DirectoryNodeRef()); 1558 if (ancestor != NULL) { 1559 if (ancestor != fBaseAncestor) { 1560 // The directory is a true ancestor -- the notification cannot be 1561 // of interest, since the node didn't match a known ancestor. 1562 return true; 1563 } 1564 1565 // The directory is our path. If watching recursively, just fall 1566 // through. Otherwise, we want to pass on the notification, if directory 1567 // watching is enabled. 1568 if (!_WatchRecursively()) { 1569 if (notify && (fFlags & B_WATCH_DIRECTORY) != 0) { 1570 _NotifyEntryCreatedOrRemoved(entryRef, nodeRef, 1571 make_path(fPath, entryRef.name), false, B_ENTRY_REMOVED); 1572 // We don't know whether this was a directory, but it 1573 // doesn't matter in this case. 1574 } 1575 return true; 1576 } 1577 } 1578 1579 if (!_WatchRecursively()) { 1580 // That shouldn't happen, since we only watch the ancestors in this 1581 // case. 1582 return true; 1583 } 1584 1585 Node* directoryNode = _GetNode(entryRef.DirectoryNodeRef()); 1586 if (directoryNode == NULL) { 1587 // We shouldn't get a notification, if we don't known the directory. 1588 return true; 1589 } 1590 1591 Directory* directory = directoryNode->ToDirectory(); 1592 if (directory == NULL) { 1593 // We might be out of sync with reality or the notification is just 1594 // late. The former case is extremely unlikely (we are watching the node 1595 // and its parent directory after all) and rather hard to verify. 1596 return true; 1597 } 1598 1599 Entry* nodeEntry = directory->FindEntry(entryRef.name); 1600 if (nodeEntry == NULL) { 1601 // might be a non-directory node while we're in directories-only mode 1602 return true; 1603 } 1604 1605 if (!dryRun) { 1606 if (_keepEntry != NULL) 1607 *_keepEntry = nodeEntry; 1608 else 1609 _DeleteEntry(nodeEntry, notify); 1610 } 1611 return true; 1612 } 1613 1614 1615 bool 1616 PathHandler::_CheckDuplicateEntryNotification(int32 opcode, 1617 const entry_ref& toEntryRef, const node_ref& nodeRef, 1618 const entry_ref* fromEntryRef) 1619 { 1620 if (opcode == fDuplicateEntryNotificationOpcode 1621 && nodeRef == fDuplicateEntryNotificationNodeRef 1622 && toEntryRef == fDuplicateEntryNotificationToEntryRef 1623 && (fromEntryRef == NULL 1624 || *fromEntryRef == fDuplicateEntryNotificationFromEntryRef)) { 1625 return true; 1626 } 1627 1628 fDuplicateEntryNotificationOpcode = opcode; 1629 fDuplicateEntryNotificationNodeRef = nodeRef; 1630 fDuplicateEntryNotificationToEntryRef = toEntryRef; 1631 fDuplicateEntryNotificationFromEntryRef = fromEntryRef != NULL 1632 ? *fromEntryRef : entry_ref(); 1633 return false; 1634 } 1635 1636 1637 void 1638 PathHandler::_UnsetDuplicateEntryNotification() 1639 { 1640 fDuplicateEntryNotificationOpcode = B_STAT_CHANGED; 1641 fDuplicateEntryNotificationNodeRef = node_ref(); 1642 fDuplicateEntryNotificationFromEntryRef = entry_ref(); 1643 fDuplicateEntryNotificationToEntryRef = entry_ref(); 1644 } 1645 1646 1647 Ancestor* 1648 PathHandler::_GetAncestor(const node_ref& nodeRef) const 1649 { 1650 return fAncestors.Lookup(nodeRef); 1651 } 1652 1653 1654 status_t 1655 PathHandler::_AddNode(const node_ref& nodeRef, bool isDirectory, bool notify, 1656 Entry* entry, Node** _node) 1657 { 1658 TRACE("%p->PathHandler::_AddNode(%" B_PRIdDEV ":%" B_PRIdINO 1659 ", isDirectory: %d, notify: %d)\n", this, nodeRef.device, nodeRef.node, 1660 isDirectory, notify); 1661 1662 // If hard links are supported, we may already know the node. 1663 Node* node = _GetNode(nodeRef); 1664 if (node != NULL) { 1665 if (entry != NULL) { 1666 entry->SetNode(node); 1667 node->AddNodeEntry(entry); 1668 } 1669 1670 if (_node != NULL) 1671 *_node = node; 1672 return B_OK; 1673 } 1674 1675 // create the node 1676 Directory* directoryNode = NULL; 1677 if (isDirectory) 1678 node = directoryNode = Directory::Create(nodeRef); 1679 else 1680 node = new(std::nothrow) Node(nodeRef); 1681 1682 if (node == NULL) 1683 return B_NO_MEMORY; 1684 1685 ObjectDeleter<Node> nodeDeleter(node); 1686 1687 // start watching (don't do that for the base node, since we watch it 1688 // already via fBaseAncestor) 1689 if (nodeRef != fBaseAncestor->NodeRef()) { 1690 uint32 flags = (fFlags & WATCH_NODE_FLAG_MASK) | B_WATCH_DIRECTORY; 1691 status_t error = sWatchingInterface->WatchNode(&nodeRef, flags, this); 1692 if (error != B_OK) 1693 return error; 1694 } 1695 1696 fNodes.Insert(nodeDeleter.Detach()); 1697 1698 if (entry != NULL) { 1699 entry->SetNode(node); 1700 node->AddNodeEntry(entry); 1701 } 1702 1703 if (_node != NULL) 1704 *_node = node; 1705 1706 if (!isDirectory) 1707 return B_OK; 1708 1709 // recursively add the directory's descendents 1710 BDirectory directory; 1711 if (directory.SetTo(&nodeRef) != B_OK) { 1712 if (_node != NULL) 1713 *_node = node; 1714 return B_OK; 1715 } 1716 1717 entry_ref entryRef; 1718 while (directory.GetNextRef(&entryRef) == B_OK) { 1719 struct stat st; 1720 if (BEntry(&entryRef).GetStat(&st) != B_OK) 1721 continue; 1722 1723 bool isDirectory = S_ISDIR(st.st_mode); 1724 status_t error = _AddEntryIfNeeded(directoryNode, entryRef.name, 1725 node_ref(st.st_dev, st.st_ino), isDirectory, notify); 1726 if (error != B_OK) { 1727 TRACE("%p->PathHandler::_AddNode(%" B_PRIdDEV ":%" B_PRIdINO 1728 ", isDirectory: %d, notify: %d): failed to add directory " 1729 "entry: \"%s\"\n", this, nodeRef.device, nodeRef.node, 1730 isDirectory, notify, entryRef.name); 1731 continue; 1732 } 1733 } 1734 1735 return B_OK; 1736 } 1737 1738 1739 void 1740 PathHandler::_DeleteNode(Node* node, bool notify) 1741 { 1742 if (Directory* directory = node->ToDirectory()) { 1743 Entry* entry = directory->RemoveAllEntries(); 1744 while (entry != NULL) { 1745 Entry* nextEntry = entry->HashNext(); 1746 _DeleteEntryAlreadyRemovedFromParent(entry, notify); 1747 entry = nextEntry; 1748 } 1749 } 1750 1751 if (node->NodeRef() != fBaseAncestor->NodeRef()) 1752 sWatchingInterface->WatchNode(&node->NodeRef(), B_STOP_WATCHING, this); 1753 1754 fNodes.Remove(node); 1755 delete node; 1756 } 1757 1758 1759 Node* 1760 PathHandler::_GetNode(const node_ref& nodeRef) const 1761 { 1762 return fNodes.Lookup(nodeRef); 1763 } 1764 1765 1766 status_t 1767 PathHandler::_AddEntryIfNeeded(Directory* directory, const char* name, 1768 const node_ref& nodeRef, bool isDirectory, bool notify, 1769 Entry** _entry) 1770 { 1771 TRACE("%p->PathHandler::_AddEntryIfNeeded(%" B_PRIdDEV ":%" B_PRIdINO 1772 ":\"%s\", %" B_PRIdDEV ":%" B_PRIdINO 1773 ", isDirectory: %d, notify: %d)\n", this, directory->NodeRef().device, 1774 directory->NodeRef().node, name, nodeRef.device, nodeRef.node, 1775 isDirectory, notify); 1776 1777 if (!isDirectory && _WatchDirectoriesOnly()) { 1778 if (_entry != NULL) 1779 *_entry = NULL; 1780 return B_OK; 1781 } 1782 1783 Entry* entry = directory->CreateEntry(name, NULL); 1784 if (entry == NULL) 1785 return B_NO_MEMORY; 1786 1787 status_t error = _AddNode(nodeRef, isDirectory, notify && _WatchFilesOnly(), 1788 entry); 1789 if (error != B_OK) { 1790 directory->RemoveEntry(entry); 1791 delete entry; 1792 return error; 1793 } 1794 1795 if (notify) 1796 _NotifyEntryCreatedOrRemoved(entry, B_ENTRY_CREATED); 1797 1798 if (_entry != NULL) 1799 *_entry = entry; 1800 return B_OK; 1801 } 1802 1803 1804 void 1805 PathHandler::_DeleteEntry(Entry* entry, bool notify) 1806 { 1807 entry->Parent()->RemoveEntry(entry); 1808 _DeleteEntryAlreadyRemovedFromParent(entry, notify); 1809 } 1810 1811 1812 void 1813 PathHandler::_DeleteEntryAlreadyRemovedFromParent(Entry* entry, bool notify) 1814 { 1815 if (notify) 1816 _NotifyEntryCreatedOrRemoved(entry, B_ENTRY_REMOVED); 1817 1818 Node* node = entry->Node(); 1819 if (node->IsOnlyNodeEntry(entry)) 1820 _DeleteNode(node, notify && _WatchFilesOnly()); 1821 1822 delete entry; 1823 } 1824 1825 1826 void 1827 PathHandler::_NotifyFilesCreatedOrRemoved(Entry* entry, int32 opcode) const 1828 { 1829 Directory* directory = entry->Node()->ToDirectory(); 1830 if (directory == NULL) { 1831 _NotifyEntryCreatedOrRemoved(entry, opcode); 1832 return; 1833 } 1834 1835 for (EntryMap::Iterator it = directory->GetEntryIterator(); it.HasNext();) 1836 _NotifyFilesCreatedOrRemoved(it.Next(), opcode); 1837 } 1838 1839 1840 void 1841 PathHandler::_NotifyEntryCreatedOrRemoved(Entry* entry, int32 opcode) const 1842 { 1843 Node* node = entry->Node(); 1844 _NotifyEntryCreatedOrRemoved( 1845 NotOwningEntryRef(entry->Parent()->NodeRef(), entry->Name()), 1846 node->NodeRef(), _EntryPath(entry), node->IsDirectory(), opcode); 1847 } 1848 1849 1850 void 1851 PathHandler::_NotifyEntryCreatedOrRemoved(const entry_ref& entryRef, 1852 const node_ref& nodeRef, const char* path, bool isDirectory, int32 opcode) 1853 const 1854 { 1855 if (isDirectory ? _WatchFilesOnly() : _WatchDirectoriesOnly()) 1856 return; 1857 1858 TRACE("%p->PathHandler::_NotifyEntryCreatedOrRemoved(): entry %s: %" 1859 B_PRIdDEV ":%" B_PRIdINO ":\"%s\", node: %" B_PRIdDEV ":%" B_PRIdINO 1860 "\n", this, opcode == B_ENTRY_CREATED ? "created" : "removed", 1861 entryRef.device, entryRef.directory, entryRef.name, nodeRef.device, 1862 nodeRef.node); 1863 1864 BMessage message(B_PATH_MONITOR); 1865 message.AddInt32("opcode", opcode); 1866 message.AddInt32("device", entryRef.device); 1867 message.AddInt64("directory", entryRef.directory); 1868 message.AddInt32("node device", nodeRef.device); 1869 // This field is not in a usual node monitoring message, since the node 1870 // the created/removed entry refers to always belongs to the same FS as 1871 // the directory, as another FS cannot yet/no longer be mounted there. 1872 // In our case, however, this can very well be the case, e.g. when the 1873 // the notification is triggered in response to a directory tree having 1874 // been moved into/out of our path. 1875 message.AddInt64("node", nodeRef.node); 1876 message.AddString("name", entryRef.name); 1877 1878 _NotifyTarget(message, path); 1879 } 1880 1881 1882 void 1883 PathHandler::_NotifyEntryMoved(const entry_ref& fromEntryRef, 1884 const entry_ref& toEntryRef, const node_ref& nodeRef, const char* fromPath, 1885 const char* path, bool isDirectory, bool wasAdded, bool wasRemoved) const 1886 { 1887 if ((isDirectory && _WatchFilesOnly()) 1888 || (!isDirectory && _WatchDirectoriesOnly())) { 1889 return; 1890 } 1891 1892 TRACE("%p->PathHandler::_NotifyEntryMoved(): entry: %" B_PRIdDEV ":%" 1893 B_PRIdINO ":\"%s\" -> %" B_PRIdDEV ":%" B_PRIdINO ":\"%s\", node: %" 1894 B_PRIdDEV ":%" B_PRIdINO "\n", this, fromEntryRef.device, 1895 fromEntryRef.directory, fromEntryRef.name, toEntryRef.device, 1896 toEntryRef.directory, toEntryRef.name, nodeRef.device, nodeRef.node); 1897 1898 BMessage message(B_PATH_MONITOR); 1899 message.AddInt32("opcode", B_ENTRY_MOVED); 1900 message.AddInt32("device", fromEntryRef.device); 1901 message.AddInt64("from directory", fromEntryRef.directory); 1902 message.AddInt64("to directory", toEntryRef.directory); 1903 message.AddInt32("node device", nodeRef.device); 1904 message.AddInt64("node", nodeRef.node); 1905 message.AddString("from name", fromEntryRef.name); 1906 message.AddString("name", toEntryRef.name); 1907 1908 if (wasAdded) 1909 message.AddBool("added", true); 1910 if (wasRemoved) 1911 message.AddBool("removed", true); 1912 1913 if (fromPath != NULL && fromPath[0] != '\0') 1914 message.AddString("from path", fromPath); 1915 1916 _NotifyTarget(message, path); 1917 } 1918 1919 1920 void 1921 PathHandler::_NotifyTarget(BMessage& message, const char* path) const 1922 { 1923 message.what = B_PATH_MONITOR; 1924 if (path != NULL && path[0] != '\0') 1925 message.AddString("path", path); 1926 message.AddString("watched_path", fPath.String()); 1927 fTarget.SendMessage(&message); 1928 } 1929 1930 1931 1932 BString 1933 PathHandler::_NodePath(const Node* node) const 1934 { 1935 if (Entry* entry = node->FirstNodeEntry()) 1936 return _EntryPath(entry); 1937 return node == fBaseNode ? fPath : BString(); 1938 } 1939 1940 1941 BString 1942 PathHandler::_EntryPath(const Entry* entry) const 1943 { 1944 return make_path(_NodePath(entry->Parent()), entry->Name()); 1945 } 1946 1947 1948 bool 1949 PathHandler::_WatchRecursively() const 1950 { 1951 return (fFlags & B_WATCH_RECURSIVELY) != 0; 1952 } 1953 1954 1955 bool 1956 PathHandler::_WatchFilesOnly() const 1957 { 1958 return (fFlags & B_WATCH_FILES_ONLY) != 0; 1959 } 1960 1961 1962 bool 1963 PathHandler::_WatchDirectoriesOnly() const 1964 { 1965 return (fFlags & B_WATCH_DIRECTORIES_ONLY) != 0; 1966 } 1967 1968 1969 } // namespace 1970 1971 1972 // #pragma mark - BPathMonitor 1973 1974 1975 namespace BPrivate { 1976 1977 1978 BPathMonitor::BPathMonitor() 1979 { 1980 } 1981 1982 1983 BPathMonitor::~BPathMonitor() 1984 { 1985 } 1986 1987 1988 /*static*/ status_t 1989 BPathMonitor::StartWatching(const char* path, uint32 flags, 1990 const BMessenger& target) 1991 { 1992 TRACE("BPathMonitor::StartWatching(%s, %" B_PRIx32 ")\n", path, flags); 1993 1994 if (path == NULL || path[0] == '\0') 1995 return B_BAD_VALUE; 1996 1997 // B_WATCH_FILES_ONLY and B_WATCH_DIRECTORIES_ONLY are mutual exclusive 1998 if ((flags & B_WATCH_FILES_ONLY) != 0 1999 && (flags & B_WATCH_DIRECTORIES_ONLY) != 0) { 2000 return B_BAD_VALUE; 2001 } 2002 2003 status_t status = _InitIfNeeded(); 2004 if (status != B_OK) 2005 return status; 2006 2007 BAutolock _(sLocker); 2008 2009 Watcher* watcher = sWatchers->Lookup(target); 2010 bool newWatcher = false; 2011 if (watcher != NULL) { 2012 // If there's already a handler for the path, we'll replace it, but 2013 // add its flags. 2014 if (PathHandler* handler = watcher->Lookup(path)) { 2015 // keep old flags save for conflicting mutually exclusive ones 2016 uint32 oldFlags = handler->Flags(); 2017 const uint32 kMutuallyExclusiveFlags 2018 = B_WATCH_FILES_ONLY | B_WATCH_DIRECTORIES_ONLY; 2019 if ((flags & kMutuallyExclusiveFlags) != 0) 2020 oldFlags &= ~(uint32)kMutuallyExclusiveFlags; 2021 flags |= oldFlags; 2022 2023 watcher->Remove(handler); 2024 handler->Quit(); 2025 } 2026 } else { 2027 watcher = Watcher::Create(target); 2028 if (watcher == NULL) 2029 return B_NO_MEMORY; 2030 sWatchers->Insert(watcher); 2031 newWatcher = true; 2032 } 2033 2034 PathHandler* handler = new (std::nothrow) PathHandler(path, flags, target, 2035 sLooper); 2036 status = handler != NULL ? handler->InitCheck() : B_NO_MEMORY; 2037 2038 if (status != B_OK) { 2039 if (handler != NULL) 2040 handler->Quit(); 2041 2042 if (newWatcher) { 2043 sWatchers->Remove(watcher); 2044 delete watcher; 2045 } 2046 } 2047 2048 watcher->Insert(handler); 2049 return B_OK; 2050 } 2051 2052 2053 /*static*/ status_t 2054 BPathMonitor::StopWatching(const char* path, const BMessenger& target) 2055 { 2056 if (sLocker == NULL) 2057 return B_BAD_VALUE; 2058 2059 TRACE("BPathMonitor::StopWatching(%s)\n", path); 2060 2061 BAutolock _(sLocker); 2062 2063 Watcher* watcher = sWatchers->Lookup(target); 2064 if (watcher == NULL) 2065 return B_BAD_VALUE; 2066 2067 PathHandler* handler = watcher->Lookup(path); 2068 if (handler == NULL) 2069 return B_BAD_VALUE; 2070 2071 watcher->Remove(handler); 2072 handler->Quit(); 2073 2074 if (watcher->IsEmpty()) { 2075 sWatchers->Remove(watcher); 2076 delete watcher; 2077 } 2078 2079 return B_OK; 2080 } 2081 2082 2083 /*static*/ status_t 2084 BPathMonitor::StopWatching(const BMessenger& target) 2085 { 2086 if (sLocker == NULL) 2087 return B_BAD_VALUE; 2088 2089 BAutolock _(sLocker); 2090 2091 Watcher* watcher = sWatchers->Lookup(target); 2092 if (watcher == NULL) 2093 return B_BAD_VALUE; 2094 2095 // delete handlers 2096 PathHandler* handler = watcher->Clear(true); 2097 while (handler != NULL) { 2098 PathHandler* nextHandler = handler->HashNext(); 2099 handler->Quit(); 2100 handler = nextHandler; 2101 } 2102 2103 sWatchers->Remove(watcher); 2104 delete watcher; 2105 2106 return B_OK; 2107 } 2108 2109 2110 /*static*/ void 2111 BPathMonitor::SetWatchingInterface(BWatchingInterface* watchingInterface) 2112 { 2113 sWatchingInterface = watchingInterface != NULL 2114 ? watchingInterface : sDefaultWatchingInterface; 2115 } 2116 2117 2118 /*static*/ status_t 2119 BPathMonitor::_InitIfNeeded() 2120 { 2121 pthread_once(&sInitOnce, &BPathMonitor::_Init); 2122 return sLooper != NULL ? B_OK : B_NO_MEMORY; 2123 } 2124 2125 2126 /*static*/ void 2127 BPathMonitor::_Init() 2128 { 2129 sLocker = new (std::nothrow) BLocker("path monitor"); 2130 TRACE("Create PathMonitor locker\n"); 2131 if (sLocker == NULL) 2132 return; 2133 2134 sDefaultWatchingInterface = new(std::nothrow) BWatchingInterface; 2135 if (sDefaultWatchingInterface == NULL) 2136 return; 2137 2138 sWatchers = new(std::nothrow) WatcherMap; 2139 if (sWatchers == NULL || sWatchers->Init() != B_OK) 2140 return; 2141 2142 if (sWatchingInterface == NULL) 2143 SetWatchingInterface(sDefaultWatchingInterface); 2144 2145 BLooper* looper = new (std::nothrow) BLooper("PathMonitor looper"); 2146 TRACE("Start PathMonitor looper\n"); 2147 if (looper == NULL) 2148 return; 2149 thread_id thread = looper->Run(); 2150 if (thread < 0) { 2151 delete looper; 2152 return; 2153 } 2154 2155 sLooper = looper; 2156 } 2157 2158 2159 // #pragma mark - BWatchingInterface 2160 2161 2162 BPathMonitor::BWatchingInterface::BWatchingInterface() 2163 { 2164 } 2165 2166 2167 BPathMonitor::BWatchingInterface::~BWatchingInterface() 2168 { 2169 } 2170 2171 2172 status_t 2173 BPathMonitor::BWatchingInterface::WatchNode(const node_ref* node, uint32 flags, 2174 const BMessenger& target) 2175 { 2176 return watch_node(node, flags, target); 2177 } 2178 2179 2180 status_t 2181 BPathMonitor::BWatchingInterface::WatchNode(const node_ref* node, uint32 flags, 2182 const BHandler* handler, const BLooper* looper) 2183 { 2184 return watch_node(node, flags, handler, looper); 2185 } 2186 2187 2188 status_t 2189 BPathMonitor::BWatchingInterface::StopWatching(const BMessenger& target) 2190 { 2191 return stop_watching(target); 2192 } 2193 2194 2195 status_t 2196 BPathMonitor::BWatchingInterface::StopWatching(const BHandler* handler, 2197 const BLooper* looper) 2198 { 2199 return stop_watching(handler, looper); 2200 } 2201 2202 2203 } // namespace BPrivate 2204