repository_infos: Enable automatic updates on the main Haiku repostiory.
[haiku.git] / src / kits / storage / PathMonitor.cpp
blob2462437313d8c7be13072f719b47d4ba219bb64f
1 /*
2 * Copyright 2007-2013, Haiku, Inc. All Rights Reserved.
3 * Distributed under the terms of the MIT License.
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 */
12 #include <PathMonitor.h>
14 #include <pthread.h>
15 #include <stdio.h>
17 #include <Autolock.h>
18 #include <Directory.h>
19 #include <Entry.h>
20 #include <Handler.h>
21 #include <Locker.h>
22 #include <Looper.h>
23 #include <Path.h>
24 #include <String.h>
26 #include <AutoDeleter.h>
27 #include <NotOwningEntryRef.h>
28 #include <ObjectList.h>
29 #include <util/OpenHashTable.h>
30 #include <util/SinglyLinkedList.h>
33 #undef TRACE
34 //#define TRACE_PATH_MONITOR
35 #ifdef TRACE_PATH_MONITOR
36 # define TRACE(...) debug_printf("BPathMonitor: " __VA_ARGS__)
37 #else
38 # define TRACE(...) ;
39 #endif
42 // TODO: Support symlink components in the path.
43 // TODO: Support mounting/unmounting of volumes in path components and within
44 // the watched path tree.
47 #define WATCH_NODE_FLAG_MASK 0x00ff
50 namespace {
53 struct Directory;
54 struct Node;
55 struct WatcherHashDefinition;
56 typedef BOpenHashTable<WatcherHashDefinition> WatcherMap;
59 static pthread_once_t sInitOnce = PTHREAD_ONCE_INIT;
60 static WatcherMap* sWatchers = NULL;
61 static BLooper* sLooper = NULL;
62 static BPathMonitor::BWatchingInterface* sDefaultWatchingInterface = NULL;
63 static BPathMonitor::BWatchingInterface* sWatchingInterface = NULL;
66 // #pragma mark -
69 /*! Returns empty path, if either \a parent or \a subPath is empty or an
70 allocation fails.
72 static BString
73 make_path(const BString& parent, const char* subPath)
75 BString path = parent;
76 int32 length = path.Length();
77 if (length == 0 || subPath[0] == '\0')
78 return BString();
80 if (parent.ByteAt(length - 1) != '/') {
81 path << '/';
82 if (path.Length() < ++length)
83 return BString();
86 path << subPath;
87 if (path.Length() <= length)
88 return BString();
89 return path;
93 // #pragma mark - Ancestor
96 class Ancestor {
97 public:
98 Ancestor(Ancestor* parent, const BString& path, size_t pathComponentOffset)
100 fParent(parent),
101 fChild(NULL),
102 fPath(path),
103 fEntryRef(-1, -1, fPath.String() + pathComponentOffset),
104 fNodeRef(),
105 fWatchingFlags(0),
106 fIsDirectory(false)
108 if (pathComponentOffset == 0) {
109 // must be "/"
110 fEntryRef.SetTo(-1, -1, ".");
113 if (fParent != NULL)
114 fParent->fChild = this;
117 Ancestor* Parent() const
119 return fParent;
122 Ancestor* Child() const
124 return fChild;
127 const BString& Path() const
129 return fPath;
132 const char* Name() const
134 return fEntryRef.name;
137 bool Exists() const
139 return fNodeRef.device >= 0;
142 const NotOwningEntryRef& EntryRef() const
144 return fEntryRef;
147 const node_ref& NodeRef() const
149 return fNodeRef;
152 bool IsDirectory() const
154 return fIsDirectory;
157 status_t StartWatching(uint32 pathFlags, BHandler* target)
159 // init entry ref
160 BEntry entry;
161 status_t error = entry.SetTo(fPath);
162 if (error != B_OK)
163 return error;
165 entry_ref entryRef;
166 error = entry.GetRef(&entryRef);
167 if (error != B_OK)
168 return error;
170 fEntryRef.device = entryRef.device;
171 fEntryRef.directory = entryRef.directory;
173 // init node ref
174 struct stat st;
175 error = entry.GetStat(&st);
176 if (error != B_OK)
177 return error == B_ENTRY_NOT_FOUND ? B_OK : error;
179 fNodeRef = node_ref(st.st_dev, st.st_ino);
180 fIsDirectory = S_ISDIR(st.st_mode);
182 // start watching
183 uint32 flags = fChild == NULL ? pathFlags : B_WATCH_DIRECTORY;
184 // In theory B_WATCH_NAME would suffice for all existing ancestors,
185 // plus B_WATCH_DIRECTORY for the parent of the first not existing
186 // ancestor. In practice this complicates the transitions when an
187 // ancestor is created/removed/moved.
188 if (flags != 0) {
189 error = sWatchingInterface->WatchNode(&fNodeRef, flags, target);
190 TRACE(" started to watch ancestor %p (\"%s\", %#" B_PRIx32
191 ") -> %s\n", this, Name(), flags, strerror(error));
192 if (error != B_OK)
193 return error;
196 fWatchingFlags = flags;
197 return B_OK;
200 void StopWatching(BHandler* target)
202 // stop watching
203 if (fWatchingFlags != 0) {
204 sWatchingInterface->WatchNode(&fNodeRef, B_STOP_WATCHING, target);
205 fWatchingFlags = 0;
208 // uninitialize node and entry ref
209 fIsDirectory = false;
210 fNodeRef = node_ref();
211 fEntryRef.SetDirectoryNodeRef(node_ref());
214 Ancestor*& HashNext()
216 return fHashNext;
219 private:
220 Ancestor* fParent;
221 Ancestor* fChild;
222 Ancestor* fHashNext;
223 BString fPath;
224 NotOwningEntryRef fEntryRef;
225 node_ref fNodeRef;
226 uint32 fWatchingFlags;
227 bool fIsDirectory;
231 // #pragma mark - AncestorMap
234 struct AncestorHashDefinition {
235 typedef node_ref KeyType;
236 typedef Ancestor ValueType;
238 size_t HashKey(const node_ref& key) const
240 return size_t(key.device ^ key.node);
243 size_t Hash(Ancestor* value) const
245 return HashKey(value->NodeRef());
248 bool Compare(const node_ref& key, Ancestor* value) const
250 return key == value->NodeRef();
253 Ancestor*& GetLink(Ancestor* value) const
255 return value->HashNext();
260 typedef BOpenHashTable<AncestorHashDefinition> AncestorMap;
263 // #pragma mark - Entry
266 class Entry : public SinglyLinkedListLinkImpl<Entry> {
267 public:
268 Entry(Directory* parent, const BString& name, ::Node* node)
270 fParent(parent),
271 fName(name),
272 fNode(node)
276 Directory* Parent() const
278 return fParent;
281 const BString& Name() const
283 return fName;
286 ::Node* Node() const
288 return fNode;
291 void SetNode(::Node* node)
293 fNode = node;
296 inline NotOwningEntryRef EntryRef() const;
298 Entry*& HashNext()
300 return fHashNext;
303 private:
304 Directory* fParent;
305 BString fName;
306 ::Node* fNode;
307 Entry* fHashNext;
310 typedef SinglyLinkedList<Entry> EntryList;
313 // EntryMap
316 struct EntryHashDefinition {
317 typedef const char* KeyType;
318 typedef Entry ValueType;
320 size_t HashKey(const char* key) const
322 return BString::HashValue(key);
325 size_t Hash(Entry* value) const
327 return value->Name().HashValue();
330 bool Compare(const char* key, Entry* value) const
332 return value->Name() == key;
335 Entry*& GetLink(Entry* value) const
337 return value->HashNext();
342 typedef BOpenHashTable<EntryHashDefinition> EntryMap;
345 // #pragma mark - Node
348 class Node {
349 public:
350 Node(const node_ref& nodeRef)
352 fNodeRef(nodeRef)
356 virtual ~Node()
360 virtual bool IsDirectory() const
362 return false;
365 virtual Directory* ToDirectory()
367 return NULL;
370 const node_ref& NodeRef() const
372 return fNodeRef;
375 const EntryList& Entries() const
377 return fEntries;
380 bool HasEntries() const
382 return !fEntries.IsEmpty();
385 Entry* FirstNodeEntry() const
387 return fEntries.Head();
390 bool IsOnlyNodeEntry(Entry* entry) const
392 return entry == fEntries.Head() && fEntries.GetNext(entry) == NULL;
395 void AddNodeEntry(Entry* entry)
397 fEntries.Add(entry);
400 void RemoveNodeEntry(Entry* entry)
402 fEntries.Remove(entry);
405 Node*& HashNext()
407 return fHashNext;
410 private:
411 node_ref fNodeRef;
412 EntryList fEntries;
413 Node* fHashNext;
417 struct NodeHashDefinition {
418 typedef node_ref KeyType;
419 typedef Node ValueType;
421 size_t HashKey(const node_ref& key) const
423 return size_t(key.device ^ key.node);
426 size_t Hash(Node* value) const
428 return HashKey(value->NodeRef());
431 bool Compare(const node_ref& key, Node* value) const
433 return key == value->NodeRef();
436 Node*& GetLink(Node* value) const
438 return value->HashNext();
443 typedef BOpenHashTable<NodeHashDefinition> NodeMap;
446 // #pragma mark - Directory
449 class Directory : public Node {
450 public:
451 static Directory* Create(const node_ref& nodeRef)
453 Directory* directory = new(std::nothrow) Directory(nodeRef);
454 if (directory == NULL || directory->fEntries.Init() != B_OK) {
455 delete directory;
456 return NULL;
459 return directory;
462 virtual bool IsDirectory() const
464 return true;
467 virtual Directory* ToDirectory()
469 return this;
472 Entry* FindEntry(const char* name) const
474 return fEntries.Lookup(name);
477 Entry* CreateEntry(const BString& name, Node* node)
479 Entry* entry = new(std::nothrow) Entry(this, name, node);
480 if (entry == NULL || entry->Name().IsEmpty()) {
481 delete entry;
482 return NULL;
485 AddEntry(entry);
486 return entry;
489 void AddEntry(Entry* entry)
491 fEntries.Insert(entry);
494 void RemoveEntry(Entry* entry)
496 fEntries.Remove(entry);
499 EntryMap::Iterator GetEntryIterator() const
501 return fEntries.GetIterator();
504 Entry* RemoveAllEntries()
506 return fEntries.Clear(true);
509 private:
510 Directory(const node_ref& nodeRef)
512 Node(nodeRef)
516 private:
517 EntryMap fEntries;
521 // #pragma mark - Entry
524 inline NotOwningEntryRef
525 Entry::EntryRef() const
527 return NotOwningEntryRef(fParent->NodeRef(), fName);
531 // #pragma mark - PathHandler
534 class PathHandler : public BHandler {
535 public:
536 PathHandler(const char* path, uint32 flags,
537 const BMessenger& target, BLooper* looper);
538 virtual ~PathHandler();
540 status_t InitCheck() const;
541 void Quit();
543 const BString& OriginalPath() const
544 { return fOriginalPath; }
545 uint32 Flags() const { return fFlags; }
547 virtual void MessageReceived(BMessage* message);
549 PathHandler*& HashNext() { return fHashNext; }
551 private:
552 status_t _CreateAncestors();
553 status_t _StartWatchingAncestors(Ancestor* ancestor,
554 bool notify);
555 void _StopWatchingAncestors(Ancestor* ancestor,
556 bool notify);
558 void _EntryCreated(BMessage* message);
559 void _EntryRemoved(BMessage* message);
560 void _EntryMoved(BMessage* message);
561 void _NodeChanged(BMessage* message);
563 bool _EntryCreated(const NotOwningEntryRef& entryRef,
564 const node_ref& nodeRef, bool isDirectory,
565 bool dryRun, bool notify, Entry** _entry);
566 bool _EntryRemoved(const NotOwningEntryRef& entryRef,
567 const node_ref& nodeRef, bool dryRun,
568 bool notify, Entry** _keepEntry);
570 bool _CheckDuplicateEntryNotification(int32 opcode,
571 const entry_ref& toEntryRef,
572 const node_ref& nodeRef,
573 const entry_ref* fromEntryRef = NULL);
574 void _UnsetDuplicateEntryNotification();
576 Ancestor* _GetAncestor(const node_ref& nodeRef) const;
578 status_t _AddNode(const node_ref& nodeRef,
579 bool isDirectory, bool notify,
580 Entry* entry = NULL, Node** _node = NULL);
581 void _DeleteNode(Node* node, bool notify);
582 Node* _GetNode(const node_ref& nodeRef) const;
584 status_t _AddEntryIfNeeded(Directory* directory,
585 const char* name, const node_ref& nodeRef,
586 bool isDirectory, bool notify,
587 Entry** _entry = NULL);
588 void _DeleteEntry(Entry* entry, bool notify);
589 void _DeleteEntryAlreadyRemovedFromParent(
590 Entry* entry, bool notify);
592 void _NotifyFilesCreatedOrRemoved(Entry* entry,
593 int32 opcode) const;
594 void _NotifyEntryCreatedOrRemoved(Entry* entry,
595 int32 opcode) const;
596 void _NotifyEntryCreatedOrRemoved(
597 const entry_ref& entryRef,
598 const node_ref& nodeRef, const char* path,
599 bool isDirectory, int32 opcode) const;
600 void _NotifyEntryMoved(const entry_ref& fromEntryRef,
601 const entry_ref& toEntryRef,
602 const node_ref& nodeRef,
603 const char* fromPath, const char* path,
604 bool isDirectory, bool wasAdded,
605 bool wasRemoved) const;
606 void _NotifyTarget(BMessage& message,
607 const char* path) const;
609 BString _NodePath(const Node* node) const;
610 BString _EntryPath(const Entry* entry) const;
613 bool _WatchRecursively() const;
614 bool _WatchFilesOnly() const;
615 bool _WatchDirectoriesOnly() const;
617 private:
618 BMessenger fTarget;
619 uint32 fFlags;
620 status_t fStatus;
621 BString fOriginalPath;
622 BString fPath;
623 Ancestor* fRoot;
624 Ancestor* fBaseAncestor;
625 Node* fBaseNode;
626 AncestorMap fAncestors;
627 NodeMap fNodes;
628 PathHandler* fHashNext;
629 int32 fDuplicateEntryNotificationOpcode;
630 node_ref fDuplicateEntryNotificationNodeRef;
631 entry_ref fDuplicateEntryNotificationToEntryRef;
632 entry_ref fDuplicateEntryNotificationFromEntryRef;
636 struct PathHandlerHashDefinition {
637 typedef const char* KeyType;
638 typedef PathHandler ValueType;
640 size_t HashKey(const char* key) const
642 return BString::HashValue(key);
645 size_t Hash(PathHandler* value) const
647 return value->OriginalPath().HashValue();
650 bool Compare(const char* key, PathHandler* value) const
652 return key == value->OriginalPath();
655 PathHandler*& GetLink(PathHandler* value) const
657 return value->HashNext();
662 typedef BOpenHashTable<PathHandlerHashDefinition> PathHandlerMap;
665 // #pragma mark - Watcher
668 struct Watcher : public PathHandlerMap {
669 static Watcher* Create(const BMessenger& target)
671 Watcher* watcher = new(std::nothrow) Watcher(target);
672 if (watcher == NULL || watcher->Init() != B_OK) {
673 delete watcher;
674 return NULL;
676 return watcher;
679 const BMessenger& Target() const
681 return fTarget;
684 Watcher*& HashNext()
686 return fHashNext;
689 private:
690 Watcher(const BMessenger& target)
692 fTarget(target)
696 private:
697 BMessenger fTarget;
698 Watcher* fHashNext;
702 struct WatcherHashDefinition {
703 typedef BMessenger KeyType;
704 typedef Watcher ValueType;
706 size_t HashKey(const BMessenger& key) const
708 return key.HashValue();
711 size_t Hash(Watcher* value) const
713 return HashKey(value->Target());
716 bool Compare(const BMessenger& key, Watcher* value) const
718 return key == value->Target();
721 Watcher*& GetLink(Watcher* value) const
723 return value->HashNext();
728 // #pragma mark - PathHandler
731 PathHandler::PathHandler(const char* path, uint32 flags,
732 const BMessenger& target, BLooper* looper)
734 BHandler(path),
735 fTarget(target),
736 fFlags(flags),
737 fStatus(B_OK),
738 fOriginalPath(path),
739 fPath(),
740 fRoot(NULL),
741 fBaseAncestor(NULL),
742 fBaseNode(NULL),
743 fAncestors(),
744 fNodes()
746 TRACE("%p->PathHandler::PathHandler(\"%s\", %#" B_PRIx32 ")\n", this, path,
747 flags);
749 _UnsetDuplicateEntryNotification();
751 fStatus = fAncestors.Init();
752 if (fStatus != B_OK)
753 return;
755 fStatus = fNodes.Init();
756 if (fStatus != B_OK)
757 return;
759 // normalize the flags
760 if ((fFlags & B_WATCH_RECURSIVELY) != 0) {
761 // We add B_WATCH_NAME and B_WATCH_DIRECTORY as needed, so clear them
762 // here.
763 fFlags &= ~uint32(B_WATCH_NAME | B_WATCH_DIRECTORY);
764 } else {
765 // The B_WATCH_*_ONLY flags are only valid for the recursive mode.
766 // B_WATCH_NAME is implied (we watch the parent directory).
767 fFlags &= ~uint32(B_WATCH_FILES_ONLY | B_WATCH_DIRECTORIES_ONLY
768 | B_WATCH_NAME);
771 // Normalize the path a bit. We can't use BPath, as it may really normalize
772 // the path, i.e. resolve symlinks and such, which may cause us to monitor
773 // the wrong path. We want some normalization, though:
774 // * relative -> absolute path
775 // * fold duplicate '/'s
776 // * omit "." components
777 // * fail when encountering ".." components
779 // make absolute
780 BString normalizedPath;
781 if (path[0] == '/') {
782 normalizedPath = "/";
783 path++;
784 } else
785 normalizedPath = BPath(".").Path();
786 if (normalizedPath.IsEmpty()) {
787 fStatus = B_NO_MEMORY;
788 return;
791 // parse path components
792 const char* pathEnd = path + strlen(path);
793 for (;;) {
794 // skip '/'s
795 while (path[0] == '/')
796 path++;
797 if (path == pathEnd)
798 break;
800 const char* componentEnd = strchr(path, '/');
801 if (componentEnd == NULL)
802 componentEnd = pathEnd;
803 size_t componentLength = componentEnd - path;
805 // handle ".' and ".."
806 if (path[0] == '.') {
807 if (componentLength == 1) {
808 path = componentEnd;
809 continue;
811 if (componentLength == 2 && path[1] == '.') {
812 fStatus = B_BAD_VALUE;
813 return;
817 int32 normalizedPathLength = normalizedPath.Length();
818 if (normalizedPath.ByteAt(normalizedPathLength - 1) != '/') {
819 normalizedPath << '/';
820 normalizedPathLength++;
822 normalizedPath.Append(path, componentEnd - path);
823 normalizedPathLength += int32(componentEnd - path);
825 if (normalizedPath.Length() != normalizedPathLength) {
826 fStatus = B_NO_MEMORY;
827 return;
830 path = componentEnd;
833 fPath = normalizedPath;
835 // Create the Ancestor objects -- they correspond to the path components and
836 // are used for watching changes that affect the entries on the path.
837 fStatus = _CreateAncestors();
838 if (fStatus != B_OK)
839 return;
841 // add ourselves to the looper
842 looper->AddHandler(this);
844 // start watching
845 fStatus = _StartWatchingAncestors(fRoot, false);
846 if (fStatus != B_OK)
847 return;
851 PathHandler::~PathHandler()
853 TRACE("%p->PathHandler::~PathHandler(\"%s\", %#" B_PRIx32 ")\n", this,
854 fPath.String(), fFlags);
856 if (fBaseNode != NULL)
857 _DeleteNode(fBaseNode, false);
859 while (fRoot != NULL) {
860 Ancestor* nextAncestor = fRoot->Child();
861 delete fRoot;
862 fRoot = nextAncestor;
867 status_t
868 PathHandler::InitCheck() const
870 return fStatus;
874 void
875 PathHandler::Quit()
877 TRACE("%p->PathHandler::Quit()\n", this);
878 sWatchingInterface->StopWatching(this);
879 sLooper->RemoveHandler(this);
880 delete this;
884 void
885 PathHandler::MessageReceived(BMessage* message)
887 switch (message->what) {
888 case B_NODE_MONITOR:
890 int32 opcode;
891 if (message->FindInt32("opcode", &opcode) != B_OK)
892 return;
894 switch (opcode) {
895 case B_ENTRY_CREATED:
896 _EntryCreated(message);
897 break;
899 case B_ENTRY_REMOVED:
900 _EntryRemoved(message);
901 break;
903 case B_ENTRY_MOVED:
904 _EntryMoved(message);
905 break;
907 default:
908 _UnsetDuplicateEntryNotification();
909 _NodeChanged(message);
910 break;
913 break;
916 default:
917 BHandler::MessageReceived(message);
918 break;
923 status_t
924 PathHandler::_CreateAncestors()
926 TRACE("%p->PathHandler::_CreateAncestors()\n", this);
928 // create the Ancestor objects
929 const char* path = fPath.String();
930 const char* pathEnd = path + fPath.Length();
931 const char* component = path;
933 Ancestor* ancestor = NULL;
935 while (component < pathEnd) {
936 const char* componentEnd = component == path
937 ? component + 1 : strchr(component, '/');
938 if (componentEnd == NULL)
939 componentEnd = pathEnd;
941 BString ancestorPath(path, componentEnd - path);
942 if (ancestorPath.IsEmpty())
943 return B_NO_MEMORY;
945 ancestor = new(std::nothrow) Ancestor(ancestor, ancestorPath,
946 component - path);
947 TRACE(" created ancestor %p (\"%s\" / \"%s\")\n", ancestor,
948 ancestor->Path().String(), ancestor->Name());
949 if (ancestor == NULL)
950 return B_NO_MEMORY;
952 if (fRoot == NULL)
953 fRoot = ancestor;
955 component = componentEnd[0] == '/' ? componentEnd + 1 : componentEnd;
958 fBaseAncestor = ancestor;
960 return B_OK;
964 status_t
965 PathHandler::_StartWatchingAncestors(Ancestor* startAncestor, bool notify)
967 TRACE("%p->PathHandler::_StartWatchingAncestors(%p, %d)\n", this,
968 startAncestor, notify);
970 // The watch flags for the path (if it exists). Recursively implies
971 // directory, since we need to watch the entries.
972 uint32 watchFlags = (fFlags & WATCH_NODE_FLAG_MASK)
973 | (_WatchRecursively() ? B_WATCH_DIRECTORY : 0);
975 for (Ancestor* ancestor = startAncestor; ancestor != NULL;
976 ancestor = ancestor->Child()) {
977 status_t error = ancestor->StartWatching(watchFlags, this);
978 if (error != B_OK)
979 return error;
981 if (!ancestor->Exists()) {
982 TRACE(" -> ancestor doesn't exist\n");
983 break;
986 fAncestors.Insert(ancestor);
989 if (!fBaseAncestor->Exists())
990 return B_OK;
992 if (notify) {
993 _NotifyEntryCreatedOrRemoved(fBaseAncestor->EntryRef(),
994 fBaseAncestor->NodeRef(), fPath, fBaseAncestor->IsDirectory(),
995 B_ENTRY_CREATED);
998 if (!_WatchRecursively())
999 return B_OK;
1001 status_t error = _AddNode(fBaseAncestor->NodeRef(),
1002 fBaseAncestor->IsDirectory(), notify && _WatchFilesOnly(), NULL,
1003 &fBaseNode);
1004 if (error != B_OK)
1005 return error;
1007 return B_OK;
1011 void
1012 PathHandler::_StopWatchingAncestors(Ancestor* ancestor, bool notify)
1014 // stop watching the tree below path
1015 if (fBaseNode != NULL) {
1016 _DeleteNode(fBaseNode, notify && _WatchFilesOnly());
1017 fBaseNode = NULL;
1020 if (notify && fBaseAncestor->Exists()
1021 && (fBaseAncestor->IsDirectory()
1022 ? !_WatchFilesOnly() : !_WatchDirectoriesOnly())) {
1023 _NotifyEntryCreatedOrRemoved(fBaseAncestor->EntryRef(),
1024 fBaseAncestor->NodeRef(), fPath, fBaseAncestor->IsDirectory(),
1025 B_ENTRY_REMOVED);
1028 // stop watching the ancestors and uninitialize their entries
1029 for (; ancestor != NULL; ancestor = ancestor->Child()) {
1030 if (ancestor->Exists())
1031 fAncestors.Remove(ancestor);
1032 ancestor->StopWatching(this);
1037 void
1038 PathHandler::_EntryCreated(BMessage* message)
1040 // TODO: Unless we're watching files only, we might want to forward (some
1041 // of) the messages that don't agree with our model, since our client
1042 // maintains its model at a different time and the notification might be
1043 // necessary to keep it up-to-date. E.g. consider the following case:
1044 // 1. a directory is created
1045 // 2. a file is created in the directory
1046 // 3. the file is removed from the directory
1047 // If we get the notification after 1. and before 2., we pass it on to the
1048 // client, which may get it after 2. and before 3., thus seeing the file.
1049 // If we then get the entry-created notification after 3., we don't see the
1050 // file anymore and ignore the notification as well as the following
1051 // entry-removed notification. That is the client will never know that the
1052 // file has been removed. This can only happen in recursive mode. Otherwise
1053 // (and with B_WATCH_DIRECTORY) we just pass on all notifications.
1054 // A possible solution could be to just create a zombie entry and pass on
1055 // the entry-created notification. We wouldn't be able to adhere to the
1056 // B_WATCH_FILES_ONLY/B_WATCH_DIRECTORIES_ONLY flags, but that should be
1057 // acceptable. Either the client hasn't seen the entry either -- then it
1058 // doesn't matter -- or it likely has ignored a not matching entry anyway.
1060 NotOwningEntryRef entryRef;
1061 node_ref nodeRef;
1063 if (message->FindInt32("device", &nodeRef.device) != B_OK
1064 || message->FindInt64("node", &nodeRef.node) != B_OK
1065 || message->FindInt64("directory", &entryRef.directory) != B_OK
1066 || message->FindString("name", (const char**)&entryRef.name) != B_OK) {
1067 return;
1069 entryRef.device = nodeRef.device;
1071 if (_CheckDuplicateEntryNotification(B_ENTRY_CREATED, entryRef, nodeRef))
1072 return;
1074 TRACE("%p->PathHandler::_EntryCreated(): entry: %" B_PRIdDEV ":%" B_PRIdINO
1075 ":\"%s\", node: %" B_PRIdDEV ":%" B_PRIdINO "\n", this, entryRef.device,
1076 entryRef.directory, entryRef.name, nodeRef.device, nodeRef.node);
1078 BEntry entry;
1079 struct stat st;
1080 if (entry.SetTo(&entryRef) != B_OK || entry.GetStat(&st) != B_OK
1081 || nodeRef != node_ref(st.st_dev, st.st_ino)) {
1082 return;
1085 _EntryCreated(entryRef, nodeRef, S_ISDIR(st.st_mode), false, true, NULL);
1089 void
1090 PathHandler::_EntryRemoved(BMessage* message)
1092 NotOwningEntryRef entryRef;
1093 node_ref nodeRef;
1095 if (message->FindInt32("device", &nodeRef.device) != B_OK
1096 || message->FindInt64("node", &nodeRef.node) != B_OK
1097 || message->FindInt64("directory", &entryRef.directory) != B_OK
1098 || message->FindString("name", (const char**)&entryRef.name) != B_OK) {
1099 return;
1101 entryRef.device = nodeRef.device;
1103 if (_CheckDuplicateEntryNotification(B_ENTRY_REMOVED, entryRef, nodeRef))
1104 return;
1106 TRACE("%p->PathHandler::_EntryRemoved(): entry: %" B_PRIdDEV ":%" B_PRIdINO
1107 ":\"%s\", node: %" B_PRIdDEV ":%" B_PRIdINO "\n", this, entryRef.device,
1108 entryRef.directory, entryRef.name, nodeRef.device, nodeRef.node);
1110 _EntryRemoved(entryRef, nodeRef, false, true, NULL);
1114 void
1115 PathHandler::_EntryMoved(BMessage* message)
1117 NotOwningEntryRef fromEntryRef;
1118 NotOwningEntryRef toEntryRef;
1119 node_ref nodeRef;
1121 if (message->FindInt32("node device", &nodeRef.device) != B_OK
1122 || message->FindInt64("node", &nodeRef.node) != B_OK
1123 || message->FindInt32("device", &fromEntryRef.device) != B_OK
1124 || message->FindInt64("from directory", &fromEntryRef.directory) != B_OK
1125 || message->FindInt64("to directory", &toEntryRef.directory) != B_OK
1126 || message->FindString("from name", (const char**)&fromEntryRef.name)
1127 != B_OK
1128 || message->FindString("name", (const char**)&toEntryRef.name)
1129 != B_OK) {
1130 return;
1132 toEntryRef.device = fromEntryRef.device;
1134 if (_CheckDuplicateEntryNotification(B_ENTRY_MOVED, toEntryRef, nodeRef,
1135 &fromEntryRef)) {
1136 return;
1139 TRACE("%p->PathHandler::_EntryMoved(): entry: %" B_PRIdDEV ":%" B_PRIdINO
1140 ":\"%s\" -> %" B_PRIdDEV ":%" B_PRIdINO ":\"%s\", node: %" B_PRIdDEV
1141 ":%" B_PRIdINO "\n", this, fromEntryRef.device, fromEntryRef.directory,
1142 fromEntryRef.name, toEntryRef.device, toEntryRef.directory,
1143 toEntryRef.name, nodeRef.device, nodeRef.node);
1145 BEntry entry;
1146 struct stat st;
1147 if (entry.SetTo(&toEntryRef) != B_OK || entry.GetStat(&st) != B_OK
1148 || nodeRef != node_ref(st.st_dev, st.st_ino)) {
1149 _EntryRemoved(fromEntryRef, nodeRef, false, true, NULL);
1150 return;
1152 bool isDirectory = S_ISDIR(st.st_mode);
1154 Ancestor* fromAncestor = _GetAncestor(fromEntryRef.DirectoryNodeRef());
1155 Ancestor* toAncestor = _GetAncestor(toEntryRef.DirectoryNodeRef());
1157 if (_WatchRecursively()) {
1158 Node* fromDirectoryNode = _GetNode(fromEntryRef.DirectoryNodeRef());
1159 Node* toDirectoryNode = _GetNode(toEntryRef.DirectoryNodeRef());
1160 if (fromDirectoryNode != NULL || toDirectoryNode != NULL) {
1161 // Check whether _EntryRemoved()/_EntryCreated() can handle the
1162 // respective entry regularly (i.e. don't encounter an out-of-sync
1163 // issue) or don't need to be called at all (entry outside the
1164 // monitored tree).
1165 if ((fromDirectoryNode == NULL
1166 || _EntryRemoved(fromEntryRef, nodeRef, true, false, NULL))
1167 && (toDirectoryNode == NULL
1168 || _EntryCreated(toEntryRef, nodeRef, isDirectory, true,
1169 false, NULL))) {
1170 // The entries can be handled regularly. We delegate the work to
1171 // _EntryRemoved() and _EntryCreated() and only handle the
1172 // notification ourselves.
1174 // handle removed
1175 Entry* removedEntry = NULL;
1176 if (fromDirectoryNode != NULL) {
1177 _EntryRemoved(fromEntryRef, nodeRef, false, false,
1178 &removedEntry);
1181 // handle created
1182 Entry* createdEntry = NULL;
1183 if (toDirectoryNode != NULL) {
1184 _EntryCreated(toEntryRef, nodeRef, isDirectory, false,
1185 false, &createdEntry);
1188 // notify
1189 if (_WatchFilesOnly() && isDirectory) {
1190 // recursively iterate through the removed and created
1191 // hierarchy and send notifications for the files
1192 if (removedEntry != NULL) {
1193 _NotifyFilesCreatedOrRemoved(removedEntry,
1194 B_ENTRY_REMOVED);
1197 if (createdEntry != NULL) {
1198 _NotifyFilesCreatedOrRemoved(createdEntry,
1199 B_ENTRY_CREATED);
1201 } else {
1202 BString fromPath;
1203 if (fromDirectoryNode != NULL) {
1204 fromPath = make_path(_NodePath(fromDirectoryNode),
1205 fromEntryRef.name);
1208 BString path;
1209 if (toDirectoryNode != NULL) {
1210 path = make_path(_NodePath(toDirectoryNode),
1211 toEntryRef.name);
1214 _NotifyEntryMoved(fromEntryRef, toEntryRef, nodeRef,
1215 fromPath, path, isDirectory, fromDirectoryNode == NULL,
1216 toDirectoryNode == NULL);
1219 if (removedEntry != NULL)
1220 _DeleteEntry(removedEntry, false);
1221 } else {
1222 // The entries can't be handled regularly. We delegate all the
1223 // work to _EntryRemoved() and _EntryCreated(). This will
1224 // generate separate entry-removed and entry-created
1225 // notifications.
1227 // handle removed
1228 if (fromDirectoryNode != NULL)
1229 _EntryRemoved(fromEntryRef, nodeRef, false, true, NULL);
1231 // handle created
1232 if (toDirectoryNode != NULL) {
1233 _EntryCreated(toEntryRef, nodeRef, isDirectory, false, true,
1234 NULL);
1238 return;
1241 if (fromAncestor == fBaseAncestor || toAncestor == fBaseAncestor) {
1242 // That should never happen, as we should have found a matching
1243 // directory node in this case.
1244 #ifdef DEBUG
1245 debugger("path ancestor exists, but doesn't have a directory");
1246 // Could actually be an out-of-memory situation, if we simply failed
1247 // to create the directory earlier.
1248 #endif
1249 _StopWatchingAncestors(fRoot, false);
1250 _StartWatchingAncestors(fRoot, false);
1251 return;
1253 } else {
1254 // Non-recursive mode: This notification is only of interest to us, if
1255 // it is either a move into/within/out of the path and B_WATCH_DIRECTORY
1256 // is set, or an ancestor might be affected.
1257 if (fromAncestor == NULL && toAncestor == NULL)
1258 return;
1260 if (fromAncestor == fBaseAncestor || toAncestor == fBaseAncestor) {
1261 if ((fFlags & B_WATCH_DIRECTORY) != 0) {
1262 BString fromPath;
1263 if (fromAncestor == fBaseAncestor)
1264 fromPath = make_path(fPath, fromEntryRef.name);
1266 BString path;
1267 if (toAncestor == fBaseAncestor)
1268 path = make_path(fPath, toEntryRef.name);
1270 _NotifyEntryMoved(fromEntryRef, toEntryRef, nodeRef,
1271 fromPath, path, isDirectory, fromAncestor == NULL,
1272 toAncestor == NULL);
1274 return;
1278 if (fromAncestor == NULL && toAncestor == NULL)
1279 return;
1281 if (fromAncestor == NULL) {
1282 _EntryCreated(toEntryRef, nodeRef, isDirectory, false, true, NULL);
1283 return;
1286 if (toAncestor == NULL) {
1287 _EntryRemoved(fromEntryRef, nodeRef, false, true, NULL);
1288 return;
1291 // An entry was moved in a true ancestor directory or between true ancestor
1292 // directories. Unless the moved entry was or becomes our base ancestor, we
1293 // let _EntryRemoved() and _EntryCreated() handle it.
1294 bool fromIsBase = fromAncestor == fBaseAncestor->Parent()
1295 && strcmp(fromEntryRef.name, fBaseAncestor->Name()) == 0;
1296 bool toIsBase = toAncestor == fBaseAncestor->Parent()
1297 && strcmp(toEntryRef.name, fBaseAncestor->Name()) == 0;
1298 if (fromIsBase || toIsBase) {
1299 // This might be a duplicate notification. Check whether our model
1300 // already reflects the change. Otherwise stop/start watching the base
1301 // ancestor as required.
1302 bool notifyFilesRecursively = _WatchFilesOnly() && isDirectory;
1303 if (fromIsBase) {
1304 if (!fBaseAncestor->Exists())
1305 return;
1306 _StopWatchingAncestors(fBaseAncestor, notifyFilesRecursively);
1307 } else {
1308 if (fBaseAncestor->Exists()) {
1309 if (fBaseAncestor->NodeRef() == nodeRef
1310 && isDirectory == fBaseAncestor->IsDirectory()) {
1311 return;
1314 // We're out of sync with reality.
1315 _StopWatchingAncestors(fBaseAncestor, true);
1316 _StartWatchingAncestors(fBaseAncestor, true);
1317 return;
1320 _StartWatchingAncestors(fBaseAncestor, notifyFilesRecursively);
1323 if (!notifyFilesRecursively) {
1324 _NotifyEntryMoved(fromEntryRef, toEntryRef, nodeRef,
1325 fromIsBase ? fPath.String() : NULL,
1326 toIsBase ? fPath.String() : NULL,
1327 isDirectory, toIsBase, fromIsBase);
1329 return;
1332 _EntryRemoved(fromEntryRef, nodeRef, false, true, NULL);
1333 _EntryCreated(toEntryRef, nodeRef, isDirectory, false, true, NULL);
1337 void
1338 PathHandler::_NodeChanged(BMessage* message)
1340 node_ref nodeRef;
1342 if (message->FindInt32("device", &nodeRef.device) != B_OK
1343 || message->FindInt64("node", &nodeRef.node) != B_OK) {
1344 return;
1347 TRACE("%p->PathHandler::_NodeChanged(): node: %" B_PRIdDEV ":%" B_PRIdINO
1348 ", %s%s\n", this, nodeRef.device, nodeRef.node,
1349 message->GetInt32("opcode", B_STAT_CHANGED) == B_ATTR_CHANGED
1350 ? "attribute: " : "stat",
1351 message->GetInt32("opcode", B_STAT_CHANGED) == B_ATTR_CHANGED
1352 ? message->GetString("attr", "") : "");
1354 bool isDirectory = false;
1355 BString path;
1356 if (Ancestor* ancestor = _GetAncestor(nodeRef)) {
1357 if (ancestor != fBaseAncestor)
1358 return;
1359 isDirectory = ancestor->IsDirectory();
1360 path = fPath;
1361 } else if (Node* node = _GetNode(nodeRef)) {
1362 isDirectory = node->IsDirectory();
1363 path = _NodePath(node);
1364 } else
1365 return;
1367 if (isDirectory ? _WatchFilesOnly() : _WatchDirectoriesOnly())
1368 return;
1370 _NotifyTarget(*message, path);
1374 bool
1375 PathHandler::_EntryCreated(const NotOwningEntryRef& entryRef,
1376 const node_ref& nodeRef, bool isDirectory, bool dryRun, bool notify,
1377 Entry** _entry)
1379 if (_entry != NULL)
1380 *_entry = NULL;
1382 Ancestor* ancestor = _GetAncestor(nodeRef);
1383 if (ancestor != NULL) {
1384 if (isDirectory == ancestor->IsDirectory()
1385 && entryRef == ancestor->EntryRef()) {
1386 // just a duplicate notification
1387 TRACE(" -> we already know the ancestor\n");
1388 return true;
1391 struct stat ancestorStat;
1392 if (BEntry(&ancestor->EntryRef()).GetStat(&ancestorStat) == B_OK
1393 && node_ref(ancestorStat.st_dev, ancestorStat.st_ino)
1394 == ancestor->NodeRef()
1395 && S_ISDIR(ancestorStat.st_mode) == ancestor->IsDirectory()) {
1396 // Our information for the ancestor is up-to-date, so ignore the
1397 // notification.
1398 TRACE(" -> we know a different ancestor, but our info is "
1399 "up-to-date\n");
1400 return true;
1403 // We're out of sync with reality.
1404 TRACE(" -> ancestor mismatch -> resyncing\n");
1405 if (!dryRun) {
1406 _StopWatchingAncestors(ancestor, true);
1407 _StartWatchingAncestors(ancestor, true);
1409 return false;
1412 ancestor = _GetAncestor(entryRef.DirectoryNodeRef());
1413 if (ancestor != NULL) {
1414 if (ancestor != fBaseAncestor) {
1415 // The directory is a true ancestor -- the notification is only of
1416 // interest, if the entry matches the child ancestor.
1417 Ancestor* childAncestor = ancestor->Child();
1418 if (strcmp(entryRef.name, childAncestor->Name()) != 0) {
1419 TRACE(" -> not an ancestor entry we're interested in "
1420 "(\"%s\")\n", childAncestor->Name());
1421 return true;
1424 if (!dryRun) {
1425 if (childAncestor->Exists()) {
1426 TRACE(" ancestor entry mismatch -> resyncing\n");
1427 // We're out of sync with reality -- the new entry refers to
1428 // a different node.
1429 _StopWatchingAncestors(childAncestor, true);
1432 TRACE(" -> starting to watch newly appeared ancestor\n");
1433 _StartWatchingAncestors(childAncestor, true);
1435 return false;
1438 // The directory is our path. If watching recursively, just fall
1439 // through. Otherwise, we want to pass on the notification, if directory
1440 // watching is enabled.
1441 if (!_WatchRecursively()) {
1442 if ((fFlags & B_WATCH_DIRECTORY) != 0) {
1443 _NotifyEntryCreatedOrRemoved(entryRef, nodeRef,
1444 make_path(fPath, entryRef.name), isDirectory,
1445 B_ENTRY_CREATED);
1447 return true;
1451 if (!_WatchRecursively()) {
1452 // That shouldn't happen, since we only watch the ancestors in this
1453 // case.
1454 return true;
1457 Node* directoryNode = _GetNode(entryRef.DirectoryNodeRef());
1458 if (directoryNode == NULL)
1459 return true;
1461 Directory* directory = directoryNode->ToDirectory();
1462 if (directory == NULL) {
1463 // We're out of sync with reality.
1464 if (!dryRun) {
1465 if (Entry* nodeEntry = directory->FirstNodeEntry()) {
1466 // remove the entry that is in the way and re-add the proper
1467 // entry
1468 NotOwningEntryRef directoryEntryRef = nodeEntry->EntryRef();
1469 BString directoryName = nodeEntry->Name();
1470 _DeleteEntry(nodeEntry, true);
1471 _EntryCreated(directoryEntryRef, entryRef.DirectoryNodeRef(),
1472 true, false, notify, NULL);
1473 } else {
1474 // It's either the base node or something's severely fishy.
1475 // Resync the whole path.
1476 _StopWatchingAncestors(fBaseAncestor, true);
1477 _StartWatchingAncestors(fBaseAncestor, true);
1481 return false;
1484 // Check, if there's a colliding entry.
1485 if (Entry* nodeEntry = directory->FindEntry(entryRef.name)) {
1486 Node* entryNode = nodeEntry->Node();
1487 if (entryNode != NULL && entryNode->NodeRef() == nodeRef)
1488 return true;
1490 // We're out of sync with reality -- the new entry refers to a different
1491 // node.
1492 _DeleteEntry(nodeEntry, true);
1495 if (dryRun)
1496 return true;
1498 _AddEntryIfNeeded(directory, entryRef.name, nodeRef, isDirectory, notify,
1499 _entry);
1500 return true;
1504 bool
1505 PathHandler::_EntryRemoved(const NotOwningEntryRef& entryRef,
1506 const node_ref& nodeRef, bool dryRun, bool notify, Entry** _keepEntry)
1508 if (_keepEntry != NULL)
1509 *_keepEntry = NULL;
1511 Ancestor* ancestor = _GetAncestor(nodeRef);
1512 if (ancestor != NULL) {
1513 // The node is an ancestor. If this is a true match, stop watching the
1514 // ancestor.
1515 if (!ancestor->Exists())
1516 return true;
1518 if (entryRef != ancestor->EntryRef()) {
1519 // We might be out of sync with reality -- the new entry refers to a
1520 // different node.
1521 struct stat ancestorStat;
1522 if (BEntry(&ancestor->EntryRef()).GetStat(&ancestorStat) != B_OK) {
1523 if (!dryRun)
1524 _StopWatchingAncestors(ancestor, true);
1525 return false;
1528 if (node_ref(ancestorStat.st_dev, ancestorStat.st_ino)
1529 != ancestor->NodeRef()
1530 || S_ISDIR(ancestorStat.st_mode) != ancestor->IsDirectory()) {
1531 if (!dryRun) {
1532 _StopWatchingAncestors(ancestor, true);
1533 _StartWatchingAncestors(ancestor, true);
1535 return false;
1537 return true;
1540 if (!dryRun)
1541 _StopWatchingAncestors(ancestor, true);
1542 return false;
1545 ancestor = _GetAncestor(entryRef.DirectoryNodeRef());
1546 if (ancestor != NULL) {
1547 if (ancestor != fBaseAncestor) {
1548 // The directory is a true ancestor -- the notification cannot be
1549 // of interest, since the node didn't match a known ancestor.
1550 return true;
1553 // The directory is our path. If watching recursively, just fall
1554 // through. Otherwise, we want to pass on the notification, if directory
1555 // watching is enabled.
1556 if (!_WatchRecursively()) {
1557 if (notify && (fFlags & B_WATCH_DIRECTORY) != 0) {
1558 _NotifyEntryCreatedOrRemoved(entryRef, nodeRef,
1559 make_path(fPath, entryRef.name), false, B_ENTRY_REMOVED);
1560 // We don't know whether this was a directory, but it
1561 // doesn't matter in this case.
1563 return true;
1567 if (!_WatchRecursively()) {
1568 // That shouldn't happen, since we only watch the ancestors in this
1569 // case.
1570 return true;
1573 Node* directoryNode = _GetNode(entryRef.DirectoryNodeRef());
1574 if (directoryNode == NULL) {
1575 // We shouldn't get a notification, if we don't known the directory.
1576 return true;
1579 Directory* directory = directoryNode->ToDirectory();
1580 if (directory == NULL) {
1581 // We might be out of sync with reality or the notification is just
1582 // late. The former case is extremely unlikely (we are watching the node
1583 // and its parent directory after all) and rather hard to verify.
1584 return true;
1587 Entry* nodeEntry = directory->FindEntry(entryRef.name);
1588 if (nodeEntry == NULL) {
1589 // might be a non-directory node while we're in directories-only mode
1590 return true;
1593 if (!dryRun) {
1594 if (_keepEntry != NULL)
1595 *_keepEntry = nodeEntry;
1596 else
1597 _DeleteEntry(nodeEntry, notify);
1599 return true;
1603 bool
1604 PathHandler::_CheckDuplicateEntryNotification(int32 opcode,
1605 const entry_ref& toEntryRef, const node_ref& nodeRef,
1606 const entry_ref* fromEntryRef)
1608 if (opcode == fDuplicateEntryNotificationOpcode
1609 && nodeRef == fDuplicateEntryNotificationNodeRef
1610 && toEntryRef == fDuplicateEntryNotificationToEntryRef
1611 && (fromEntryRef == NULL
1612 || *fromEntryRef == fDuplicateEntryNotificationFromEntryRef)) {
1613 return true;
1616 fDuplicateEntryNotificationOpcode = opcode;
1617 fDuplicateEntryNotificationNodeRef = nodeRef;
1618 fDuplicateEntryNotificationToEntryRef = toEntryRef;
1619 fDuplicateEntryNotificationFromEntryRef = fromEntryRef != NULL
1620 ? *fromEntryRef : entry_ref();
1621 return false;
1625 void
1626 PathHandler::_UnsetDuplicateEntryNotification()
1628 fDuplicateEntryNotificationOpcode = B_STAT_CHANGED;
1629 fDuplicateEntryNotificationNodeRef = node_ref();
1630 fDuplicateEntryNotificationFromEntryRef = entry_ref();
1631 fDuplicateEntryNotificationToEntryRef = entry_ref();
1635 Ancestor*
1636 PathHandler::_GetAncestor(const node_ref& nodeRef) const
1638 return fAncestors.Lookup(nodeRef);
1642 status_t
1643 PathHandler::_AddNode(const node_ref& nodeRef, bool isDirectory, bool notify,
1644 Entry* entry, Node** _node)
1646 TRACE("%p->PathHandler::_AddNode(%" B_PRIdDEV ":%" B_PRIdINO
1647 ", isDirectory: %d, notify: %d)\n", this, nodeRef.device, nodeRef.node,
1648 isDirectory, notify);
1650 // If hard links are supported, we may already know the node.
1651 Node* node = _GetNode(nodeRef);
1652 if (node != NULL) {
1653 if (entry != NULL) {
1654 entry->SetNode(node);
1655 node->AddNodeEntry(entry);
1658 if (_node != NULL)
1659 *_node = node;
1660 return B_OK;
1663 // create the node
1664 Directory* directoryNode = NULL;
1665 if (isDirectory)
1666 node = directoryNode = Directory::Create(nodeRef);
1667 else
1668 node = new(std::nothrow) Node(nodeRef);
1670 if (node == NULL)
1671 return B_NO_MEMORY;
1673 ObjectDeleter<Node> nodeDeleter(node);
1675 // start watching (don't do that for the base node, since we watch it
1676 // already via fBaseAncestor)
1677 if (nodeRef != fBaseAncestor->NodeRef()) {
1678 uint32 flags = (fFlags & WATCH_NODE_FLAG_MASK) | B_WATCH_DIRECTORY;
1679 status_t error = sWatchingInterface->WatchNode(&nodeRef, flags, this);
1680 if (error != B_OK)
1681 return error;
1684 fNodes.Insert(nodeDeleter.Detach());
1686 if (entry != NULL) {
1687 entry->SetNode(node);
1688 node->AddNodeEntry(entry);
1691 if (_node != NULL)
1692 *_node = node;
1694 if (!isDirectory)
1695 return B_OK;
1697 // recursively add the directory's descendents
1698 BDirectory directory;
1699 if (directory.SetTo(&nodeRef) != B_OK) {
1700 if (_node != NULL)
1701 *_node = node;
1702 return B_OK;
1705 entry_ref entryRef;
1706 while (directory.GetNextRef(&entryRef) == B_OK) {
1707 struct stat st;
1708 if (BEntry(&entryRef).GetStat(&st) != B_OK)
1709 continue;
1711 bool isDirectory = S_ISDIR(st.st_mode);
1712 status_t error = _AddEntryIfNeeded(directoryNode, entryRef.name,
1713 node_ref(st.st_dev, st.st_ino), isDirectory, notify);
1714 if (error != B_OK) {
1715 TRACE("%p->PathHandler::_AddNode(%" B_PRIdDEV ":%" B_PRIdINO
1716 ", isDirectory: %d, notify: %d): failed to add directory "
1717 "entry: \"%s\"\n", this, nodeRef.device, nodeRef.node,
1718 isDirectory, notify, entryRef.name);
1719 continue;
1723 return B_OK;
1727 void
1728 PathHandler::_DeleteNode(Node* node, bool notify)
1730 if (Directory* directory = node->ToDirectory()) {
1731 Entry* entry = directory->RemoveAllEntries();
1732 while (entry != NULL) {
1733 Entry* nextEntry = entry->HashNext();
1734 _DeleteEntryAlreadyRemovedFromParent(entry, notify);
1735 entry = nextEntry;
1739 if (node->NodeRef() != fBaseAncestor->NodeRef())
1740 sWatchingInterface->WatchNode(&node->NodeRef(), B_STOP_WATCHING, this);
1742 fNodes.Remove(node);
1743 delete node;
1747 Node*
1748 PathHandler::_GetNode(const node_ref& nodeRef) const
1750 return fNodes.Lookup(nodeRef);
1754 status_t
1755 PathHandler::_AddEntryIfNeeded(Directory* directory, const char* name,
1756 const node_ref& nodeRef, bool isDirectory, bool notify,
1757 Entry** _entry)
1759 TRACE("%p->PathHandler::_AddEntryIfNeeded(%" B_PRIdDEV ":%" B_PRIdINO
1760 ":\"%s\", %" B_PRIdDEV ":%" B_PRIdINO
1761 ", isDirectory: %d, notify: %d)\n", this, directory->NodeRef().device,
1762 directory->NodeRef().node, name, nodeRef.device, nodeRef.node,
1763 isDirectory, notify);
1765 if (!isDirectory && _WatchDirectoriesOnly()) {
1766 if (_entry != NULL)
1767 *_entry = NULL;
1768 return B_OK;
1771 Entry* entry = directory->CreateEntry(name, NULL);
1772 if (entry == NULL)
1773 return B_NO_MEMORY;
1775 status_t error = _AddNode(nodeRef, isDirectory, notify && _WatchFilesOnly(),
1776 entry);
1777 if (error != B_OK) {
1778 directory->RemoveEntry(entry);
1779 delete entry;
1780 return error;
1783 if (notify)
1784 _NotifyEntryCreatedOrRemoved(entry, B_ENTRY_CREATED);
1786 if (_entry != NULL)
1787 *_entry = entry;
1788 return B_OK;
1792 void
1793 PathHandler::_DeleteEntry(Entry* entry, bool notify)
1795 entry->Parent()->RemoveEntry(entry);
1796 _DeleteEntryAlreadyRemovedFromParent(entry, notify);
1800 void
1801 PathHandler::_DeleteEntryAlreadyRemovedFromParent(Entry* entry, bool notify)
1803 if (notify)
1804 _NotifyEntryCreatedOrRemoved(entry, B_ENTRY_REMOVED);
1806 Node* node = entry->Node();
1807 if (node->IsOnlyNodeEntry(entry))
1808 _DeleteNode(node, notify && _WatchFilesOnly());
1810 delete entry;
1814 void
1815 PathHandler::_NotifyFilesCreatedOrRemoved(Entry* entry, int32 opcode) const
1817 Directory* directory = entry->Node()->ToDirectory();
1818 if (directory == NULL) {
1819 _NotifyEntryCreatedOrRemoved(entry, opcode);
1820 return;
1823 for (EntryMap::Iterator it = directory->GetEntryIterator(); it.HasNext();)
1824 _NotifyFilesCreatedOrRemoved(it.Next(), opcode);
1828 void
1829 PathHandler::_NotifyEntryCreatedOrRemoved(Entry* entry, int32 opcode) const
1831 Node* node = entry->Node();
1832 _NotifyEntryCreatedOrRemoved(
1833 NotOwningEntryRef(entry->Parent()->NodeRef(), entry->Name()),
1834 node->NodeRef(), _EntryPath(entry), node->IsDirectory(), opcode);
1838 void
1839 PathHandler::_NotifyEntryCreatedOrRemoved(const entry_ref& entryRef,
1840 const node_ref& nodeRef, const char* path, bool isDirectory, int32 opcode)
1841 const
1843 if (isDirectory ? _WatchFilesOnly() : _WatchDirectoriesOnly())
1844 return;
1846 TRACE("%p->PathHandler::_NotifyEntryCreatedOrRemoved(): entry %s: %"
1847 B_PRIdDEV ":%" B_PRIdINO ":\"%s\", node: %" B_PRIdDEV ":%" B_PRIdINO
1848 "\n", this, opcode == B_ENTRY_CREATED ? "created" : "removed",
1849 entryRef.device, entryRef.directory, entryRef.name, nodeRef.device,
1850 nodeRef.node);
1852 BMessage message(B_PATH_MONITOR);
1853 message.AddInt32("opcode", opcode);
1854 message.AddInt32("device", entryRef.device);
1855 message.AddInt64("directory", entryRef.directory);
1856 message.AddInt32("node device", nodeRef.device);
1857 // This field is not in a usual node monitoring message, since the node
1858 // the created/removed entry refers to always belongs to the same FS as
1859 // the directory, as another FS cannot yet/no longer be mounted there.
1860 // In our case, however, this can very well be the case, e.g. when the
1861 // the notification is triggered in response to a directory tree having
1862 // been moved into/out of our path.
1863 message.AddInt64("node", nodeRef.node);
1864 message.AddString("name", entryRef.name);
1866 _NotifyTarget(message, path);
1870 void
1871 PathHandler::_NotifyEntryMoved(const entry_ref& fromEntryRef,
1872 const entry_ref& toEntryRef, const node_ref& nodeRef, const char* fromPath,
1873 const char* path, bool isDirectory, bool wasAdded, bool wasRemoved) const
1875 if ((isDirectory && _WatchFilesOnly())
1876 || (!isDirectory && _WatchDirectoriesOnly())) {
1877 return;
1880 TRACE("%p->PathHandler::_NotifyEntryMoved(): entry: %" B_PRIdDEV ":%"
1881 B_PRIdINO ":\"%s\" -> %" B_PRIdDEV ":%" B_PRIdINO ":\"%s\", node: %"
1882 B_PRIdDEV ":%" B_PRIdINO "\n", this, fromEntryRef.device,
1883 fromEntryRef.directory, fromEntryRef.name, toEntryRef.device,
1884 toEntryRef.directory, toEntryRef.name, nodeRef.device, nodeRef.node);
1886 BMessage message(B_PATH_MONITOR);
1887 message.AddInt32("opcode", B_ENTRY_MOVED);
1888 message.AddInt32("device", fromEntryRef.device);
1889 message.AddInt64("from directory", fromEntryRef.directory);
1890 message.AddInt64("to directory", toEntryRef.directory);
1891 message.AddInt32("node device", nodeRef.device);
1892 message.AddInt64("node", nodeRef.node);
1893 message.AddString("from name", fromEntryRef.name);
1894 message.AddString("name", toEntryRef.name);
1896 if (wasAdded)
1897 message.AddBool("added", true);
1898 if (wasRemoved)
1899 message.AddBool("removed", true);
1901 if (fromPath != NULL && fromPath[0] != '\0')
1902 message.AddString("from path", fromPath);
1904 _NotifyTarget(message, path);
1908 void
1909 PathHandler::_NotifyTarget(BMessage& message, const char* path) const
1911 message.what = B_PATH_MONITOR;
1912 if (path != NULL && path[0] != '\0')
1913 message.AddString("path", path);
1914 message.AddString("watched_path", fPath.String());
1915 fTarget.SendMessage(&message);
1920 BString
1921 PathHandler::_NodePath(const Node* node) const
1923 if (Entry* entry = node->FirstNodeEntry())
1924 return _EntryPath(entry);
1925 return node == fBaseNode ? fPath : BString();
1929 BString
1930 PathHandler::_EntryPath(const Entry* entry) const
1932 return make_path(_NodePath(entry->Parent()), entry->Name());
1936 bool
1937 PathHandler::_WatchRecursively() const
1939 return (fFlags & B_WATCH_RECURSIVELY) != 0;
1943 bool
1944 PathHandler::_WatchFilesOnly() const
1946 return (fFlags & B_WATCH_FILES_ONLY) != 0;
1950 bool
1951 PathHandler::_WatchDirectoriesOnly() const
1953 return (fFlags & B_WATCH_DIRECTORIES_ONLY) != 0;
1957 } // namespace
1960 // #pragma mark - BPathMonitor
1963 namespace BPrivate {
1966 BPathMonitor::BPathMonitor()
1971 BPathMonitor::~BPathMonitor()
1976 /*static*/ status_t
1977 BPathMonitor::StartWatching(const char* path, uint32 flags,
1978 const BMessenger& target)
1980 TRACE("BPathMonitor::StartWatching(%s, %" B_PRIx32 ")\n", path, flags);
1982 if (path == NULL || path[0] == '\0')
1983 return B_BAD_VALUE;
1985 // B_WATCH_FILES_ONLY and B_WATCH_DIRECTORIES_ONLY are mutual exclusive
1986 if ((flags & B_WATCH_FILES_ONLY) != 0
1987 && (flags & B_WATCH_DIRECTORIES_ONLY) != 0) {
1988 return B_BAD_VALUE;
1991 status_t status = _InitIfNeeded();
1992 if (status != B_OK)
1993 return status;
1995 BAutolock _(sLooper);
1997 Watcher* watcher = sWatchers->Lookup(target);
1998 bool newWatcher = false;
1999 if (watcher != NULL) {
2000 // If there's already a handler for the path, we'll replace it, but
2001 // add its flags.
2002 if (PathHandler* handler = watcher->Lookup(path)) {
2003 // keep old flags save for conflicting mutually exclusive ones
2004 uint32 oldFlags = handler->Flags();
2005 const uint32 kMutuallyExclusiveFlags
2006 = B_WATCH_FILES_ONLY | B_WATCH_DIRECTORIES_ONLY;
2007 if ((flags & kMutuallyExclusiveFlags) != 0)
2008 oldFlags &= ~(uint32)kMutuallyExclusiveFlags;
2009 flags |= oldFlags;
2011 watcher->Remove(handler);
2012 handler->Quit();
2014 } else {
2015 watcher = Watcher::Create(target);
2016 if (watcher == NULL)
2017 return B_NO_MEMORY;
2018 sWatchers->Insert(watcher);
2019 newWatcher = true;
2022 PathHandler* handler = new (std::nothrow) PathHandler(path, flags, target,
2023 sLooper);
2024 status = handler != NULL ? handler->InitCheck() : B_NO_MEMORY;
2026 if (status != B_OK) {
2027 if (handler != NULL)
2028 handler->Quit();
2030 if (newWatcher) {
2031 sWatchers->Remove(watcher);
2032 delete watcher;
2034 return status;
2037 watcher->Insert(handler);
2038 return B_OK;
2042 /*static*/ status_t
2043 BPathMonitor::StopWatching(const char* path, const BMessenger& target)
2045 if (sLooper == NULL)
2046 return B_BAD_VALUE;
2048 TRACE("BPathMonitor::StopWatching(%s)\n", path);
2050 BAutolock _(sLooper);
2052 Watcher* watcher = sWatchers->Lookup(target);
2053 if (watcher == NULL)
2054 return B_BAD_VALUE;
2056 PathHandler* handler = watcher->Lookup(path);
2057 if (handler == NULL)
2058 return B_BAD_VALUE;
2060 watcher->Remove(handler);
2061 handler->Quit();
2063 if (watcher->IsEmpty()) {
2064 sWatchers->Remove(watcher);
2065 delete watcher;
2068 return B_OK;
2072 /*static*/ status_t
2073 BPathMonitor::StopWatching(const BMessenger& target)
2075 if (sLooper == NULL)
2076 return B_BAD_VALUE;
2078 BAutolock _(sLooper);
2080 Watcher* watcher = sWatchers->Lookup(target);
2081 if (watcher == NULL)
2082 return B_BAD_VALUE;
2084 // delete handlers
2085 PathHandler* handler = watcher->Clear(true);
2086 while (handler != NULL) {
2087 PathHandler* nextHandler = handler->HashNext();
2088 handler->Quit();
2089 handler = nextHandler;
2092 sWatchers->Remove(watcher);
2093 delete watcher;
2095 return B_OK;
2099 /*static*/ void
2100 BPathMonitor::SetWatchingInterface(BWatchingInterface* watchingInterface)
2102 sWatchingInterface = watchingInterface != NULL
2103 ? watchingInterface : sDefaultWatchingInterface;
2107 /*static*/ status_t
2108 BPathMonitor::_InitIfNeeded()
2110 pthread_once(&sInitOnce, &BPathMonitor::_Init);
2111 return sLooper != NULL ? B_OK : B_NO_MEMORY;
2115 /*static*/ void
2116 BPathMonitor::_Init()
2118 sDefaultWatchingInterface = new(std::nothrow) BWatchingInterface;
2119 if (sDefaultWatchingInterface == NULL)
2120 return;
2122 sWatchers = new(std::nothrow) WatcherMap;
2123 if (sWatchers == NULL || sWatchers->Init() != B_OK)
2124 return;
2126 if (sWatchingInterface == NULL)
2127 SetWatchingInterface(sDefaultWatchingInterface);
2129 BLooper* looper = new (std::nothrow) BLooper("PathMonitor looper");
2130 TRACE("Start PathMonitor looper\n");
2131 if (looper == NULL)
2132 return;
2133 thread_id thread = looper->Run();
2134 if (thread < 0) {
2135 delete looper;
2136 return;
2139 sLooper = looper;
2143 // #pragma mark - BWatchingInterface
2146 BPathMonitor::BWatchingInterface::BWatchingInterface()
2151 BPathMonitor::BWatchingInterface::~BWatchingInterface()
2156 status_t
2157 BPathMonitor::BWatchingInterface::WatchNode(const node_ref* node, uint32 flags,
2158 const BMessenger& target)
2160 return watch_node(node, flags, target);
2164 status_t
2165 BPathMonitor::BWatchingInterface::WatchNode(const node_ref* node, uint32 flags,
2166 const BHandler* handler, const BLooper* looper)
2168 return watch_node(node, flags, handler, looper);
2172 status_t
2173 BPathMonitor::BWatchingInterface::StopWatching(const BMessenger& target)
2175 return stop_watching(target);
2179 status_t
2180 BPathMonitor::BWatchingInterface::StopWatching(const BHandler* handler,
2181 const BLooper* looper)
2183 return stop_watching(handler, looper);
2187 } // namespace BPrivate