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