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(%ld, %lld, `%s')\n", volumeID,
723			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%lx, %ld, %ld)\n",
795		queryDomain, queryString, flags, remotePort, remoteToken);
796
797	// allocate the handle
798	QueryHandle* queryHandle = new(std::nothrow) QueryHandle(remotePort,
799		remoteToken);
800	if (!queryHandle)
801		return B_NO_MEMORY;
802	ObjectDeleter<QueryHandle> handleDeleter(queryHandle);
803
804	// allocate a query handler, if this is a live query
805	QueryHandler* queryHandler = NULL;
806	if (liveQuery) {
807		queryHandler = new(std::nothrow) QueryHandler(this, queryDomain,
808			queryHandle);
809		if (!queryHandler)
810			return B_NO_MEMORY;
811
812		fNodeMonitor->Lock();
813		fNodeMonitor->AddHandler(queryHandler);
814		fNodeMonitor->Unlock();
815		queryHandle->SetQueryListener(queryHandler);
816	}
817
818	// iterate through the volumes and create a query for each one
819	// supporting queries
820	for (VolumeMap::Iterator it = fVolumes->GetIterator(); it.HasNext();) {
821		Volume* volume = it.Next().value;
822		if (!volume->KnowsQuery())
823			continue;
824
825		// The volume should either be contained by the client volume or
826		// the other way around. Otherwise they are located in different
827		// branches of the FS tree and don't have common nodes.
828		if (!queryDomain->QueryDomainIntersectsWith(volume))
829			continue;
830		PRINT("VolumeManager::OpenQuery(): adding Query for volume %ld"
831			"\n", volume->GetID());
832
833		// create the query for this volume
834		BVolume bVolume(volume->GetID());
835		Query* query = new(std::nothrow) Query;
836		if (!query)
837			return B_NO_MEMORY;
838
839		// init the query
840		ObjectDeleter<Query> queryDeleter(query);
841		status_t error = query->SetVolume(&bVolume);
842		if (error != B_OK)
843			return error;
844		error = query->SetPredicate(queryString);
845		if (error != B_OK)
846			return error;
847		if (liveQuery) {
848			error = query->SetTarget(queryHandler);
849			if (error != B_OK)
850				return error;
851		}
852
853		// fetch
854		error = query->Fetch();
855		if (error != B_OK)
856			return error;
857
858		queryHandle->AddQuery(query);
859		queryDeleter.Detach();
860	}
861
862	*handle = queryHandle;
863	handleDeleter.Detach();
864	return B_OK;
865}
866
867// CompletePathToRoot
868status_t
869VolumeManager::CompletePathToRoot(Directory* directory)
870{
871	if (!directory)
872		return B_BAD_VALUE;
873
874	while (directory != GetRootDirectory()) {
875		// if the dir has a valid entry referring to it, we've nothing to do
876		if (directory->GetActualReferringEntry())
877			return B_OK;
878
879		// get a proper entry_ref
880		BEntry bEntry;
881		entry_ref entryRef(directory->GetVolumeID(), directory->GetID(), ".");
882		status_t error = FDManager::SetEntry(&bEntry, &entryRef);
883		if (error == B_OK)
884			error = bEntry.GetRef(&entryRef);
885		if (error != B_OK)
886			return error;
887
888		// if the entry is already loaded, we're done
889		if (GetEntry(entryRef))
890			return B_OK;
891
892		// the entry is not yet known -- load it
893		Entry* entry;
894		error = LoadEntry(entryRef.device, entryRef.directory, entryRef.name,
895			true, &entry);
896		if (error != B_OK)
897			return error;
898
899		// get the entry's parent dir and enter the next round
900		directory = entry->GetDirectory();
901	}
902
903	return B_OK;
904}
905
906// GetPath
907status_t
908VolumeManager::GetPath(Entry* entry, Path* path)
909{
910	// get directory path
911	status_t error = GetPath(entry->GetDirectory(), path);
912	if (error != B_OK)
913		return error;
914
915	// append the entry name
916	return path->Append(entry->GetName());
917}
918
919// GetPath
920status_t
921VolumeManager::GetPath(Node* node, Path* path)
922{
923	if (node == GetRootDirectory())
924		return path->SetTo("/");
925
926	// get an entry referring to the node
927	Entry* entry = node->GetActualReferringEntry();
928	if (!entry) {
929		// if the node is a directory, we complete the path to the root and
930		// try again
931		if (Directory* directory = dynamic_cast<Directory*>(node)) {
932			CompletePathToRoot(directory);
933			entry = node->GetActualReferringEntry();
934		}
935
936		if (!entry)
937			return B_ERROR;
938	}
939
940	return GetPath(entry, path);
941}
942
943// DirectoryContains
944bool
945VolumeManager::DirectoryContains(Directory* directory, Entry* entry)
946{
947	if (!directory || !entry)
948		return false;
949
950	return DirectoryContains(directory, entry->GetDirectory(), true);
951}
952
953// DirectoryContains
954bool
955VolumeManager::DirectoryContains(Directory* directory, Directory* descendant,
956	bool reflexive)
957{
958	if (!directory || !descendant)
959		return false;
960
961	// a directory contains itself, just as defined by the caller
962	if (directory == descendant)
963		return reflexive;
964
965	// if the directory is the root directory, it contains everything
966	Directory* rootDir = GetRootDirectory();
967	if (directory == rootDir)
968		return true;
969
970	// recursively get the descendant's parent dir until reaching the root dir
971	// or the given dir
972	while (descendant != rootDir) {
973		descendant = GetParentDirectory(descendant);
974		if (!descendant)
975			return false;
976
977		if (descendant == directory)
978			return true;
979	}
980
981	return false;
982}
983
984// DirectoryContains
985bool
986VolumeManager::DirectoryContains(Directory* directory, Node* descendant,
987	bool reflexive)
988{
989	if (!directory || !descendant)
990		return false;
991
992	// if the node is a directory, let the other version do the job
993	if (Directory* dir = dynamic_cast<Directory*>(descendant))
994		return DirectoryContains(directory, dir, reflexive);
995
996	// iterate through the referring entries and check, if the directory
997	// contains any of them
998	for (Entry* entry = descendant->GetFirstReferringEntry();
999		 entry;
1000		 entry = descendant->GetNextReferringEntry(entry)) {
1001		if (DirectoryContains(directory, entry))
1002			return true;
1003	}
1004
1005	return false;
1006}
1007
1008
1009// #pragma mark -
1010
1011// ProcessNodeMonitoringEvent
1012void
1013VolumeManager::ProcessNodeMonitoringEvent(NodeMonitoringEvent* event)
1014{
1015	if (fNodeMonitoringEvents.Push(event) != B_OK)
1016		delete event;
1017}
1018
1019// _AddVolume
1020status_t
1021VolumeManager::_AddVolume(dev_t volumeID, Volume** _volume)
1022{
1023	if (GetVolume(volumeID))
1024		return B_OK;
1025
1026	// create the volume
1027	Volume* volume = new(std::nothrow) Volume(volumeID);
1028	if (!volume)
1029		RETURN_ERROR(B_NO_MEMORY);
1030	ObjectDeleter<Volume> volumeDeleter(volume);
1031	status_t error = volume->Init();
1032	if (error != B_OK)
1033		RETURN_ERROR(error);
1034
1035	// add it
1036	error = fVolumes->Put(volumeID, volume);
1037	if (error != B_OK)
1038		RETURN_ERROR(error);
1039
1040	// add the root node
1041	error = AddNode(volume->GetRootDirectory());
1042	if (error != B_OK) {
1043		fVolumes->Remove(volumeID);
1044		RETURN_ERROR(error);
1045	}
1046
1047	// complete the root dir path
1048	CompletePathToRoot(volume->GetRootDirectory());
1049
1050	volumeDeleter.Detach();
1051	if (_volume)
1052		*_volume = volume;
1053	return B_OK;
1054}
1055
1056// _EntryCreated
1057void
1058VolumeManager::_EntryCreated(EntryCreatedEvent* event)
1059{
1060	// get the directory
1061	Directory* directory = GetDirectory(event->volumeID, event->directoryID);
1062	if (!directory)
1063		return;
1064
1065	// check, if there is an earlier similar event
1066	bool notify = true;
1067	NoAllocEntryRef ref(event->volumeID, event->directoryID,
1068		event->name.GetString());
1069	EntryCreatedEvent* oldEvent = fEntryCreatedEvents->Get(ref);
1070
1071	// remove the old event
1072	if (oldEvent) {
1073		fEntryCreatedEvents->Remove(ref);
1074		fRecentNodeMonitoringEvents.Remove(oldEvent);
1075		notify = !_IsRecentEvent(oldEvent);
1076		oldEvent->ReleaseReference();
1077	}
1078
1079	// add the new event
1080	if (fEntryCreatedEvents->Put(ref, event) == B_OK) {
1081		fRecentNodeMonitoringEvents.Insert(event);
1082		event->AcquireReference();
1083	}
1084
1085	// if the directory is complete or at least has iterators attached to it,
1086	// we load the entry
1087	if (directory->IsComplete() || directory->HasDirIterators()) {
1088		Entry* entry;
1089		LoadEntry(ref.device, ref.directory, ref.name, false, &entry);
1090	}
1091
1092	// send notifications
1093	if (notify) {
1094		for (ClientVolumeMap::Iterator it = fClientVolumes->GetIterator();
1095			 it.HasNext();) {
1096			ClientVolume* clientVolume = it.Next().value;
1097			if (DirectoryContains(clientVolume->GetRootDirectory(), directory,
1098				true)) {
1099				clientVolume->ProcessNodeMonitoringEvent(event);
1100			}
1101		}
1102	}
1103}
1104
1105// _EntryRemoved
1106void
1107VolumeManager::_EntryRemoved(EntryRemovedEvent* event, bool keepNode)
1108{
1109	// get node and directory
1110	Node* node = GetNode(event->nodeVolumeID, event->nodeID);
1111	Directory* directory = GetDirectory(event->volumeID, event->directoryID);
1112	if (!directory)
1113		return;
1114
1115	// find the entry
1116	Entry* entry = NULL;
1117	if (node) {
1118		if (event->name.GetLength() == 0) {
1119			for (entry = node->GetFirstReferringEntry();
1120				 entry;
1121				 entry = node->GetNextReferringEntry(entry)) {
1122				if (!entry->Exists()) {
1123					event->name.SetTo(entry->GetName());
1124					break;
1125				}
1126			}
1127		} else {
1128			entry = GetEntry(directory->GetVolumeID(), directory->GetID(),
1129				event->name.GetString());
1130		}
1131	}
1132
1133	// check, if there is an earlier similar event
1134	bool notify = true;
1135	NoAllocEntryRef ref(event->volumeID, event->directoryID,
1136		event->name.GetString());
1137	EntryRemovedEvent* oldEvent = fEntryRemovedEvents->Get(ref);
1138		// TODO: Under BeOS R5 the entry name is not encoded in the
1139		// "entry removed" node monitoring message. If we have seen the entry
1140		// before, we can get the entry nevertheless (see above). Usually we
1141		// get 2 "entry removed" events: One for watching the directory and one
1142		// for watching the node. After the first one has been processed, we've
1143		// forgotten everything about the entry and we won't be able to find out
1144		// the entry's name for the second one. Hence we will never find the
1145		// previous event in the fEntryRemovedEvents map. We should probably
1146		// fall back to using a NodeRef as key under BeOS R5.
1147
1148	// remove the old event
1149	if (oldEvent) {
1150		fEntryRemovedEvents->Remove(ref);
1151		fRecentNodeMonitoringEvents.Remove(oldEvent);
1152		notify = !_IsRecentEvent(oldEvent);
1153		oldEvent->ReleaseReference();
1154	}
1155
1156	// add the new event
1157	if (fEntryRemovedEvents->Put(ref, event) == B_OK) {
1158		fRecentNodeMonitoringEvents.Insert(event);
1159		event->AcquireReference();
1160	}
1161
1162	// remove the entry
1163	if (entry) {
1164		RemoveEntry(entry);
1165		delete entry;
1166	}
1167
1168	// remove the node, if it doesn't have any more actual referring entries
1169	if (node && !keepNode && !node->GetActualReferringEntry()) {
1170		RemoveNode(node);
1171		if (node != node->GetVolume()->GetRootDirectory())
1172			delete node;
1173	}
1174
1175	// send notifications
1176	if (notify) {
1177		NodeRef nodeRef(event->nodeVolumeID, event->nodeID);
1178		for (ClientVolumeMap::Iterator it = fClientVolumes->GetIterator();
1179			 it.HasNext();) {
1180			// We send a notification, if the client volume contains the entry,
1181			// but also, if the removed entry refers to the client volume's
1182			// root. The client connection has a special handling for this
1183			// case.
1184			ClientVolume* clientVolume = it.Next().value;
1185			Directory* rootDir = clientVolume->GetRootDirectory();
1186			if (DirectoryContains(rootDir, directory, true)
1187				|| clientVolume->GetRootNodeRef() == nodeRef) {
1188				clientVolume->ProcessNodeMonitoringEvent(event);
1189			}
1190		}
1191	}
1192}
1193
1194// _EntryMoved
1195void
1196VolumeManager::_EntryMoved(EntryMovedEvent* event)
1197{
1198	_CheckVolumeRootMoved(event);
1199
1200	Directory* fromDirectory
1201		= GetDirectory(event->volumeID, event->fromDirectoryID);
1202	Directory* toDirectory
1203		= GetDirectory(event->volumeID, event->toDirectoryID);
1204	Node* node = GetNode(event->nodeVolumeID, event->nodeID);
1205
1206	// we should at least have one of the directories
1207	if (!fromDirectory && !toDirectory)
1208		return;
1209
1210	// find the old entry
1211	Entry* oldEntry = NULL;
1212	if (node) {
1213		if (event->fromName.GetLength() == 0) {
1214			for (oldEntry = node->GetFirstReferringEntry();
1215				 oldEntry;
1216				 oldEntry = node->GetNextReferringEntry(oldEntry)) {
1217				if (!oldEntry->Exists()) {
1218					event->fromName.SetTo(oldEntry->GetName());
1219					break;
1220				}
1221			}
1222		} else {
1223			oldEntry = GetEntry(event->volumeID, event->fromDirectoryID,
1224				event->fromName.GetString());
1225		}
1226	}
1227
1228	// check, if there is an earlier similar event
1229	bool notify = true;
1230	if (event->fromName.GetLength() > 0) {
1231		EntryMovedEventKey key(event->volumeID, event->fromDirectoryID,
1232			event->fromName.GetString(), event->toDirectoryID,
1233			event->toName.GetString());
1234		EntryMovedEvent* oldEvent = fEntryMovedEvents->Get(key);
1235
1236		// remove the old event
1237		if (oldEvent) {
1238			fEntryMovedEvents->Remove(key);
1239			fRecentNodeMonitoringEvents.Remove(oldEvent);
1240			notify = !_IsRecentEvent(oldEvent);
1241			oldEvent->ReleaseReference();
1242		}
1243
1244		// add the new event
1245		if (fEntryMovedEvents->Put(key, event) == B_OK) {
1246			fRecentNodeMonitoringEvents.Insert(event);
1247			event->AcquireReference();
1248		}
1249	}
1250
1251	// remove the old entry
1252	if (oldEntry) {
1253		RemoveEntry(oldEntry);
1254		delete oldEntry;
1255	}
1256
1257	// If the to directory is complete or at least has iterators attached to it,
1258	// we load the new entry. We also load it, if the node is the root of a
1259	// volume.
1260	if (toDirectory
1261		&& (toDirectory->IsComplete() || toDirectory->HasDirIterators()
1262			|| (node && node == node->GetVolume()->GetRootDirectory()))) {
1263		Entry* newEntry;
1264		LoadEntry(event->volumeID, event->toDirectoryID,
1265			event->toName.GetString(), false, &newEntry);
1266	}
1267
1268	// remove the node, if it doesn't have any more actual referring entries
1269	if (node && !node->GetActualReferringEntry()) {
1270		RemoveNode(node);
1271		if (node != node->GetVolume()->GetRootDirectory())
1272			delete node;
1273	}
1274
1275	// send notifications
1276	if (notify) {
1277		for (ClientVolumeMap::Iterator it = fClientVolumes->GetIterator();
1278			 it.HasNext();) {
1279			ClientVolume* clientVolume = it.Next().value;
1280
1281			// check, if it contains the from/to directories
1282			Directory* rootDir = clientVolume->GetRootDirectory();
1283			bool containsFrom = DirectoryContains(rootDir, fromDirectory, true);
1284			bool containsTo = DirectoryContains(rootDir, toDirectory, true);
1285
1286			if (containsFrom) {
1287				if (containsTo) {
1288					// contains source and target dir
1289					clientVolume->ProcessNodeMonitoringEvent(event);
1290				} else {
1291					// contains only the source dir: generate an "entry removed"
1292					// event
1293					EntryRemovedEvent *removedEvent
1294						= new(std::nothrow) EntryRemovedEvent;
1295					if (!removedEvent)
1296						continue;
1297					removedEvent->opcode = B_ENTRY_REMOVED;
1298					removedEvent->time = event->time;
1299					removedEvent->volumeID = event->volumeID;
1300					removedEvent->directoryID = event->fromDirectoryID;
1301					removedEvent->nodeVolumeID = event->nodeVolumeID;
1302					removedEvent->nodeID = event->nodeID;
1303					if (event->fromName.GetLength() > 0)
1304						removedEvent->name = event->fromName;
1305					clientVolume->ProcessNodeMonitoringEvent(removedEvent);
1306					removedEvent->ReleaseReference();
1307				}
1308			} else if (containsTo) {
1309				// contains only the target directory: generate an
1310				// "entry created" event
1311				EntryCreatedEvent *createdEvent
1312					= new(std::nothrow) EntryCreatedEvent;
1313				if (!createdEvent)
1314					continue;
1315				createdEvent->opcode = B_ENTRY_CREATED;
1316				createdEvent->time = event->time;
1317				createdEvent->volumeID = event->volumeID;
1318				createdEvent->directoryID = event->toDirectoryID;
1319				createdEvent->name = event->toName;
1320				clientVolume->ProcessNodeMonitoringEvent(createdEvent);
1321				createdEvent->ReleaseReference();
1322			}
1323		}
1324	}
1325}
1326
1327// _NodeStatChanged
1328void
1329VolumeManager::_NodeStatChanged(StatChangedEvent* event)
1330{
1331	// get the node
1332	Node* node = GetNode(event->volumeID, event->nodeID);
1333	if (!node)
1334		return;
1335
1336	// check, if there is an earlier similar event
1337	bool notify = true;
1338	NodeRef ref(event->volumeID, event->nodeID);
1339	StatChangedEvent* oldEvent = fNodeStatChangedEvents->Get(ref);
1340
1341	// remove the old event
1342	if (oldEvent) {
1343		fNodeStatChangedEvents->Remove(ref);
1344		fRecentNodeMonitoringEvents.Remove(oldEvent);
1345		notify = !_IsRecentEvent(oldEvent);
1346		oldEvent->ReleaseReference();
1347	}
1348
1349	// add the new event
1350	if (fNodeStatChangedEvents->Put(ref, event) == B_OK) {
1351		fRecentNodeMonitoringEvents.Insert(event);
1352		event->AcquireReference();
1353	}
1354
1355	if (notify) {
1356		// update the cached node stat
1357		node->UpdateStat();
1358
1359		// send notifications
1360		for (ClientVolumeMap::Iterator it = fClientVolumes->GetIterator();
1361			 it.HasNext();) {
1362			ClientVolume* clientVolume = it.Next().value;
1363			if (DirectoryContains(clientVolume->GetRootDirectory(), node, true))
1364				clientVolume->ProcessNodeMonitoringEvent(event);
1365		}
1366	}
1367}
1368
1369// _NodeAttributeChanged
1370void
1371VolumeManager::_NodeAttributeChanged(AttributeChangedEvent* event)
1372{
1373	// get the node
1374	Node* node = GetNode(event->volumeID, event->nodeID);
1375	if (!node)
1376		return;
1377
1378	// check, if there is an earlier similar event
1379	bool notify = true;
1380	AttributeRef ref(event->volumeID, event->nodeID,
1381		event->attribute.GetString());
1382	AttributeChangedEvent* oldEvent = fNodeAttributeChangedEvents->Get(ref);
1383
1384	// remove the old event
1385	if (oldEvent) {
1386		fNodeAttributeChangedEvents->Remove(ref);
1387		fRecentNodeMonitoringEvents.Remove(oldEvent);
1388		notify = !_IsRecentEvent(oldEvent);
1389		oldEvent->ReleaseReference();
1390	}
1391
1392	// add the new event
1393	if (fNodeAttributeChangedEvents->Put(ref, event) == B_OK) {
1394		fRecentNodeMonitoringEvents.Insert(event);
1395		event->AcquireReference();
1396	}
1397
1398	// send notifications
1399	if (notify) {
1400		for (ClientVolumeMap::Iterator it = fClientVolumes->GetIterator();
1401			 it.HasNext();) {
1402			ClientVolume* clientVolume = it.Next().value;
1403			if (DirectoryContains(clientVolume->GetRootDirectory(), node, true))
1404				clientVolume->ProcessNodeMonitoringEvent(event);
1405		}
1406	}
1407}
1408
1409// _VolumeMounted
1410void
1411VolumeManager::_VolumeMounted(VolumeMountedEvent* event)
1412{
1413	entry_ref rootRef;
1414	bool rootRefInitialized = false;
1415
1416	// remove the entry referring to the covered directory
1417	Directory* coveredDirectory = GetDirectory(event->volumeID,
1418		event->directoryID);
1419	if (coveredDirectory) {
1420		if (Entry* entry = coveredDirectory->GetActualReferringEntry()) {
1421			// get an entry for later
1422			rootRef = entry->GetEntryRef();
1423			rootRefInitialized = true;
1424
1425			// send the "entry removed" event
1426			EntryRemovedEvent* event;
1427			if (_GenerateEntryRemovedEvent(entry, system_time(),
1428					&event) == B_OK) {
1429				_EntryRemoved(event, true);
1430				event->ReleaseReference();
1431			} else {
1432				RemoveEntry(entry);
1433				delete entry;
1434			}
1435		}
1436	}
1437
1438	// add the volume
1439	_AddVolume(event->newVolumeID);
1440
1441	// generate an "entry created" event for the root dir entry
1442	if (rootRefInitialized)
1443		_GenerateEntryCreatedEvent(rootRef, event->time);
1444}
1445
1446// _VolumeUnmounted
1447void
1448VolumeManager::_VolumeUnmounted(VolumeUnmountedEvent* event)
1449{
1450	// get the volume
1451	Volume* volume = GetVolume(event->volumeID);
1452	if (!volume)
1453		return;
1454
1455	entry_ref rootRef;
1456	bool rootRefInitialized = false;
1457
1458	// remove all actual entries referring to the root directory (should only
1459	// be one)
1460	if (Directory* rootDir = volume->GetRootDirectory()) {
1461		// get an entry ref for the root dir
1462		if (Entry* entry = rootDir->GetActualReferringEntry()) {
1463			rootRef = entry->GetEntryRef();
1464			rootRefInitialized = true;
1465		}
1466
1467		Entry* entry = rootDir->GetFirstReferringEntry();
1468		while (entry) {
1469			Entry* nextEntry = rootDir->GetNextReferringEntry(entry);
1470
1471			if (entry->IsActualEntry()) {
1472				EntryRemovedEvent* removedEvent;
1473				if (_GenerateEntryRemovedEvent(entry, event->time,
1474						&removedEvent) == B_OK) {
1475					_EntryRemoved(removedEvent, true);
1476					removedEvent->ReleaseReference();
1477				} else {
1478					RemoveEntry(entry);
1479					delete entry;
1480				}
1481			}
1482
1483			entry = nextEntry;
1484		}
1485	}
1486
1487	// remove all entries of the volume
1488	while (Entry* entry = volume->GetFirstEntry()) {
1489		bool remove = true;
1490		if (entry->IsActualEntry()) {
1491			if (_GenerateEntryRemovedEvent(entry, event->time) != B_OK)
1492				remove = false;
1493		}
1494
1495		if (remove) {
1496			RemoveEntry(entry);
1497			delete entry;
1498		}
1499	}
1500
1501	// remove all nodes
1502	while (Node* node = volume->GetFirstNode()) {
1503		RemoveNode(node);
1504		if (node != volume->GetRootDirectory())
1505			delete node;
1506	}
1507
1508	// remove the volume
1509	fVolumes->Remove(volume->GetID());
1510	delete volume;
1511
1512	// generate an "entry created" event for the covered node
1513	if (rootRefInitialized)
1514		_GenerateEntryCreatedEvent(rootRef, event->time);
1515}
1516
1517// _QueryEntryCreated
1518void
1519VolumeManager::_QueryEntryCreated(EntryCreatedEvent* event)
1520{
1521	// get the query handler
1522	QueryHandler* queryHandler
1523		= dynamic_cast<QueryHandler*>(event->queryHandler);
1524	if (!queryHandler)
1525		return;
1526
1527	// load the entry (just to make sure that it really exists)
1528	Entry* entry = NULL;
1529	status_t error = LoadEntry(event->volumeID, event->directoryID,
1530		event->name.GetString(), true, &entry);
1531	if (error != B_OK)
1532		return;
1533
1534	// get remote port and token
1535	if (!queryHandler->LockLooper())
1536		return;
1537
1538	QueryHandle* queryHandle = queryHandler->GetQueryHandle();
1539	event->remotePort = queryHandle->GetRemotePort();
1540	event->remoteToken = queryHandle->GetRemoteToken();
1541	queryHandler->UnlockLooper();
1542
1543	// send a notification to the client volume
1544	queryHandler->GetQueryDomain()->ProcessQueryEvent(event);
1545}
1546
1547// _QueryEntryRemoved
1548void
1549VolumeManager::_QueryEntryRemoved(EntryRemovedEvent* event)
1550{
1551	// get the query handler
1552	QueryHandler* queryHandler
1553		= dynamic_cast<QueryHandler*>(event->queryHandler);
1554	if (!queryHandler)
1555		return;
1556
1557	// load the directory (just to make sure that it really exists)
1558	Directory* directory = NULL;
1559	status_t error = LoadDirectory(event->volumeID, event->directoryID,
1560		&directory);
1561	if (error != B_OK)
1562		return;
1563
1564	// get remote port and token
1565	if (!queryHandler->LockLooper())
1566		return;
1567	QueryHandle* queryHandle = queryHandler->GetQueryHandle();
1568	event->remotePort = queryHandle->GetRemotePort();
1569	event->remoteToken = queryHandle->GetRemoteToken();
1570	queryHandler->UnlockLooper();
1571
1572	// send a notification to the client volume
1573	queryHandler->GetQueryDomain()->ProcessQueryEvent(event);
1574}
1575
1576// _QueryEntryMoved
1577void
1578VolumeManager::_QueryEntryMoved(EntryMovedEvent* event)
1579{
1580	// we simply split the event into a `removed' and a `created' event
1581
1582	// allocate the events
1583	EntryRemovedEvent* removedEvent = new(std::nothrow) EntryRemovedEvent;
1584	EntryCreatedEvent* createdEvent = new(std::nothrow) EntryCreatedEvent;
1585	if (!removedEvent || !createdEvent) {
1586		delete removedEvent;
1587		delete createdEvent;
1588		return;
1589	}
1590
1591	// init the removed event
1592	removedEvent->opcode = B_ENTRY_REMOVED;
1593	removedEvent->time = event->time;
1594	removedEvent->queryHandler = event->queryHandler;
1595	removedEvent->queryHandler->AcquireReference();
1596	removedEvent->volumeID = event->volumeID;
1597	removedEvent->directoryID = event->fromDirectoryID;
1598	removedEvent->nodeVolumeID = event->volumeID;
1599	removedEvent->nodeID = event->nodeID;
1600	removedEvent->name = event->fromName;
1601
1602	// init the created event
1603	createdEvent->opcode = B_ENTRY_CREATED;
1604	createdEvent->time = event->time;
1605	createdEvent->queryHandler = event->queryHandler;
1606	createdEvent->queryHandler->AcquireReference();
1607	createdEvent->volumeID = event->volumeID;
1608	createdEvent->directoryID = event->toDirectoryID;
1609	createdEvent->nodeID = event->nodeID;
1610	createdEvent->name = event->toName;
1611
1612	// send them
1613	_QueryEntryRemoved(removedEvent);
1614	removedEvent->ReleaseReference();
1615	_QueryEntryCreated(createdEvent);
1616	createdEvent->ReleaseReference();
1617}
1618
1619// _IsRecentEvent
1620bool
1621VolumeManager::_IsRecentEvent(NodeMonitoringEvent* event) const
1622{
1623	return (event && system_time() < event->time + kRecentEventLifeTime);
1624}
1625
1626// _GenerateEntryCreatedEvent
1627status_t
1628VolumeManager::_GenerateEntryCreatedEvent(const entry_ref& ref, bigtime_t time,
1629	EntryCreatedEvent** _event)
1630{
1631	// load the entry
1632	Entry* entry;
1633	status_t error = LoadEntry(ref.device, ref.directory, ref.name, true,
1634		&entry);
1635	if (error != B_OK)
1636		return error;
1637
1638	// create the event
1639	EntryCreatedEvent* event = new(std::nothrow) EntryCreatedEvent;
1640	if (!event)
1641		return B_NO_MEMORY;
1642
1643	// fill in the fields
1644	event->opcode = B_ENTRY_CREATED;
1645	event->time = time;
1646	event->volumeID = entry->GetVolumeID();
1647	event->directoryID = entry->GetDirectoryID();
1648	event->nodeID = entry->GetNode()->GetID();
1649	event->name.SetTo(entry->GetName());
1650
1651	if (_event) {
1652		*_event = event;
1653	} else {
1654		_EntryCreated(event);
1655		event->ReleaseReference();
1656	}
1657
1658	return B_OK;
1659}
1660
1661// _GenerateEntryRemovedEvent
1662status_t
1663VolumeManager::_GenerateEntryRemovedEvent(Entry* entry, bigtime_t time,
1664	EntryRemovedEvent** _event)
1665{
1666	if (!entry)
1667		return B_BAD_VALUE;
1668
1669	// create the event
1670	EntryRemovedEvent* event = new(std::nothrow) EntryRemovedEvent;
1671	if (!event)
1672		return B_NO_MEMORY;
1673
1674	// fill in the fields
1675	event->opcode = B_ENTRY_REMOVED;
1676	event->time = time;
1677	event->volumeID = entry->GetVolumeID();
1678	event->directoryID = entry->GetDirectoryID();
1679	event->nodeVolumeID = entry->GetNode()->GetVolumeID();
1680	event->nodeID = entry->GetNode()->GetID();
1681	event->name.SetTo(entry->GetName());
1682
1683	if (_event) {
1684		*_event = event;
1685	} else {
1686		_EntryRemoved(event, false);
1687		event->ReleaseReference();
1688	}
1689
1690	return B_OK;
1691}
1692
1693// _CheckVolumeRootMoved
1694void
1695VolumeManager::_CheckVolumeRootMoved(EntryMovedEvent* event)
1696{
1697	// If a volume root is moved, the sent node monitoring message does
1698	// unforunately contain the node_ref of the covered node, not that of the
1699	// volume root -- a BeOS R5 VFS bug. Since we have the entry_ref of the
1700	// new entry, we can stat the node.
1701
1702	// check whether the node is the root of a volume
1703	NoAllocEntryRef ref(event->volumeID, event->toDirectoryID,
1704		event->toName.GetString());
1705	BEntry entry;
1706	struct stat st;
1707	if (FDManager::SetEntry(&entry, &ref) == B_OK
1708		&& entry.GetStat(&st) == B_OK) {
1709		event->nodeVolumeID = st.st_dev;
1710		event->nodeID = st.st_ino;
1711		if (Volume* volume = GetVolume(st.st_dev)) {
1712			if (volume->GetRootID() == st.st_ino) {
1713				PRINT("Mount point for volume %ld renamed\n",
1714					volume->GetID());
1715			}
1716		}
1717	}
1718}
1719
1720// _NodeMonitoringProcessorEntry
1721int32
1722VolumeManager::_NodeMonitoringProcessorEntry(void* data)
1723{
1724	return ((VolumeManager*)data)->_NodeMonitoringProcessor();
1725}
1726
1727// _NodeMonitoryProcessor
1728int32
1729VolumeManager::_NodeMonitoringProcessor()
1730{
1731	do {
1732		NodeMonitoringEvent* event = NULL;
1733		status_t error = fNodeMonitoringEvents.Pop(&event);
1734
1735		VolumeManagerLocker managerLocker;
1736
1737		while (error == B_OK) {
1738			if (event->queryHandler) {
1739				switch (event->opcode) {
1740					case B_ENTRY_CREATED:
1741						_QueryEntryCreated(
1742							dynamic_cast<EntryCreatedEvent*>(event));
1743						break;
1744					case B_ENTRY_REMOVED:
1745						_QueryEntryRemoved(
1746							dynamic_cast<EntryRemovedEvent*>(event));
1747						break;
1748					case B_ENTRY_MOVED:
1749						_QueryEntryMoved(dynamic_cast<EntryMovedEvent*>(event));
1750						break;
1751				}
1752			} else {
1753				switch (event->opcode) {
1754					case B_ENTRY_CREATED:
1755						_EntryCreated(dynamic_cast<EntryCreatedEvent*>(event));
1756						break;
1757					case B_ENTRY_REMOVED:
1758						_EntryRemoved(dynamic_cast<EntryRemovedEvent*>(event),
1759							false);
1760						break;
1761					case B_ENTRY_MOVED:
1762						_EntryMoved(dynamic_cast<EntryMovedEvent*>(event));
1763						break;
1764					case B_STAT_CHANGED:
1765						_NodeStatChanged(
1766							dynamic_cast<StatChangedEvent*>(event));
1767						break;
1768					case B_ATTR_CHANGED:
1769						_NodeAttributeChanged(
1770							dynamic_cast<AttributeChangedEvent*>(event));
1771						break;
1772					case B_DEVICE_MOUNTED:
1773						_VolumeMounted(dynamic_cast<VolumeMountedEvent*>(event));
1774						break;
1775					case B_DEVICE_UNMOUNTED:
1776						_VolumeUnmounted(
1777							dynamic_cast<VolumeUnmountedEvent*>(event));
1778						break;
1779				}
1780			}
1781			event->ReleaseReference();
1782
1783			// If there is another event available, get it as long as we
1784			// have the VolumeManager lock.
1785			error = fNodeMonitoringEvents.Pop(&event, 0);
1786		}
1787	} while (!fTerminating);
1788
1789	return 0;
1790}
1791
1792
1793// sManager
1794VolumeManager* VolumeManager::sManager = NULL;
1795