xref: /haiku/src/add-ons/kernel/file_systems/netfs/server/VolumeManager.cpp (revision 21258e2674226d6aa732321b6f8494841895af5f)
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 
30 const bigtime_t kRecentEventLifeTime = 100000;	// 0.1 s
31 
32 // QueryHandler
33 class VolumeManager::QueryHandler : public BHandler, public QueryListener,
34 	public BReferenceable {
35 public:
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 
104 private:
105 	NodeMonitorListener*	fListener;
106 	QueryDomain*			fQueryDomain;
107 	QueryHandle*			fHandle;
108 };
109 
110 // VolumeMap
111 struct VolumeManager::VolumeMap : HashMap<HashKey32<dev_t>, Volume*> {
112 };
113 
114 // ClientVolumeMap
115 struct VolumeManager::ClientVolumeMap
116 	: HashMap<HashKey32<int32>, ClientVolume*> {
117 };
118 
119 // private BeOS syscalls to set the FD and node monitor slot limits
120 extern "C" int _kset_fd_limit_(int num);
121 extern "C" int _kset_mon_limit_(int num);
122 
123 
124 // EntryCreatedEventMap
125 struct VolumeManager::EntryCreatedEventMap
126 	: HashMap<EntryRef, EntryCreatedEvent*> {
127 };
128 
129 // EntryRemovedEventMap
130 struct VolumeManager::EntryRemovedEventMap
131 	: HashMap<EntryRef, EntryRemovedEvent*> {
132 };
133 
134 // EntryMovedEventKey
135 struct 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
175 struct VolumeManager::EntryMovedEventMap : HashMap<EntryRef, EntryMovedEvent*> {
176 };
177 
178 // NodeStatChangedEventMap
179 struct VolumeManager::NodeStatChangedEventMap
180 	: HashMap<NodeRef, StatChangedEvent*> {
181 };
182 
183 typedef EntryRef AttributeRef;
184 
185 // NodeAttributeChangedEventMap
186 struct VolumeManager::NodeAttributeChangedEventMap
187 	: HashMap<AttributeRef, AttributeChangedEvent*> {
188 };
189 
190 
191 // #pragma mark -
192 
193 // constructor
194 VolumeManager::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
214 VolumeManager::~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
275 status_t
276 VolumeManager::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
360 Volume*
361 VolumeManager::GetRootVolume() const
362 {
363 	return fRootVolume;
364 }
365 
366 // AddClientVolume
367 status_t
368 VolumeManager::AddClientVolume(ClientVolume* clientVolume)
369 {
370 	if (!clientVolume)
371 		return B_BAD_VALUE;
372 
373 	return fClientVolumes->Put(clientVolume->GetID(), clientVolume);
374 }
375 
376 // RemoveClientVolume
377 void
378 VolumeManager::RemoveClientVolume(ClientVolume* clientVolume)
379 {
380 	if (!clientVolume)
381 		return;
382 
383 	fClientVolumes->Remove(clientVolume->GetID());
384 }
385 
386 // CreateDefault
387 status_t
388 VolumeManager::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
408 void
409 VolumeManager::DeleteDefault()
410 {
411 	if (sManager) {
412 		delete sManager;
413 		sManager = NULL;
414 	}
415 }
416 
417 // GetDefault
418 VolumeManager*
419 VolumeManager::GetDefault()
420 {
421 	return sManager;
422 }
423 
424 // Lock
425 bool
426 VolumeManager::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
441 void
442 VolumeManager::Unlock()
443 {
444 	return fLock.Unlock();
445 }
446 
447 // GetRevision
448 int64
449 VolumeManager::GetRevision() const
450 {
451 	return fRevision;
452 }
453 
454 // GetVolume
455 Volume*
456 VolumeManager::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
469 status_t
470 VolumeManager::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
485 void
486 VolumeManager::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
512 Node*
513 VolumeManager::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
521 status_t
522 VolumeManager::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
558 Directory*
559 VolumeManager::GetDirectory(dev_t volumeID, ino_t nodeID)
560 {
561 	return dynamic_cast<Directory*>(GetNode(volumeID, nodeID));
562 }
563 
564 // GetRootDirectory
565 Directory*
566 VolumeManager::GetRootDirectory() const
567 {
568 	return (fRootVolume ? fRootVolume->GetRootDirectory() : NULL);
569 }
570 
571 // GetParentDirectory
572 Directory*
573 VolumeManager::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
589 status_t
590 VolumeManager::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
632 status_t
633 VolumeManager::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
658 void
659 VolumeManager::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
678 void
679 VolumeManager::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
699 Entry*
700 VolumeManager::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
708 Entry*
709 VolumeManager::GetEntry(const entry_ref& ref)
710 {
711 	return GetEntry(ref.device, ref.directory, ref.name);
712 }
713 
714 // LoadEntry
715 status_t
716 VolumeManager::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
787 status_t
788 VolumeManager::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
868 status_t
869 VolumeManager::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
907 status_t
908 VolumeManager::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
920 status_t
921 VolumeManager::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
944 bool
945 VolumeManager::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
954 bool
955 VolumeManager::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
985 bool
986 VolumeManager::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
1012 void
1013 VolumeManager::ProcessNodeMonitoringEvent(NodeMonitoringEvent* event)
1014 {
1015 	if (fNodeMonitoringEvents.Push(event) != B_OK)
1016 		delete event;
1017 }
1018 
1019 // _AddVolume
1020 status_t
1021 VolumeManager::_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
1057 void
1058 VolumeManager::_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
1106 void
1107 VolumeManager::_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
1195 void
1196 VolumeManager::_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
1328 void
1329 VolumeManager::_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
1370 void
1371 VolumeManager::_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
1410 void
1411 VolumeManager::_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
1447 void
1448 VolumeManager::_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
1518 void
1519 VolumeManager::_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
1548 void
1549 VolumeManager::_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
1577 void
1578 VolumeManager::_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
1620 bool
1621 VolumeManager::_IsRecentEvent(NodeMonitoringEvent* event) const
1622 {
1623 	return (event && system_time() < event->time + kRecentEventLifeTime);
1624 }
1625 
1626 // _GenerateEntryCreatedEvent
1627 status_t
1628 VolumeManager::_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
1662 status_t
1663 VolumeManager::_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
1694 void
1695 VolumeManager::_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
1721 int32
1722 VolumeManager::_NodeMonitoringProcessorEntry(void* data)
1723 {
1724 	return ((VolumeManager*)data)->_NodeMonitoringProcessor();
1725 }
1726 
1727 // _NodeMonitoryProcessor
1728 int32
1729 VolumeManager::_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
1794 VolumeManager* VolumeManager::sManager = NULL;
1795