1// StatCacheServer.cpp
2
3#include <dirent.h>
4#include <errno.h>
5#include <stdio.h>
6#include <string.h>
7#include <sys/stat.h>
8#include <unistd.h>
9
10#include <Application.h>
11#include <Autolock.h>
12#include <Message.h>
13#include <NodeMonitor.h>
14#include <Path.h>
15
16#include "StatCacheServer.h"
17#include "StatCacheServerImpl.h"
18
19//#define DBG(x) { x; }
20#define DBG(x)
21#define OUT(format...) {printf(format); fflush(stdout);}
22
23static const int32 kMaxSymlinks = 32;
24
25// node monitor constants
26static const int32 kDefaultNodeMonitorLimit = 4096;
27static const int32 kNodeMonitorLimitIncrement = 512;
28
29// private BeOS syscall to set the  node monitor slot limits
30extern "C" int _kset_mon_limit_(int num);
31
32static inline bool
33is_dot_or_dotdot(const char* name)
34{
35	if (name && name[0] == '.')
36		return (name[1] == '\0' || name[1] == '.' && name[2] == '\0');
37	return false;
38}
39
40// get_dirent_size
41static inline
42int32
43get_dirent_size(const char *name)
44{
45	dirent *dummy = NULL;
46	int32 entrySize = (dummy->d_name + strlen(name) + 1) - (char*)dummy;
47	return (entrySize + 3) & ~0x3;
48}
49
50// node_ref_hash
51static inline
52uint32
53node_ref_hash(dev_t device, ino_t node)
54{
55	uint32 hash = device;
56	hash = hash * 17 + (uint32)node;
57	hash = hash * 17 + (uint32)(node >> 32);
58	return hash;
59}
60
61// string_hash
62//
63// from the Dragon Book: a slightly modified hashpjw()
64static inline
65uint32
66string_hash(const char *name)
67{
68	uint32 h = 0;
69	if (name) {
70		for (; *name; name++) {
71			uint32 g = h & 0xf0000000;
72			if (g)
73				h ^= g >> 24;
74			h = (h << 4) + *name;
75		}
76	}
77	return h;
78}
79
80// NodeRefHash
81size_t
82NodeRefHash::operator()(const node_ref &nodeRef) const
83{
84	return node_ref_hash(nodeRef.device, nodeRef.node);
85}
86
87// EntryRefHash
88size_t
89EntryRefHash::operator()(const entry_ref &entryRef) const
90{
91	uint32 hash = node_ref_hash(entryRef.device, entryRef.directory);
92	hash = hash * 17 + string_hash(entryRef.name);
93	return hash;
94}
95
96
97// #pragma mark -
98
99// constructor
100Entry::Entry()
101	: Referencable(),
102	  fParent(NULL),
103	  fName(),
104	  fNode(NULL),
105	  fPrevious(NULL),
106	  fNext(NULL)
107{
108}
109
110// destructor
111Entry::~Entry()
112{
113	SetNode(NULL);
114}
115
116// SetTo
117status_t
118Entry::SetTo(Directory *parent, const char *name)
119{
120	fParent = parent;
121	fName = name;
122	return B_OK;
123}
124
125// GetParent
126Directory *
127Entry::GetParent() const
128{
129	return fParent;
130}
131
132// GetName
133const char *
134Entry::GetName() const
135{
136	return fName.c_str();
137}
138
139// SetNode
140void
141Entry::SetNode(Node *node)
142{
143	if (fNode != node) {
144		if (fNode)
145			fNode->RemoveReference();
146		fNode = node;
147		if (fNode) {
148			fNode->AddReference();
149			if (!fNode->GetEntry() && !is_dot_or_dotdot(fName.c_str()))
150				fNode->SetEntry(this);
151		}
152	}
153}
154
155// GetNode
156Node *
157Entry::GetNode() const
158{
159	return fNode;
160}
161
162// SetPrevious
163void
164Entry::SetPrevious(Entry *entry)
165{
166	fPrevious = entry;
167}
168
169// GetPrevious
170Entry *
171Entry::GetPrevious() const
172{
173	return fPrevious;
174}
175
176// SetNext
177void
178Entry::SetNext(Entry *entry)
179{
180	fNext = entry;
181}
182
183// GetNext
184Entry *
185Entry::GetNext() const
186{
187	return fNext;
188}
189
190// GetEntryRef
191entry_ref
192Entry::GetEntryRef() const
193{
194	node_ref dirRef(fParent->GetNodeRef());
195	return entry_ref(dirRef.device, dirRef.node, fName.c_str());
196}
197
198// GetPath
199status_t
200Entry::GetPath(string& path)
201{
202	if (!fParent)
203		return B_ERROR;
204
205	// get directory path
206	status_t error = fParent->GetPath(path);
207	if (error != B_OK)
208		return error;
209
210	// append the entry name
211	if (path[path.length() - 1] != '/')
212		path += '/';
213	path += fName;
214
215	return B_OK;
216}
217
218// Unreferenced
219void
220Entry::Unreferenced()
221{
222	NodeManager::GetDefault()->EntryUnreferenced(this);
223}
224
225
226// #pragma mark -
227
228// constructor
229Node::Node(const struct stat &st)
230	: Referencable(),
231	  fEntry(NULL),
232	  fStat(st),
233	  fStatValid(false)
234{
235}
236
237// destructor
238Node::~Node()
239{
240	// stop watching the node
241	NodeManager::GetDefault()->StopWatching(this);
242
243	SetEntry(NULL);
244}
245
246// SetTo
247status_t
248Node::SetTo(Entry *entry)
249{
250	// start watching the node
251	status_t error = NodeManager::GetDefault()->StartWatching(this);
252	if (error != B_OK)
253		return error;
254
255	SetEntry(entry);
256
257	// update the stat
258	return UpdateStat();
259}
260
261// GetPath
262status_t
263Node::GetPath(string& path)
264{
265	if (this == NodeManager::GetDefault()->GetRootDirectory()) {
266		path = "/";
267		return B_OK;
268	}
269
270	if (!fEntry)
271		return B_ERROR;
272	return fEntry->GetPath(path);
273}
274
275// GetStat
276const struct stat &
277Node::GetStat() const
278{
279	return fStat;
280}
281
282// GetStat
283status_t
284Node::GetStat(struct stat *st)
285{
286	if (!fStatValid) {
287		status_t error = UpdateStat();
288		if (error != B_OK)
289			return error;
290	}
291	*st = fStat;
292	return B_OK;
293}
294
295// UpdateStat
296status_t
297Node::UpdateStat()
298{
299	// get path
300	string path;
301	status_t error = GetPath(path);
302	if (error != B_OK)
303		return error;
304
305DBG(OUT("disk access: lstat(): %s\n", path.c_str()));
306
307	// read stat
308	if (lstat(path.c_str(), &fStat) < 0)
309		return errno;
310	fStatValid = true;
311	return B_OK;
312}
313
314// MarkStatInvalid
315void
316Node::MarkStatInvalid()
317{
318	fStatValid = false;
319}
320
321// SetEntry
322void
323Node::SetEntry(Entry *entry)
324{
325	if (entry != fEntry) {
326		if (fEntry)
327			fEntry->RemoveReference();
328		fEntry = entry;
329		if (fEntry)
330			fEntry->AddReference();
331	}
332}
333
334// GetEntry
335Entry *
336Node::GetEntry() const
337{
338	return fEntry;
339}
340
341// GetNodeRef
342node_ref
343Node::GetNodeRef() const
344{
345	node_ref nodeRef;
346	nodeRef.device = fStat.st_dev;
347	nodeRef.node = fStat.st_ino;
348	return nodeRef;
349}
350
351// Unreferenced
352void
353Node::Unreferenced()
354{
355	NodeManager::GetDefault()->NodeUnreferenced(this);
356}
357
358
359// #pragma mark -
360
361// constructor
362Directory::Directory(const struct stat &st)
363	: Node(st),
364	  fFirstEntry(NULL),
365	  fLastEntry(NULL),
366	  fIsComplete(false)
367{
368}
369
370// destructor
371Directory::~Directory()
372{
373	while (Entry *entry = GetFirstEntry())
374		RemoveEntry(entry);
375}
376
377// SetTo
378status_t
379Directory::SetTo(Entry *entry)
380{
381	return Node::SetTo(entry);
382}
383
384// FindEntry
385status_t
386Directory::FindEntry(const char *name, Entry **entry)
387{
388	entry_ref ref(fStat.st_dev, fStat.st_ino, name);
389	if (!fIsComplete)
390		return NodeManager::GetDefault()->CreateEntry(ref, NULL, entry);
391	*entry = NodeManager::GetDefault()->GetEntry(ref);
392	return (*entry ? B_OK : B_ENTRY_NOT_FOUND);
393}
394
395// GetFirstEntry
396Entry *
397Directory::GetFirstEntry() const
398{
399	return fFirstEntry;
400}
401
402// GetNextEntry
403Entry *
404Directory::GetNextEntry(Entry *entry) const
405{
406	return (entry ? entry->GetNext() : NULL);
407}
408
409// ReadAllEntries
410status_t
411Directory::ReadAllEntries()
412{
413	if (fIsComplete)
414		return B_OK;
415
416	// get the path
417	string path;
418	status_t error = GetPath(path);
419	if (error != B_OK)
420		return error;
421
422DBG(OUT("disk access: opendir(): %s\n", path.c_str()));
423
424	// open the directory
425	DIR *dir = opendir(path.c_str());
426	if (!dir)
427		return errno;
428
429	// read the directory
430	while (dirent *entry = readdir(dir)) {
431		Entry *dummy;
432		FindEntry(entry->d_name, &dummy);
433	}
434	closedir(dir);
435
436	fIsComplete = true;
437
438	return B_OK;
439}
440
441// IsComplete
442bool
443Directory::IsComplete() const
444{
445	return fIsComplete;
446}
447
448// AddEntry
449void
450Directory::AddEntry(Entry *entry)
451{
452	if (fLastEntry) {
453		entry->SetPrevious(fLastEntry);
454		entry->SetNext(NULL);
455		fLastEntry->SetNext(entry);
456		fLastEntry = entry;
457	} else {
458		entry->SetPrevious(NULL);
459		entry->SetNext(NULL);
460		fFirstEntry = fLastEntry = entry;
461	}
462	entry->AddReference();
463
464	// the reference the "." entry has, shall be ignored
465	if (strcmp(entry->GetName(), ".") == 0)
466		fReferenceBaseCount++;
467}
468
469// RemoveEntry
470void
471Directory::RemoveEntry(Entry *entry)
472{
473	if (entry->GetParent() != this)
474		return;
475
476	// the reference the "." entry has, shall be ignored
477	if (strcmp(entry->GetName(), ".") == 0)
478		fReferenceBaseCount--;
479
480	if (entry->GetPrevious())
481		entry->GetPrevious()->SetNext(entry->GetNext());
482	else
483		fFirstEntry = entry->GetNext();
484	if (entry->GetNext())
485		entry->GetNext()->SetPrevious(entry->GetPrevious());
486	else
487		fLastEntry = entry->GetPrevious();
488	entry->SetPrevious(NULL);
489	entry->SetNext(NULL);
490	entry->RemoveReference();
491}
492
493
494// #pragma mark -
495
496// constructor
497SymLink::SymLink(const struct stat &st)
498	: Node(st),
499	  fTarget()
500{
501}
502
503// destructor
504SymLink::~SymLink()
505{
506}
507
508// SetTo
509status_t
510SymLink::SetTo(Entry *entry)
511{
512	// node initialization
513	status_t error = Node::SetTo(entry);
514	if (error != B_OK)
515		return error;
516
517	// get the entry path
518	string path;
519	error = entry->GetPath(path);
520	if (error != B_OK)
521		return error;
522
523	// read the link
524	char target[B_PATH_NAME_LENGTH + 1];
525	ssize_t bytesRead = readlink(path.c_str(), target, B_PATH_NAME_LENGTH);
526	if (bytesRead < 0)
527		return errno;
528	target[bytesRead] = '\0';
529	fTarget = target;
530	return B_OK;
531}
532
533// GetTarget
534const char *
535SymLink::GetTarget() const
536{
537	return fTarget.c_str();
538}
539
540
541// #pragma mark -
542
543// destructor
544NodeMonitor::NodeMonitor()
545	// higher priority and larger queue, since we must not miss update events
546	: BLooper("node monitor", B_DISPLAY_PRIORITY, 1000),
547	  fCurrentNodeMonitorLimit(kDefaultNodeMonitorLimit),
548	  fMessageCountSem(-1)
549{
550}
551
552// destructor
553NodeMonitor::~NodeMonitor()
554{
555	delete_sem(fMessageCountSem);
556}
557
558// Init
559status_t
560NodeMonitor::Init()
561{
562	fMessageCountSem = create_sem(0, "nm message count");
563	if (fMessageCountSem < 0)
564		return fMessageCountSem;
565	return B_OK;
566}
567
568// MessageReceived
569void
570NodeMonitor::MessageReceived(BMessage *message)
571{
572	switch (message->what) {
573		case B_NODE_MONITOR:
574			DetachCurrentMessage();
575			fMessageQueue.AddMessage(message);
576			release_sem(fMessageCountSem);
577			break;
578		default:
579			BLooper::MessageReceived(message);
580	}
581}
582
583// StartWatching
584status_t
585NodeMonitor::StartWatching(Node *node)
586{
587	if (!node)
588		return B_BAD_VALUE;
589	uint32 flags = B_WATCH_STAT;
590	if (S_ISDIR(node->GetStat().st_mode))
591		flags |= B_WATCH_DIRECTORY;
592	node_ref ref = node->GetNodeRef();
593	status_t error = watch_node(&ref, flags, this);
594	// If starting to watch the node fail, we allocate more node
595	// monitoring slots and try again.
596	if (error != B_OK) {
597		fCurrentNodeMonitorLimit += kNodeMonitorLimitIncrement;
598		error = _kset_mon_limit_(fCurrentNodeMonitorLimit);
599		if (error == B_OK)
600			error = watch_node(&ref, flags, this);
601	}
602	return error;
603}
604
605// StopWatching
606status_t
607NodeMonitor::StopWatching(Node *node)
608{
609	if (!node)
610		return B_BAD_VALUE;
611	node_ref ref = node->GetNodeRef();
612	return watch_node(&ref, B_STOP_WATCHING, this);
613}
614
615// GetNextMonitoringMessage
616status_t
617NodeMonitor::GetNextMonitoringMessage(BMessage **_message)
618{
619	// acquire the semaphore
620	status_t error = B_OK;
621	do {
622		error = acquire_sem(fMessageCountSem);
623	} while (error == B_INTERRUPTED);
624	if (error != B_OK)
625		return error;
626
627	// get the message
628	BMessage *message = fMessageQueue.NextMessage();
629	if (!message)
630		return B_ERROR;
631	*_message = message;
632	return B_OK;
633}
634
635
636// #pragma mark -
637
638// constructor
639PathResolver::PathResolver()
640	: fSymLinkCounter(0)
641{
642}
643
644// FindEntry
645status_t
646PathResolver::FindEntry(const char *path, bool traverse, Entry **_entry)
647{
648	return FindEntry(NULL, path, traverse, _entry);
649}
650
651// FindEntry
652status_t
653PathResolver::FindEntry(Entry *entry, const char *path, bool traverse,
654	Entry **_entry)
655{
656	// we accept only absolute paths, if no entry was given
657	if (!path || (!entry && *path != '/'))
658		return B_BAD_VALUE;
659
660	// get the root directory for absolute paths
661	if (*path == '/') {
662		entry = NodeManager::GetDefault()->GetRootDirectory()->GetEntry();
663		// skip '/'
664		while (*path == '/')
665			path++;
666	}
667
668	while (*path != '\0') {
669		// get path component
670		int componentLen;
671		if (char *nextSlash = strchr(path, '/'))
672			componentLen = nextSlash - path;
673		else
674			componentLen = strlen(path);
675		string component(path, componentLen);
676		path += componentLen;
677
678		// resolve symlink
679		Node *node = entry->GetNode();
680		if (SymLink *symlink = dynamic_cast<SymLink*>(node)) {
681			status_t error = ResolveSymlink(symlink, &node);
682			if (error != B_OK)
683				return error;
684		}
685
686		// find the entry
687		if (Directory *dir = dynamic_cast<Directory*>(node)) {
688			status_t error = dir->FindEntry(component.c_str(), &entry);
689			if (error != B_OK)
690				return error;
691		} else
692			return B_ENTRY_NOT_FOUND;
693
694		// skip '/'
695		while (*path == '/')
696			path++;
697	}
698
699	// traverse leaf symlink, if requested
700	if (traverse) {
701		status_t error = ResolveSymlink(entry, &entry);
702		if (error != B_OK)
703			return error;
704	}
705
706	*_entry = entry;
707	return B_OK;
708}
709
710// FindNode
711status_t
712PathResolver::FindNode(const char *path, bool traverse, Node **node)
713{
714	Entry *entry;
715	status_t error = FindEntry(path, traverse, &entry);
716	if (error != B_OK)
717		return error;
718	if (!entry->GetNode())
719		return B_ENTRY_NOT_FOUND;
720	*node = entry->GetNode();
721	return B_OK;
722}
723
724// ResolveSymlink
725status_t
726PathResolver::ResolveSymlink(Node *node, Node **_node)
727{
728	Entry *entry;
729	status_t error = ResolveSymlink(node, &entry);
730	if (error != B_OK)
731		return error;
732	if (!entry->GetNode())
733		return B_ENTRY_NOT_FOUND;
734	*_node = entry->GetNode();
735	return B_OK;
736}
737
738// ResolveSymlink
739status_t
740PathResolver::ResolveSymlink(Node *node, Entry **entry)
741{
742	return ResolveSymlink(node->GetEntry(), entry);
743}
744
745// ResolveSymlink
746status_t
747PathResolver::ResolveSymlink(Entry *entry, Entry **_entry)
748{
749	if (!entry->GetNode())
750		return B_ENTRY_NOT_FOUND;
751
752	SymLink *symlink = dynamic_cast<SymLink*>(entry->GetNode());
753	if (!symlink) {
754		*_entry = entry;
755		return B_OK;
756	}
757
758	if (fSymLinkCounter > kMaxSymlinks)
759		return B_LINK_LIMIT;
760
761	const char *target = symlink->GetTarget();
762	if (!target || !symlink->GetEntry() || !symlink->GetEntry()->GetParent()
763		|| !symlink->GetEntry()->GetParent()->GetEntry())
764		return B_ENTRY_NOT_FOUND;
765
766	fSymLinkCounter++;
767	status_t error = FindEntry(symlink->GetEntry()->GetParent()->GetEntry(),
768		target, true, _entry);
769	fSymLinkCounter--;
770	return error;
771}
772
773
774// #pragma mark -
775
776// constructor
777NodeManager::NodeManager()
778	: BLocker("node manager"),
779	  fRootDirectory(NULL),
780	  fNodeMonitor(NULL),
781	  fNodeMonitoringProcessor(-1)
782{
783}
784
785// destructor
786NodeManager::~NodeManager()
787{
788	if (fNodeMonitor) {
789		fNodeMonitor->Lock();
790		fNodeMonitor->Quit();
791	}
792
793	if (fNodeMonitoringProcessor >= 0) {
794		status_t status;
795		wait_for_thread(fNodeMonitoringProcessor, &status);
796	}
797}
798
799// GetDefault
800NodeManager *
801NodeManager::GetDefault()
802{
803	return &sManager;
804}
805
806// Init
807status_t
808NodeManager::Init()
809{
810	// create the node monitor
811	fNodeMonitor = new NodeMonitor;
812	status_t error = fNodeMonitor->Init();
813	if (error != B_OK)
814		return error;
815	fNodeMonitor->Run();
816
817	// spawn the node monitoring processor
818	fNodeMonitoringProcessor = spawn_thread(&_NodeMonitoringProcessorEntry,
819		"node monitoring processor", B_NORMAL_PRIORITY, this);
820	if (fNodeMonitoringProcessor < 0)
821		return fNodeMonitoringProcessor;
822	resume_thread(fNodeMonitoringProcessor);
823
824	// get root dir stat
825	struct stat st;
826	if (lstat("/", &st) < 0)
827		return errno;
828
829	// create the root node
830	node_ref nodeRef;
831	nodeRef.device = st.st_dev;
832	nodeRef.node = st.st_ino;
833	fRootDirectory = new Directory(st);
834	fNodes[nodeRef] = fRootDirectory;
835
836	// create an entry pointing to the root node
837	entry_ref entryRef(st.st_dev, st.st_ino, ".");
838	Entry *entry = new Entry;
839	error = entry->SetTo(fRootDirectory, ".");
840	if (error != B_OK)
841		return error;
842	entry->SetNode(fRootDirectory);
843	fEntries[entryRef] = entry;
844
845	// now we can initialize the root directory
846	error = fRootDirectory->SetTo(entry);
847
848	return error;
849}
850
851// GetRootDirectory
852Directory *
853NodeManager::GetRootDirectory() const
854{
855	return fRootDirectory;
856}
857
858// GetNode
859Node *
860NodeManager::GetNode(const node_ref &nodeRef)
861{
862	NodeMap::iterator it = fNodes.find(nodeRef);
863	if (it == fNodes.end())
864		return NULL;
865	return it->second;
866}
867
868// GetEntry
869Entry *
870NodeManager::GetEntry(const entry_ref &entryRef)
871{
872	EntryMap::iterator it = fEntries.find(entryRef);
873	if (it == fEntries.end())
874		return NULL;
875	return it->second;
876}
877
878// CreateEntry
879status_t
880NodeManager::CreateEntry(const entry_ref &entryRef, const node_ref *nodeRef,
881	Entry **_entry)
882{
883	Entry *entry = GetEntry(entryRef);
884
885	// If the entry is known, but its node is not the one it should be, we
886	// remove the entry.
887	if (nodeRef && entry && entry->GetNode()
888		&& entry->GetNode()->GetNodeRef() != *nodeRef) {
889		RemoveEntry(entry);
890		entry = NULL;
891	}
892
893	if (!entry) {
894		// entry does not yet exist -- create it
895
896		// get the parent directory
897		node_ref parentDirRef;
898		parentDirRef.device = entryRef.device;
899		parentDirRef.node = entryRef.directory;
900		Directory *dir;
901		status_t error = CreateDirectory(parentDirRef, &dir);
902		if (error != B_OK)
903			return error;
904
905		// if the directory hasn't created it, we need to do that now
906		entry = GetEntry(entryRef);
907		if (!entry) {
908			entry = new Entry;
909			error = entry->SetTo(dir, entryRef.name);
910			if (error != B_OK) {
911				delete entry;
912				return error;
913			}
914
915			// get the entry's node
916			Node *node;
917			error = NodeManager::GetDefault()->_CreateNode(entry, &node);
918			if (error != B_OK) {
919				delete entry;
920				return error;
921			}
922
923			// If the node already existed, but points to another entry, we
924			// remove that entry now. We probably missed the respective
925			// B_ENTRY_REMOVED notification.
926			if (node->GetEntry() && node->GetEntry() != entry
927				&& !is_dot_or_dotdot(entry->GetName())) {
928				RemoveEntry(node->GetEntry());
929
930				// reinit node watching
931				StopWatching(node);
932				StartWatching(node);
933			}
934
935			entry->SetNode(node);
936			node->RemoveReference();
937				// reference acquired by _CreateNode()
938
939			// initialization successful: add the entry to the dir and to the
940			// entry map
941			dir->AddEntry(entry);
942			fEntries[entryRef] = entry;
943			entry->RemoveReference();
944DBG(
945string path;
946entry->GetPath(path);
947OUT("entry created: `%s'\n", path.c_str());
948)
949		}
950	}
951
952	*_entry = entry;
953	return B_OK;
954}
955
956// CreateDirectory
957status_t
958NodeManager::CreateDirectory(const node_ref &nodeRef, Directory **_dir)
959{
960	Node *node = GetNode(nodeRef);
961	if (!node) {
962		// node not yet known -- load the directory
963		// get the full path
964		entry_ref entryRef(nodeRef.device, nodeRef.node, ".");
965		BPath path;
966		status_t error = path.SetTo(&entryRef);
967		if (error != B_OK)
968			return error;
969
970		// find the node
971		error = PathResolver().FindNode(path.Path(), false, &node);
972		if (error != B_OK)
973			return error;
974	}
975
976	// node found -- check, if it is a directory
977	Directory *dir = dynamic_cast<Directory*>(node);
978	if (!dir)
979		return B_NOT_A_DIRECTORY;
980
981	*_dir = dir;
982	return B_OK;
983}
984
985// RemoveEntry
986void
987NodeManager::RemoveEntry(Entry *entry)
988{
989	if (!entry)
990		return;
991
992DBG(
993string path;
994entry->GetPath(path);
995OUT("entry removed: `%s'\n", path.c_str());
996)
997
998	// get a temporary reference, so that the entry will not be deleted when
999	// we unset the node
1000	entry->AddReference();
1001
1002	// remove from directory and node
1003	if (entry->GetParent())
1004		entry->GetParent()->RemoveEntry(entry);
1005
1006	// detach from node
1007	Node *node = entry->GetNode();
1008	if (node) {
1009		if (node->GetEntry() == entry)
1010			node->SetEntry(NULL);
1011		entry->SetNode(NULL);
1012	}
1013
1014	// surrender our temporary reference: now the entry should be unreference
1015	entry->RemoveReference();
1016}
1017
1018// MoveEntry
1019void
1020NodeManager::MoveEntry(Entry *entry, const entry_ref &newRef,
1021	const node_ref &nodeRef)
1022{
1023	// get the target directory
1024	node_ref newDirRef;
1025	newDirRef.device = newRef.device;
1026	newDirRef.node = newRef.directory;
1027	Directory *newDir = dynamic_cast<Directory*>(GetNode(newDirRef));
1028	if (!newDir) {
1029		// target directory unknown -- simply remove the entry
1030		RemoveEntry(entry);
1031		return;
1032	}
1033
1034	// If the directory, the name, or the node (missed B_ENTRY_REMOVED and
1035	// B_ENTRY_CREATED) changed, we remove the old entry and create a new one.
1036	Node *node = entry->GetNode();
1037	if (newDir != entry->GetParent()
1038		|| strcmp(newRef.name, entry->GetName()) != 0
1039		|| (node && node->GetNodeRef() != nodeRef)) {
1040		// get a temporary reference to the node, so it won't be unnecessarily
1041		// deleted
1042		if (node)
1043			node->AddReference();
1044
1045		RemoveEntry(entry);
1046		CreateEntry(newRef, &nodeRef, &entry);
1047
1048		if (node)
1049			node->RemoveReference();
1050	}
1051}
1052
1053// EntryUnreferenced
1054void
1055NodeManager::EntryUnreferenced(Entry *entry)
1056{
1057DBG(OUT("NodeManager::EntryUnreferenced(%p): (%p, `%s')\n", entry, entry->GetParent(), entry->GetName()));
1058	// remove entry from the map and delete it
1059	if (fEntries.erase(entry->GetEntryRef()) > 0)
1060		delete entry;
1061}
1062
1063// NodeUnreferenced
1064void
1065NodeManager::NodeUnreferenced(Node *node)
1066{
1067DBG(OUT("NodeManager::NodeUnreferenced(%p): entry: %p\n", node, node->GetEntry()));
1068	// remove node from the map and delete it
1069	if (fNodes.erase(node->GetNodeRef()) > 0)
1070		delete node;
1071}
1072
1073// StartWatching
1074status_t
1075NodeManager::StartWatching(Node *node)
1076{
1077	return fNodeMonitor->StartWatching(node);
1078}
1079
1080// StopWatching
1081status_t
1082NodeManager::StopWatching(Node *node)
1083{
1084	return fNodeMonitor->StopWatching(node);
1085}
1086
1087// _NodeMonitoringProcessorEntry
1088int32
1089NodeManager::_NodeMonitoringProcessorEntry(void *data)
1090{
1091	return ((NodeManager*)data)->_NodeMonitoringProcessor();
1092}
1093
1094// _NodeMonitoringProcessor
1095int32
1096NodeManager::_NodeMonitoringProcessor()
1097{
1098	BMessage *message;
1099	while (fNodeMonitor->GetNextMonitoringMessage(&message) == B_OK) {
1100		int32 opcode;
1101		if (message->FindInt32("opcode", &opcode) == B_OK) {
1102			BAutolock _(this);
1103			switch (opcode) {
1104				case B_ENTRY_CREATED:
1105					_EntryCreated(message);
1106					break;
1107				case B_ENTRY_REMOVED:
1108					_EntryRemoved(message);
1109					break;
1110				case B_ENTRY_MOVED:
1111					_EntryMoved(message);
1112					break;
1113				case B_STAT_CHANGED:
1114					_StatChanged(message);
1115					break;
1116			}
1117		}
1118		delete message;
1119	}
1120}
1121
1122// _CreateNode
1123//
1124// On success the caller gets a reference to the node, they are required to
1125// surrender, if done with the node.
1126status_t
1127NodeManager::_CreateNode(Entry *entry, Node **_node)
1128{
1129	// get the path
1130	string path;
1131	status_t error = entry->GetPath(path);
1132	if (error != B_OK)
1133		return error;
1134
1135DBG(OUT("disk access: lstat(): %s\n", path.c_str()));
1136
1137	// read the stat
1138	struct stat st;
1139	if (lstat(path.c_str(), &st) < 0)
1140		return errno;
1141
1142	// check, if the node does already exist
1143	node_ref nodeRef;
1144	nodeRef.device = st.st_dev;
1145	nodeRef.node = st.st_ino;
1146	Node *node = GetNode(nodeRef);
1147
1148	if (node) {
1149		node->AddReference();
1150	} else {
1151		// node does not yet exist -- create it
1152		if (S_ISLNK(st.st_mode))
1153			node = new SymLink(st);
1154		else if (S_ISDIR(st.st_mode))
1155			node = new Directory(st);
1156		else
1157			node = new Node(st);
1158
1159		error = node->SetTo(entry);
1160		if (error != B_OK) {
1161			delete node;
1162			return error;
1163		}
1164
1165		fNodes[nodeRef] = node;
1166	}
1167
1168	*_node = node;
1169	return B_OK;
1170}
1171
1172// _EntryCreated
1173void
1174NodeManager::_EntryCreated(BMessage *message)
1175{
1176	// get the info
1177	node_ref dirNodeRef;
1178	node_ref nodeRef;
1179	const char* name;
1180	if (message->FindInt32("device", &dirNodeRef.device) != B_OK
1181		|| message->FindInt64("directory", &dirNodeRef.node) != B_OK
1182		|| message->FindInt64("node", &nodeRef.node) != B_OK
1183		|| message->FindString("name", &name) != B_OK) {
1184		return;
1185	}
1186	nodeRef.device = dirNodeRef.device;
1187
1188	// get the directory
1189	Node *node = NodeManager::GetDefault()->GetNode(dirNodeRef);
1190	Directory *dir = dynamic_cast<Directory*>(node);
1191	if (!dir)
1192		return;
1193
1194	// add the entry, if the directory is complete
1195	if (dir->IsComplete()) {
1196		Entry *entry;
1197		if (dir->FindEntry(name, &entry) != B_OK) {
1198			entry_ref ref(dirNodeRef.device, dirNodeRef.node, name);
1199			NodeManager::GetDefault()->CreateEntry(ref, &nodeRef, &entry);
1200		}
1201	}
1202}
1203
1204// _EntryRemoved
1205void
1206NodeManager::_EntryRemoved(BMessage *message)
1207{
1208	// get the info
1209	node_ref nodeRef;
1210	const char* name;
1211	if (message->FindInt32("device", &nodeRef.device) != B_OK
1212//		|| message->FindInt64("directory", &nodeRef.node) != B_OK
1213		|| message->FindInt64("node", &nodeRef.node) != B_OK) {
1214		return;
1215	}
1216
1217	// get the node
1218	Node *node = NodeManager::GetDefault()->GetNode(nodeRef);
1219	if (!node)
1220		return;
1221
1222	// remove it
1223	NodeManager::GetDefault()->RemoveEntry(node->GetEntry());
1224}
1225
1226// _EntryMoved
1227void
1228NodeManager::_EntryMoved(BMessage *message)
1229{
1230	// get the info
1231	node_ref nodeRef;
1232	ino_t newDirID;
1233	const char* name;
1234	if (message->FindInt32("device", &nodeRef.device) != B_OK
1235//		|| message->FindInt64("from directory", &fromDirectoryID) != B_OK
1236		|| message->FindInt64("to directory", &newDirID) != B_OK
1237		|| message->FindInt64("node", &nodeRef.node) != B_OK
1238		|| message->FindString("name", &name) != B_OK) {
1239		return;
1240	}
1241
1242	// get the node
1243	Node *node = NodeManager::GetDefault()->GetNode(nodeRef);
1244	if (!node) {
1245		// create it if not present
1246		Entry *entry;
1247		entry_ref newRef(nodeRef.device, newDirID, name);
1248		NodeManager::GetDefault()->CreateEntry(newRef, &nodeRef, &entry);
1249		return;
1250	}
1251
1252	// move it
1253	entry_ref newRef(nodeRef.device, newDirID, name);
1254	NodeManager::GetDefault()->MoveEntry(node->GetEntry(), newRef, nodeRef);
1255}
1256
1257// _StatChanged
1258void
1259NodeManager::_StatChanged(BMessage *message)
1260{
1261	// get the node ref
1262	node_ref nodeRef;
1263	if (message->FindInt32("device", &nodeRef.device) != B_OK
1264		|| message->FindInt64("node", &nodeRef.node)) {
1265		return;
1266	}
1267
1268	// get the node
1269	Node *node = GetNode(nodeRef);
1270	if (!node)
1271		return;
1272
1273	node->MarkStatInvalid();
1274}
1275
1276// sManager
1277NodeManager NodeManager::sManager;
1278
1279
1280// #pragma mark -
1281
1282// read_request
1283static
1284status_t
1285read_request(port_id port, stat_cache_request &request)
1286{
1287	status_t error = B_OK;
1288	bool done = false;
1289	do {
1290		int32 code;
1291		ssize_t bytesRead = read_port(port, &code, &request,
1292			sizeof(stat_cache_request));
1293		if (bytesRead < 0) {
1294			error = bytesRead;
1295			done = (error != B_INTERRUPTED);
1296		} else if (bytesRead
1297			< ((stat_cache_request*)NULL)->path + 2 - (char*)NULL) {
1298DBG(OUT("request too short: %ld\n", bytesRead));
1299			error = B_ERROR;
1300		} else {
1301			done = true;
1302			error = B_OK;
1303		}
1304	} while (!done);
1305	return error;
1306}
1307
1308// handle_stat_request
1309static
1310status_t
1311handle_stat_request(stat_cache_request &request)
1312{
1313DBG(OUT("handle_stat_request(): `%s'\n", request.path));
1314	stat_cache_stat_reply reply;
1315
1316	// get the node
1317	PathResolver resolver;
1318	Node *node;
1319	reply.error = resolver.FindNode(request.path, true, &node);
1320
1321	// get the stat
1322	if (reply.error == B_OK)
1323		reply.error = node->GetStat(&reply.st);
1324DBG(OUT("  -> `%s'\n", strerror(reply.error)));
1325
1326	// send the reply
1327	return write_port(request.replyPort, 0, &reply, sizeof(reply));
1328}
1329
1330// handle_read_dir_request
1331static
1332status_t
1333handle_read_dir_request(stat_cache_request &request)
1334{
1335DBG(OUT("handle_read_dir_request(): `%s'\n", request.path));
1336	// get the directory
1337	PathResolver resolver;
1338	Node *node = NULL;
1339	status_t error = resolver.FindNode(request.path, true, &node);
1340	Directory *dir = dynamic_cast<Directory*>(node);
1341	if (error == B_OK && !dir)
1342		error = B_NOT_A_DIRECTORY;
1343
1344	// read all entries
1345	if (error == B_OK)
1346		error = dir->ReadAllEntries();
1347
1348	// compute the reply size
1349	int32 replySize = sizeof(stat_cache_readdir_reply);
1350	int32 entryCount = 0;
1351	if (error == B_OK) {
1352		for (Entry *entry = dir->GetFirstEntry();
1353			 entry;
1354			 entry = dir->GetNextEntry(entry)) {
1355			replySize += get_dirent_size(entry->GetName());
1356			entryCount++;
1357		}
1358	}
1359
1360	// allocate a reply
1361	stat_cache_readdir_reply *reply
1362		= (stat_cache_readdir_reply*)new uint8[replySize];
1363	reply->error = error;
1364	reply->entryCount = entryCount;
1365
1366	// copy the entries into the reply
1367	if (error == B_OK) {
1368		uint8 *buffer = reply->buffer;
1369		for (Entry *entry = dir->GetFirstEntry();
1370			 entry;
1371			 entry = dir->GetNextEntry(entry)) {
1372			// get the required info
1373			int32 entrySize = get_dirent_size(entry->GetName());
1374			node_ref parentNodeRef(entry->GetParent()->GetNodeRef());
1375			node_ref nodeRef(entry->GetNode()->GetNodeRef());
1376
1377			// fill in the dirent
1378			dirent *ent = (dirent*)buffer;
1379			ent->d_pdev = parentNodeRef.device;
1380			ent->d_pino = parentNodeRef.node;
1381			ent->d_dev = nodeRef.device;
1382			ent->d_ino = nodeRef.node;
1383			ent->d_reclen = entrySize;
1384			strcpy(ent->d_name, entry->GetName());
1385
1386			buffer += entrySize;
1387		}
1388	}
1389
1390DBG(OUT("  -> entryCount: %ld, error: `%s'\n", reply->entryCount, strerror(reply->error)));
1391	// send the reply
1392	error = write_port(request.replyPort, 0, reply, replySize);
1393	delete[] (uint8*)reply;
1394	return error;
1395}
1396
1397// request_loop
1398int32
1399request_loop(void *data)
1400{
1401	port_id requestPort = *(port_id*)data;
1402
1403	// init node manager
1404	status_t error = NodeManager::GetDefault()->Init();
1405	if (error != B_OK) {
1406		fprintf(stderr, "Failed to init node manager: %s\n", strerror(error));
1407		return 1;
1408	}
1409
1410	// handle requests until our port goes away
1411	stat_cache_request request;
1412	while (read_request(requestPort, request) == B_OK) {
1413		BAutolock _(NodeManager::GetDefault());
1414		switch (request.command) {
1415			case STAT_CACHE_COMMAND_STAT:
1416				handle_stat_request(request);
1417				break;
1418			case STAT_CACHE_COMMAND_READDIR:
1419				handle_read_dir_request(request);
1420				break;
1421			default:
1422				fprintf(stderr, "Unknown command: %ld\n", request.command);
1423				break;
1424		}
1425	}
1426
1427	// delete the request port
1428	if (delete_port(requestPort) == B_OK)
1429		be_app->PostMessage(B_QUIT_REQUESTED);
1430	return 0;
1431}
1432
1433// main
1434int
1435main()
1436{
1437	// create the request port
1438	port_id requestPort = create_port(1, STAT_CACHE_SERVER_PORT_NAME);
1439	if (requestPort < 0) {
1440		fprintf(stderr, "Failed to create request port: %s\n",
1441			strerror(requestPort));
1442		return 1;
1443	}
1444
1445	// start the request handling loop
1446	thread_id requestLoop = spawn_thread(request_loop, "request loop",
1447		B_NORMAL_PRIORITY, &requestPort);
1448	if (resume_thread(requestLoop) < B_OK)
1449		return -1;
1450
1451	// the BApplication is only needed for a smooth server shutdown
1452	BApplication app("application/x-vnd.haiku.jam-cacheserver");
1453	app.Run();
1454
1455	// delete the request port
1456	delete_port(requestPort);
1457
1458	// wait for the request handling loop to quit
1459	status_t result;
1460	wait_for_thread(requestLoop, &result);
1461
1462	return 0;
1463}
1464