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