1 /*
2 * Copyright 2007-2013, Haiku, Inc. All Rights Reserved.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 * Axel Dörfler, axeld@pinc-software.de
7 * Stephan Aßmus, superstippi@gmx.de
8 * Ingo Weinhold, ingo_weinhold@gmx.de
9 */
10
11
12 #include <PathMonitor.h>
13
14 #include <pthread.h>
15 #include <stdio.h>
16
17 #include <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>
25
26 #include <AutoDeleter.h>
27 #include <NotOwningEntryRef.h>
28 #include <ObjectList.h>
29 #include <util/OpenHashTable.h>
30 #include <util/SinglyLinkedList.h>
31
32
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
40
41
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.
45
46
47 #define WATCH_NODE_FLAG_MASK 0x00ff
48
49
50 namespace {
51
52
53 class Directory;
54 class Node;
55 struct WatcherHashDefinition;
56 typedef BOpenHashTable<WatcherHashDefinition> WatcherMap;
57
58
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;
64
65
66 // #pragma mark -
67
68
69 /*! Returns empty path, if either \a parent or \a subPath is empty or an
70 allocation fails.
71 */
72 static BString
make_path(const BString & parent,const char * subPath)73 make_path(const BString& parent, const char* subPath)
74 {
75 BString path = parent;
76 int32 length = path.Length();
77 if (length == 0 || subPath[0] == '\0')
78 return BString();
79
80 if (parent.ByteAt(length - 1) != '/') {
81 path << '/';
82 if (path.Length() < ++length)
83 return BString();
84 }
85
86 path << subPath;
87 if (path.Length() <= length)
88 return BString();
89 return path;
90 }
91
92
93 // #pragma mark - Ancestor
94
95
96 class Ancestor {
97 public:
Ancestor(Ancestor * parent,const BString & path,size_t pathComponentOffset)98 Ancestor(Ancestor* parent, const BString& path, size_t pathComponentOffset)
99 :
100 fParent(parent),
101 fChild(NULL),
102 fPath(path),
103 fEntryRef(-1, -1, fPath.String() + pathComponentOffset),
104 fNodeRef(),
105 fWatchingFlags(0),
106 fIsDirectory(false)
107 {
108 if (pathComponentOffset == 0) {
109 // must be "/"
110 fEntryRef.SetTo(-1, -1, ".");
111 }
112
113 if (fParent != NULL)
114 fParent->fChild = this;
115 }
116
Parent() const117 Ancestor* Parent() const
118 {
119 return fParent;
120 }
121
Child() const122 Ancestor* Child() const
123 {
124 return fChild;
125 }
126
Path() const127 const BString& Path() const
128 {
129 return fPath;
130 }
131
Name() const132 const char* Name() const
133 {
134 return fEntryRef.name;
135 }
136
Exists() const137 bool Exists() const
138 {
139 return fNodeRef.device >= 0;
140 }
141
EntryRef() const142 const NotOwningEntryRef& EntryRef() const
143 {
144 return fEntryRef;
145 }
146
NodeRef() const147 const node_ref& NodeRef() const
148 {
149 return fNodeRef;
150 }
151
IsDirectory() const152 bool IsDirectory() const
153 {
154 return fIsDirectory;
155 }
156
StartWatching(uint32 pathFlags,BHandler * target)157 status_t StartWatching(uint32 pathFlags, BHandler* target)
158 {
159 // init entry ref
160 BEntry entry;
161 status_t error = entry.SetTo(fPath);
162 if (error != B_OK)
163 return error;
164
165 entry_ref entryRef;
166 error = entry.GetRef(&entryRef);
167 if (error != B_OK)
168 return error;
169
170 fEntryRef.device = entryRef.device;
171 fEntryRef.directory = entryRef.directory;
172
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;
178
179 fNodeRef = node_ref(st.st_dev, st.st_ino);
180 fIsDirectory = S_ISDIR(st.st_mode);
181
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;
194 }
195
196 fWatchingFlags = flags;
197 return B_OK;
198 }
199
StopWatching(BHandler * target)200 void StopWatching(BHandler* target)
201 {
202 // stop watching
203 if (fWatchingFlags != 0) {
204 sWatchingInterface->WatchNode(&fNodeRef, B_STOP_WATCHING, target);
205 fWatchingFlags = 0;
206 }
207
208 // uninitialize node and entry ref
209 fIsDirectory = false;
210 fNodeRef = node_ref();
211 fEntryRef.SetDirectoryNodeRef(node_ref());
212 }
213
HashNext()214 Ancestor*& HashNext()
215 {
216 return fHashNext;
217 }
218
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;
228 };
229
230
231 // #pragma mark - AncestorMap
232
233
234 struct AncestorHashDefinition {
235 typedef node_ref KeyType;
236 typedef Ancestor ValueType;
237
HashKey__anon48725b2b0111::AncestorHashDefinition238 size_t HashKey(const node_ref& key) const
239 {
240 return size_t(key.device ^ key.node);
241 }
242
Hash__anon48725b2b0111::AncestorHashDefinition243 size_t Hash(Ancestor* value) const
244 {
245 return HashKey(value->NodeRef());
246 }
247
Compare__anon48725b2b0111::AncestorHashDefinition248 bool Compare(const node_ref& key, Ancestor* value) const
249 {
250 return key == value->NodeRef();
251 }
252
GetLink__anon48725b2b0111::AncestorHashDefinition253 Ancestor*& GetLink(Ancestor* value) const
254 {
255 return value->HashNext();
256 }
257 };
258
259
260 typedef BOpenHashTable<AncestorHashDefinition> AncestorMap;
261
262
263 // #pragma mark - Entry
264
265
266 class Entry : public SinglyLinkedListLinkImpl<Entry> {
267 public:
Entry(Directory * parent,const BString & name,::Node * node)268 Entry(Directory* parent, const BString& name, ::Node* node)
269 :
270 fParent(parent),
271 fName(name),
272 fNode(node)
273 {
274 }
275
Parent() const276 Directory* Parent() const
277 {
278 return fParent;
279 }
280
Name() const281 const BString& Name() const
282 {
283 return fName;
284 }
285
Node() const286 ::Node* Node() const
287 {
288 return fNode;
289 }
290
SetNode(::Node * node)291 void SetNode(::Node* node)
292 {
293 fNode = node;
294 }
295
296 inline NotOwningEntryRef EntryRef() const;
297
HashNext()298 Entry*& HashNext()
299 {
300 return fHashNext;
301 }
302
303 private:
304 Directory* fParent;
305 BString fName;
306 ::Node* fNode;
307 Entry* fHashNext;
308 };
309
310 typedef SinglyLinkedList<Entry> EntryList;
311
312
313 // EntryMap
314
315
316 struct EntryHashDefinition {
317 typedef const char* KeyType;
318 typedef Entry ValueType;
319
HashKey__anon48725b2b0111::EntryHashDefinition320 size_t HashKey(const char* key) const
321 {
322 return BString::HashValue(key);
323 }
324
Hash__anon48725b2b0111::EntryHashDefinition325 size_t Hash(Entry* value) const
326 {
327 return value->Name().HashValue();
328 }
329
Compare__anon48725b2b0111::EntryHashDefinition330 bool Compare(const char* key, Entry* value) const
331 {
332 return value->Name() == key;
333 }
334
GetLink__anon48725b2b0111::EntryHashDefinition335 Entry*& GetLink(Entry* value) const
336 {
337 return value->HashNext();
338 }
339 };
340
341
342 typedef BOpenHashTable<EntryHashDefinition> EntryMap;
343
344
345 // #pragma mark - Node
346
347
348 class Node {
349 public:
Node(const node_ref & nodeRef)350 Node(const node_ref& nodeRef)
351 :
352 fNodeRef(nodeRef)
353 {
354 }
355
~Node()356 virtual ~Node()
357 {
358 }
359
IsDirectory() const360 virtual bool IsDirectory() const
361 {
362 return false;
363 }
364
ToDirectory()365 virtual Directory* ToDirectory()
366 {
367 return NULL;
368 }
369
NodeRef() const370 const node_ref& NodeRef() const
371 {
372 return fNodeRef;
373 }
374
Entries() const375 const EntryList& Entries() const
376 {
377 return fEntries;
378 }
379
HasEntries() const380 bool HasEntries() const
381 {
382 return !fEntries.IsEmpty();
383 }
384
FirstNodeEntry() const385 Entry* FirstNodeEntry() const
386 {
387 return fEntries.Head();
388 }
389
IsOnlyNodeEntry(Entry * entry) const390 bool IsOnlyNodeEntry(Entry* entry) const
391 {
392 return entry == fEntries.Head() && fEntries.GetNext(entry) == NULL;
393 }
394
AddNodeEntry(Entry * entry)395 void AddNodeEntry(Entry* entry)
396 {
397 fEntries.Add(entry);
398 }
399
RemoveNodeEntry(Entry * entry)400 void RemoveNodeEntry(Entry* entry)
401 {
402 fEntries.Remove(entry);
403 }
404
HashNext()405 Node*& HashNext()
406 {
407 return fHashNext;
408 }
409
410 private:
411 node_ref fNodeRef;
412 EntryList fEntries;
413 Node* fHashNext;
414 };
415
416
417 struct NodeHashDefinition {
418 typedef node_ref KeyType;
419 typedef Node ValueType;
420
HashKey__anon48725b2b0111::NodeHashDefinition421 size_t HashKey(const node_ref& key) const
422 {
423 return size_t(key.device ^ key.node);
424 }
425
Hash__anon48725b2b0111::NodeHashDefinition426 size_t Hash(Node* value) const
427 {
428 return HashKey(value->NodeRef());
429 }
430
Compare__anon48725b2b0111::NodeHashDefinition431 bool Compare(const node_ref& key, Node* value) const
432 {
433 return key == value->NodeRef();
434 }
435
GetLink__anon48725b2b0111::NodeHashDefinition436 Node*& GetLink(Node* value) const
437 {
438 return value->HashNext();
439 }
440 };
441
442
443 typedef BOpenHashTable<NodeHashDefinition> NodeMap;
444
445
446 // #pragma mark - Directory
447
448
449 class Directory : public Node {
450 public:
Create(const node_ref & nodeRef)451 static Directory* Create(const node_ref& nodeRef)
452 {
453 Directory* directory = new(std::nothrow) Directory(nodeRef);
454 if (directory == NULL || directory->fEntries.Init() != B_OK) {
455 delete directory;
456 return NULL;
457 }
458
459 return directory;
460 }
461
IsDirectory() const462 virtual bool IsDirectory() const
463 {
464 return true;
465 }
466
ToDirectory()467 virtual Directory* ToDirectory()
468 {
469 return this;
470 }
471
FindEntry(const char * name) const472 Entry* FindEntry(const char* name) const
473 {
474 return fEntries.Lookup(name);
475 }
476
CreateEntry(const BString & name,Node * node)477 Entry* CreateEntry(const BString& name, Node* node)
478 {
479 Entry* entry = new(std::nothrow) Entry(this, name, node);
480 if (entry == NULL || entry->Name().IsEmpty()) {
481 delete entry;
482 return NULL;
483 }
484
485 AddEntry(entry);
486 return entry;
487 }
488
AddEntry(Entry * entry)489 void AddEntry(Entry* entry)
490 {
491 fEntries.Insert(entry);
492 }
493
RemoveEntry(Entry * entry)494 void RemoveEntry(Entry* entry)
495 {
496 fEntries.Remove(entry);
497 }
498
GetEntryIterator() const499 EntryMap::Iterator GetEntryIterator() const
500 {
501 return fEntries.GetIterator();
502 }
503
RemoveAllEntries()504 Entry* RemoveAllEntries()
505 {
506 return fEntries.Clear(true);
507 }
508
509 private:
Directory(const node_ref & nodeRef)510 Directory(const node_ref& nodeRef)
511 :
512 Node(nodeRef)
513 {
514 }
515
516 private:
517 EntryMap fEntries;
518 };
519
520
521 // #pragma mark - Entry
522
523
524 inline NotOwningEntryRef
EntryRef() const525 Entry::EntryRef() const
526 {
527 return NotOwningEntryRef(fParent->NodeRef(), fName);
528 }
529
530
531 // #pragma mark - PathHandler
532
533
534 class PathHandler : public BHandler {
535 public:
536 PathHandler(const char* path, uint32 flags,
537 const BMessenger& target, BLooper* looper);
538 virtual ~PathHandler();
539
540 status_t InitCheck() const;
541 void Quit();
542
OriginalPath() const543 const BString& OriginalPath() const
544 { return fOriginalPath; }
Flags() const545 uint32 Flags() const { return fFlags; }
546
547 virtual void MessageReceived(BMessage* message);
548
HashNext()549 PathHandler*& HashNext() { return fHashNext; }
550
551 private:
552 status_t _CreateAncestors();
553 status_t _StartWatchingAncestors(Ancestor* ancestor,
554 bool notify);
555 void _StopWatchingAncestors(Ancestor* ancestor,
556 bool notify);
557
558 void _EntryCreated(BMessage* message);
559 void _EntryRemoved(BMessage* message);
560 void _EntryMoved(BMessage* message);
561 void _NodeChanged(BMessage* message);
562
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);
569
570 bool _CheckDuplicateEntryNotification(int32 opcode,
571 const entry_ref& toEntryRef,
572 const node_ref& nodeRef,
573 const entry_ref* fromEntryRef = NULL);
574 void _UnsetDuplicateEntryNotification();
575
576 Ancestor* _GetAncestor(const node_ref& nodeRef) const;
577
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;
583
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);
591
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;
608
609 BString _NodePath(const Node* node) const;
610 BString _EntryPath(const Entry* entry) const;
611
612
613 bool _WatchRecursively() const;
614 bool _WatchFilesOnly() const;
615 bool _WatchDirectoriesOnly() const;
616
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;
633 };
634
635
636 struct PathHandlerHashDefinition {
637 typedef const char* KeyType;
638 typedef PathHandler ValueType;
639
HashKey__anon48725b2b0111::PathHandlerHashDefinition640 size_t HashKey(const char* key) const
641 {
642 return BString::HashValue(key);
643 }
644
Hash__anon48725b2b0111::PathHandlerHashDefinition645 size_t Hash(PathHandler* value) const
646 {
647 return value->OriginalPath().HashValue();
648 }
649
Compare__anon48725b2b0111::PathHandlerHashDefinition650 bool Compare(const char* key, PathHandler* value) const
651 {
652 return key == value->OriginalPath();
653 }
654
GetLink__anon48725b2b0111::PathHandlerHashDefinition655 PathHandler*& GetLink(PathHandler* value) const
656 {
657 return value->HashNext();
658 }
659 };
660
661
662 typedef BOpenHashTable<PathHandlerHashDefinition> PathHandlerMap;
663
664
665 // #pragma mark - Watcher
666
667
668 struct Watcher : public PathHandlerMap {
Create__anon48725b2b0111::Watcher669 static Watcher* Create(const BMessenger& target)
670 {
671 Watcher* watcher = new(std::nothrow) Watcher(target);
672 if (watcher == NULL || watcher->Init() != B_OK) {
673 delete watcher;
674 return NULL;
675 }
676 return watcher;
677 }
678
Target__anon48725b2b0111::Watcher679 const BMessenger& Target() const
680 {
681 return fTarget;
682 }
683
HashNext__anon48725b2b0111::Watcher684 Watcher*& HashNext()
685 {
686 return fHashNext;
687 }
688
689 private:
Watcher__anon48725b2b0111::Watcher690 Watcher(const BMessenger& target)
691 :
692 fTarget(target)
693 {
694 }
695
696 private:
697 BMessenger fTarget;
698 Watcher* fHashNext;
699 };
700
701
702 struct WatcherHashDefinition {
703 typedef BMessenger KeyType;
704 typedef Watcher ValueType;
705
HashKey__anon48725b2b0111::WatcherHashDefinition706 size_t HashKey(const BMessenger& key) const
707 {
708 return key.HashValue();
709 }
710
Hash__anon48725b2b0111::WatcherHashDefinition711 size_t Hash(Watcher* value) const
712 {
713 return HashKey(value->Target());
714 }
715
Compare__anon48725b2b0111::WatcherHashDefinition716 bool Compare(const BMessenger& key, Watcher* value) const
717 {
718 return key == value->Target();
719 }
720
GetLink__anon48725b2b0111::WatcherHashDefinition721 Watcher*& GetLink(Watcher* value) const
722 {
723 return value->HashNext();
724 }
725 };
726
727
728 // #pragma mark - PathHandler
729
730
PathHandler(const char * path,uint32 flags,const BMessenger & target,BLooper * looper)731 PathHandler::PathHandler(const char* path, uint32 flags,
732 const BMessenger& target, BLooper* looper)
733 :
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()
745 {
746 TRACE("%p->PathHandler::PathHandler(\"%s\", %#" B_PRIx32 ")\n", this, path,
747 flags);
748
749 _UnsetDuplicateEntryNotification();
750
751 fStatus = fAncestors.Init();
752 if (fStatus != B_OK)
753 return;
754
755 fStatus = fNodes.Init();
756 if (fStatus != B_OK)
757 return;
758
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);
769 }
770
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
778
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;
789 }
790
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;
799
800 const char* componentEnd = strchr(path, '/');
801 if (componentEnd == NULL)
802 componentEnd = pathEnd;
803 size_t componentLength = componentEnd - path;
804
805 // handle ".' and ".."
806 if (path[0] == '.') {
807 if (componentLength == 1) {
808 path = componentEnd;
809 continue;
810 }
811 if (componentLength == 2 && path[1] == '.') {
812 fStatus = B_BAD_VALUE;
813 return;
814 }
815 }
816
817 int32 normalizedPathLength = normalizedPath.Length();
818 if (normalizedPath.ByteAt(normalizedPathLength - 1) != '/') {
819 normalizedPath << '/';
820 normalizedPathLength++;
821 }
822 normalizedPath.Append(path, componentEnd - path);
823 normalizedPathLength += int32(componentEnd - path);
824
825 if (normalizedPath.Length() != normalizedPathLength) {
826 fStatus = B_NO_MEMORY;
827 return;
828 }
829
830 path = componentEnd;
831 }
832
833 fPath = normalizedPath;
834
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;
840
841 // add ourselves to the looper
842 looper->AddHandler(this);
843
844 // start watching
845 fStatus = _StartWatchingAncestors(fRoot, false);
846 if (fStatus != B_OK)
847 return;
848 }
849
850
~PathHandler()851 PathHandler::~PathHandler()
852 {
853 TRACE("%p->PathHandler::~PathHandler(\"%s\", %#" B_PRIx32 ")\n", this,
854 fPath.String(), fFlags);
855
856 if (fBaseNode != NULL)
857 _DeleteNode(fBaseNode, false);
858
859 while (fRoot != NULL) {
860 Ancestor* nextAncestor = fRoot->Child();
861 delete fRoot;
862 fRoot = nextAncestor;
863 }
864 }
865
866
867 status_t
InitCheck() const868 PathHandler::InitCheck() const
869 {
870 return fStatus;
871 }
872
873
874 void
Quit()875 PathHandler::Quit()
876 {
877 TRACE("%p->PathHandler::Quit()\n", this);
878 sWatchingInterface->StopWatching(this);
879 sLooper->RemoveHandler(this);
880 delete this;
881 }
882
883
884 void
MessageReceived(BMessage * message)885 PathHandler::MessageReceived(BMessage* message)
886 {
887 switch (message->what) {
888 case B_NODE_MONITOR:
889 {
890 int32 opcode;
891 if (message->FindInt32("opcode", &opcode) != B_OK)
892 return;
893
894 switch (opcode) {
895 case B_ENTRY_CREATED:
896 _EntryCreated(message);
897 break;
898
899 case B_ENTRY_REMOVED:
900 _EntryRemoved(message);
901 break;
902
903 case B_ENTRY_MOVED:
904 _EntryMoved(message);
905 break;
906
907 default:
908 _UnsetDuplicateEntryNotification();
909 _NodeChanged(message);
910 break;
911 }
912
913 break;
914 }
915
916 default:
917 BHandler::MessageReceived(message);
918 break;
919 }
920 }
921
922
923 status_t
_CreateAncestors()924 PathHandler::_CreateAncestors()
925 {
926 TRACE("%p->PathHandler::_CreateAncestors()\n", this);
927
928 // create the Ancestor objects
929 const char* path = fPath.String();
930 const char* pathEnd = path + fPath.Length();
931 const char* component = path;
932
933 Ancestor* ancestor = NULL;
934
935 while (component < pathEnd) {
936 const char* componentEnd = component == path
937 ? component + 1 : strchr(component, '/');
938 if (componentEnd == NULL)
939 componentEnd = pathEnd;
940
941 BString ancestorPath(path, componentEnd - path);
942 if (ancestorPath.IsEmpty())
943 return B_NO_MEMORY;
944
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;
951
952 if (fRoot == NULL)
953 fRoot = ancestor;
954
955 component = componentEnd[0] == '/' ? componentEnd + 1 : componentEnd;
956 }
957
958 fBaseAncestor = ancestor;
959
960 return B_OK;
961 }
962
963
964 status_t
_StartWatchingAncestors(Ancestor * startAncestor,bool notify)965 PathHandler::_StartWatchingAncestors(Ancestor* startAncestor, bool notify)
966 {
967 TRACE("%p->PathHandler::_StartWatchingAncestors(%p, %d)\n", this,
968 startAncestor, notify);
969
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);
974
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;
980
981 if (!ancestor->Exists()) {
982 TRACE(" -> ancestor doesn't exist\n");
983 break;
984 }
985
986 fAncestors.Insert(ancestor);
987 }
988
989 if (!fBaseAncestor->Exists())
990 return B_OK;
991
992 if (notify) {
993 _NotifyEntryCreatedOrRemoved(fBaseAncestor->EntryRef(),
994 fBaseAncestor->NodeRef(), fPath, fBaseAncestor->IsDirectory(),
995 B_ENTRY_CREATED);
996 }
997
998 if (!_WatchRecursively())
999 return B_OK;
1000
1001 status_t error = _AddNode(fBaseAncestor->NodeRef(),
1002 fBaseAncestor->IsDirectory(), notify && _WatchFilesOnly(), NULL,
1003 &fBaseNode);
1004 if (error != B_OK)
1005 return error;
1006
1007 return B_OK;
1008 }
1009
1010
1011 void
_StopWatchingAncestors(Ancestor * ancestor,bool notify)1012 PathHandler::_StopWatchingAncestors(Ancestor* ancestor, bool notify)
1013 {
1014 // stop watching the tree below path
1015 if (fBaseNode != NULL) {
1016 _DeleteNode(fBaseNode, notify && _WatchFilesOnly());
1017 fBaseNode = NULL;
1018 }
1019
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);
1026 }
1027
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);
1033 }
1034 }
1035
1036
1037 void
_EntryCreated(BMessage * message)1038 PathHandler::_EntryCreated(BMessage* message)
1039 {
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.
1059
1060 NotOwningEntryRef entryRef;
1061 node_ref nodeRef;
1062
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;
1068 }
1069 entryRef.device = nodeRef.device;
1070
1071 if (_CheckDuplicateEntryNotification(B_ENTRY_CREATED, entryRef, nodeRef))
1072 return;
1073
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);
1077
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;
1083 }
1084
1085 _EntryCreated(entryRef, nodeRef, S_ISDIR(st.st_mode), false, true, NULL);
1086 }
1087
1088
1089 void
_EntryRemoved(BMessage * message)1090 PathHandler::_EntryRemoved(BMessage* message)
1091 {
1092 NotOwningEntryRef entryRef;
1093 node_ref nodeRef;
1094
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;
1100 }
1101 entryRef.device = nodeRef.device;
1102
1103 if (_CheckDuplicateEntryNotification(B_ENTRY_REMOVED, entryRef, nodeRef))
1104 return;
1105
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);
1109
1110 _EntryRemoved(entryRef, nodeRef, false, true, NULL);
1111 }
1112
1113
1114 void
_EntryMoved(BMessage * message)1115 PathHandler::_EntryMoved(BMessage* message)
1116 {
1117 NotOwningEntryRef fromEntryRef;
1118 NotOwningEntryRef toEntryRef;
1119 node_ref nodeRef;
1120
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;
1131 }
1132 toEntryRef.device = fromEntryRef.device;
1133
1134 if (_CheckDuplicateEntryNotification(B_ENTRY_MOVED, toEntryRef, nodeRef,
1135 &fromEntryRef)) {
1136 return;
1137 }
1138
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);
1144
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;
1151 }
1152 bool isDirectory = S_ISDIR(st.st_mode);
1153
1154 Ancestor* fromAncestor = _GetAncestor(fromEntryRef.DirectoryNodeRef());
1155 Ancestor* toAncestor = _GetAncestor(toEntryRef.DirectoryNodeRef());
1156
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.
1173
1174 // handle removed
1175 Entry* removedEntry = NULL;
1176 if (fromDirectoryNode != NULL) {
1177 _EntryRemoved(fromEntryRef, nodeRef, false, false,
1178 &removedEntry);
1179 }
1180
1181 // handle created
1182 Entry* createdEntry = NULL;
1183 if (toDirectoryNode != NULL) {
1184 _EntryCreated(toEntryRef, nodeRef, isDirectory, false,
1185 false, &createdEntry);
1186 }
1187
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);
1195 }
1196
1197 if (createdEntry != NULL) {
1198 _NotifyFilesCreatedOrRemoved(createdEntry,
1199 B_ENTRY_CREATED);
1200 }
1201 } else {
1202 BString fromPath;
1203 if (fromDirectoryNode != NULL) {
1204 fromPath = make_path(_NodePath(fromDirectoryNode),
1205 fromEntryRef.name);
1206 }
1207
1208 BString path;
1209 if (toDirectoryNode != NULL) {
1210 path = make_path(_NodePath(toDirectoryNode),
1211 toEntryRef.name);
1212 }
1213
1214 _NotifyEntryMoved(fromEntryRef, toEntryRef, nodeRef,
1215 fromPath, path, isDirectory, fromDirectoryNode == NULL,
1216 toDirectoryNode == NULL);
1217 }
1218
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.
1226
1227 // handle removed
1228 if (fromDirectoryNode != NULL)
1229 _EntryRemoved(fromEntryRef, nodeRef, false, true, NULL);
1230
1231 // handle created
1232 if (toDirectoryNode != NULL) {
1233 _EntryCreated(toEntryRef, nodeRef, isDirectory, false, true,
1234 NULL);
1235 }
1236 }
1237
1238 return;
1239 }
1240
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;
1252 }
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;
1259
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);
1265
1266 BString path;
1267 if (toAncestor == fBaseAncestor)
1268 path = make_path(fPath, toEntryRef.name);
1269
1270 _NotifyEntryMoved(fromEntryRef, toEntryRef, nodeRef,
1271 fromPath, path, isDirectory, fromAncestor == NULL,
1272 toAncestor == NULL);
1273 }
1274 return;
1275 }
1276 }
1277
1278 if (fromAncestor == NULL && toAncestor == NULL)
1279 return;
1280
1281 if (fromAncestor == NULL) {
1282 _EntryCreated(toEntryRef, nodeRef, isDirectory, false, true, NULL);
1283 return;
1284 }
1285
1286 if (toAncestor == NULL) {
1287 _EntryRemoved(fromEntryRef, nodeRef, false, true, NULL);
1288 return;
1289 }
1290
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;
1312 }
1313
1314 // We're out of sync with reality.
1315 _StopWatchingAncestors(fBaseAncestor, true);
1316 _StartWatchingAncestors(fBaseAncestor, true);
1317 return;
1318 }
1319
1320 _StartWatchingAncestors(fBaseAncestor, notifyFilesRecursively);
1321 }
1322
1323 if (!notifyFilesRecursively) {
1324 _NotifyEntryMoved(fromEntryRef, toEntryRef, nodeRef,
1325 fromIsBase ? fPath.String() : NULL,
1326 toIsBase ? fPath.String() : NULL,
1327 isDirectory, toIsBase, fromIsBase);
1328 }
1329 return;
1330 }
1331
1332 _EntryRemoved(fromEntryRef, nodeRef, false, true, NULL);
1333 _EntryCreated(toEntryRef, nodeRef, isDirectory, false, true, NULL);
1334 }
1335
1336
1337 void
_NodeChanged(BMessage * message)1338 PathHandler::_NodeChanged(BMessage* message)
1339 {
1340 node_ref nodeRef;
1341
1342 if (message->FindInt32("device", &nodeRef.device) != B_OK
1343 || message->FindInt64("node", &nodeRef.node) != B_OK) {
1344 return;
1345 }
1346
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", "") : "");
1353
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;
1366
1367 if (isDirectory ? _WatchFilesOnly() : _WatchDirectoriesOnly())
1368 return;
1369
1370 _NotifyTarget(*message, path);
1371 }
1372
1373
1374 bool
_EntryCreated(const NotOwningEntryRef & entryRef,const node_ref & nodeRef,bool isDirectory,bool dryRun,bool notify,Entry ** _entry)1375 PathHandler::_EntryCreated(const NotOwningEntryRef& entryRef,
1376 const node_ref& nodeRef, bool isDirectory, bool dryRun, bool notify,
1377 Entry** _entry)
1378 {
1379 if (_entry != NULL)
1380 *_entry = NULL;
1381
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;
1389 }
1390
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;
1401 }
1402
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);
1408 }
1409 return false;
1410 }
1411
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;
1422 }
1423
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);
1430 }
1431
1432 TRACE(" -> starting to watch newly appeared ancestor\n");
1433 _StartWatchingAncestors(childAncestor, true);
1434 }
1435 return false;
1436 }
1437
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);
1446 }
1447 return true;
1448 }
1449 }
1450
1451 if (!_WatchRecursively()) {
1452 // That shouldn't happen, since we only watch the ancestors in this
1453 // case.
1454 return true;
1455 }
1456
1457 Node* directoryNode = _GetNode(entryRef.DirectoryNodeRef());
1458 if (directoryNode == NULL)
1459 return true;
1460
1461 Directory* directory = directoryNode->ToDirectory();
1462 if (directory == NULL) {
1463 // We're out of sync with reality.
1464 if (!dryRun) {
1465 if (Entry* nodeEntry = directoryNode->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);
1478 }
1479 }
1480
1481 return false;
1482 }
1483
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;
1489
1490 // We're out of sync with reality -- the new entry refers to a different
1491 // node.
1492 _DeleteEntry(nodeEntry, true);
1493 }
1494
1495 if (dryRun)
1496 return true;
1497
1498 _AddEntryIfNeeded(directory, entryRef.name, nodeRef, isDirectory, notify,
1499 _entry);
1500 return true;
1501 }
1502
1503
1504 bool
_EntryRemoved(const NotOwningEntryRef & entryRef,const node_ref & nodeRef,bool dryRun,bool notify,Entry ** _keepEntry)1505 PathHandler::_EntryRemoved(const NotOwningEntryRef& entryRef,
1506 const node_ref& nodeRef, bool dryRun, bool notify, Entry** _keepEntry)
1507 {
1508 if (_keepEntry != NULL)
1509 *_keepEntry = NULL;
1510
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;
1517
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;
1526 }
1527
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);
1534 }
1535 return false;
1536 }
1537 return true;
1538 }
1539
1540 if (!dryRun)
1541 _StopWatchingAncestors(ancestor, true);
1542 return false;
1543 }
1544
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;
1551 }
1552
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.
1562 }
1563 return true;
1564 }
1565 }
1566
1567 if (!_WatchRecursively()) {
1568 // That shouldn't happen, since we only watch the ancestors in this
1569 // case.
1570 return true;
1571 }
1572
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;
1577 }
1578
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;
1585 }
1586
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;
1591 }
1592
1593 if (!dryRun) {
1594 if (_keepEntry != NULL)
1595 *_keepEntry = nodeEntry;
1596 else
1597 _DeleteEntry(nodeEntry, notify);
1598 }
1599 return true;
1600 }
1601
1602
1603 bool
_CheckDuplicateEntryNotification(int32 opcode,const entry_ref & toEntryRef,const node_ref & nodeRef,const entry_ref * fromEntryRef)1604 PathHandler::_CheckDuplicateEntryNotification(int32 opcode,
1605 const entry_ref& toEntryRef, const node_ref& nodeRef,
1606 const entry_ref* fromEntryRef)
1607 {
1608 if (opcode == fDuplicateEntryNotificationOpcode
1609 && nodeRef == fDuplicateEntryNotificationNodeRef
1610 && toEntryRef == fDuplicateEntryNotificationToEntryRef
1611 && (fromEntryRef == NULL
1612 || *fromEntryRef == fDuplicateEntryNotificationFromEntryRef)) {
1613 return true;
1614 }
1615
1616 fDuplicateEntryNotificationOpcode = opcode;
1617 fDuplicateEntryNotificationNodeRef = nodeRef;
1618 fDuplicateEntryNotificationToEntryRef = toEntryRef;
1619 fDuplicateEntryNotificationFromEntryRef = fromEntryRef != NULL
1620 ? *fromEntryRef : entry_ref();
1621 return false;
1622 }
1623
1624
1625 void
_UnsetDuplicateEntryNotification()1626 PathHandler::_UnsetDuplicateEntryNotification()
1627 {
1628 fDuplicateEntryNotificationOpcode = B_STAT_CHANGED;
1629 fDuplicateEntryNotificationNodeRef = node_ref();
1630 fDuplicateEntryNotificationFromEntryRef = entry_ref();
1631 fDuplicateEntryNotificationToEntryRef = entry_ref();
1632 }
1633
1634
1635 Ancestor*
_GetAncestor(const node_ref & nodeRef) const1636 PathHandler::_GetAncestor(const node_ref& nodeRef) const
1637 {
1638 return fAncestors.Lookup(nodeRef);
1639 }
1640
1641
1642 status_t
_AddNode(const node_ref & nodeRef,bool isDirectory,bool notify,Entry * entry,Node ** _node)1643 PathHandler::_AddNode(const node_ref& nodeRef, bool isDirectory, bool notify,
1644 Entry* entry, Node** _node)
1645 {
1646 TRACE("%p->PathHandler::_AddNode(%" B_PRIdDEV ":%" B_PRIdINO
1647 ", isDirectory: %d, notify: %d)\n", this, nodeRef.device, nodeRef.node,
1648 isDirectory, notify);
1649
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);
1656 }
1657
1658 if (_node != NULL)
1659 *_node = node;
1660 return B_OK;
1661 }
1662
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);
1669
1670 if (node == NULL)
1671 return B_NO_MEMORY;
1672
1673 ObjectDeleter<Node> nodeDeleter(node);
1674
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;
1682 }
1683
1684 fNodes.Insert(nodeDeleter.Detach());
1685
1686 if (entry != NULL) {
1687 entry->SetNode(node);
1688 node->AddNodeEntry(entry);
1689 }
1690
1691 if (_node != NULL)
1692 *_node = node;
1693
1694 if (!isDirectory)
1695 return B_OK;
1696
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;
1703 }
1704
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;
1710
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;
1720 }
1721 }
1722
1723 return B_OK;
1724 }
1725
1726
1727 void
_DeleteNode(Node * node,bool notify)1728 PathHandler::_DeleteNode(Node* node, bool notify)
1729 {
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;
1736 }
1737 }
1738
1739 if (node->NodeRef() != fBaseAncestor->NodeRef())
1740 sWatchingInterface->WatchNode(&node->NodeRef(), B_STOP_WATCHING, this);
1741
1742 fNodes.Remove(node);
1743 delete node;
1744 }
1745
1746
1747 Node*
_GetNode(const node_ref & nodeRef) const1748 PathHandler::_GetNode(const node_ref& nodeRef) const
1749 {
1750 return fNodes.Lookup(nodeRef);
1751 }
1752
1753
1754 status_t
_AddEntryIfNeeded(Directory * directory,const char * name,const node_ref & nodeRef,bool isDirectory,bool notify,Entry ** _entry)1755 PathHandler::_AddEntryIfNeeded(Directory* directory, const char* name,
1756 const node_ref& nodeRef, bool isDirectory, bool notify,
1757 Entry** _entry)
1758 {
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);
1764
1765 if (!isDirectory && _WatchDirectoriesOnly()) {
1766 if (_entry != NULL)
1767 *_entry = NULL;
1768 return B_OK;
1769 }
1770
1771 Entry* entry = directory->CreateEntry(name, NULL);
1772 if (entry == NULL)
1773 return B_NO_MEMORY;
1774
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;
1781 }
1782
1783 if (notify)
1784 _NotifyEntryCreatedOrRemoved(entry, B_ENTRY_CREATED);
1785
1786 if (_entry != NULL)
1787 *_entry = entry;
1788 return B_OK;
1789 }
1790
1791
1792 void
_DeleteEntry(Entry * entry,bool notify)1793 PathHandler::_DeleteEntry(Entry* entry, bool notify)
1794 {
1795 entry->Parent()->RemoveEntry(entry);
1796 _DeleteEntryAlreadyRemovedFromParent(entry, notify);
1797 }
1798
1799
1800 void
_DeleteEntryAlreadyRemovedFromParent(Entry * entry,bool notify)1801 PathHandler::_DeleteEntryAlreadyRemovedFromParent(Entry* entry, bool notify)
1802 {
1803 if (notify)
1804 _NotifyEntryCreatedOrRemoved(entry, B_ENTRY_REMOVED);
1805
1806 Node* node = entry->Node();
1807 if (node->IsOnlyNodeEntry(entry))
1808 _DeleteNode(node, notify && _WatchFilesOnly());
1809
1810 delete entry;
1811 }
1812
1813
1814 void
_NotifyFilesCreatedOrRemoved(Entry * entry,int32 opcode) const1815 PathHandler::_NotifyFilesCreatedOrRemoved(Entry* entry, int32 opcode) const
1816 {
1817 Directory* directory = entry->Node()->ToDirectory();
1818 if (directory == NULL) {
1819 _NotifyEntryCreatedOrRemoved(entry, opcode);
1820 return;
1821 }
1822
1823 for (EntryMap::Iterator it = directory->GetEntryIterator(); it.HasNext();)
1824 _NotifyFilesCreatedOrRemoved(it.Next(), opcode);
1825 }
1826
1827
1828 void
_NotifyEntryCreatedOrRemoved(Entry * entry,int32 opcode) const1829 PathHandler::_NotifyEntryCreatedOrRemoved(Entry* entry, int32 opcode) const
1830 {
1831 Node* node = entry->Node();
1832 _NotifyEntryCreatedOrRemoved(
1833 NotOwningEntryRef(entry->Parent()->NodeRef(), entry->Name()),
1834 node->NodeRef(), _EntryPath(entry), node->IsDirectory(), opcode);
1835 }
1836
1837
1838 void
_NotifyEntryCreatedOrRemoved(const entry_ref & entryRef,const node_ref & nodeRef,const char * path,bool isDirectory,int32 opcode) const1839 PathHandler::_NotifyEntryCreatedOrRemoved(const entry_ref& entryRef,
1840 const node_ref& nodeRef, const char* path, bool isDirectory, int32 opcode)
1841 const
1842 {
1843 if (isDirectory ? _WatchFilesOnly() : _WatchDirectoriesOnly())
1844 return;
1845
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);
1851
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);
1865
1866 _NotifyTarget(message, path);
1867 }
1868
1869
1870 void
_NotifyEntryMoved(const entry_ref & fromEntryRef,const entry_ref & toEntryRef,const node_ref & nodeRef,const char * fromPath,const char * path,bool isDirectory,bool wasAdded,bool wasRemoved) const1871 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
1874 {
1875 if ((isDirectory && _WatchFilesOnly())
1876 || (!isDirectory && _WatchDirectoriesOnly())) {
1877 return;
1878 }
1879
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);
1885
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);
1895
1896 if (wasAdded)
1897 message.AddBool("added", true);
1898 if (wasRemoved)
1899 message.AddBool("removed", true);
1900
1901 if (fromPath != NULL && fromPath[0] != '\0')
1902 message.AddString("from path", fromPath);
1903
1904 _NotifyTarget(message, path);
1905 }
1906
1907
1908 void
_NotifyTarget(BMessage & message,const char * path) const1909 PathHandler::_NotifyTarget(BMessage& message, const char* path) const
1910 {
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);
1916 }
1917
1918
1919
1920 BString
_NodePath(const Node * node) const1921 PathHandler::_NodePath(const Node* node) const
1922 {
1923 if (Entry* entry = node->FirstNodeEntry())
1924 return _EntryPath(entry);
1925 return node == fBaseNode ? fPath : BString();
1926 }
1927
1928
1929 BString
_EntryPath(const Entry * entry) const1930 PathHandler::_EntryPath(const Entry* entry) const
1931 {
1932 return make_path(_NodePath(entry->Parent()), entry->Name());
1933 }
1934
1935
1936 bool
_WatchRecursively() const1937 PathHandler::_WatchRecursively() const
1938 {
1939 return (fFlags & B_WATCH_RECURSIVELY) != 0;
1940 }
1941
1942
1943 bool
_WatchFilesOnly() const1944 PathHandler::_WatchFilesOnly() const
1945 {
1946 return (fFlags & B_WATCH_FILES_ONLY) != 0;
1947 }
1948
1949
1950 bool
_WatchDirectoriesOnly() const1951 PathHandler::_WatchDirectoriesOnly() const
1952 {
1953 return (fFlags & B_WATCH_DIRECTORIES_ONLY) != 0;
1954 }
1955
1956
1957 } // namespace
1958
1959
1960 // #pragma mark - BPathMonitor
1961
1962
1963 namespace BPrivate {
1964
1965
BPathMonitor()1966 BPathMonitor::BPathMonitor()
1967 {
1968 }
1969
1970
~BPathMonitor()1971 BPathMonitor::~BPathMonitor()
1972 {
1973 }
1974
1975
1976 /*static*/ status_t
StartWatching(const char * path,uint32 flags,const BMessenger & target)1977 BPathMonitor::StartWatching(const char* path, uint32 flags,
1978 const BMessenger& target)
1979 {
1980 TRACE("BPathMonitor::StartWatching(%s, %" B_PRIx32 ")\n", path, flags);
1981
1982 if (path == NULL || path[0] == '\0')
1983 return B_BAD_VALUE;
1984
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;
1989 }
1990
1991 status_t status = _InitIfNeeded();
1992 if (status != B_OK)
1993 return status;
1994
1995 BAutolock _(sLooper);
1996
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;
2010
2011 watcher->Remove(handler);
2012 handler->Quit();
2013 }
2014 } else {
2015 watcher = Watcher::Create(target);
2016 if (watcher == NULL)
2017 return B_NO_MEMORY;
2018 sWatchers->Insert(watcher);
2019 newWatcher = true;
2020 }
2021
2022 PathHandler* handler = new (std::nothrow) PathHandler(path, flags, target,
2023 sLooper);
2024 status = handler != NULL ? handler->InitCheck() : B_NO_MEMORY;
2025
2026 if (status != B_OK) {
2027 if (handler != NULL)
2028 handler->Quit();
2029
2030 if (newWatcher) {
2031 sWatchers->Remove(watcher);
2032 delete watcher;
2033 }
2034 return status;
2035 }
2036
2037 watcher->Insert(handler);
2038 return B_OK;
2039 }
2040
2041
2042 /*static*/ status_t
StopWatching(const char * path,const BMessenger & target)2043 BPathMonitor::StopWatching(const char* path, const BMessenger& target)
2044 {
2045 if (sLooper == NULL)
2046 return B_BAD_VALUE;
2047
2048 TRACE("BPathMonitor::StopWatching(%s)\n", path);
2049
2050 BAutolock _(sLooper);
2051
2052 Watcher* watcher = sWatchers->Lookup(target);
2053 if (watcher == NULL)
2054 return B_BAD_VALUE;
2055
2056 PathHandler* handler = watcher->Lookup(path);
2057 if (handler == NULL)
2058 return B_BAD_VALUE;
2059
2060 watcher->Remove(handler);
2061 handler->Quit();
2062
2063 if (watcher->IsEmpty()) {
2064 sWatchers->Remove(watcher);
2065 delete watcher;
2066 }
2067
2068 return B_OK;
2069 }
2070
2071
2072 /*static*/ status_t
StopWatching(const BMessenger & target)2073 BPathMonitor::StopWatching(const BMessenger& target)
2074 {
2075 if (sLooper == NULL)
2076 return B_BAD_VALUE;
2077
2078 BAutolock _(sLooper);
2079
2080 Watcher* watcher = sWatchers->Lookup(target);
2081 if (watcher == NULL)
2082 return B_BAD_VALUE;
2083
2084 // delete handlers
2085 PathHandler* handler = watcher->Clear(true);
2086 while (handler != NULL) {
2087 PathHandler* nextHandler = handler->HashNext();
2088 handler->Quit();
2089 handler = nextHandler;
2090 }
2091
2092 sWatchers->Remove(watcher);
2093 delete watcher;
2094
2095 return B_OK;
2096 }
2097
2098
2099 /*static*/ void
SetWatchingInterface(BWatchingInterface * watchingInterface)2100 BPathMonitor::SetWatchingInterface(BWatchingInterface* watchingInterface)
2101 {
2102 sWatchingInterface = watchingInterface != NULL
2103 ? watchingInterface : sDefaultWatchingInterface;
2104 }
2105
2106
2107 /*static*/ status_t
_InitIfNeeded()2108 BPathMonitor::_InitIfNeeded()
2109 {
2110 pthread_once(&sInitOnce, &BPathMonitor::_Init);
2111 return sLooper != NULL ? B_OK : B_NO_MEMORY;
2112 }
2113
2114
2115 /*static*/ void
_Init()2116 BPathMonitor::_Init()
2117 {
2118 sDefaultWatchingInterface = new(std::nothrow) BWatchingInterface;
2119 if (sDefaultWatchingInterface == NULL)
2120 return;
2121
2122 sWatchers = new(std::nothrow) WatcherMap;
2123 if (sWatchers == NULL || sWatchers->Init() != B_OK)
2124 return;
2125
2126 if (sWatchingInterface == NULL)
2127 SetWatchingInterface(sDefaultWatchingInterface);
2128
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;
2137 }
2138
2139 sLooper = looper;
2140 }
2141
2142
2143 // #pragma mark - BWatchingInterface
2144
2145
BWatchingInterface()2146 BPathMonitor::BWatchingInterface::BWatchingInterface()
2147 {
2148 }
2149
2150
~BWatchingInterface()2151 BPathMonitor::BWatchingInterface::~BWatchingInterface()
2152 {
2153 }
2154
2155
2156 status_t
WatchNode(const node_ref * node,uint32 flags,const BMessenger & target)2157 BPathMonitor::BWatchingInterface::WatchNode(const node_ref* node, uint32 flags,
2158 const BMessenger& target)
2159 {
2160 return watch_node(node, flags, target);
2161 }
2162
2163
2164 status_t
WatchNode(const node_ref * node,uint32 flags,const BHandler * handler,const BLooper * looper)2165 BPathMonitor::BWatchingInterface::WatchNode(const node_ref* node, uint32 flags,
2166 const BHandler* handler, const BLooper* looper)
2167 {
2168 return watch_node(node, flags, handler, looper);
2169 }
2170
2171
2172 status_t
StopWatching(const BMessenger & target)2173 BPathMonitor::BWatchingInterface::StopWatching(const BMessenger& target)
2174 {
2175 return stop_watching(target);
2176 }
2177
2178
2179 status_t
StopWatching(const BHandler * handler,const BLooper * looper)2180 BPathMonitor::BWatchingInterface::StopWatching(const BHandler* handler,
2181 const BLooper* looper)
2182 {
2183 return stop_watching(handler, looper);
2184 }
2185
2186
2187 } // namespace BPrivate
2188