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