2 * Copyright 2007-2013, Haiku, Inc. All Rights Reserved.
3 * Distributed under the terms of the MIT License.
6 * Axel Dörfler, axeld@pinc-software.de
7 * Stephan Aßmus, superstippi@gmx.de
8 * Ingo Weinhold, ingo_weinhold@gmx.de
12 #include <PathMonitor.h>
18 #include <Directory.h>
26 #include <AutoDeleter.h>
27 #include <NotOwningEntryRef.h>
28 #include <ObjectList.h>
29 #include <util/OpenHashTable.h>
30 #include <util/SinglyLinkedList.h>
34 //#define TRACE_PATH_MONITOR
35 #ifdef TRACE_PATH_MONITOR
36 # define TRACE(...) debug_printf("BPathMonitor: " __VA_ARGS__)
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
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
;
69 /*! Returns empty path, if either \a parent or \a subPath is empty or an
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')
80 if (parent
.ByteAt(length
- 1) != '/') {
82 if (path
.Length() < ++length
)
87 if (path
.Length() <= length
)
93 // #pragma mark - Ancestor
98 Ancestor(Ancestor
* parent
, const BString
& path
, size_t pathComponentOffset
)
103 fEntryRef(-1, -1, fPath
.String() + pathComponentOffset
),
108 if (pathComponentOffset
== 0) {
110 fEntryRef
.SetTo(-1, -1, ".");
114 fParent
->fChild
= this;
117 Ancestor
* Parent() const
122 Ancestor
* Child() const
127 const BString
& Path() const
132 const char* Name() const
134 return fEntryRef
.name
;
139 return fNodeRef
.device
>= 0;
142 const NotOwningEntryRef
& EntryRef() const
147 const node_ref
& NodeRef() const
152 bool IsDirectory() const
157 status_t
StartWatching(uint32 pathFlags
, BHandler
* target
)
161 status_t error
= entry
.SetTo(fPath
);
166 error
= entry
.GetRef(&entryRef
);
170 fEntryRef
.device
= entryRef
.device
;
171 fEntryRef
.directory
= entryRef
.directory
;
175 error
= entry
.GetStat(&st
);
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
);
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.
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
));
196 fWatchingFlags
= flags
;
200 void StopWatching(BHandler
* target
)
203 if (fWatchingFlags
!= 0) {
204 sWatchingInterface
->WatchNode(&fNodeRef
, B_STOP_WATCHING
, target
);
208 // uninitialize node and entry ref
209 fIsDirectory
= false;
210 fNodeRef
= node_ref();
211 fEntryRef
.SetDirectoryNodeRef(node_ref());
214 Ancestor
*& HashNext()
224 NotOwningEntryRef fEntryRef
;
226 uint32 fWatchingFlags
;
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
> {
268 Entry(Directory
* parent
, const BString
& name
, ::Node
* node
)
276 Directory
* Parent() const
281 const BString
& Name() const
291 void SetNode(::Node
* node
)
296 inline NotOwningEntryRef
EntryRef() const;
310 typedef SinglyLinkedList
<Entry
> EntryList
;
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
350 Node(const node_ref
& nodeRef
)
360 virtual bool IsDirectory() const
365 virtual Directory
* ToDirectory()
370 const node_ref
& NodeRef() const
375 const EntryList
& Entries() const
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
)
400 void RemoveNodeEntry(Entry
* entry
)
402 fEntries
.Remove(entry
);
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
{
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
) {
462 virtual bool IsDirectory() const
467 virtual Directory
* ToDirectory()
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()) {
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);
510 Directory(const node_ref
& nodeRef
)
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
{
536 PathHandler(const char* path
, uint32 flags
,
537 const BMessenger
& target
, BLooper
* looper
);
538 virtual ~PathHandler();
540 status_t
InitCheck() const;
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
; }
552 status_t
_CreateAncestors();
553 status_t
_StartWatchingAncestors(Ancestor
* ancestor
,
555 void _StopWatchingAncestors(Ancestor
* ancestor
,
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
,
594 void _NotifyEntryCreatedOrRemoved(Entry
* entry
,
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;
621 BString fOriginalPath
;
624 Ancestor
* fBaseAncestor
;
626 AncestorMap fAncestors
;
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
) {
679 const BMessenger
& Target() const
690 Watcher(const BMessenger
& target
)
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
)
746 TRACE("%p->PathHandler::PathHandler(\"%s\", %#" B_PRIx32
")\n", this, path
,
749 _UnsetDuplicateEntryNotification();
751 fStatus
= fAncestors
.Init();
755 fStatus
= fNodes
.Init();
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
763 fFlags
&= ~uint32(B_WATCH_NAME
| B_WATCH_DIRECTORY
);
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
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
780 BString normalizedPath
;
781 if (path
[0] == '/') {
782 normalizedPath
= "/";
785 normalizedPath
= BPath(".").Path();
786 if (normalizedPath
.IsEmpty()) {
787 fStatus
= B_NO_MEMORY
;
791 // parse path components
792 const char* pathEnd
= path
+ strlen(path
);
795 while (path
[0] == '/')
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) {
811 if (componentLength
== 2 && path
[1] == '.') {
812 fStatus
= B_BAD_VALUE
;
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
;
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();
841 // add ourselves to the looper
842 looper
->AddHandler(this);
845 fStatus
= _StartWatchingAncestors(fRoot
, false);
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();
862 fRoot
= nextAncestor
;
868 PathHandler::InitCheck() const
877 TRACE("%p->PathHandler::Quit()\n", this);
878 sWatchingInterface
->StopWatching(this);
879 sLooper
->RemoveHandler(this);
885 PathHandler::MessageReceived(BMessage
* message
)
887 switch (message
->what
) {
891 if (message
->FindInt32("opcode", &opcode
) != B_OK
)
895 case B_ENTRY_CREATED
:
896 _EntryCreated(message
);
899 case B_ENTRY_REMOVED
:
900 _EntryRemoved(message
);
904 _EntryMoved(message
);
908 _UnsetDuplicateEntryNotification();
909 _NodeChanged(message
);
917 BHandler::MessageReceived(message
);
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())
945 ancestor
= new(std::nothrow
) Ancestor(ancestor
, ancestorPath
,
947 TRACE(" created ancestor %p (\"%s\" / \"%s\")\n", ancestor
,
948 ancestor
->Path().String(), ancestor
->Name());
949 if (ancestor
== NULL
)
955 component
= componentEnd
[0] == '/' ? componentEnd
+ 1 : componentEnd
;
958 fBaseAncestor
= ancestor
;
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);
981 if (!ancestor
->Exists()) {
982 TRACE(" -> ancestor doesn't exist\n");
986 fAncestors
.Insert(ancestor
);
989 if (!fBaseAncestor
->Exists())
993 _NotifyEntryCreatedOrRemoved(fBaseAncestor
->EntryRef(),
994 fBaseAncestor
->NodeRef(), fPath
, fBaseAncestor
->IsDirectory(),
998 if (!_WatchRecursively())
1001 status_t error
= _AddNode(fBaseAncestor
->NodeRef(),
1002 fBaseAncestor
->IsDirectory(), notify
&& _WatchFilesOnly(), NULL
,
1012 PathHandler::_StopWatchingAncestors(Ancestor
* ancestor
, bool notify
)
1014 // stop watching the tree below path
1015 if (fBaseNode
!= NULL
) {
1016 _DeleteNode(fBaseNode
, notify
&& _WatchFilesOnly());
1020 if (notify
&& fBaseAncestor
->Exists()
1021 && (fBaseAncestor
->IsDirectory()
1022 ? !_WatchFilesOnly() : !_WatchDirectoriesOnly())) {
1023 _NotifyEntryCreatedOrRemoved(fBaseAncestor
->EntryRef(),
1024 fBaseAncestor
->NodeRef(), fPath
, fBaseAncestor
->IsDirectory(),
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);
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
;
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
) {
1069 entryRef
.device
= nodeRef
.device
;
1071 if (_CheckDuplicateEntryNotification(B_ENTRY_CREATED
, entryRef
, nodeRef
))
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
);
1080 if (entry
.SetTo(&entryRef
) != B_OK
|| entry
.GetStat(&st
) != B_OK
1081 || nodeRef
!= node_ref(st
.st_dev
, st
.st_ino
)) {
1085 _EntryCreated(entryRef
, nodeRef
, S_ISDIR(st
.st_mode
), false, true, NULL
);
1090 PathHandler::_EntryRemoved(BMessage
* message
)
1092 NotOwningEntryRef entryRef
;
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
) {
1101 entryRef
.device
= nodeRef
.device
;
1103 if (_CheckDuplicateEntryNotification(B_ENTRY_REMOVED
, entryRef
, nodeRef
))
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
);
1115 PathHandler::_EntryMoved(BMessage
* message
)
1117 NotOwningEntryRef fromEntryRef
;
1118 NotOwningEntryRef toEntryRef
;
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
)
1128 || message
->FindString("name", (const char**)&toEntryRef
.name
)
1132 toEntryRef
.device
= fromEntryRef
.device
;
1134 if (_CheckDuplicateEntryNotification(B_ENTRY_MOVED
, toEntryRef
, nodeRef
,
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
);
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
);
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
1165 if ((fromDirectoryNode
== NULL
1166 || _EntryRemoved(fromEntryRef
, nodeRef
, true, false, NULL
))
1167 && (toDirectoryNode
== NULL
1168 || _EntryCreated(toEntryRef
, nodeRef
, isDirectory
, true,
1170 // The entries can be handled regularly. We delegate the work to
1171 // _EntryRemoved() and _EntryCreated() and only handle the
1172 // notification ourselves.
1175 Entry
* removedEntry
= NULL
;
1176 if (fromDirectoryNode
!= NULL
) {
1177 _EntryRemoved(fromEntryRef
, nodeRef
, false, false,
1182 Entry
* createdEntry
= NULL
;
1183 if (toDirectoryNode
!= NULL
) {
1184 _EntryCreated(toEntryRef
, nodeRef
, isDirectory
, false,
1185 false, &createdEntry
);
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
,
1197 if (createdEntry
!= NULL
) {
1198 _NotifyFilesCreatedOrRemoved(createdEntry
,
1203 if (fromDirectoryNode
!= NULL
) {
1204 fromPath
= make_path(_NodePath(fromDirectoryNode
),
1209 if (toDirectoryNode
!= NULL
) {
1210 path
= make_path(_NodePath(toDirectoryNode
),
1214 _NotifyEntryMoved(fromEntryRef
, toEntryRef
, nodeRef
,
1215 fromPath
, path
, isDirectory
, fromDirectoryNode
== NULL
,
1216 toDirectoryNode
== NULL
);
1219 if (removedEntry
!= NULL
)
1220 _DeleteEntry(removedEntry
, false);
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
1228 if (fromDirectoryNode
!= NULL
)
1229 _EntryRemoved(fromEntryRef
, nodeRef
, false, true, NULL
);
1232 if (toDirectoryNode
!= NULL
) {
1233 _EntryCreated(toEntryRef
, nodeRef
, isDirectory
, false, true,
1241 if (fromAncestor
== fBaseAncestor
|| toAncestor
== fBaseAncestor
) {
1242 // That should never happen, as we should have found a matching
1243 // directory node in this case.
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.
1249 _StopWatchingAncestors(fRoot
, false);
1250 _StartWatchingAncestors(fRoot
, false);
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
)
1260 if (fromAncestor
== fBaseAncestor
|| toAncestor
== fBaseAncestor
) {
1261 if ((fFlags
& B_WATCH_DIRECTORY
) != 0) {
1263 if (fromAncestor
== fBaseAncestor
)
1264 fromPath
= make_path(fPath
, fromEntryRef
.name
);
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
);
1278 if (fromAncestor
== NULL
&& toAncestor
== NULL
)
1281 if (fromAncestor
== NULL
) {
1282 _EntryCreated(toEntryRef
, nodeRef
, isDirectory
, false, true, NULL
);
1286 if (toAncestor
== NULL
) {
1287 _EntryRemoved(fromEntryRef
, nodeRef
, false, true, NULL
);
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
;
1304 if (!fBaseAncestor
->Exists())
1306 _StopWatchingAncestors(fBaseAncestor
, notifyFilesRecursively
);
1308 if (fBaseAncestor
->Exists()) {
1309 if (fBaseAncestor
->NodeRef() == nodeRef
1310 && isDirectory
== fBaseAncestor
->IsDirectory()) {
1314 // We're out of sync with reality.
1315 _StopWatchingAncestors(fBaseAncestor
, true);
1316 _StartWatchingAncestors(fBaseAncestor
, true);
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
);
1332 _EntryRemoved(fromEntryRef
, nodeRef
, false, true, NULL
);
1333 _EntryCreated(toEntryRef
, nodeRef
, isDirectory
, false, true, NULL
);
1338 PathHandler::_NodeChanged(BMessage
* message
)
1342 if (message
->FindInt32("device", &nodeRef
.device
) != B_OK
1343 || message
->FindInt64("node", &nodeRef
.node
) != B_OK
) {
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;
1356 if (Ancestor
* ancestor
= _GetAncestor(nodeRef
)) {
1357 if (ancestor
!= fBaseAncestor
)
1359 isDirectory
= ancestor
->IsDirectory();
1361 } else if (Node
* node
= _GetNode(nodeRef
)) {
1362 isDirectory
= node
->IsDirectory();
1363 path
= _NodePath(node
);
1367 if (isDirectory
? _WatchFilesOnly() : _WatchDirectoriesOnly())
1370 _NotifyTarget(*message
, path
);
1375 PathHandler::_EntryCreated(const NotOwningEntryRef
& entryRef
,
1376 const node_ref
& nodeRef
, bool isDirectory
, bool dryRun
, bool notify
,
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");
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
1398 TRACE(" -> we know a different ancestor, but our info is "
1403 // We're out of sync with reality.
1404 TRACE(" -> ancestor mismatch -> resyncing\n");
1406 _StopWatchingAncestors(ancestor
, true);
1407 _StartWatchingAncestors(ancestor
, true);
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());
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);
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
,
1451 if (!_WatchRecursively()) {
1452 // That shouldn't happen, since we only watch the ancestors in this
1457 Node
* directoryNode
= _GetNode(entryRef
.DirectoryNodeRef());
1458 if (directoryNode
== NULL
)
1461 Directory
* directory
= directoryNode
->ToDirectory();
1462 if (directory
== NULL
) {
1463 // We're out of sync with reality.
1465 if (Entry
* nodeEntry
= directory
->FirstNodeEntry()) {
1466 // remove the entry that is in the way and re-add the proper
1468 NotOwningEntryRef directoryEntryRef
= nodeEntry
->EntryRef();
1469 BString directoryName
= nodeEntry
->Name();
1470 _DeleteEntry(nodeEntry
, true);
1471 _EntryCreated(directoryEntryRef
, entryRef
.DirectoryNodeRef(),
1472 true, false, notify
, NULL
);
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);
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
)
1490 // We're out of sync with reality -- the new entry refers to a different
1492 _DeleteEntry(nodeEntry
, true);
1498 _AddEntryIfNeeded(directory
, entryRef
.name
, nodeRef
, isDirectory
, notify
,
1505 PathHandler::_EntryRemoved(const NotOwningEntryRef
& entryRef
,
1506 const node_ref
& nodeRef
, bool dryRun
, bool notify
, Entry
** _keepEntry
)
1508 if (_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
1515 if (!ancestor
->Exists())
1518 if (entryRef
!= ancestor
->EntryRef()) {
1519 // We might be out of sync with reality -- the new entry refers to a
1521 struct stat ancestorStat
;
1522 if (BEntry(&ancestor
->EntryRef()).GetStat(&ancestorStat
) != B_OK
) {
1524 _StopWatchingAncestors(ancestor
, true);
1528 if (node_ref(ancestorStat
.st_dev
, ancestorStat
.st_ino
)
1529 != ancestor
->NodeRef()
1530 || S_ISDIR(ancestorStat
.st_mode
) != ancestor
->IsDirectory()) {
1532 _StopWatchingAncestors(ancestor
, true);
1533 _StartWatchingAncestors(ancestor
, true);
1541 _StopWatchingAncestors(ancestor
, true);
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.
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.
1567 if (!_WatchRecursively()) {
1568 // That shouldn't happen, since we only watch the ancestors in this
1573 Node
* directoryNode
= _GetNode(entryRef
.DirectoryNodeRef());
1574 if (directoryNode
== NULL
) {
1575 // We shouldn't get a notification, if we don't known the directory.
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.
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
1594 if (_keepEntry
!= NULL
)
1595 *_keepEntry
= nodeEntry
;
1597 _DeleteEntry(nodeEntry
, notify
);
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
)) {
1616 fDuplicateEntryNotificationOpcode
= opcode
;
1617 fDuplicateEntryNotificationNodeRef
= nodeRef
;
1618 fDuplicateEntryNotificationToEntryRef
= toEntryRef
;
1619 fDuplicateEntryNotificationFromEntryRef
= fromEntryRef
!= NULL
1620 ? *fromEntryRef
: entry_ref();
1626 PathHandler::_UnsetDuplicateEntryNotification()
1628 fDuplicateEntryNotificationOpcode
= B_STAT_CHANGED
;
1629 fDuplicateEntryNotificationNodeRef
= node_ref();
1630 fDuplicateEntryNotificationFromEntryRef
= entry_ref();
1631 fDuplicateEntryNotificationToEntryRef
= entry_ref();
1636 PathHandler::_GetAncestor(const node_ref
& nodeRef
) const
1638 return fAncestors
.Lookup(nodeRef
);
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
);
1653 if (entry
!= NULL
) {
1654 entry
->SetNode(node
);
1655 node
->AddNodeEntry(entry
);
1664 Directory
* directoryNode
= NULL
;
1666 node
= directoryNode
= Directory::Create(nodeRef
);
1668 node
= new(std::nothrow
) Node(nodeRef
);
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);
1684 fNodes
.Insert(nodeDeleter
.Detach());
1686 if (entry
!= NULL
) {
1687 entry
->SetNode(node
);
1688 node
->AddNodeEntry(entry
);
1697 // recursively add the directory's descendents
1698 BDirectory directory
;
1699 if (directory
.SetTo(&nodeRef
) != B_OK
) {
1706 while (directory
.GetNextRef(&entryRef
) == B_OK
) {
1708 if (BEntry(&entryRef
).GetStat(&st
) != B_OK
)
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
);
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
);
1739 if (node
->NodeRef() != fBaseAncestor
->NodeRef())
1740 sWatchingInterface
->WatchNode(&node
->NodeRef(), B_STOP_WATCHING
, this);
1742 fNodes
.Remove(node
);
1748 PathHandler::_GetNode(const node_ref
& nodeRef
) const
1750 return fNodes
.Lookup(nodeRef
);
1755 PathHandler::_AddEntryIfNeeded(Directory
* directory
, const char* name
,
1756 const node_ref
& nodeRef
, bool isDirectory
, bool notify
,
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()) {
1771 Entry
* entry
= directory
->CreateEntry(name
, NULL
);
1775 status_t error
= _AddNode(nodeRef
, isDirectory
, notify
&& _WatchFilesOnly(),
1777 if (error
!= B_OK
) {
1778 directory
->RemoveEntry(entry
);
1784 _NotifyEntryCreatedOrRemoved(entry
, B_ENTRY_CREATED
);
1793 PathHandler::_DeleteEntry(Entry
* entry
, bool notify
)
1795 entry
->Parent()->RemoveEntry(entry
);
1796 _DeleteEntryAlreadyRemovedFromParent(entry
, notify
);
1801 PathHandler::_DeleteEntryAlreadyRemovedFromParent(Entry
* entry
, bool notify
)
1804 _NotifyEntryCreatedOrRemoved(entry
, B_ENTRY_REMOVED
);
1806 Node
* node
= entry
->Node();
1807 if (node
->IsOnlyNodeEntry(entry
))
1808 _DeleteNode(node
, notify
&& _WatchFilesOnly());
1815 PathHandler::_NotifyFilesCreatedOrRemoved(Entry
* entry
, int32 opcode
) const
1817 Directory
* directory
= entry
->Node()->ToDirectory();
1818 if (directory
== NULL
) {
1819 _NotifyEntryCreatedOrRemoved(entry
, opcode
);
1823 for (EntryMap::Iterator it
= directory
->GetEntryIterator(); it
.HasNext();)
1824 _NotifyFilesCreatedOrRemoved(it
.Next(), opcode
);
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
);
1839 PathHandler::_NotifyEntryCreatedOrRemoved(const entry_ref
& entryRef
,
1840 const node_ref
& nodeRef
, const char* path
, bool isDirectory
, int32 opcode
)
1843 if (isDirectory
? _WatchFilesOnly() : _WatchDirectoriesOnly())
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
,
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
);
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())) {
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
);
1897 message
.AddBool("added", true);
1899 message
.AddBool("removed", true);
1901 if (fromPath
!= NULL
&& fromPath
[0] != '\0')
1902 message
.AddString("from path", fromPath
);
1904 _NotifyTarget(message
, path
);
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
);
1921 PathHandler::_NodePath(const Node
* node
) const
1923 if (Entry
* entry
= node
->FirstNodeEntry())
1924 return _EntryPath(entry
);
1925 return node
== fBaseNode
? fPath
: BString();
1930 PathHandler::_EntryPath(const Entry
* entry
) const
1932 return make_path(_NodePath(entry
->Parent()), entry
->Name());
1937 PathHandler::_WatchRecursively() const
1939 return (fFlags
& B_WATCH_RECURSIVELY
) != 0;
1944 PathHandler::_WatchFilesOnly() const
1946 return (fFlags
& B_WATCH_FILES_ONLY
) != 0;
1951 PathHandler::_WatchDirectoriesOnly() const
1953 return (fFlags
& B_WATCH_DIRECTORIES_ONLY
) != 0;
1960 // #pragma mark - BPathMonitor
1963 namespace BPrivate
{
1966 BPathMonitor::BPathMonitor()
1971 BPathMonitor::~BPathMonitor()
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')
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) {
1991 status_t status
= _InitIfNeeded();
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
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
;
2011 watcher
->Remove(handler
);
2015 watcher
= Watcher::Create(target
);
2016 if (watcher
== NULL
)
2018 sWatchers
->Insert(watcher
);
2022 PathHandler
* handler
= new (std::nothrow
) PathHandler(path
, flags
, target
,
2024 status
= handler
!= NULL
? handler
->InitCheck() : B_NO_MEMORY
;
2026 if (status
!= B_OK
) {
2027 if (handler
!= NULL
)
2031 sWatchers
->Remove(watcher
);
2037 watcher
->Insert(handler
);
2043 BPathMonitor::StopWatching(const char* path
, const BMessenger
& target
)
2045 if (sLooper
== NULL
)
2048 TRACE("BPathMonitor::StopWatching(%s)\n", path
);
2050 BAutolock
_(sLooper
);
2052 Watcher
* watcher
= sWatchers
->Lookup(target
);
2053 if (watcher
== NULL
)
2056 PathHandler
* handler
= watcher
->Lookup(path
);
2057 if (handler
== NULL
)
2060 watcher
->Remove(handler
);
2063 if (watcher
->IsEmpty()) {
2064 sWatchers
->Remove(watcher
);
2073 BPathMonitor::StopWatching(const BMessenger
& target
)
2075 if (sLooper
== NULL
)
2078 BAutolock
_(sLooper
);
2080 Watcher
* watcher
= sWatchers
->Lookup(target
);
2081 if (watcher
== NULL
)
2085 PathHandler
* handler
= watcher
->Clear(true);
2086 while (handler
!= NULL
) {
2087 PathHandler
* nextHandler
= handler
->HashNext();
2089 handler
= nextHandler
;
2092 sWatchers
->Remove(watcher
);
2100 BPathMonitor::SetWatchingInterface(BWatchingInterface
* watchingInterface
)
2102 sWatchingInterface
= watchingInterface
!= NULL
2103 ? watchingInterface
: sDefaultWatchingInterface
;
2108 BPathMonitor::_InitIfNeeded()
2110 pthread_once(&sInitOnce
, &BPathMonitor::_Init
);
2111 return sLooper
!= NULL
? B_OK
: B_NO_MEMORY
;
2116 BPathMonitor::_Init()
2118 sDefaultWatchingInterface
= new(std::nothrow
) BWatchingInterface
;
2119 if (sDefaultWatchingInterface
== NULL
)
2122 sWatchers
= new(std::nothrow
) WatcherMap
;
2123 if (sWatchers
== NULL
|| sWatchers
->Init() != B_OK
)
2126 if (sWatchingInterface
== NULL
)
2127 SetWatchingInterface(sDefaultWatchingInterface
);
2129 BLooper
* looper
= new (std::nothrow
) BLooper("PathMonitor looper");
2130 TRACE("Start PathMonitor looper\n");
2133 thread_id thread
= looper
->Run();
2143 // #pragma mark - BWatchingInterface
2146 BPathMonitor::BWatchingInterface::BWatchingInterface()
2151 BPathMonitor::BWatchingInterface::~BWatchingInterface()
2157 BPathMonitor::BWatchingInterface::WatchNode(const node_ref
* node
, uint32 flags
,
2158 const BMessenger
& target
)
2160 return watch_node(node
, flags
, target
);
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
);
2173 BPathMonitor::BWatchingInterface::StopWatching(const BMessenger
& target
)
2175 return stop_watching(target
);
2180 BPathMonitor::BWatchingInterface::StopWatching(const BHandler
* handler
,
2181 const BLooper
* looper
)
2183 return stop_watching(handler
, looper
);
2187 } // namespace BPrivate