1f30198a0SAxel Dörfler/*
204382d49SIngo Weinhold * Copyright 2007-2013, Haiku, Inc. All Rights Reserved.
3f30198a0SAxel Dörfler * Distributed under the terms of the MIT License.
4f30198a0SAxel Dörfler *
5f30198a0SAxel Dörfler * Authors:
6f30198a0SAxel Dörfler *		Axel D��rfler, axeld@pinc-software.de
704382d49SIngo Weinhold *		Stephan A��mus, superstippi@gmx.de
804382d49SIngo Weinhold *		Ingo Weinhold, ingo_weinhold@gmx.de
9f30198a0SAxel Dörfler */
10f30198a0SAxel Dörfler
11f30198a0SAxel Dörfler
12f30198a0SAxel Dörfler#include <PathMonitor.h>
13f30198a0SAxel Dörfler
146ca95bd8SIngo Weinhold#include <pthread.h>
156ca95bd8SIngo Weinhold#include <stdio.h>
166ca95bd8SIngo Weinhold
17d00539e3SAxel Dörfler#include <Autolock.h>
18f30198a0SAxel Dörfler#include <Directory.h>
19f30198a0SAxel Dörfler#include <Entry.h>
20f30198a0SAxel Dörfler#include <Handler.h>
21d00539e3SAxel Dörfler#include <Locker.h>
22f30198a0SAxel Dörfler#include <Looper.h>
23f30198a0SAxel Dörfler#include <Path.h>
24d00539e3SAxel Dörfler#include <String.h>
25f30198a0SAxel Dörfler
2604382d49SIngo Weinhold#include <AutoDeleter.h>
2704382d49SIngo Weinhold#include <NotOwningEntryRef.h>
286ca95bd8SIngo Weinhold#include <ObjectList.h>
2904382d49SIngo Weinhold#include <util/OpenHashTable.h>
3004382d49SIngo Weinhold#include <util/SinglyLinkedList.h>
316ca95bd8SIngo Weinhold
32f30198a0SAxel Dörfler
332702a877SAxel Dörfler#undef TRACE
342702a877SAxel Dörfler//#define TRACE_PATH_MONITOR
352702a877SAxel Dörfler#ifdef TRACE_PATH_MONITOR
3604382d49SIngo Weinhold#	define TRACE(...) debug_printf("BPathMonitor: " __VA_ARGS__)
372702a877SAxel Dörfler#else
3804382d49SIngo Weinhold#	define TRACE(...) ;
392702a877SAxel Dörfler#endif
402702a877SAxel Dörfler
416ca95bd8SIngo Weinhold
4204382d49SIngo Weinhold// TODO: Support symlink components in the path.
4304382d49SIngo Weinhold// TODO: Support mounting/unmounting of volumes in path components and within
4404382d49SIngo Weinhold// the watched path tree.
45f30198a0SAxel Dörfler
466ca95bd8SIngo Weinhold
47f30198a0SAxel Dörfler#define WATCH_NODE_FLAG_MASK	0x00ff
48f30198a0SAxel Dörfler
49f30198a0SAxel Dörfler
5004382d49SIngo Weinholdnamespace {
5104382d49SIngo Weinhold
5204382d49SIngo Weinhold
539216fc01SAugustin Cavalierclass Directory;
549216fc01SAugustin Cavalierclass Node;
5504382d49SIngo Weinholdstruct WatcherHashDefinition;
5604382d49SIngo Weinholdtypedef BOpenHashTable<WatcherHashDefinition> WatcherMap;
5704382d49SIngo Weinhold
5804382d49SIngo Weinhold
5904382d49SIngo Weinholdstatic pthread_once_t sInitOnce = PTHREAD_ONCE_INIT;
6004382d49SIngo Weinholdstatic WatcherMap* sWatchers = NULL;
6104382d49SIngo Weinholdstatic BLooper* sLooper = NULL;
6204382d49SIngo Weinholdstatic BPathMonitor::BWatchingInterface* sDefaultWatchingInterface = NULL;
6304382d49SIngo Weinholdstatic BPathMonitor::BWatchingInterface* sWatchingInterface = NULL;
6404382d49SIngo Weinhold
6504382d49SIngo Weinhold
6604382d49SIngo Weinhold//	#pragma mark -
6704382d49SIngo Weinhold
6804382d49SIngo Weinhold
6904382d49SIngo Weinhold/*! Returns empty path, if either \a parent or \a subPath is empty or an
7004382d49SIngo Weinhold	allocation fails.
7104382d49SIngo Weinhold */
7204382d49SIngo Weinholdstatic BString
7304382d49SIngo Weinholdmake_path(const BString& parent, const char* subPath)
7404382d49SIngo Weinhold{
7504382d49SIngo Weinhold	BString path = parent;
7604382d49SIngo Weinhold	int32 length = path.Length();
7704382d49SIngo Weinhold	if (length == 0 || subPath[0] == '\0')
7804382d49SIngo Weinhold		return BString();
7904382d49SIngo Weinhold
8004382d49SIngo Weinhold	if (parent.ByteAt(length - 1) != '/') {
8104382d49SIngo Weinhold		path << '/';
8204382d49SIngo Weinhold		if (path.Length() < ++length)
8304382d49SIngo Weinhold			return BString();
8404382d49SIngo Weinhold	}
8504382d49SIngo Weinhold
8604382d49SIngo Weinhold	path << subPath;
8704382d49SIngo Weinhold	if (path.Length() <= length)
8804382d49SIngo Weinhold		return BString();
8904382d49SIngo Weinhold	return path;
9004382d49SIngo Weinhold}
9104382d49SIngo Weinhold
92afbd081aSStephan Aßmus
9304382d49SIngo Weinhold//	#pragma mark - Ancestor
9404382d49SIngo Weinhold
9504382d49SIngo Weinhold
9604382d49SIngo Weinholdclass Ancestor {
9704382d49SIngo Weinholdpublic:
9804382d49SIngo Weinhold	Ancestor(Ancestor* parent, const BString& path, size_t pathComponentOffset)
9904382d49SIngo Weinhold		:
10004382d49SIngo Weinhold		fParent(parent),
10104382d49SIngo Weinhold		fChild(NULL),
10204382d49SIngo Weinhold		fPath(path),
10304382d49SIngo Weinhold		fEntryRef(-1, -1, fPath.String() + pathComponentOffset),
10404382d49SIngo Weinhold		fNodeRef(),
10504382d49SIngo Weinhold		fWatchingFlags(0),
10604382d49SIngo Weinhold		fIsDirectory(false)
107c41c3f9bSMarcus Overhagen	{
10804382d49SIngo Weinhold		if (pathComponentOffset == 0) {
10904382d49SIngo Weinhold			// must be "/"
11004382d49SIngo Weinhold			fEntryRef.SetTo(-1, -1, ".");
111c41c3f9bSMarcus Overhagen		}
112c41c3f9bSMarcus Overhagen
11304382d49SIngo Weinhold		if (fParent != NULL)
11404382d49SIngo Weinhold			fParent->fChild = this;
11504382d49SIngo Weinhold	}
11604382d49SIngo Weinhold
11704382d49SIngo Weinhold	Ancestor* Parent() const
11804382d49SIngo Weinhold	{
11904382d49SIngo Weinhold		return fParent;
12004382d49SIngo Weinhold	}
12104382d49SIngo Weinhold
12204382d49SIngo Weinhold	Ancestor* Child() const
12304382d49SIngo Weinhold	{
12404382d49SIngo Weinhold		return fChild;
12504382d49SIngo Weinhold	}
12604382d49SIngo Weinhold
12704382d49SIngo Weinhold	const BString& Path() const
12804382d49SIngo Weinhold	{
12904382d49SIngo Weinhold		return fPath;
13004382d49SIngo Weinhold	}
13104382d49SIngo Weinhold
13204382d49SIngo Weinhold	const char* Name() const
13304382d49SIngo Weinhold	{
13404382d49SIngo Weinhold		return fEntryRef.name;
13504382d49SIngo Weinhold	}
13604382d49SIngo Weinhold
13704382d49SIngo Weinhold	bool Exists() const
13804382d49SIngo Weinhold	{
13904382d49SIngo Weinhold		return fNodeRef.device >= 0;
14004382d49SIngo Weinhold	}
14104382d49SIngo Weinhold
14204382d49SIngo Weinhold	const NotOwningEntryRef& EntryRef() const
14304382d49SIngo Weinhold	{
14404382d49SIngo Weinhold		return fEntryRef;
14504382d49SIngo Weinhold	}
14604382d49SIngo Weinhold
14704382d49SIngo Weinhold	const node_ref& NodeRef() const
14804382d49SIngo Weinhold	{
14904382d49SIngo Weinhold		return fNodeRef;
15004382d49SIngo Weinhold	}
15104382d49SIngo Weinhold
15204382d49SIngo Weinhold	bool IsDirectory() const
15304382d49SIngo Weinhold	{
15404382d49SIngo Weinhold		return fIsDirectory;
15504382d49SIngo Weinhold	}
15604382d49SIngo Weinhold
15704382d49SIngo Weinhold	status_t StartWatching(uint32 pathFlags, BHandler* target)
15804382d49SIngo Weinhold	{
15904382d49SIngo Weinhold		// init entry ref
16004382d49SIngo Weinhold		BEntry entry;
16104382d49SIngo Weinhold		status_t error = entry.SetTo(fPath);
16204382d49SIngo Weinhold		if (error != B_OK)
16304382d49SIngo Weinhold			return error;
16404382d49SIngo Weinhold
16504382d49SIngo Weinhold		entry_ref entryRef;
16604382d49SIngo Weinhold		error = entry.GetRef(&entryRef);
16704382d49SIngo Weinhold		if (error != B_OK)
16804382d49SIngo Weinhold			return error;
16904382d49SIngo Weinhold
17004382d49SIngo Weinhold		fEntryRef.device = entryRef.device;
17104382d49SIngo Weinhold		fEntryRef.directory = entryRef.directory;
17204382d49SIngo Weinhold
17304382d49SIngo Weinhold		// init node ref
17404382d49SIngo Weinhold		struct stat st;
17504382d49SIngo Weinhold		error = entry.GetStat(&st);
17604382d49SIngo Weinhold		if (error != B_OK)
17704382d49SIngo Weinhold			return error == B_ENTRY_NOT_FOUND ? B_OK : error;
17804382d49SIngo Weinhold
17904382d49SIngo Weinhold		fNodeRef = node_ref(st.st_dev, st.st_ino);
18004382d49SIngo Weinhold		fIsDirectory = S_ISDIR(st.st_mode);
18104382d49SIngo Weinhold
18204382d49SIngo Weinhold		// start watching
183c869b2bbSMichael Lotz		uint32 flags = fChild == NULL ? pathFlags : B_WATCH_DIRECTORY;
18404382d49SIngo Weinhold			// In theory B_WATCH_NAME would suffice for all existing ancestors,
18504382d49SIngo Weinhold			// plus B_WATCH_DIRECTORY for the parent of the first not existing
18604382d49SIngo Weinhold			// ancestor. In practice this complicates the transitions when an
18704382d49SIngo Weinhold			// ancestor is created/removed/moved.
18804382d49SIngo Weinhold		if (flags != 0) {
18904382d49SIngo Weinhold			error = sWatchingInterface->WatchNode(&fNodeRef, flags, target);
19004382d49SIngo Weinhold			TRACE("  started to watch ancestor %p (\"%s\", %#" B_PRIx32
19104382d49SIngo Weinhold				") -> %s\n", this, Name(), flags, strerror(error));
19204382d49SIngo Weinhold			if (error != B_OK)
19304382d49SIngo Weinhold				return error;
19404382d49SIngo Weinhold		}
19504382d49SIngo Weinhold
19604382d49SIngo Weinhold		fWatchingFlags = flags;
19704382d49SIngo Weinhold		return B_OK;
19804382d49SIngo Weinhold	}
19904382d49SIngo Weinhold
20004382d49SIngo Weinhold	void StopWatching(BHandler* target)
20104382d49SIngo Weinhold	{
20204382d49SIngo Weinhold		// stop watching
20304382d49SIngo Weinhold		if (fWatchingFlags != 0) {
20404382d49SIngo Weinhold			sWatchingInterface->WatchNode(&fNodeRef, B_STOP_WATCHING, target);
20504382d49SIngo Weinhold			fWatchingFlags = 0;
20604382d49SIngo Weinhold		}
20704382d49SIngo Weinhold
20804382d49SIngo Weinhold		// uninitialize node and entry ref
20904382d49SIngo Weinhold		fIsDirectory = false;
21004382d49SIngo Weinhold		fNodeRef = node_ref();
21104382d49SIngo Weinhold		fEntryRef.SetDirectoryNodeRef(node_ref());
21204382d49SIngo Weinhold	}
21304382d49SIngo Weinhold
21404382d49SIngo Weinhold	Ancestor*& HashNext()
21504382d49SIngo Weinhold	{
21604382d49SIngo Weinhold		return fHashNext;
21704382d49SIngo Weinhold	}
21804382d49SIngo Weinhold
21904382d49SIngo Weinholdprivate:
22004382d49SIngo Weinhold	Ancestor*			fParent;
22104382d49SIngo Weinhold	Ancestor*			fChild;
22204382d49SIngo Weinhold	Ancestor*			fHashNext;
22304382d49SIngo Weinhold	BString				fPath;
22404382d49SIngo Weinhold	NotOwningEntryRef	fEntryRef;
22504382d49SIngo Weinhold	node_ref			fNodeRef;
22604382d49SIngo Weinhold	uint32				fWatchingFlags;
22704382d49SIngo Weinhold	bool				fIsDirectory;
22804382d49SIngo Weinhold};
22904382d49SIngo Weinhold
23004382d49SIngo Weinhold
23104382d49SIngo Weinhold//	#pragma mark - AncestorMap
23204382d49SIngo Weinhold
23304382d49SIngo Weinhold
23404382d49SIngo Weinholdstruct AncestorHashDefinition {
23504382d49SIngo Weinhold	typedef	node_ref	KeyType;
23604382d49SIngo Weinhold	typedef	Ancestor	ValueType;
23704382d49SIngo Weinhold
23804382d49SIngo Weinhold	size_t HashKey(const node_ref& key) const
23904382d49SIngo Weinhold	{
24004382d49SIngo Weinhold		return size_t(key.device ^ key.node);
24104382d49SIngo Weinhold	}
24204382d49SIngo Weinhold
24304382d49SIngo Weinhold	size_t Hash(Ancestor* value) const
24404382d49SIngo Weinhold	{
24504382d49SIngo Weinhold		return HashKey(value->NodeRef());
24604382d49SIngo Weinhold	}
24704382d49SIngo Weinhold
24804382d49SIngo Weinhold	bool Compare(const node_ref& key, Ancestor* value) const
24904382d49SIngo Weinhold	{
25004382d49SIngo Weinhold		return key == value->NodeRef();
25104382d49SIngo Weinhold	}
25204382d49SIngo Weinhold
25304382d49SIngo Weinhold	Ancestor*& GetLink(Ancestor* value) const
25404382d49SIngo Weinhold	{
25504382d49SIngo Weinhold		return value->HashNext();
25604382d49SIngo Weinhold	}
257d00539e3SAxel Dörfler};
258c41c3f9bSMarcus Overhagen
259d00539e3SAxel Dörfler
26004382d49SIngo Weinholdtypedef BOpenHashTable<AncestorHashDefinition> AncestorMap;
26104382d49SIngo Weinhold
26204382d49SIngo Weinhold
26304382d49SIngo Weinhold//	#pragma mark - Entry
26404382d49SIngo Weinhold
265d00539e3SAxel Dörfler
26604382d49SIngo Weinholdclass Entry : public SinglyLinkedListLinkImpl<Entry> {
26704382d49SIngo Weinholdpublic:
26804382d49SIngo Weinhold	Entry(Directory* parent, const BString& name, ::Node* node)
26904382d49SIngo Weinhold		:
27004382d49SIngo Weinhold		fParent(parent),
27104382d49SIngo Weinhold		fName(name),
27204382d49SIngo Weinhold		fNode(node)
27304382d49SIngo Weinhold	{
27404382d49SIngo Weinhold	}
27504382d49SIngo Weinhold
27604382d49SIngo Weinhold	Directory* Parent() const
27704382d49SIngo Weinhold	{
27804382d49SIngo Weinhold		return fParent;
27904382d49SIngo Weinhold	}
28004382d49SIngo Weinhold
28104382d49SIngo Weinhold	const BString& Name() const
28204382d49SIngo Weinhold	{
28304382d49SIngo Weinhold		return fName;
28404382d49SIngo Weinhold	}
28504382d49SIngo Weinhold
28604382d49SIngo Weinhold	::Node* Node() const
28704382d49SIngo Weinhold	{
28804382d49SIngo Weinhold		return fNode;
28904382d49SIngo Weinhold	}
29004382d49SIngo Weinhold
29104382d49SIngo Weinhold	void SetNode(::Node* node)
29204382d49SIngo Weinhold	{
29304382d49SIngo Weinhold		fNode = node;
29404382d49SIngo Weinhold	}
29504382d49SIngo Weinhold
29604382d49SIngo Weinhold	inline NotOwningEntryRef EntryRef() const;
29704382d49SIngo Weinhold
29804382d49SIngo Weinhold	Entry*& HashNext()
29904382d49SIngo Weinhold	{
30004382d49SIngo Weinhold		return fHashNext;
30104382d49SIngo Weinhold	}
30204382d49SIngo Weinhold
30304382d49SIngo Weinholdprivate:
30404382d49SIngo Weinhold	Directory*	fParent;
30504382d49SIngo Weinhold	BString		fName;
30604382d49SIngo Weinhold	::Node*		fNode;
30704382d49SIngo Weinhold	Entry*		fHashNext;
308d00539e3SAxel Dörfler};
309d00539e3SAxel Dörfler
31004382d49SIngo Weinholdtypedef SinglyLinkedList<Entry> EntryList;
311f30198a0SAxel Dörfler
312f30198a0SAxel Dörfler
31304382d49SIngo Weinhold// EntryMap
31404382d49SIngo Weinhold
31504382d49SIngo Weinhold
31604382d49SIngo Weinholdstruct EntryHashDefinition {
31704382d49SIngo Weinhold	typedef	const char*	KeyType;
31804382d49SIngo Weinhold	typedef	Entry		ValueType;
31904382d49SIngo Weinhold
32004382d49SIngo Weinhold	size_t HashKey(const char* key) const
32104382d49SIngo Weinhold	{
32204382d49SIngo Weinhold		return BString::HashValue(key);
32304382d49SIngo Weinhold	}
324f30198a0SAxel Dörfler
32504382d49SIngo Weinhold	size_t Hash(Entry* value) const
32604382d49SIngo Weinhold	{
32704382d49SIngo Weinhold		return value->Name().HashValue();
32804382d49SIngo Weinhold	}
32904382d49SIngo Weinhold
33004382d49SIngo Weinhold	bool Compare(const char* key, Entry* value) const
33104382d49SIngo Weinhold	{
33204382d49SIngo Weinhold		return value->Name() == key;
33304382d49SIngo Weinhold	}
33404382d49SIngo Weinhold
33504382d49SIngo Weinhold	Entry*& GetLink(Entry* value) const
33604382d49SIngo Weinhold	{
33704382d49SIngo Weinhold		return value->HashNext();
33804382d49SIngo Weinhold	}
339f30198a0SAxel Dörfler};
340f30198a0SAxel Dörfler
341d00539e3SAxel Dörfler
34204382d49SIngo Weinholdtypedef BOpenHashTable<EntryHashDefinition> EntryMap;
34304382d49SIngo Weinhold
34404382d49SIngo Weinhold
34504382d49SIngo Weinhold//	#pragma mark - Node
34604382d49SIngo Weinhold
34704382d49SIngo Weinhold
34804382d49SIngo Weinholdclass Node {
34904382d49SIngo Weinholdpublic:
35004382d49SIngo Weinhold	Node(const node_ref& nodeRef)
35104382d49SIngo Weinhold		:
35204382d49SIngo Weinhold		fNodeRef(nodeRef)
35304382d49SIngo Weinhold	{
35404382d49SIngo Weinhold	}
35504382d49SIngo Weinhold
35604382d49SIngo Weinhold	virtual ~Node()
35704382d49SIngo Weinhold	{
35804382d49SIngo Weinhold	}
35904382d49SIngo Weinhold
36004382d49SIngo Weinhold	virtual bool IsDirectory() const
36104382d49SIngo Weinhold	{
36204382d49SIngo Weinhold		return false;
36304382d49SIngo Weinhold	}
364f30198a0SAxel Dörfler
36504382d49SIngo Weinhold	virtual Directory* ToDirectory()
36604382d49SIngo Weinhold	{
36704382d49SIngo Weinhold		return NULL;
36804382d49SIngo Weinhold	}
36904382d49SIngo Weinhold
37004382d49SIngo Weinhold	const node_ref& NodeRef() const
37104382d49SIngo Weinhold	{
37204382d49SIngo Weinhold		return fNodeRef;
37304382d49SIngo Weinhold	}
37404382d49SIngo Weinhold
37504382d49SIngo Weinhold	const EntryList& Entries() const
37604382d49SIngo Weinhold	{
37704382d49SIngo Weinhold		return fEntries;
37804382d49SIngo Weinhold	}
37904382d49SIngo Weinhold
38004382d49SIngo Weinhold	bool HasEntries() const
38104382d49SIngo Weinhold	{
38204382d49SIngo Weinhold		return !fEntries.IsEmpty();
38304382d49SIngo Weinhold	}
38404382d49SIngo Weinhold
38504382d49SIngo Weinhold	Entry* FirstNodeEntry() const
38604382d49SIngo Weinhold	{
38704382d49SIngo Weinhold		return fEntries.Head();
38804382d49SIngo Weinhold	}
38904382d49SIngo Weinhold
39004382d49SIngo Weinhold	bool IsOnlyNodeEntry(Entry* entry) const
39104382d49SIngo Weinhold	{
39204382d49SIngo Weinhold		return entry == fEntries.Head() && fEntries.GetNext(entry) == NULL;
39304382d49SIngo Weinhold	}
39404382d49SIngo Weinhold
39504382d49SIngo Weinhold	void AddNodeEntry(Entry* entry)
39604382d49SIngo Weinhold	{
39704382d49SIngo Weinhold		fEntries.Add(entry);
39804382d49SIngo Weinhold	}
39904382d49SIngo Weinhold
40004382d49SIngo Weinhold	void RemoveNodeEntry(Entry* entry)
40104382d49SIngo Weinhold	{
40204382d49SIngo Weinhold		fEntries.Remove(entry);
40304382d49SIngo Weinhold	}
40404382d49SIngo Weinhold
40504382d49SIngo Weinhold	Node*& HashNext()
40604382d49SIngo Weinhold	{
40704382d49SIngo Weinhold		return fHashNext;
40804382d49SIngo Weinhold	}
40904382d49SIngo Weinhold
41004382d49SIngo Weinholdprivate:
41104382d49SIngo Weinhold	node_ref			fNodeRef;
41204382d49SIngo Weinhold	EntryList			fEntries;
41304382d49SIngo Weinhold	Node*				fHashNext;
41404382d49SIngo Weinhold};
41504382d49SIngo Weinhold
41604382d49SIngo Weinhold
41704382d49SIngo Weinholdstruct NodeHashDefinition {
41804382d49SIngo Weinhold	typedef	node_ref	KeyType;
41904382d49SIngo Weinhold	typedef	Node		ValueType;
42004382d49SIngo Weinhold
42104382d49SIngo Weinhold	size_t HashKey(const node_ref& key) const
42204382d49SIngo Weinhold	{
42304382d49SIngo Weinhold		return size_t(key.device ^ key.node);
42404382d49SIngo Weinhold	}
42504382d49SIngo Weinhold
42604382d49SIngo Weinhold	size_t Hash(Node* value) const
42704382d49SIngo Weinhold	{
42804382d49SIngo Weinhold		return HashKey(value->NodeRef());
42904382d49SIngo Weinhold	}
43004382d49SIngo Weinhold
43104382d49SIngo Weinhold	bool Compare(const node_ref& key, Node* value) const
43204382d49SIngo Weinhold	{
43304382d49SIngo Weinhold		return key == value->NodeRef();
43404382d49SIngo Weinhold	}
43504382d49SIngo Weinhold
43604382d49SIngo Weinhold	Node*& GetLink(Node* value) const
43704382d49SIngo Weinhold	{
43804382d49SIngo Weinhold		return value->HashNext();
43904382d49SIngo Weinhold	}
44004382d49SIngo Weinhold};
44104382d49SIngo Weinhold
44204382d49SIngo Weinhold
44304382d49SIngo Weinholdtypedef BOpenHashTable<NodeHashDefinition> NodeMap;
44404382d49SIngo Weinhold
44504382d49SIngo Weinhold
44604382d49SIngo Weinhold//	#pragma mark - Directory
44704382d49SIngo Weinhold
44804382d49SIngo Weinhold
44904382d49SIngo Weinholdclass Directory : public Node {
45004382d49SIngo Weinholdpublic:
45104382d49SIngo Weinhold	static Directory* Create(const node_ref& nodeRef)
45204382d49SIngo Weinhold	{
45304382d49SIngo Weinhold		Directory* directory = new(std::nothrow) Directory(nodeRef);
45404382d49SIngo Weinhold		if (directory == NULL || directory->fEntries.Init() != B_OK) {
45504382d49SIngo Weinhold			delete directory;
45604382d49SIngo Weinhold			return NULL;
45704382d49SIngo Weinhold		}
45804382d49SIngo Weinhold
45904382d49SIngo Weinhold		return directory;
46004382d49SIngo Weinhold	}
46104382d49SIngo Weinhold
46204382d49SIngo Weinhold	virtual bool IsDirectory() const
46304382d49SIngo Weinhold	{
46404382d49SIngo Weinhold		return true;
46504382d49SIngo Weinhold	}
46604382d49SIngo Weinhold
46704382d49SIngo Weinhold	virtual Directory* ToDirectory()
46804382d49SIngo Weinhold	{
46904382d49SIngo Weinhold		return this;
47004382d49SIngo Weinhold	}
47104382d49SIngo Weinhold
47204382d49SIngo Weinhold	Entry* FindEntry(const char* name) const
47304382d49SIngo Weinhold	{
47404382d49SIngo Weinhold		return fEntries.Lookup(name);
47504382d49SIngo Weinhold	}
47604382d49SIngo Weinhold
47704382d49SIngo Weinhold	Entry* CreateEntry(const BString& name, Node* node)
47804382d49SIngo Weinhold	{
47904382d49SIngo Weinhold		Entry* entry = new(std::nothrow) Entry(this, name, node);
48004382d49SIngo Weinhold		if (entry == NULL || entry->Name().IsEmpty()) {
48104382d49SIngo Weinhold			delete entry;
48204382d49SIngo Weinhold			return NULL;
48304382d49SIngo Weinhold		}
48404382d49SIngo Weinhold
48504382d49SIngo Weinhold		AddEntry(entry);
48604382d49SIngo Weinhold		return entry;
48704382d49SIngo Weinhold	}
48804382d49SIngo Weinhold
48904382d49SIngo Weinhold	void AddEntry(Entry* entry)
49004382d49SIngo Weinhold	{
49104382d49SIngo Weinhold		fEntries.Insert(entry);
49204382d49SIngo Weinhold	}
493f30198a0SAxel Dörfler
49404382d49SIngo Weinhold	void RemoveEntry(Entry* entry)
49504382d49SIngo Weinhold	{
49604382d49SIngo Weinhold		fEntries.Remove(entry);
49704382d49SIngo Weinhold	}
49804382d49SIngo Weinhold
49904382d49SIngo Weinhold	EntryMap::Iterator GetEntryIterator() const
50004382d49SIngo Weinhold	{
50104382d49SIngo Weinhold		return fEntries.GetIterator();
50204382d49SIngo Weinhold	}
50304382d49SIngo Weinhold
50404382d49SIngo Weinhold	Entry* RemoveAllEntries()
50504382d49SIngo Weinhold	{
50604382d49SIngo Weinhold		return fEntries.Clear(true);
50704382d49SIngo Weinhold	}
50804382d49SIngo Weinhold
50904382d49SIngo Weinholdprivate:
51004382d49SIngo Weinhold	Directory(const node_ref& nodeRef)
51104382d49SIngo Weinhold		:
51204382d49SIngo Weinhold		Node(nodeRef)
51304382d49SIngo Weinhold	{
51404382d49SIngo Weinhold	}
51504382d49SIngo Weinhold
51604382d49SIngo Weinholdprivate:
51704382d49SIngo Weinhold	EntryMap	fEntries;
51804382d49SIngo Weinhold};
51904382d49SIngo Weinhold
52004382d49SIngo Weinhold
52104382d49SIngo Weinhold//	#pragma mark - Entry
52204382d49SIngo Weinhold
52304382d49SIngo Weinhold
52404382d49SIngo Weinholdinline NotOwningEntryRef
52504382d49SIngo WeinholdEntry::EntryRef() const
526f30198a0SAxel Dörfler{
52704382d49SIngo Weinhold	return NotOwningEntryRef(fParent->NodeRef(), fName);
52804382d49SIngo Weinhold}
52904382d49SIngo Weinhold
53004382d49SIngo Weinhold
53104382d49SIngo Weinhold//	#pragma mark - PathHandler
53204382d49SIngo Weinhold
53304382d49SIngo Weinhold
53404382d49SIngo Weinholdclass PathHandler : public BHandler {
53504382d49SIngo Weinholdpublic:
53604382d49SIngo Weinhold								PathHandler(const char* path, uint32 flags,
53704382d49SIngo Weinhold									const BMessenger& target, BLooper* looper);
53804382d49SIngo Weinhold	virtual						~PathHandler();
53904382d49SIngo Weinhold
54004382d49SIngo Weinhold			status_t			InitCheck() const;
54104382d49SIngo Weinhold			void				Quit();
54204382d49SIngo Weinhold
54304382d49SIngo Weinhold			const BString&		OriginalPath() const
54404382d49SIngo Weinhold									{ return fOriginalPath; }
54504382d49SIngo Weinhold			uint32				Flags() const	{ return fFlags; }
54604382d49SIngo Weinhold
54704382d49SIngo Weinhold	virtual	void				MessageReceived(BMessage* message);
54804382d49SIngo Weinhold
54904382d49SIngo Weinhold			PathHandler*&		HashNext()	{ return fHashNext; }
55004382d49SIngo Weinhold
55104382d49SIngo Weinholdprivate:
55204382d49SIngo Weinhold			status_t			_CreateAncestors();
55304382d49SIngo Weinhold			status_t			_StartWatchingAncestors(Ancestor* ancestor,
55404382d49SIngo Weinhold									bool notify);
55504382d49SIngo Weinhold			void				_StopWatchingAncestors(Ancestor* ancestor,
55604382d49SIngo Weinhold									bool notify);
55704382d49SIngo Weinhold
55804382d49SIngo Weinhold			void				_EntryCreated(BMessage* message);
55904382d49SIngo Weinhold			void				_EntryRemoved(BMessage* message);
56004382d49SIngo Weinhold			void				_EntryMoved(BMessage* message);
56104382d49SIngo Weinhold			void				_NodeChanged(BMessage* message);
56204382d49SIngo Weinhold
56304382d49SIngo Weinhold			bool				_EntryCreated(const NotOwningEntryRef& entryRef,
56404382d49SIngo Weinhold									const node_ref& nodeRef, bool isDirectory,
56504382d49SIngo Weinhold									bool dryRun, bool notify, Entry** _entry);
56604382d49SIngo Weinhold			bool				_EntryRemoved(const NotOwningEntryRef& entryRef,
56704382d49SIngo Weinhold									const node_ref& nodeRef, bool dryRun,
56804382d49SIngo Weinhold									bool notify, Entry** _keepEntry);
56904382d49SIngo Weinhold
57004382d49SIngo Weinhold			bool				_CheckDuplicateEntryNotification(int32 opcode,
57104382d49SIngo Weinhold									const entry_ref& toEntryRef,
57204382d49SIngo Weinhold									const node_ref& nodeRef,
57304382d49SIngo Weinhold									const entry_ref* fromEntryRef = NULL);
57404382d49SIngo Weinhold			void				_UnsetDuplicateEntryNotification();
57504382d49SIngo Weinhold
57604382d49SIngo Weinhold			Ancestor*			_GetAncestor(const node_ref& nodeRef) const;
57704382d49SIngo Weinhold
57804382d49SIngo Weinhold			status_t			_AddNode(const node_ref& nodeRef,
57904382d49SIngo Weinhold									bool isDirectory, bool notify,
580c869b2bbSMichael Lotz									Entry* entry = NULL, Node** _node = NULL);
58104382d49SIngo Weinhold			void				_DeleteNode(Node* node, bool notify);
58204382d49SIngo Weinhold			Node*				_GetNode(const node_ref& nodeRef) const;
58304382d49SIngo Weinhold
58404382d49SIngo Weinhold			status_t			_AddEntryIfNeeded(Directory* directory,
58504382d49SIngo Weinhold									const char* name, const node_ref& nodeRef,
58604382d49SIngo Weinhold									bool isDirectory, bool notify,
58704382d49SIngo Weinhold									Entry** _entry = NULL);
58804382d49SIngo Weinhold			void				_DeleteEntry(Entry* entry, bool notify);
58904382d49SIngo Weinhold			void				_DeleteEntryAlreadyRemovedFromParent(
59004382d49SIngo Weinhold									Entry* entry, bool notify);
59104382d49SIngo Weinhold
59204382d49SIngo Weinhold			void				_NotifyFilesCreatedOrRemoved(Entry* entry,
59304382d49SIngo Weinhold									int32 opcode) const;
59404382d49SIngo Weinhold			void				_NotifyEntryCreatedOrRemoved(Entry* entry,
59504382d49SIngo Weinhold									int32 opcode) const;
59604382d49SIngo Weinhold			void				_NotifyEntryCreatedOrRemoved(
59704382d49SIngo Weinhold									const entry_ref& entryRef,
59804382d49SIngo Weinhold									const node_ref& nodeRef, const char* path,
59904382d49SIngo Weinhold									bool isDirectory, int32 opcode) const;
60004382d49SIngo Weinhold			void				_NotifyEntryMoved(const entry_ref& fromEntryRef,
60104382d49SIngo Weinhold									const entry_ref& toEntryRef,
602e19f9091SIngo Weinhold									const node_ref& nodeRef,
603c869b2bbSMichael Lotz									const char* fromPath, const char* path,
60404382d49SIngo Weinhold									bool isDirectory, bool wasAdded,
60504382d49SIngo Weinhold									bool wasRemoved) const;
60604382d49SIngo Weinhold			void				_NotifyTarget(BMessage& message,
60704382d49SIngo Weinhold									const char* path) const;
60804382d49SIngo Weinhold
60904382d49SIngo Weinhold			BString				_NodePath(const Node* node) const;
61004382d49SIngo Weinhold			BString				_EntryPath(const Entry* entry) const;
61104382d49SIngo Weinhold
61204382d49SIngo Weinhold
61304382d49SIngo Weinhold			bool				_WatchRecursively() const;
61404382d49SIngo Weinhold			bool				_WatchFilesOnly() const;
61504382d49SIngo Weinhold			bool				_WatchDirectoriesOnly() const;
61604382d49SIngo Weinhold
61704382d49SIngo Weinholdprivate:
61804382d49SIngo Weinhold			BMessenger			fTarget;
61904382d49SIngo Weinhold			uint32				fFlags;
62004382d49SIngo Weinhold			status_t			fStatus;
62104382d49SIngo Weinhold			BString				fOriginalPath;
62204382d49SIngo Weinhold			BString				fPath;
62304382d49SIngo Weinhold			Ancestor*			fRoot;
62404382d49SIngo Weinhold			Ancestor*			fBaseAncestor;
62504382d49SIngo Weinhold			Node*				fBaseNode;
62604382d49SIngo Weinhold			AncestorMap			fAncestors;
62704382d49SIngo Weinhold			NodeMap				fNodes;
62804382d49SIngo Weinhold			PathHandler*		fHashNext;
62904382d49SIngo Weinhold			int32				fDuplicateEntryNotificationOpcode;
63004382d49SIngo Weinhold			node_ref			fDuplicateEntryNotificationNodeRef;
63104382d49SIngo Weinhold			entry_ref			fDuplicateEntryNotificationToEntryRef;
63204382d49SIngo Weinhold			entry_ref			fDuplicateEntryNotificationFromEntryRef;
63304382d49SIngo Weinhold};
63404382d49SIngo Weinhold
63504382d49SIngo Weinhold
63604382d49SIngo Weinholdstruct PathHandlerHashDefinition {
63704382d49SIngo Weinhold	typedef	const char*	KeyType;
63804382d49SIngo Weinhold	typedef	PathHandler	ValueType;
63904382d49SIngo Weinhold
64004382d49SIngo Weinhold	size_t HashKey(const char* key) const
64104382d49SIngo Weinhold	{
64204382d49SIngo Weinhold		return BString::HashValue(key);
64304382d49SIngo Weinhold	}
64404382d49SIngo Weinhold
64504382d49SIngo Weinhold	size_t Hash(PathHandler* value) const
64604382d49SIngo Weinhold	{
64704382d49SIngo Weinhold		return value->OriginalPath().HashValue();
64804382d49SIngo Weinhold	}
64904382d49SIngo Weinhold
65004382d49SIngo Weinhold	bool Compare(const char* key, PathHandler* value) const
65104382d49SIngo Weinhold	{
65204382d49SIngo Weinhold		return key == value->OriginalPath();
65304382d49SIngo Weinhold	}
65404382d49SIngo Weinhold
65504382d49SIngo Weinhold	PathHandler*& GetLink(PathHandler* value) const
65604382d49SIngo Weinhold	{
65704382d49SIngo Weinhold		return value->HashNext();
65804382d49SIngo Weinhold	}
65904382d49SIngo Weinhold};
66004382d49SIngo Weinhold
66104382d49SIngo Weinhold
66204382d49SIngo Weinholdtypedef BOpenHashTable<PathHandlerHashDefinition> PathHandlerMap;
66304382d49SIngo Weinhold
66404382d49SIngo Weinhold
66504382d49SIngo Weinhold//	#pragma mark - Watcher
66604382d49SIngo Weinhold
66704382d49SIngo Weinhold
66804382d49SIngo Weinholdstruct Watcher : public PathHandlerMap {
66904382d49SIngo Weinhold	static Watcher* Create(const BMessenger& target)
67004382d49SIngo Weinhold	{
67104382d49SIngo Weinhold		Watcher* watcher = new(std::nothrow) Watcher(target);
67204382d49SIngo Weinhold		if (watcher == NULL || watcher->Init() != B_OK) {
67304382d49SIngo Weinhold			delete watcher;
67404382d49SIngo Weinhold			return NULL;
67504382d49SIngo Weinhold		}
67604382d49SIngo Weinhold		return watcher;
67704382d49SIngo Weinhold	}
67804382d49SIngo Weinhold
67904382d49SIngo Weinhold	const BMessenger& Target() const
68004382d49SIngo Weinhold	{
68104382d49SIngo Weinhold		return fTarget;
68204382d49SIngo Weinhold	}
68304382d49SIngo Weinhold
68404382d49SIngo Weinhold	Watcher*& HashNext()
68504382d49SIngo Weinhold	{
68604382d49SIngo Weinhold		return fHashNext;
68704382d49SIngo Weinhold	}
68804382d49SIngo Weinhold
68904382d49SIngo Weinholdprivate:
69004382d49SIngo Weinhold	Watcher(const BMessenger& target)
69104382d49SIngo Weinhold		:
69204382d49SIngo Weinhold		fTarget(target)
69304382d49SIngo Weinhold	{
69404382d49SIngo Weinhold	}
69504382d49SIngo Weinhold
69604382d49SIngo Weinholdprivate:
69704382d49SIngo Weinhold	BMessenger		fTarget;
69804382d49SIngo Weinhold	Watcher*		fHashNext;
69904382d49SIngo Weinhold};
70004382d49SIngo Weinhold
70104382d49SIngo Weinhold
70204382d49SIngo Weinholdstruct WatcherHashDefinition {
70304382d49SIngo Weinhold	typedef	BMessenger	KeyType;
70404382d49SIngo Weinhold	typedef	Watcher		ValueType;
70504382d49SIngo Weinhold
70604382d49SIngo Weinhold	size_t HashKey(const BMessenger& key) const
70704382d49SIngo Weinhold	{
70804382d49SIngo Weinhold		return key.HashValue();
70904382d49SIngo Weinhold	}
71004382d49SIngo Weinhold
71104382d49SIngo Weinhold	size_t Hash(Watcher* value) const
71204382d49SIngo Weinhold	{
71304382d49SIngo Weinhold		return HashKey(value->Target());
71404382d49SIngo Weinhold	}
71504382d49SIngo Weinhold
71604382d49SIngo Weinhold	bool Compare(const BMessenger& key, Watcher* value) const
71704382d49SIngo Weinhold	{
71804382d49SIngo Weinhold		return key == value->Target();
71904382d49SIngo Weinhold	}
72004382d49SIngo Weinhold
72104382d49SIngo Weinhold	Watcher*& GetLink(Watcher* value) const
72204382d49SIngo Weinhold	{
72304382d49SIngo Weinhold		return value->HashNext();
72404382d49SIngo Weinhold	}
72504382d49SIngo Weinhold};
726f30198a0SAxel Dörfler
727f30198a0SAxel Dörfler
72804382d49SIngo Weinhold//	#pragma mark - PathHandler
729f30198a0SAxel Dörfler
730f30198a0SAxel Dörfler
73104382d49SIngo WeinholdPathHandler::PathHandler(const char* path, uint32 flags,
73204382d49SIngo Weinhold	const BMessenger& target, BLooper* looper)
73304382d49SIngo Weinhold	:
73404382d49SIngo Weinhold	BHandler(path),
73504382d49SIngo Weinhold	fTarget(target),
73604382d49SIngo Weinhold	fFlags(flags),
73704382d49SIngo Weinhold	fStatus(B_OK),
73804382d49SIngo Weinhold	fOriginalPath(path),
73904382d49SIngo Weinhold	fPath(),
74004382d49SIngo Weinhold	fRoot(NULL),
74104382d49SIngo Weinhold	fBaseAncestor(NULL),
74204382d49SIngo Weinhold	fBaseNode(NULL),
74304382d49SIngo Weinhold	fAncestors(),
74404382d49SIngo Weinhold	fNodes()
745f30198a0SAxel Dörfler{
74604382d49SIngo Weinhold	TRACE("%p->PathHandler::PathHandler(\"%s\", %#" B_PRIx32 ")\n", this, path,
74704382d49SIngo Weinhold		flags);
748afbd081aSStephan Aßmus
74904382d49SIngo Weinhold	_UnsetDuplicateEntryNotification();
750afbd081aSStephan Aßmus
75104382d49SIngo Weinhold	fStatus = fAncestors.Init();
75204382d49SIngo Weinhold	if (fStatus != B_OK)
75304382d49SIngo Weinhold		return;
754afbd081aSStephan Aßmus
75504382d49SIngo Weinhold	fStatus = fNodes.Init();
75604382d49SIngo Weinhold	if (fStatus != B_OK)
75704382d49SIngo Weinhold		return;
758f30198a0SAxel Dörfler
75904382d49SIngo Weinhold	// normalize the flags
76004382d49SIngo Weinhold	if ((fFlags & B_WATCH_RECURSIVELY) != 0) {
76104382d49SIngo Weinhold		// We add B_WATCH_NAME and B_WATCH_DIRECTORY as needed, so clear them
76204382d49SIngo Weinhold		// here.
76304382d49SIngo Weinhold		fFlags &= ~uint32(B_WATCH_NAME | B_WATCH_DIRECTORY);
76404382d49SIngo Weinhold	} else {
76504382d49SIngo Weinhold		// The B_WATCH_*_ONLY flags are only valid for the recursive mode.
76604382d49SIngo Weinhold		// B_WATCH_NAME is implied (we watch the parent directory).
76704382d49SIngo Weinhold		fFlags &= ~uint32(B_WATCH_FILES_ONLY | B_WATCH_DIRECTORIES_ONLY
76804382d49SIngo Weinhold			| B_WATCH_NAME);
76904382d49SIngo Weinhold	}
770f30198a0SAxel Dörfler
77104382d49SIngo Weinhold	// Normalize the path a bit. We can't use BPath, as it may really normalize
77204382d49SIngo Weinhold	// the path, i.e. resolve symlinks and such, which may cause us to monitor
77304382d49SIngo Weinhold	// the wrong path. We want some normalization, though:
77404382d49SIngo Weinhold	// * relative -> absolute path
77504382d49SIngo Weinhold	// * fold duplicate '/'s
77604382d49SIngo Weinhold	// * omit "." components
77704382d49SIngo Weinhold	// * fail when encountering ".." components
77804382d49SIngo Weinhold
77904382d49SIngo Weinhold	// make absolute
78004382d49SIngo Weinhold	BString normalizedPath;