1// VolumeManager.cpp
2
3#include "VolumeManager.h"
4
5#include <new>
6
7#include <sys/stat.h>
8
9#include <AutoDeleter.h>
10#include <Entry.h>
11#include <fs_info.h>
12#include <fs_query.h>
13#include <HashMap.h>
14#include <NodeMonitor.h>
15#include <Volume.h>
16
17#include "ClientVolume.h"
18#include "DebugSupport.h"
19#include "Directory.h"
20#include "Entry.h"
21#include "FDManager.h"
22#include "NodeHandle.h"
23#include "Path.h"
24#include "QueryDomain.h"
25#include "Volume.h"
26
27// TODO: We should filter recent events at some point. Otherwise we'll end up
28// with one event of each kind for each entry/node.
29
30const bigtime_t kRecentEventLifeTime = 100000;	// 0.1 s
31
32// QueryHandler
33class VolumeManager::QueryHandler : public BHandler, public QueryListener,
34	public BReferenceable {
35public:
36	QueryHandler(NodeMonitorListener* listener, QueryDomain* queryDomain,
37		QueryHandle* handle)
38		:
39		BHandler(),
40		QueryListener(),
41		BReferenceable(),
42		fListener(listener),
43		fQueryDomain(queryDomain),
44		fHandle(handle)
45	{
46	}
47
48	QueryDomain* GetQueryDomain() const
49	{
50		return fQueryDomain;
51	}
52
53	QueryHandle* GetQueryHandle() const
54	{
55		return fHandle;
56	}
57
58	virtual void MessageReceived(BMessage* message)
59	{
60		switch (message->what) {
61			case B_QUERY_UPDATE:
62			{
63				NodeMonitoringEvent* event = NULL;
64				int32 opcode;
65				if (message->FindInt32("opcode", &opcode) == B_OK) {
66					switch (opcode) {
67						case B_ENTRY_CREATED:
68							event = new(std::nothrow) EntryCreatedEvent;
69							break;
70						case B_ENTRY_REMOVED:
71							event = new(std::nothrow) EntryRemovedEvent;
72							break;
73						case B_ENTRY_MOVED:
74							event = new(std::nothrow) EntryMovedEvent;
75							break;
76					}
77				}
78				if (event) {
79					event->queryHandler = this;
80					AcquireReference();
81					if (event->Init(message) == B_OK)
82						fListener->ProcessNodeMonitoringEvent(event);
83					else
84						delete event;
85				}
86				break;
87			}
88			default:
89				BHandler::MessageReceived(message);
90		}
91	}
92
93	virtual void QueryHandleClosed(QueryHandle* handle)
94	{
95		BLooper* looper = Looper();
96		if (looper && looper->Lock()) {
97			looper->RemoveHandler(this);
98			looper->Unlock();
99		}
100		handle->SetQueryListener(NULL);
101		ReleaseReference();
102	}
103
104private:
105	NodeMonitorListener*	fListener;
106	QueryDomain*			fQueryDomain;
107	QueryHandle*			fHandle;
108};
109
110// VolumeMap
111struct VolumeManager::VolumeMap : HashMap<HashKey32<dev_t>, Volume*> {
112};
113
114// ClientVolumeMap
115struct VolumeManager::ClientVolumeMap
116	: HashMap<HashKey32<int32>, ClientVolume*> {
117};
118
119// private BeOS syscalls to set the FD and node monitor slot limits
120extern "C" int _kset_fd_limit_(int num);
121extern "C" int _kset_mon_limit_(int num);
122
123
124// EntryCreatedEventMap
125struct VolumeManager::EntryCreatedEventMap
126	: HashMap<EntryRef, EntryCreatedEvent*> {
127};
128
129// EntryRemovedEventMap
130struct VolumeManager::EntryRemovedEventMap
131	: HashMap<EntryRef, EntryRemovedEvent*> {
132};
133
134// EntryMovedEventKey
135struct EntryMovedEventKey : public EntryRef {
136	EntryMovedEventKey()
137	{
138	}
139
140	EntryMovedEventKey(dev_t volumeID, ino_t fromDirectory,
141		const char* fromName, ino_t toDirectory, const char* toName)
142		: EntryRef(volumeID, fromDirectory, fromName),
143		  toDirectory(toDirectory),
144		  toName(toName)
145	{
146
147	}
148
149	uint32 GetHashCode() const
150	{
151		uint32 hash = EntryRef::GetHashCode();
152		hash = 17 * hash + (uint32)(toDirectory >> 32);
153		hash = 17 * hash + (uint32)toDirectory;
154		hash = 17 * hash + string_hash(toName.GetString());
155		return hash;
156	}
157
158	bool operator==(const EntryMovedEventKey& other) const
159	{
160		return (*(const EntryRef*)this) == other
161			&& toDirectory == other.toDirectory
162			&& toName == other.toName;
163	}
164
165	bool operator!=(const EntryMovedEventKey& other) const
166	{
167		return !(*this == other);
168	}
169
170	ino_t		toDirectory;
171	HashString	toName;
172};
173
174// EntryMovedEventMap
175struct VolumeManager::EntryMovedEventMap : HashMap<EntryRef, EntryMovedEvent*> {
176};
177
178// NodeStatChangedEventMap
179struct VolumeManager::NodeStatChangedEventMap
180	: HashMap<NodeRef, StatChangedEvent*> {
181};
182
183typedef EntryRef AttributeRef;
184
185// NodeAttributeChangedEventMap
186struct VolumeManager::NodeAttributeChangedEventMap
187	: HashMap<AttributeRef, AttributeChangedEvent*> {
188};
189
190
191// #pragma mark -
192
193// constructor
194VolumeManager::VolumeManager()
195	: fLock("volume manager"),
196	  fVolumes(NULL),
197	  fRootVolume(NULL),
198	  fClientVolumes(NULL),
199	  fNodeMonitor(NULL),
200	  fNodeMonitoringProcessor(-1),
201	  fNodeMonitoringEvents(),
202	  fRecentNodeMonitoringEvents(),
203	  fEntryCreatedEvents(NULL),
204	  fEntryRemovedEvents(NULL),
205	  fEntryMovedEvents(NULL),
206	  fNodeStatChangedEvents(NULL),
207	  fNodeAttributeChangedEvents(NULL),
208	  fRevision(0),
209	  fTerminating(false)
210{
211}
212
213// destructor
214VolumeManager::~VolumeManager()
215{
216	// terminate the node monitor and the node monitoring processor
217	fTerminating = true;
218	if (fNodeMonitor && fNodeMonitor->Lock())
219		fNodeMonitor->Quit();
220	fNodeMonitoringEvents.Close(true);
221	if (fNodeMonitoringProcessor >= 0) {
222		int32 result;
223		wait_for_thread(fNodeMonitoringProcessor, &result);
224	}
225
226	// delete all events
227	// entry created events
228	for (EntryCreatedEventMap::Iterator it = fEntryCreatedEvents->GetIterator();
229		 it.HasNext();) {
230		it.Next().value->ReleaseReference();
231	}
232	delete fEntryCreatedEvents;
233
234	// entry removed events
235	for (EntryRemovedEventMap::Iterator it = fEntryRemovedEvents->GetIterator();
236		 it.HasNext();) {
237		it.Next().value->ReleaseReference();
238	}
239	delete fEntryRemovedEvents;
240
241	// entry moved events
242	for (EntryMovedEventMap::Iterator it = fEntryMovedEvents->GetIterator();
243		 it.HasNext();) {
244		it.Next().value->ReleaseReference();
245	}
246	delete fEntryMovedEvents;
247
248	// stat changed events
249	for (NodeStatChangedEventMap::Iterator it
250			= fNodeStatChangedEvents->GetIterator();
251		 it.HasNext();) {
252		it.Next().value->ReleaseReference();
253	}
254	delete fNodeStatChangedEvents;
255
256	// attribute changed events
257	for (NodeAttributeChangedEventMap::Iterator it
258			= fNodeAttributeChangedEvents->GetIterator();
259		 it.HasNext();) {
260		it.Next().value->ReleaseReference();
261	}
262	delete fNodeAttributeChangedEvents;
263
264	delete fClientVolumes;
265
266	// delete the volumes
267	for (VolumeMap::Iterator it = fVolumes->GetIterator(); it.HasNext();) {
268		Volume* volume = it.Next().value;
269		delete volume;
270	}
271	delete fVolumes;
272}
273
274// Init
275status_t
276VolumeManager::Init()
277{
278	// check node monitoring message queue
279	status_t error = fNodeMonitoringEvents.InitCheck();
280	if (error != B_OK)
281		return error;
282
283	// entry created event map
284	fEntryCreatedEvents = new(std::nothrow) EntryCreatedEventMap;
285	if (!fEntryCreatedEvents)
286		return B_NO_MEMORY;
287
288	// entry removed event map
289	fEntryRemovedEvents = new(std::nothrow) EntryRemovedEventMap;
290	if (!fEntryRemovedEvents)
291		return B_NO_MEMORY;
292
293	// entry moved event map
294	fEntryMovedEvents = new(std::nothrow) EntryMovedEventMap;
295	if (!fEntryMovedEvents)
296		return B_NO_MEMORY;
297
298	// node stat changed event map
299	fNodeStatChangedEvents = new(std::nothrow) NodeStatChangedEventMap;
300	if (!fNodeStatChangedEvents)
301		return B_NO_MEMORY;
302
303	// node attribute changed event map
304	fNodeAttributeChangedEvents = new(std::nothrow) NodeAttributeChangedEventMap;
305	if (!fNodeAttributeChangedEvents)
306		return B_NO_MEMORY;
307
308	// create the node monitor
309	fNodeMonitor = new(std::nothrow) NodeMonitor(this);
310	if (!fNodeMonitor)
311		return B_NO_MEMORY;
312
313	// create the volume map
314	fVolumes = new(std::nothrow) VolumeMap;
315	if (!fVolumes)
316		return B_NO_MEMORY;
317	if (fVolumes->InitCheck() != B_OK)
318		return fVolumes->InitCheck();
319
320	// create the client volume map
321	fClientVolumes = new(std::nothrow) ClientVolumeMap;
322	if (!fClientVolumes)
323		return B_NO_MEMORY;
324	if (fClientVolumes->InitCheck() != B_OK)
325		return fClientVolumes->InitCheck();
326
327	// start the node monitor
328	thread_id monitorThread = fNodeMonitor->Run();
329	if (monitorThread < 0) {
330		delete fNodeMonitor;
331		fNodeMonitor = NULL;
332		return monitorThread;
333	}
334
335	// create all volumes
336	int32 cookie = 0;
337	dev_t volumeID;
338	while ((volumeID = next_dev(&cookie)) >= 0)
339		_AddVolume(volumeID);
340
341	// get the root volume
342	volumeID = dev_for_path("/");
343	if (volumeID < 0)
344		return volumeID;
345	fRootVolume = GetVolume(volumeID, true);
346	if (!fRootVolume)
347		return B_ERROR;
348
349	// spawn the node monitoring message processor
350	fNodeMonitoringProcessor = spawn_thread(&_NodeMonitoringProcessorEntry,
351		"node monitoring processor", B_NORMAL_PRIORITY, this);
352	if (fNodeMonitoringProcessor < 0)
353		return fNodeMonitoringProcessor;
354	resume_thread(fNodeMonitoringProcessor);
355
356	return B_OK;
357}
358
359// GetRootVolume
360Volume*
361VolumeManager::GetRootVolume() const
362{
363	return fRootVolume;
364}
365
366// AddClientVolume
367status_t
368VolumeManager::AddClientVolume(ClientVolume* clientVolume)
369{
370	if (!clientVolume)
371		return B_BAD_VALUE;
372
373	return fClientVolumes->Put(clientVolume->GetID(), clientVolume);
374}
375
376// RemoveClientVolume
377void
378VolumeManager::RemoveClientVolume(ClientVolume* clientVolume)
379{
380	if (!clientVolume)
381		return;
382
383	fClientVolumes->Remove(clientVolume->GetID());
384}
385
386// CreateDefault
387status_t
388VolumeManager::CreateDefault()
389{
390	if (sManager)
391		return B_OK;
392
393	VolumeManager* manager = new(std::nothrow) VolumeManager;
394	if (!manager)
395		return B_NO_MEMORY;
396
397	status_t error = manager->Init();
398	if (error != B_OK) {
399		delete manager;
400		return error;
401	}
402
403	sManager = manager;
404	return B_OK;
405}
406
407// DeleteDefault
408void
409VolumeManager::DeleteDefault()
410{
411	if (sManager) {
412		delete sManager;
413		sManager = NULL;
414	}
415}
416
417// GetDefault
418VolumeManager*
419VolumeManager::GetDefault()
420{
421	return sManager;
422}
423
424// Lock
425bool
426VolumeManager::Lock()
427{
428	bool alreadyLocked = fLock.IsLocked();
429
430	bool success = fLock.Lock();
431
432	// If locking was successful and we didn't have a lock before, we increment
433	// the revision.
434	if (success && !alreadyLocked)
435		fRevision++;
436
437	return success;
438}
439
440// Unlock
441void
442VolumeManager::Unlock()
443{
444	return fLock.Unlock();
445}
446
447// GetRevision
448int64
449VolumeManager::GetRevision() const
450{
451	return fRevision;
452}
453
454// GetVolume
455Volume*
456VolumeManager::GetVolume(dev_t volumeID, bool add)
457{
458	Volume* volume = fVolumes->Get(volumeID);
459	if (!volume && add)
460		_AddVolume(volumeID, &volume);
461
462	return volume;
463}
464
465
466// #pragma mark -
467
468// AddNode
469status_t
470VolumeManager::AddNode(Node* node)
471{
472	if (!node || !node->GetVolume())
473		return B_BAD_VALUE;
474
475	status_t error = node->GetVolume()->AddNode(node);
476
477	// start watching the node
478	if (error == B_OK)
479		fNodeMonitor->StartWatching(node->GetNodeRef());
480
481	return error;
482}
483
484// RemoveNode
485void
486VolumeManager::RemoveNode(Node* node)
487{
488	if (!node)
489		return;
490
491	// if the node is a directory, we remove all its entries first
492	if (Directory* directory = dynamic_cast<Directory*>(node)) {
493		while (Entry* entry = directory->GetFirstEntry()) {
494			RemoveEntry(entry);
495			delete entry;
496		}
497	}
498
499	// remove all referring entries
500	while (Entry* entry = node->GetFirstReferringEntry())
501		RemoveEntry(entry);
502
503	// remove the node from the volume
504	if (node->GetVolume())
505		node->GetVolume()->RemoveNode(node);
506
507	// stop watching the node
508	fNodeMonitor->StopWatching(node->GetNodeRef());
509}
510
511// GetNode
512Node*
513VolumeManager::GetNode(dev_t volumeID, ino_t nodeID)
514{
515	if (Volume* volume = GetVolume(volumeID))
516		return volume->GetNode(nodeID);
517	return NULL;
518}
519
520// LoadNode
521status_t
522VolumeManager::LoadNode(const struct stat& st, Node** _node)
523{
524	Node* node = GetNode(st.st_dev, st.st_ino);
525	if (!node) {
526		// node not known yet: create it
527
528		// get the volume
529		Volume* volume = GetVolume(st.st_dev, true);
530		if (!volume)
531			return B_BAD_VALUE;
532
533		// create the node
534		if (S_ISDIR(st.st_mode))
535			node = new(std::nothrow) Directory(volume, st);
536		else
537			node = new(std::nothrow) Node(volume, st);
538		if (!node)
539			return B_NO_MEMORY;
540
541		// add it
542		status_t error = AddNode(node);
543		if (error != B_OK) {
544			delete node;
545			return error;
546		}
547	}
548
549	if (_node)
550		*_node = node;
551	return B_OK;
552}
553
554
555// #pragma mark -
556
557// GetDirectory
558Directory*
559VolumeManager::GetDirectory(dev_t volumeID, ino_t nodeID)
560{
561	return dynamic_cast<Directory*>(GetNode(volumeID, nodeID));
562}
563
564// GetRootDirectory
565Directory*
566VolumeManager::GetRootDirectory() const
567{
568	return (fRootVolume ? fRootVolume->GetRootDirectory() : NULL);
569}
570
571// GetParentDirectory
572Directory*
573VolumeManager::GetParentDirectory(Directory* directory)
574{
575	if (!directory)
576		return NULL;
577
578	// get ".." entry
579	Entry* parentEntry;
580	if (LoadEntry(directory->GetVolumeID(), directory->GetID(), "..", true,
581			&parentEntry) != B_OK) {
582		return NULL;
583	}
584
585	return dynamic_cast<Directory*>(parentEntry->GetNode());
586}
587
588// LoadDirectory
589status_t
590VolumeManager::LoadDirectory(dev_t volumeID, ino_t directoryID,
591	Directory** _directory)
592{
593	// try to get the node
594	Node* node = GetNode(volumeID, directoryID);
595	bool newNode = false;
596	if (!node) {
597		// directory not yet loaded: stat it
598		NoAllocEntryRef entryRef(volumeID, directoryID, ".");
599		struct stat st;
600		BEntry bEntry;
601		status_t error = FDManager::SetEntry(&bEntry, &entryRef);
602		if (error == B_OK)
603			error = bEntry.GetStat(&st);
604		if (error != B_OK)
605			return error;
606
607		// load the node
608		error = LoadNode(st, &node);
609		if (error != B_OK)
610			return error;
611
612		newNode = true;
613	}
614
615	// check, if the node is a directory
616	Directory* directory = dynamic_cast<Directory*>(node);
617	if (!directory)
618		return B_NOT_A_DIRECTORY;
619
620	if (newNode)
621		CompletePathToRoot(directory);
622
623	if (_directory)
624		*_directory = directory;
625	return B_OK;
626}
627
628
629// #pragma mark -
630
631// AddEntry
632status_t
633VolumeManager::AddEntry(Entry* entry)
634{
635	if (!entry || !entry->GetVolume() || !entry->GetDirectory()
636		|| ! entry->GetNode()) {
637		return B_BAD_VALUE;
638	}
639
640	// add the entry to the volume
641	status_t error = entry->GetVolume()->AddEntry(entry);
642	if (error != B_OK)
643		return error;
644
645	// add the entry to its directory and node
646	entry->GetDirectory()->AddEntry(entry);
647	entry->GetNode()->AddReferringEntry(entry);
648
649//PRINT(("VolumeManager::AddEntry(): %ld, %lld, `%s', dir: %p, "
650//"entry count: %ld\n", entry->GetVolumeID(), entry->GetDirectoryID(),
651//entry->GetName(), entry->GetDirectory(),
652//entry->GetDirectory()->CountEntries()));
653
654	return B_OK;
655}
656
657// RemoveEntry
658void
659VolumeManager::RemoveEntry(Entry* entry)
660{
661	if (entry) {
662		// remove the entry from the volume
663		if (entry->GetVolume())
664			entry->GetVolume()->RemoveEntry(entry);
665
666		// remove the entry from the directory and its node
667		entry->GetDirectory()->RemoveEntry(entry);
668		entry->GetNode()->RemoveReferringEntry(entry);
669
670//PRINT(("VolumeManager::RemoveEntry(): %ld, %lld, `%s', dir: %p, "
671//"entry count: %ld\n", entry->GetVolumeID(), entry->GetDirectoryID(),
672//entry->GetName(), entry->GetDirectory(),
673//entry->GetDirectory()->CountEntries()));
674	}
675}
676
677// DeleteEntry
678void
679VolumeManager::DeleteEntry(Entry* entry, bool keepNode)
680{
681	if (!entry)
682		return;
683
684	Node* node = entry->GetNode();
685
686	// remove the entry
687	RemoveEntry(entry);
688	delete entry;
689
690	// remove the node, if it doesn't have any more actual referring entries
691	if (!keepNode && !node->GetActualReferringEntry()) {
692		RemoveNode(node);
693		if (node != node->GetVolume()->GetRootDirectory())
694			delete node;
695	}
696}
697
698// GetEntry
699Entry*
700VolumeManager::GetEntry(dev_t volumeID, ino_t directoryID, const char* name)
701{
702	if (Volume* volume = GetVolume(volumeID))
703		return volume->GetEntry(directoryID, name);
704	return NULL;
705}
706
707// GetEntry
708Entry*
709VolumeManager::GetEntry(const entry_ref& ref)
710{
711	return GetEntry(ref.device, ref.directory, ref.name);
712}
713
714// LoadEntry
715status_t
716VolumeManager::LoadEntry(dev_t volumeID, ino_t directoryID, const char* name,
717	bool loadDir, Entry** _entry)
718{
719	Entry* entry = GetEntry(volumeID, directoryID, name);
720	if (!entry) {
721		// entry not known yet: create it
722		PRINT("VolumeManager::LoadEntry(%" B_PRIdDEV ", "
723			"%" B_PRIdINO ", `%s')\n", volumeID, directoryID, name);
724
725		// get the volume
726		Volume* volume = GetVolume(volumeID, true);
727		if (!volume)
728			return B_BAD_VALUE;
729
730		// get the directory
731		status_t error = B_OK;
732		Directory* directory = GetDirectory(volumeID, directoryID);
733		if (!directory) {
734			if (!loadDir)
735				return B_ENTRY_NOT_FOUND;
736
737//PRINT(("  loading directory...\n"));
738			// load the directory
739			error = LoadDirectory(volumeID, directoryID, &directory);
740			if (error != B_OK)
741				return error;
742		}
743
744//PRINT(("  opening BNode...\n"));
745		// stat the entry
746		NoAllocEntryRef entryRef(volumeID, directoryID, name);
747		struct stat st;
748		BNode bNode;
749		error = bNode.SetTo(&entryRef);
750//PRINT(("  stat()ing BNode...\n"));
751		if (error == B_OK)
752			error = bNode.GetStat(&st);
753		if (error != B_OK)
754			return error;
755
756//PRINT(("  loading node...\n"));
757		// load the node
758		Node* node;
759		error = LoadNode(st, &node);
760		if (error != B_OK)
761			return error;
762
763//PRINT(("  creating and adding entry...\n"));
764		// create the entry
765		entry = new(std::nothrow) Entry(volume, directory, name, node);
766		if (!entry)
767			return B_NO_MEMORY;
768
769		// add it
770		error = AddEntry(entry);
771		if (error != B_OK) {
772			delete entry;
773			return error;
774		}
775//PRINT(("  adding entry done\n"));
776	}
777
778	if (_entry)
779		*_entry = entry;
780	return B_OK;
781}
782
783
784// #pragma mark -
785
786// OpenQuery
787status_t
788VolumeManager::OpenQuery(QueryDomain* queryDomain, const char* queryString,
789	uint32 flags, port_id remotePort, int32 remoteToken, QueryHandle** handle)
790{
791	if (!queryDomain || !queryString || !handle)
792		return B_BAD_VALUE;
793	bool liveQuery = (flags & B_LIVE_QUERY);
794	PRINT("VolumeManager::OpenQuery(%p, \"%s\", 0x%" B_PRIx32 ", "
795		"%" B_PRId32 ", %" B_PRId32 ")\n",
796		queryDomain, queryString, flags, remotePort, remoteToken);
797
798	// allocate the handle
799	QueryHandle* queryHandle = new(std::nothrow) QueryHandle(remotePort,
800		remoteToken);
801	if (!queryHandle)
802		return B_NO_MEMORY;
803	ObjectDeleter<QueryHandle> handleDeleter(queryHandle);
804
805	// allocate a query handler, if this is a live query
806	QueryHandler* queryHandler = NULL;
807	if (liveQuery) {
808		queryHandler = new(std::nothrow) QueryHandler(this, queryDomain,
809			queryHandle);
810		if (!queryHandler)
811			return B_NO_MEMORY;
812
813		fNodeMonitor->Lock();
814		fNodeMonitor->AddHandler(queryHandler);
815		fNodeMonitor->Unlock();
816		queryHandle->SetQueryListener(queryHandler);
817	}
818
819	// iterate through the volumes and create a query for each one
820	// supporting queries
821	for (VolumeMap::Iterator it = fVolumes->GetIterator(); it.HasNext();) {
822		Volume* volume = it.Next().value;
823		if (!volume->KnowsQuery())
824			continue;
825
826		// The volume should either be contained by the client volume or
827		// the other way around. Otherwise they are located in different
828		// branches of the FS tree and don't have common nodes.
829		if (!queryDomain->QueryDomainIntersectsWith(volume))
830			continue;
831		PRINT("VolumeManager::OpenQuery(): adding Query for volume "
832			"%" B_PRIdDEV "\n", volume->GetID());
833
834		// create the query for this volume
835		BVolume bVolume(volume->GetID());
836		Query* query = new(std::nothrow) Query;
837		if (!query)
838			return B_NO_MEMORY;
839
840		// init the query
841		ObjectDeleter<Query> queryDeleter(query);
842		status_t error = query->SetVolume(&bVolume);
843		if (error != B_OK)
844			return error;
845		error = query->SetPredicate(queryString);
846		if (error != B_OK)
847			return error;
848		if (liveQuery) {
849			error = query->SetTarget(queryHandler);
850			if (error != B_OK)
851				return error;
852		}
853
854		// fetch
855		error = query->Fetch();
856		if (error != B_OK)
857			return error;
858
859		queryHandle->AddQuery(query);
860		queryDeleter.Detach();
861	}
862
863	*handle = queryHandle;
864	handleDeleter.Detach();
865	return B_OK;
866}
867
868// CompletePathToRoot
869status_t
870VolumeManager::CompletePathToRoot(Directory* directory)
871{
872	if (!directory)
873		return B_BAD_VALUE;
874
875	while (directory != GetRootDirectory()) {
876		// if the dir has a valid entry referring to it, we've nothing to do
877		if (directory->GetActualReferringEntry())
878			return B_OK;
879
880		// get a proper entry_ref
881		BEntry bEntry;
882		entry_ref entryRef(directory->GetVolumeID(), directory->GetID(), ".");
883		status_t error = FDManager::SetEntry(&bEntry, &entryRef);
884		if (error == B_OK)
885			error = bEntry.GetRef(&entryRef);
886		if (error != B_OK)
887			return error;
888
889		// if the entry is already loaded, we're done
890		if (GetEntry(entryRef))
891			return B_OK;
892
893		// the entry is not yet known -- load it
894		Entry* entry;
895		error = LoadEntry(entryRef.device, entryRef.directory, entryRef.name,
896			true, &entry);
897		if (error != B_OK)
898			return error;
899
900		// get the entry's parent dir and enter the next round
901		directory = entry->GetDirectory();
902	}
903
904	return B_OK;
905}
906
907// GetPath
908status_t
909VolumeManager::GetPath(Entry* entry, Path* path)
910{
911	// get directory path
912	status_t error = GetPath(entry->GetDirectory(), path);
913	if (error != B_OK)
914		return error;
915
916	// append the entry name
917	return path->Append(entry->GetName());
918}
919
920// GetPath
921status_t
922VolumeManager::GetPath(Node* node, Path* path)
923{
924	if (node == GetRootDirectory())
925		return path->SetTo("/");
926
927	// get an entry referring to the node
928	Entry* entry = node->GetActualReferringEntry();
929	if (!entry) {
930		// if the node is a directory, we complete the path to the root and
931		// try again
932		if (Directory* directory = dynamic_cast<Directory*>(node)) {
933			CompletePathToRoot(directory);
934			entry = node->GetActualReferringEntry();
935		}
936
937		if (!entry)
938			return B_ERROR;
939	}
940
941	return GetPath(entry, path);
942}
943
944// DirectoryContains
945bool
946VolumeManager::DirectoryContains(Directory* directory, Entry* entry)
947{
948	if (!directory || !entry)
949		return false;
950
951	return DirectoryContains(directory, entry->GetDirectory(), true);
952}
953
954// DirectoryContains
955bool
956VolumeManager::DirectoryContains(Directory* directory, Directory* descendant,
957	bool reflexive)
958{
959	if (!directory || !descendant)
960		return false;
961
962	// a directory contains itself, just as defined by the caller
963	if (directory == descendant)
964		return reflexive;
965
966	// if the directory is the root directory, it contains everything
967	Directory* rootDir = GetRootDirectory();
968	if (directory == rootDir)
969		return true;
970
971	// recursively get the descendant's parent dir until reaching the root dir
972	// or the given dir
973	while (descendant != rootDir) {
974		descendant = GetParentDirectory(descendant);
975		if (!descendant)
976			return false;
977
978		if (descendant == directory)
979			return true;
980	}
981
982	return false;
983}
984
985// DirectoryContains
986bool
987VolumeManager::DirectoryContains(Directory* directory, Node* descendant,
988	bool reflexive)
989{
990	if (!directory || !descendant)
991		return false;
992
993	// if the node is a directory, let the other version do the job
994	if (Directory* dir = dynamic_cast<Directory*>(descendant))
995		return DirectoryContains(directory, dir, reflexive);
996
997	// iterate through the referring entries and check, if the directory
998	// contains any of them
999	for (Entry* entry = descendant->GetFirstReferringEntry();
1000		 entry;
1001		 entry = descendant->GetNextReferringEntry(entry)) {
1002		if (DirectoryContains(directory, entry))
1003			return true;
1004	}
1005
1006	return false;
1007}
1008
1009
1010// #pragma mark -
1011
1012// ProcessNodeMonitoringEvent
1013void
1014VolumeManager::ProcessNodeMonitoringEvent(NodeMonitoringEvent* event)
1015{
1016	if (fNodeMonitoringEvents.Push(event) != B_OK)
1017		delete event;
1018}
1019
1020// _AddVolume
1021status_t
1022VolumeManager::_AddVolume(dev_t volumeID, Volume** _volume)
1023{
1024	if (GetVolume(volumeID))
1025		return B_OK;
1026
1027	// create the volume
1028	Volume* volume = new(std::nothrow) Volume(volumeID);
1029	if (!volume)
1030		RETURN_ERROR(B_NO_MEMORY);
1031	ObjectDeleter<Volume> volumeDeleter(volume);
1032	status_t error = volume->Init();
1033	if (error != B_OK)
1034		RETURN_ERROR(error);
1035
1036	// add it
1037	error = fVolumes->Put(volumeID, volume);
1038	if (error != B_OK)
1039		RETURN_ERROR(error);
1040
1041	// add the root node
1042	error = AddNode(volume->GetRootDirectory());
1043	if (error != B_OK) {
1044		fVolumes->Remove(volumeID);
1045		RETURN_ERROR(error);
1046	}
1047
1048	// complete the root dir path
1049	CompletePathToRoot(volume->GetRootDirectory());
1050
1051	volumeDeleter.Detach();
1052	if (_volume)
1053		*_volume = volume;
1054	return B_OK;
1055}
1056
1057// _EntryCreated
1058void
1059VolumeManager::_EntryCreated(EntryCreatedEvent* event)
1060{
1061	// get the directory
1062	Directory* directory = GetDirectory(event->volumeID, event->directoryID);
1063	if (!directory)
1064		return;
1065
1066	// check, if there is an earlier similar event
1067	bool notify = true;
1068	NoAllocEntryRef ref(event->volumeID, event->directoryID,
1069		event->name.GetString());
1070	EntryCreatedEvent* oldEvent = fEntryCreatedEvents->Get(ref);
1071
1072	// remove the old event
1073	if (oldEvent) {
1074		fEntryCreatedEvents->Remove(ref);
1075		fRecentNodeMonitoringEvents.Remove(oldEvent);
1076		notify = !_IsRecentEvent(oldEvent);
1077		oldEvent->ReleaseReference();
1078	}
1079
1080	// add the new event
1081	if (fEntryCreatedEvents->Put(ref, event) == B_OK) {
1082		fRecentNodeMonitoringEvents.Insert(event);
1083		event->AcquireReference();
1084	}
1085
1086	// if the directory is complete or at least has iterators attached to it,
1087	// we load the entry
1088	if (directory->IsComplete() || directory->HasDirIterators()) {
1089		Entry* entry;
1090		LoadEntry(ref.device, ref.directory, ref.name, false, &entry);
1091	}
1092
1093	// send notifications
1094	if (notify) {
1095		for (ClientVolumeMap::Iterator it = fClientVolumes->GetIterator();
1096			 it.HasNext();) {
1097			ClientVolume* clientVolume = it.Next().value;
1098			if (DirectoryContains(clientVolume->GetRootDirectory(), directory,
1099				true)) {
1100				clientVolume->ProcessNodeMonitoringEvent(event);
1101			}
1102		}
1103	}
1104}
1105
1106// _EntryRemoved
1107void
1108VolumeManager::_EntryRemoved(EntryRemovedEvent* event, bool keepNode)
1109{
1110	// get node and directory
1111	Node* node = GetNode(event->nodeVolumeID, event->nodeID);
1112	Directory* directory = GetDirectory(event->volumeID, event->directoryID);
1113	if (!directory)
1114		return;
1115
1116	// find the entry
1117	Entry* entry = NULL;
1118	if (node) {
1119		if (event->name.GetLength() == 0) {
1120			for (entry = node->GetFirstReferringEntry();
1121				 entry;
1122				 entry = node->GetNextReferringEntry(entry)) {
1123				if (!entry->Exists()) {
1124					event->name.SetTo(entry->GetName());
1125					break;
1126				}
1127			}
1128		} else {
1129			entry = GetEntry(directory->GetVolumeID(), directory->GetID(),
1130				event->name.GetString());
1131		}
1132	}
1133
1134	// check, if there is an earlier similar event
1135	bool notify = true;
1136	NoAllocEntryRef ref(event->volumeID, event->directoryID,
1137		event->name.GetString());
1138	EntryRemovedEvent* oldEvent = fEntryRemovedEvents->Get(ref);
1139		// TODO: Under BeOS R5 the entry name is not encoded in the
1140		// "entry removed" node monitoring message. If we have seen the entry
1141		// before, we can get the entry nevertheless (see above). Usually we
1142		// get 2 "entry removed" events: One for watching the directory and one
1143		// for watching the node. After the first one has been processed, we've
1144		// forgotten everything about the entry and we won't be able to find out
1145		// the entry's name for the second one. Hence we will never find the
1146		// previous event in the fEntryRemovedEvents map. We should probably
1147		// fall back to using a NodeRef as key under BeOS R5.
1148
1149	// remove the old event
1150	if (oldEvent) {
1151		fEntryRemovedEvents->Remove(ref);
1152		fRecentNodeMonitoringEvents.Remove(oldEvent);
1153		notify = !_IsRecentEvent(oldEvent);
1154		oldEvent->ReleaseReference();
1155	}
1156
1157	// add the new event
1158	if (fEntryRemovedEvents->Put(ref, event) == B_OK) {
1159		fRecentNodeMonitoringEvents.Insert(event);
1160		event->AcquireReference();
1161	}
1162
1163	// remove the entry
1164	if (entry) {
1165		RemoveEntry(entry);
1166		delete entry;
1167	}
1168
1169	// remove the node, if it doesn't have any more actual referring entries
1170	if (node && !keepNode && !node->GetActualReferringEntry()) {
1171		RemoveNode(node);
1172		if (node != node->GetVolume()->GetRootDirectory())
1173			delete node;
1174	}
1175
1176	// send notifications
1177	if (notify) {
1178		NodeRef nodeRef(event->nodeVolumeID, event->nodeID);
1179		for (ClientVolumeMap::Iterator it = fClientVolumes->GetIterator();
1180			 it.HasNext();) {
1181			// We send a notification, if the client volume contains the entry,
1182			// but also, if the removed entry refers to the client volume's
1183			// root. The client connection has a special handling for this
1184			// case.
1185			ClientVolume* clientVolume = it.Next().value;
1186			Directory* rootDir = clientVolume->GetRootDirectory();
1187			if (DirectoryContains(rootDir, directory, true)
1188				|| clientVolume->GetRootNodeRef() == nodeRef) {
1189				clientVolume->ProcessNodeMonitoringEvent(event);
1190			}
1191		}
1192	}
1193}
1194
1195// _EntryMoved
1196void
1197VolumeManager::_EntryMoved(EntryMovedEvent* event)
1198{
1199	_CheckVolumeRootMoved(event);
1200
1201	Directory* fromDirectory
1202		= GetDirectory(event->volumeID, event->fromDirectoryID);
1203	Directory* toDirectory
1204		= GetDirectory(event->volumeID, event->toDirectoryID);
1205	Node* node = GetNode(event->nodeVolumeID, event->nodeID);
1206
1207	// we should at least have one of the directories
1208	if (!fromDirectory && !toDirectory)
1209		return;
1210
1211	// find the old entry
1212	Entry* oldEntry = NULL;
1213	if (node) {
1214		if (event->fromName.GetLength() == 0) {
1215			for (oldEntry = node->GetFirstReferringEntry();
1216				 oldEntry;
1217				 oldEntry = node->GetNextReferringEntry(oldEntry)) {
1218				if (!oldEntry->Exists()) {
1219					event->fromName.SetTo(oldEntry->GetName());
1220					break;
1221				}
1222			}
1223		} else {
1224			oldEntry = GetEntry(event->volumeID, event->fromDirectoryID,
1225				event->fromName.GetString());
1226		}
1227	}
1228
1229	// check, if there is an earlier similar event
1230	bool notify = true;
1231	if (event->fromName.GetLength() > 0) {
1232		EntryMovedEventKey key(event->volumeID, event->fromDirectoryID,
1233			event->fromName.GetString(), event->toDirectoryID,
1234			event->toName.GetString());
1235		EntryMovedEvent* oldEvent = fEntryMovedEvents->Get(key);
1236
1237		// remove the old event
1238		if (oldEvent) {
1239			fEntryMovedEvents->Remove(key);
1240			fRecentNodeMonitoringEvents.Remove(oldEvent);
1241			notify = !_IsRecentEvent(oldEvent);
1242			oldEvent->ReleaseReference();
1243		}
1244
1245		// add the new event
1246		if (fEntryMovedEvents->Put(key, event) == B_OK) {
1247			fRecentNodeMonitoringEvents.Insert(event);
1248			event->AcquireReference();
1249		}
1250	}
1251
1252	// remove the old entry
1253	if (oldEntry) {
1254		RemoveEntry(oldEntry);
1255		delete oldEntry;
1256	}
1257
1258	// If the to directory is complete or at least has iterators attached to it,
1259	// we load the new entry. We also load it, if the node is the root of a
1260	// volume.
1261	if (toDirectory
1262		&& (toDirectory->IsComplete() || toDirectory->HasDirIterators()
1263			|| (node && node == node->GetVolume()->GetRootDirectory()))) {
1264		Entry* newEntry;
1265		LoadEntry(event->volumeID, event->toDirectoryID,
1266			event->toName.GetString(), false, &newEntry);
1267	}
1268
1269	// remove the node, if it doesn't have any more actual referring entries
1270	if (node && !node->GetActualReferringEntry()) {
1271		RemoveNode(node);
1272		if (node != node->GetVolume()->GetRootDirectory())
1273			delete node;
1274	}
1275
1276	// send notifications
1277	if (notify) {
1278		for (ClientVolumeMap::Iterator it = fClientVolumes->GetIterator();
1279			 it.HasNext();) {
1280			ClientVolume* clientVolume = it.Next().value;
1281
1282			// check, if it contains the from/to directories
1283			Directory* rootDir = clientVolume->GetRootDirectory();
1284			bool containsFrom = DirectoryContains(rootDir, fromDirectory, true);
1285			bool containsTo = DirectoryContains(rootDir, toDirectory, true);
1286
1287			if (containsFrom) {
1288				if (containsTo) {
1289					// contains source and target dir
1290					clientVolume->ProcessNodeMonitoringEvent(event);
1291				} else {
1292					// contains only the source dir: generate an "entry removed"
1293					// event
1294					EntryRemovedEvent *removedEvent
1295						= new(std::nothrow) EntryRemovedEvent;
1296					if (!removedEvent)
1297						continue;
1298					removedEvent->opcode = B_ENTRY_REMOVED;
1299					removedEvent->time = event->time;
1300					removedEvent->volumeID = event->volumeID;
1301					removedEvent->directoryID = event->fromDirectoryID;
1302					removedEvent->nodeVolumeID = event->nodeVolumeID;
1303					removedEvent->nodeID = event->nodeID;
1304					if (event->fromName.GetLength() > 0)
1305						removedEvent->name = event->fromName;
1306					clientVolume->ProcessNodeMonitoringEvent(removedEvent);
1307					removedEvent->ReleaseReference();
1308				}
1309			} else if (containsTo) {
1310				// contains only the target directory: generate an
1311				// "entry created" event
1312				EntryCreatedEvent *createdEvent
1313					= new(std::nothrow) EntryCreatedEvent;
1314				if (!createdEvent)
1315					continue;
1316				createdEvent->opcode = B_ENTRY_CREATED;
1317				createdEvent->time = event->time;
1318				createdEvent->volumeID = event->volumeID;
1319				createdEvent->directoryID = event->toDirectoryID;
1320				createdEvent->name = event->toName;
1321				clientVolume->ProcessNodeMonitoringEvent(createdEvent);
1322				createdEvent->ReleaseReference();
1323			}
1324		}
1325	}
1326}
1327
1328// _NodeStatChanged
1329void
1330VolumeManager::_NodeStatChanged(StatChangedEvent* event)
1331{
1332	// get the node
1333	Node* node = GetNode(event->volumeID, event->nodeID);
1334	if (!node)
1335		return;
1336
1337	// check, if there is an earlier similar event
1338	bool notify = true;
1339	NodeRef ref(event->volumeID, event->nodeID);
1340	StatChangedEvent* oldEvent = fNodeStatChangedEvents->Get(ref);
1341
1342	// remove the old event
1343	if (oldEvent) {
1344		fNodeStatChangedEvents->Remove(ref);
1345		fRecentNodeMonitoringEvents.Remove(oldEvent);
1346		notify = !_IsRecentEvent(oldEvent);
1347		oldEvent->ReleaseReference();
1348	}
1349
1350	// add the new event
1351	if (fNodeStatChangedEvents->Put(ref, event) == B_OK) {
1352		fRecentNodeMonitoringEvents.Insert(event);
1353		event->AcquireReference();
1354	}
1355
1356	if (notify) {
1357		// update the cached node stat
1358		node->UpdateStat();
1359
1360		// send notifications
1361		for (ClientVolumeMap::Iterator it = fClientVolumes->GetIterator();
1362			 it.HasNext();) {
1363			ClientVolume* clientVolume = it.Next().value;
1364			if (DirectoryContains(clientVolume->GetRootDirectory(), node, true))
1365				clientVolume->ProcessNodeMonitoringEvent(event);
1366		}
1367	}
1368}
1369
1370// _NodeAttributeChanged
1371void
1372VolumeManager::_NodeAttributeChanged(AttributeChangedEvent* event)
1373{
1374	// get the node
1375	Node* node = GetNode(event->volumeID, event->nodeID);
1376	if (!node)
1377		return;
1378
1379	// check, if there is an earlier similar event
1380	bool notify = true;
1381	AttributeRef ref(event->volumeID, event->nodeID,
1382		event->attribute.GetString());
1383	AttributeChangedEvent* oldEvent = fNodeAttributeChangedEvents->Get(ref);
1384
1385	// remove the old event
1386	if (oldEvent) {
1387		fNodeAttributeChangedEvents->Remove(ref);
1388		fRecentNodeMonitoringEvents.Remove(oldEvent);
1389		notify = !_IsRecentEvent(oldEvent);
1390		oldEvent->ReleaseReference();
1391	}
1392
1393	// add the new event
1394	if (fNodeAttributeChangedEvents->Put(ref, event) == B_OK) {
1395		fRecentNodeMonitoringEvents.Insert(event);
1396		event->AcquireReference();
1397	}
1398
1399	// send notifications
1400	if (notify) {
1401		for (ClientVolumeMap::Iterator it = fClientVolumes->GetIterator();
1402			 it.HasNext();) {
1403			ClientVolume* clientVolume = it.Next().value;
1404			if (DirectoryContains(clientVolume->GetRootDirectory(), node, true))
1405				clientVolume->ProcessNodeMonitoringEvent(event);
1406		}
1407	}
1408}
1409
1410// _VolumeMounted
1411void
1412VolumeManager::_VolumeMounted(VolumeMountedEvent* event)
1413{
1414	entry_ref rootRef;
1415	bool rootRefInitialized = false;
1416
1417	// remove the entry referring to the covered directory
1418	Directory* coveredDirectory = GetDirectory(event->volumeID,
1419		event->directoryID);
1420	if (coveredDirectory) {
1421		if (Entry* entry = coveredDirectory->GetActualReferringEntry()) {
1422			// get an entry for later
1423			rootRef = entry->GetEntryRef();
1424			rootRefInitialized = true;
1425
1426			// send the "entry removed" event
1427			EntryRemovedEvent* event;
1428			if (_GenerateEntryRemovedEvent(entry, system_time(),
1429					&event) == B_OK) {
1430				_EntryRemoved(event, true);
1431				event->ReleaseReference();
1432			} else {
1433				RemoveEntry(entry);
1434				delete entry;
1435			}
1436		}
1437	}
1438
1439	// add the volume
1440	_AddVolume(event->newVolumeID);
1441
1442	// generate an "entry created" event for the root dir entry
1443	if (rootRefInitialized)
1444		_GenerateEntryCreatedEvent(rootRef, event->time);
1445}
1446
1447// _VolumeUnmounted
1448void
1449VolumeManager::_VolumeUnmounted(VolumeUnmountedEvent* event)
1450{
1451	// get the volume
1452	Volume* volume = GetVolume(event->volumeID);
1453	if (!volume)
1454		return;
1455
1456	entry_ref rootRef;
1457	bool rootRefInitialized = false;
1458
1459	// remove all actual entries referring to the root directory (should only
1460	// be one)
1461	if (Directory* rootDir = volume->GetRootDirectory()) {
1462		// get an entry ref for the root dir
1463		if (Entry* entry = rootDir->GetActualReferringEntry()) {
1464			rootRef = entry->GetEntryRef();
1465			rootRefInitialized = true;
1466		}
1467
1468		Entry* entry = rootDir->GetFirstReferringEntry();
1469		while (entry) {
1470			Entry* nextEntry = rootDir->GetNextReferringEntry(entry);
1471
1472			if (entry->IsActualEntry()) {
1473				EntryRemovedEvent* removedEvent;
1474				if (_GenerateEntryRemovedEvent(entry, event->time,
1475						&removedEvent) == B_OK) {
1476					_EntryRemoved(removedEvent, true);
1477					removedEvent->ReleaseReference();
1478				} else {
1479					RemoveEntry(entry);
1480					delete entry;
1481				}
1482			}
1483
1484			entry = nextEntry;
1485		}
1486	}
1487
1488	// remove all entries of the volume
1489	while (Entry* entry = volume->GetFirstEntry()) {
1490		bool remove = true;
1491		if (entry->IsActualEntry()) {
1492			if (_GenerateEntryRemovedEvent(entry, event->time) != B_OK)
1493				remove = false;
1494		}
1495
1496		if (remove) {
1497			RemoveEntry(entry);
1498			delete entry;
1499		}
1500	}
1501
1502	// remove all nodes
1503	while (Node* node = volume->GetFirstNode()) {
1504		RemoveNode(node);
1505		if (node != volume->GetRootDirectory())
1506			delete node;
1507	}
1508
1509	// remove the volume
1510	fVolumes->Remove(volume->GetID());
1511	delete volume;
1512
1513	// generate an "entry created" event for the covered node
1514	if (rootRefInitialized)
1515		_GenerateEntryCreatedEvent(rootRef, event->time);
1516}
1517
1518// _QueryEntryCreated
1519void
1520VolumeManager::_QueryEntryCreated(EntryCreatedEvent* event)
1521{
1522	// get the query handler
1523	QueryHandler* queryHandler
1524		= dynamic_cast<QueryHandler*>(event->queryHandler);
1525	if (!queryHandler)
1526		return;
1527
1528	// load the entry (just to make sure that it really exists)
1529	Entry* entry = NULL;
1530	status_t error = LoadEntry(event->volumeID, event->directoryID,
1531		event->name.GetString(), true, &entry);
1532	if (error != B_OK)
1533		return;
1534
1535	// get remote port and token
1536	if (!queryHandler->LockLooper())
1537		return;
1538
1539	QueryHandle* queryHandle = queryHandler->GetQueryHandle();
1540	event->remotePort = queryHandle->GetRemotePort();
1541	event->remoteToken = queryHandle->GetRemoteToken();
1542	queryHandler->UnlockLooper();
1543
1544	// send a notification to the client volume
1545	queryHandler->GetQueryDomain()->ProcessQueryEvent(event);
1546}
1547
1548// _QueryEntryRemoved
1549void
1550VolumeManager::_QueryEntryRemoved(EntryRemovedEvent* event)
1551{
1552	// get the query handler
1553	QueryHandler* queryHandler
1554		= dynamic_cast<QueryHandler*>(event->queryHandler);
1555	if (!queryHandler)
1556		return;
1557
1558	// load the directory (just to make sure that it really exists)
1559	Directory* directory = NULL;
1560	status_t error = LoadDirectory(event->volumeID, event->directoryID,
1561		&directory);
1562	if (error != B_OK)
1563		return;
1564
1565	// get remote port and token
1566	if (!queryHandler->LockLooper())
1567		return;
1568	QueryHandle* queryHandle = queryHandler->GetQueryHandle();
1569	event->remotePort = queryHandle->GetRemotePort();
1570	event->remoteToken = queryHandle->GetRemoteToken();
1571	queryHandler->UnlockLooper();
1572
1573	// send a notification to the client volume
1574	queryHandler->GetQueryDomain()->ProcessQueryEvent(event);
1575}
1576
1577// _QueryEntryMoved
1578void
1579VolumeManager::_QueryEntryMoved(EntryMovedEvent* event)
1580{
1581	// we simply split the event into a `removed' and a `created' event
1582
1583	// allocate the events
1584	EntryRemovedEvent* removedEvent = new(std::nothrow) EntryRemovedEvent;
1585	EntryCreatedEvent* createdEvent = new(std::nothrow) EntryCreatedEvent;
1586	if (!removedEvent || !createdEvent) {
1587		delete removedEvent;
1588		delete createdEvent;
1589		return;
1590	}
1591
1592	// init the removed event
1593	removedEvent->opcode = B_ENTRY_REMOVED;
1594	removedEvent->time = event->time;
1595	removedEvent->queryHandler = event->queryHandler;
1596	removedEvent->queryHandler->AcquireReference();
1597	removedEvent->volumeID = event->volumeID;
1598	removedEvent->directoryID = event->fromDirectoryID;
1599	removedEvent->nodeVolumeID = event->volumeID;
1600	removedEvent->nodeID = event->nodeID;
1601	removedEvent->name = event->fromName;
1602
1603	// init the created event
1604	createdEvent->opcode = B_ENTRY_CREATED;
1605	createdEvent->time = event->time;
1606	createdEvent->queryHandler = event->queryHandler;
1607	createdEvent->queryHandler->AcquireReference();
1608	createdEvent->volumeID = event->volumeID;
1609	createdEvent->directoryID = event->toDirectoryID;
1610	createdEvent->nodeID = event->nodeID;
1611	createdEvent->name = event->toName;
1612
1613	// send them
1614	_QueryEntryRemoved(removedEvent);
1615	removedEvent->ReleaseReference();
1616	_QueryEntryCreated(createdEvent);
1617	createdEvent->ReleaseReference();
1618}
1619
1620// _IsRecentEvent
1621bool
1622VolumeManager::_IsRecentEvent(NodeMonitoringEvent* event) const
1623{
1624	return (event && system_time() < event->time + kRecentEventLifeTime);
1625}
1626
1627// _GenerateEntryCreatedEvent
1628status_t
1629VolumeManager::_GenerateEntryCreatedEvent(const entry_ref& ref, bigtime_t time,
1630	EntryCreatedEvent** _event)
1631{
1632	// load the entry
1633	Entry* entry;
1634	status_t error = LoadEntry(ref.device, ref.directory, ref.name, true,
1635		&entry);
1636	if (error != B_OK)
1637		return error;
1638
1639	// create the event
1640	EntryCreatedEvent* event = new(std::nothrow) EntryCreatedEvent;
1641	if (!event)
1642		return B_NO_MEMORY;
1643
1644	// fill in the fields
1645	event->opcode = B_ENTRY_CREATED;
1646	event->time = time;
1647	event->volumeID = entry->GetVolumeID();
1648	event->directoryID = entry->GetDirectoryID();
1649	event->nodeID = entry->GetNode()->GetID();
1650	event->name.SetTo(entry->GetName());
1651
1652	if (_event) {
1653		*_event = event;
1654	} else {
1655		_EntryCreated(event);
1656		event->ReleaseReference();
1657	}
1658
1659	return B_OK;
1660}
1661
1662// _GenerateEntryRemovedEvent
1663status_t
1664VolumeManager::_GenerateEntryRemovedEvent(Entry* entry, bigtime_t time,
1665	EntryRemovedEvent** _event)
1666{
1667	if (!entry)
1668		return B_BAD_VALUE;
1669
1670	// create the event
1671	EntryRemovedEvent* event = new(std::nothrow) EntryRemovedEvent;
1672	if (!event)
1673		return B_NO_MEMORY;
1674
1675	// fill in the fields
1676	event->opcode = B_ENTRY_REMOVED;
1677	event->time = time;
1678	event->volumeID = entry->GetVolumeID();
1679	event->directoryID = entry->GetDirectoryID();
1680	event->nodeVolumeID = entry->GetNode()->GetVolumeID();
1681	event->nodeID = entry->GetNode()->GetID();
1682	event->name.SetTo(entry->GetName());
1683
1684	if (_event) {
1685		*_event = event;
1686	} else {
1687		_EntryRemoved(event, false);
1688		event->ReleaseReference();
1689	}
1690
1691	return B_OK;
1692}
1693
1694// _CheckVolumeRootMoved
1695void
1696VolumeManager::_CheckVolumeRootMoved(EntryMovedEvent* event)
1697{
1698	// If a volume root is moved, the sent node monitoring message does
1699	// unforunately contain the node_ref of the covered node, not that of the
1700	// volume root -- a BeOS R5 VFS bug. Since we have the entry_ref of the
1701	// new entry, we can stat the node.
1702
1703	// check whether the node is the root of a volume
1704	NoAllocEntryRef ref(event->volumeID, event->toDirectoryID,
1705		event->toName.GetString());
1706	BEntry entry;
1707	struct stat st;
1708	if (FDManager::SetEntry(&entry, &ref) == B_OK
1709		&& entry.GetStat(&st) == B_OK) {
1710		event->nodeVolumeID = st.st_dev;
1711		event->nodeID = st.st_ino;
1712		if (Volume* volume = GetVolume(st.st_dev)) {
1713			if (volume->GetRootID() == st.st_ino) {
1714				PRINT("Mount point for volume %" B_PRIdDEV " renamed\n",
1715					volume->GetID());
1716			}
1717		}
1718	}
1719}
1720
1721// _NodeMonitoringProcessorEntry
1722int32
1723VolumeManager::_NodeMonitoringProcessorEntry(void* data)
1724{
1725	return ((VolumeManager*)data)->_NodeMonitoringProcessor();
1726}
1727
1728// _NodeMonitoryProcessor
1729int32
1730VolumeManager::_NodeMonitoringProcessor()
1731{
1732	do {
1733		NodeMonitoringEvent* event = NULL;
1734		status_t error = fNodeMonitoringEvents.Pop(&event);
1735
1736		VolumeManagerLocker managerLocker;
1737
1738		while (error == B_OK) {
1739			if (event->queryHandler) {
1740				switch (event->opcode) {
1741					case B_ENTRY_CREATED:
1742						_QueryEntryCreated(
1743							dynamic_cast<EntryCreatedEvent*>(event));
1744						break;
1745					case B_ENTRY_REMOVED:
1746						_QueryEntryRemoved(
1747							dynamic_cast<EntryRemovedEvent*>(event));
1748						break;
1749					case B_ENTRY_MOVED:
1750						_QueryEntryMoved(dynamic_cast<EntryMovedEvent*>(event));
1751						break;
1752				}
1753			} else {
1754				switch (event->opcode) {
1755					case B_ENTRY_CREATED:
1756						_EntryCreated(dynamic_cast<EntryCreatedEvent*>(event));
1757						break;
1758					case B_ENTRY_REMOVED:
1759						_EntryRemoved(dynamic_cast<EntryRemovedEvent*>(event),
1760							false);
1761						break;
1762					case B_ENTRY_MOVED:
1763						_EntryMoved(dynamic_cast<EntryMovedEvent*>(event));
1764						break;
1765					case B_STAT_CHANGED:
1766						_NodeStatChanged(
1767							dynamic_cast<StatChangedEvent*>(event));
1768						break;
1769					case B_ATTR_CHANGED:
1770						_NodeAttributeChanged(
1771							dynamic_cast<AttributeChangedEvent*>(event));
1772						break;
1773					case B_DEVICE_MOUNTED:
1774						_VolumeMounted(dynamic_cast<VolumeMountedEvent*>(event));
1775						break;
1776					case B_DEVICE_UNMOUNTED:
1777						_VolumeUnmounted(
1778							dynamic_cast<VolumeUnmountedEvent*>(event));
1779						break;
1780				}
1781			}
1782			event->ReleaseReference();
1783
1784			// If there is another event available, get it as long as we
1785			// have the VolumeManager lock.
1786			error = fNodeMonitoringEvents.Pop(&event, 0);
1787		}
1788	} while (!fTerminating);
1789
1790	return 0;
1791}
1792
1793
1794// sManager
1795VolumeManager* VolumeManager::sManager = NULL;
1796