1 // VolumeManager.cpp 2 3 #include "VolumeManager.h" 4 5 #include <new> 6 7 #include <sys/stat.h> 8 9 #include <AutoDeleter.h> 10 #include <Entry.h> 11 #include <fs_info.h> 12 #include <fs_query.h> 13 #include <HashMap.h> 14 #include <NodeMonitor.h> 15 #include <Volume.h> 16 17 #include "ClientVolume.h" 18 #include "DebugSupport.h" 19 #include "Directory.h" 20 #include "Entry.h" 21 #include "FDManager.h" 22 #include "NodeHandle.h" 23 #include "Path.h" 24 #include "QueryDomain.h" 25 #include "Volume.h" 26 27 // TODO: We should filter recent events at some point. Otherwise we'll end up 28 // with one event of each kind for each entry/node. 29 30 const bigtime_t kRecentEventLifeTime = 100000; // 0.1 s 31 32 // QueryHandler 33 class VolumeManager::QueryHandler : public BHandler, public QueryListener, 34 public BReferenceable { 35 public: 36 QueryHandler(NodeMonitorListener* listener, QueryDomain* queryDomain, 37 QueryHandle* handle) 38 : 39 BHandler(), 40 QueryListener(), 41 BReferenceable(true), 42 fListener(listener), 43 fQueryDomain(queryDomain), 44 fHandle(handle) 45 { 46 } 47 48 QueryDomain* GetQueryDomain() const 49 { 50 return fQueryDomain; 51 } 52 53 QueryHandle* GetQueryHandle() const 54 { 55 return fHandle; 56 } 57 58 virtual void MessageReceived(BMessage* message) 59 { 60 switch (message->what) { 61 case B_QUERY_UPDATE: 62 { 63 NodeMonitoringEvent* event = NULL; 64 int32 opcode; 65 if (message->FindInt32("opcode", &opcode) == B_OK) { 66 switch (opcode) { 67 case B_ENTRY_CREATED: 68 event = new(std::nothrow) EntryCreatedEvent; 69 break; 70 case B_ENTRY_REMOVED: 71 event = new(std::nothrow) EntryRemovedEvent; 72 break; 73 case B_ENTRY_MOVED: 74 event = new(std::nothrow) EntryMovedEvent; 75 break; 76 } 77 } 78 if (event) { 79 event->queryHandler = this; 80 AddReference(); 81 if (event->Init(message) == B_OK) 82 fListener->ProcessNodeMonitoringEvent(event); 83 else 84 delete event; 85 } 86 break; 87 } 88 default: 89 BHandler::MessageReceived(message); 90 } 91 } 92 93 virtual void QueryHandleClosed(QueryHandle* handle) 94 { 95 BLooper* looper = Looper(); 96 if (looper && looper->Lock()) { 97 looper->RemoveHandler(this); 98 looper->Unlock(); 99 } 100 handle->SetQueryListener(NULL); 101 RemoveReference(); 102 } 103 104 private: 105 NodeMonitorListener* fListener; 106 QueryDomain* fQueryDomain; 107 QueryHandle* fHandle; 108 }; 109 110 // VolumeMap 111 struct VolumeManager::VolumeMap : HashMap<HashKey32<dev_t>, Volume*> { 112 }; 113 114 // ClientVolumeMap 115 struct VolumeManager::ClientVolumeMap 116 : HashMap<HashKey32<int32>, ClientVolume*> { 117 }; 118 119 // private BeOS syscalls to set the FD and node monitor slot limits 120 extern "C" int _kset_fd_limit_(int num); 121 extern "C" int _kset_mon_limit_(int num); 122 123 124 // EntryCreatedEventMap 125 struct VolumeManager::EntryCreatedEventMap 126 : HashMap<EntryRef, EntryCreatedEvent*> { 127 }; 128 129 // EntryRemovedEventMap 130 struct VolumeManager::EntryRemovedEventMap 131 : HashMap<EntryRef, EntryRemovedEvent*> { 132 }; 133 134 // EntryMovedEventKey 135 struct EntryMovedEventKey : public EntryRef { 136 EntryMovedEventKey() 137 { 138 } 139 140 EntryMovedEventKey(dev_t volumeID, ino_t fromDirectory, 141 const char* fromName, ino_t toDirectory, const char* toName) 142 : EntryRef(volumeID, fromDirectory, fromName), 143 toDirectory(toDirectory), 144 toName(toName) 145 { 146 147 } 148 149 uint32 GetHashCode() const 150 { 151 uint32 hash = EntryRef::GetHashCode(); 152 hash = 17 * hash + (uint32)(toDirectory >> 32); 153 hash = 17 * hash + (uint32)toDirectory; 154 hash = 17 * hash + string_hash(toName.GetString()); 155 return hash; 156 } 157 158 bool operator==(const EntryMovedEventKey& other) const 159 { 160 return (*(const EntryRef*)this) == other 161 && toDirectory == other.toDirectory 162 && toName == other.toName; 163 } 164 165 bool operator!=(const EntryMovedEventKey& other) const 166 { 167 return !(*this == other); 168 } 169 170 ino_t toDirectory; 171 HashString toName; 172 }; 173 174 // EntryMovedEventMap 175 struct VolumeManager::EntryMovedEventMap : HashMap<EntryRef, EntryMovedEvent*> { 176 }; 177 178 // NodeStatChangedEventMap 179 struct VolumeManager::NodeStatChangedEventMap 180 : HashMap<NodeRef, StatChangedEvent*> { 181 }; 182 183 typedef EntryRef AttributeRef; 184 185 // NodeAttributeChangedEventMap 186 struct VolumeManager::NodeAttributeChangedEventMap 187 : HashMap<AttributeRef, AttributeChangedEvent*> { 188 }; 189 190 191 // #pragma mark - 192 193 // constructor 194 VolumeManager::VolumeManager() 195 : fLock("volume manager"), 196 fVolumes(NULL), 197 fRootVolume(NULL), 198 fClientVolumes(NULL), 199 fNodeMonitor(NULL), 200 fNodeMonitoringProcessor(-1), 201 fNodeMonitoringEvents(), 202 fRecentNodeMonitoringEvents(), 203 fEntryCreatedEvents(NULL), 204 fEntryRemovedEvents(NULL), 205 fEntryMovedEvents(NULL), 206 fNodeStatChangedEvents(NULL), 207 fNodeAttributeChangedEvents(NULL), 208 fRevision(0), 209 fTerminating(false) 210 { 211 } 212 213 // destructor 214 VolumeManager::~VolumeManager() 215 { 216 // terminate the node monitor and the node monitoring processor 217 fTerminating = true; 218 if (fNodeMonitor && fNodeMonitor->Lock()) 219 fNodeMonitor->Quit(); 220 fNodeMonitoringEvents.Close(true); 221 if (fNodeMonitoringProcessor >= 0) { 222 int32 result; 223 wait_for_thread(fNodeMonitoringProcessor, &result); 224 } 225 226 // delete all events 227 // entry created events 228 for (EntryCreatedEventMap::Iterator it = fEntryCreatedEvents->GetIterator(); 229 it.HasNext();) { 230 it.Next().value->RemoveReference(); 231 } 232 delete fEntryCreatedEvents; 233 234 // entry removed events 235 for (EntryRemovedEventMap::Iterator it = fEntryRemovedEvents->GetIterator(); 236 it.HasNext();) { 237 it.Next().value->RemoveReference(); 238 } 239 delete fEntryRemovedEvents; 240 241 // entry moved events 242 for (EntryMovedEventMap::Iterator it = fEntryMovedEvents->GetIterator(); 243 it.HasNext();) { 244 it.Next().value->RemoveReference(); 245 } 246 delete fEntryMovedEvents; 247 248 // stat changed events 249 for (NodeStatChangedEventMap::Iterator it 250 = fNodeStatChangedEvents->GetIterator(); 251 it.HasNext();) { 252 it.Next().value->RemoveReference(); 253 } 254 delete fNodeStatChangedEvents; 255 256 // attribute changed events 257 for (NodeAttributeChangedEventMap::Iterator it 258 = fNodeAttributeChangedEvents->GetIterator(); 259 it.HasNext();) { 260 it.Next().value->RemoveReference(); 261 } 262 delete fNodeAttributeChangedEvents; 263 264 delete fClientVolumes; 265 266 // delete the volumes 267 for (VolumeMap::Iterator it = fVolumes->GetIterator(); it.HasNext();) { 268 Volume* volume = it.Next().value; 269 delete volume; 270 } 271 delete fVolumes; 272 } 273 274 // Init 275 status_t 276 VolumeManager::Init() 277 { 278 // check node monitoring message queue 279 status_t error = fNodeMonitoringEvents.InitCheck(); 280 if (error != B_OK) 281 return error; 282 283 // entry created event map 284 fEntryCreatedEvents = new(std::nothrow) EntryCreatedEventMap; 285 if (!fEntryCreatedEvents) 286 return B_NO_MEMORY; 287 288 // entry removed event map 289 fEntryRemovedEvents = new(std::nothrow) EntryRemovedEventMap; 290 if (!fEntryRemovedEvents) 291 return B_NO_MEMORY; 292 293 // entry moved event map 294 fEntryMovedEvents = new(std::nothrow) EntryMovedEventMap; 295 if (!fEntryMovedEvents) 296 return B_NO_MEMORY; 297 298 // node stat changed event map 299 fNodeStatChangedEvents = new(std::nothrow) NodeStatChangedEventMap; 300 if (!fNodeStatChangedEvents) 301 return B_NO_MEMORY; 302 303 // node attribute changed event map 304 fNodeAttributeChangedEvents = new(std::nothrow) NodeAttributeChangedEventMap; 305 if (!fNodeAttributeChangedEvents) 306 return B_NO_MEMORY; 307 308 // create the node monitor 309 fNodeMonitor = new(std::nothrow) NodeMonitor(this); 310 if (!fNodeMonitor) 311 return B_NO_MEMORY; 312 313 // create the volume map 314 fVolumes = new(std::nothrow) VolumeMap; 315 if (!fVolumes) 316 return B_NO_MEMORY; 317 if (fVolumes->InitCheck() != B_OK) 318 return fVolumes->InitCheck(); 319 320 // create the client volume map 321 fClientVolumes = new(std::nothrow) ClientVolumeMap; 322 if (!fClientVolumes) 323 return B_NO_MEMORY; 324 if (fClientVolumes->InitCheck() != B_OK) 325 return fClientVolumes->InitCheck(); 326 327 // start the node monitor 328 thread_id monitorThread = fNodeMonitor->Run(); 329 if (monitorThread < 0) { 330 delete fNodeMonitor; 331 fNodeMonitor = NULL; 332 return monitorThread; 333 } 334 335 // create all volumes 336 int32 cookie = 0; 337 dev_t volumeID; 338 while ((volumeID = next_dev(&cookie)) >= 0) 339 _AddVolume(volumeID); 340 341 // get the root volume 342 volumeID = dev_for_path("/"); 343 if (volumeID < 0) 344 return volumeID; 345 fRootVolume = GetVolume(volumeID, true); 346 if (!fRootVolume) 347 return B_ERROR; 348 349 // spawn the node monitoring message processor 350 fNodeMonitoringProcessor = spawn_thread(&_NodeMonitoringProcessorEntry, 351 "node monitoring processor", B_NORMAL_PRIORITY, this); 352 if (fNodeMonitoringProcessor < 0) 353 return fNodeMonitoringProcessor; 354 resume_thread(fNodeMonitoringProcessor); 355 356 return B_OK; 357 } 358 359 // GetRootVolume 360 Volume* 361 VolumeManager::GetRootVolume() const 362 { 363 return fRootVolume; 364 } 365 366 // AddClientVolume 367 status_t 368 VolumeManager::AddClientVolume(ClientVolume* clientVolume) 369 { 370 if (!clientVolume) 371 return B_BAD_VALUE; 372 373 return fClientVolumes->Put(clientVolume->GetID(), clientVolume); 374 } 375 376 // RemoveClientVolume 377 void 378 VolumeManager::RemoveClientVolume(ClientVolume* clientVolume) 379 { 380 if (!clientVolume) 381 return; 382 383 fClientVolumes->Remove(clientVolume->GetID()); 384 } 385 386 // CreateDefault 387 status_t 388 VolumeManager::CreateDefault() 389 { 390 if (sManager) 391 return B_OK; 392 393 VolumeManager* manager = new(std::nothrow) VolumeManager; 394 if (!manager) 395 return B_NO_MEMORY; 396 397 status_t error = manager->Init(); 398 if (error != B_OK) { 399 delete manager; 400 return error; 401 } 402 403 sManager = manager; 404 return B_OK; 405 } 406 407 // DeleteDefault 408 void 409 VolumeManager::DeleteDefault() 410 { 411 if (sManager) { 412 delete sManager; 413 sManager = NULL; 414 } 415 } 416 417 // GetDefault 418 VolumeManager* 419 VolumeManager::GetDefault() 420 { 421 return sManager; 422 } 423 424 // Lock 425 bool 426 VolumeManager::Lock() 427 { 428 bool alreadyLocked = fLock.IsLocked(); 429 430 bool success = fLock.Lock(); 431 432 // If locking was successful and we didn't have a lock before, we increment 433 // the revision. 434 if (success && !alreadyLocked) 435 fRevision++; 436 437 return success; 438 } 439 440 // Unlock 441 void 442 VolumeManager::Unlock() 443 { 444 return fLock.Unlock(); 445 } 446 447 // GetRevision 448 int64 449 VolumeManager::GetRevision() const 450 { 451 return fRevision; 452 } 453 454 // GetVolume 455 Volume* 456 VolumeManager::GetVolume(dev_t volumeID, bool add) 457 { 458 Volume* volume = fVolumes->Get(volumeID); 459 if (!volume && add) 460 _AddVolume(volumeID, &volume); 461 462 return volume; 463 } 464 465 466 // #pragma mark - 467 468 // AddNode 469 status_t 470 VolumeManager::AddNode(Node* node) 471 { 472 if (!node || !node->GetVolume()) 473 return B_BAD_VALUE; 474 475 status_t error = node->GetVolume()->AddNode(node); 476 477 // start watching the node 478 if (error == B_OK) 479 fNodeMonitor->StartWatching(node->GetNodeRef()); 480 481 return error; 482 } 483 484 // RemoveNode 485 void 486 VolumeManager::RemoveNode(Node* node) 487 { 488 if (!node) 489 return; 490 491 // if the node is a directory, we remove all its entries first 492 if (Directory* directory = dynamic_cast<Directory*>(node)) { 493 while (Entry* entry = directory->GetFirstEntry()) { 494 RemoveEntry(entry); 495 delete entry; 496 } 497 } 498 499 // remove all referring entries 500 while (Entry* entry = node->GetFirstReferringEntry()) 501 RemoveEntry(entry); 502 503 // remove the node from the volume 504 if (node->GetVolume()) 505 node->GetVolume()->RemoveNode(node); 506 507 // stop watching the node 508 fNodeMonitor->StopWatching(node->GetNodeRef()); 509 } 510 511 // GetNode 512 Node* 513 VolumeManager::GetNode(dev_t volumeID, ino_t nodeID) 514 { 515 if (Volume* volume = GetVolume(volumeID)) 516 return volume->GetNode(nodeID); 517 return NULL; 518 } 519 520 // LoadNode 521 status_t 522 VolumeManager::LoadNode(const struct stat& st, Node** _node) 523 { 524 Node* node = GetNode(st.st_dev, st.st_ino); 525 if (!node) { 526 // node not known yet: create it 527 528 // get the volume 529 Volume* volume = GetVolume(st.st_dev, true); 530 if (!volume) 531 return B_BAD_VALUE; 532 533 // create the node 534 if (S_ISDIR(st.st_mode)) 535 node = new(std::nothrow) Directory(volume, st); 536 else 537 node = new(std::nothrow) Node(volume, st); 538 if (!node) 539 return B_NO_MEMORY; 540 541 // add it 542 status_t error = AddNode(node); 543 if (error != B_OK) { 544 delete node; 545 return error; 546 } 547 } 548 549 if (_node) 550 *_node = node; 551 return B_OK; 552 } 553 554 555 // #pragma mark - 556 557 // GetDirectory 558 Directory* 559 VolumeManager::GetDirectory(dev_t volumeID, ino_t nodeID) 560 { 561 return dynamic_cast<Directory*>(GetNode(volumeID, nodeID)); 562 } 563 564 // GetRootDirectory 565 Directory* 566 VolumeManager::GetRootDirectory() const 567 { 568 return (fRootVolume ? fRootVolume->GetRootDirectory() : NULL); 569 } 570 571 // GetParentDirectory 572 Directory* 573 VolumeManager::GetParentDirectory(Directory* directory) 574 { 575 if (!directory) 576 return NULL; 577 578 // get ".." entry 579 Entry* parentEntry; 580 if (LoadEntry(directory->GetVolumeID(), directory->GetID(), "..", true, 581 &parentEntry) != B_OK) { 582 return NULL; 583 } 584 585 return dynamic_cast<Directory*>(parentEntry->GetNode()); 586 } 587 588 // LoadDirectory 589 status_t 590 VolumeManager::LoadDirectory(dev_t volumeID, ino_t directoryID, 591 Directory** _directory) 592 { 593 // try to get the node 594 Node* node = GetNode(volumeID, directoryID); 595 bool newNode = false; 596 if (!node) { 597 // directory not yet loaded: stat it 598 NoAllocEntryRef entryRef(volumeID, directoryID, "."); 599 struct stat st; 600 BEntry bEntry; 601 status_t error = FDManager::SetEntry(&bEntry, &entryRef); 602 if (error == B_OK) 603 error = bEntry.GetStat(&st); 604 if (error != B_OK) 605 return error; 606 607 // load the node 608 error = LoadNode(st, &node); 609 if (error != B_OK) 610 return error; 611 612 newNode = true; 613 } 614 615 // check, if the node is a directory 616 Directory* directory = dynamic_cast<Directory*>(node); 617 if (!directory) 618 return B_NOT_A_DIRECTORY; 619 620 if (newNode) 621 CompletePathToRoot(directory); 622 623 if (_directory) 624 *_directory = directory; 625 return B_OK; 626 } 627 628 629 // #pragma mark - 630 631 // AddEntry 632 status_t 633 VolumeManager::AddEntry(Entry* entry) 634 { 635 if (!entry || !entry->GetVolume() || !entry->GetDirectory() 636 || ! entry->GetNode()) { 637 return B_BAD_VALUE; 638 } 639 640 // add the entry to the volume 641 status_t error = entry->GetVolume()->AddEntry(entry); 642 if (error != B_OK) 643 return error; 644 645 // add the entry to its directory and node 646 entry->GetDirectory()->AddEntry(entry); 647 entry->GetNode()->AddReferringEntry(entry); 648 649 //PRINT(("VolumeManager::AddEntry(): %ld, %lld, `%s', dir: %p, " 650 //"entry count: %ld\n", entry->GetVolumeID(), entry->GetDirectoryID(), 651 //entry->GetName(), entry->GetDirectory(), 652 //entry->GetDirectory()->CountEntries())); 653 654 return B_OK; 655 } 656 657 // RemoveEntry 658 void 659 VolumeManager::RemoveEntry(Entry* entry) 660 { 661 if (entry) { 662 // remove the entry from the volume 663 if (entry->GetVolume()) 664 entry->GetVolume()->RemoveEntry(entry); 665 666 // remove the entry from the directory and its node 667 entry->GetDirectory()->RemoveEntry(entry); 668 entry->GetNode()->RemoveReferringEntry(entry); 669 670 //PRINT(("VolumeManager::RemoveEntry(): %ld, %lld, `%s', dir: %p, " 671 //"entry count: %ld\n", entry->GetVolumeID(), entry->GetDirectoryID(), 672 //entry->GetName(), entry->GetDirectory(), 673 //entry->GetDirectory()->CountEntries())); 674 } 675 } 676 677 // DeleteEntry 678 void 679 VolumeManager::DeleteEntry(Entry* entry, bool keepNode) 680 { 681 if (!entry) 682 return; 683 684 Node* node = entry->GetNode(); 685 686 // remove the entry 687 RemoveEntry(entry); 688 delete entry; 689 690 // remove the node, if it doesn't have any more actual referring entries 691 if (!keepNode && !node->GetActualReferringEntry()) { 692 RemoveNode(node); 693 if (node != node->GetVolume()->GetRootDirectory()) 694 delete node; 695 } 696 } 697 698 // GetEntry 699 Entry* 700 VolumeManager::GetEntry(dev_t volumeID, ino_t directoryID, const char* name) 701 { 702 if (Volume* volume = GetVolume(volumeID)) 703 return volume->GetEntry(directoryID, name); 704 return NULL; 705 } 706 707 // GetEntry 708 Entry* 709 VolumeManager::GetEntry(const entry_ref& ref) 710 { 711 return GetEntry(ref.device, ref.directory, ref.name); 712 } 713 714 // LoadEntry 715 status_t 716 VolumeManager::LoadEntry(dev_t volumeID, ino_t directoryID, const char* name, 717 bool loadDir, Entry** _entry) 718 { 719 Entry* entry = GetEntry(volumeID, directoryID, name); 720 if (!entry) { 721 // entry not known yet: create it 722 PRINT(("VolumeManager::LoadEntry(%ld, %lld, `%s')\n", volumeID, directoryID, 723 name)); 724 725 // get the volume 726 Volume* volume = GetVolume(volumeID, true); 727 if (!volume) 728 return B_BAD_VALUE; 729 730 // get the directory 731 status_t error = B_OK; 732 Directory* directory = GetDirectory(volumeID, directoryID); 733 if (!directory) { 734 if (!loadDir) 735 return B_ENTRY_NOT_FOUND; 736 737 //PRINT((" loading directory...\n")); 738 // load the directory 739 error = LoadDirectory(volumeID, directoryID, &directory); 740 if (error != B_OK) 741 return error; 742 } 743 744 //PRINT((" opening BNode...\n")); 745 // stat the entry 746 NoAllocEntryRef entryRef(volumeID, directoryID, name); 747 struct stat st; 748 BNode bNode; 749 error = bNode.SetTo(&entryRef); 750 //PRINT((" stat()ing BNode...\n")); 751 if (error == B_OK) 752 error = bNode.GetStat(&st); 753 if (error != B_OK) 754 return error; 755 756 //PRINT((" loading node...\n")); 757 // load the node 758 Node* node; 759 error = LoadNode(st, &node); 760 if (error != B_OK) 761 return error; 762 763 //PRINT((" creating and adding entry...\n")); 764 // create the entry 765 entry = new(std::nothrow) Entry(volume, directory, name, node); 766 if (!entry) 767 return B_NO_MEMORY; 768 769 // add it 770 error = AddEntry(entry); 771 if (error != B_OK) { 772 delete entry; 773 return error; 774 } 775 //PRINT((" adding entry done\n")); 776 } 777 778 if (_entry) 779 *_entry = entry; 780 return B_OK; 781 } 782 783 784 // #pragma mark - 785 786 // OpenQuery 787 status_t 788 VolumeManager::OpenQuery(QueryDomain* queryDomain, const char* queryString, 789 uint32 flags, port_id remotePort, int32 remoteToken, QueryHandle** handle) 790 { 791 if (!queryDomain || !queryString || !handle) 792 return B_BAD_VALUE; 793 bool liveQuery = (flags & B_LIVE_QUERY); 794 PRINT(("VolumeManager::OpenQuery(%p, \"%s\", 0x%lx, %ld, %ld)\n", queryDomain, queryString, flags, remotePort, remoteToken)); 795 796 // allocate the handle 797 QueryHandle* queryHandle = new(std::nothrow) QueryHandle(remotePort, 798 remoteToken); 799 if (!queryHandle) 800 return B_NO_MEMORY; 801 ObjectDeleter<QueryHandle> handleDeleter(queryHandle); 802 803 // allocate a query handler, if this is a live query 804 QueryHandler* queryHandler = NULL; 805 if (liveQuery) { 806 queryHandler = new(std::nothrow) QueryHandler(this, queryDomain, 807 queryHandle); 808 if (!queryHandler) 809 return B_NO_MEMORY; 810 811 fNodeMonitor->Lock(); 812 fNodeMonitor->AddHandler(queryHandler); 813 fNodeMonitor->Unlock(); 814 queryHandle->SetQueryListener(queryHandler); 815 } 816 817 // iterate through the volumes and create a query for each one 818 // supporting queries 819 for (VolumeMap::Iterator it = fVolumes->GetIterator(); it.HasNext();) { 820 Volume* volume = it.Next().value; 821 if (!volume->KnowsQuery()) 822 continue; 823 824 // The volume should either be contained by the client volume or 825 // the other way around. Otherwise they are located in different 826 // branches of the FS tree and don't have common nodes. 827 if (!queryDomain->QueryDomainIntersectsWith(volume)) 828 continue; 829 PRINT(("VolumeManager::OpenQuery(): adding Query for volume %ld\n", volume->GetID())); 830 831 // create the query for this volume 832 BVolume bVolume(volume->GetID()); 833 Query* query = new(std::nothrow) Query; 834 if (!query) 835 return B_NO_MEMORY; 836 837 // init the query 838 ObjectDeleter<Query> queryDeleter(query); 839 status_t error = query->SetVolume(&bVolume); 840 if (error != B_OK) 841 return error; 842 error = query->SetPredicate(queryString); 843 if (error != B_OK) 844 return error; 845 if (liveQuery) { 846 error = query->SetTarget(queryHandler); 847 if (error != B_OK) 848 return error; 849 } 850 851 // fetch 852 error = query->Fetch(); 853 if (error != B_OK) 854 return error; 855 856 queryHandle->AddQuery(query); 857 queryDeleter.Detach(); 858 } 859 860 *handle = queryHandle; 861 handleDeleter.Detach(); 862 return B_OK; 863 } 864 865 // CompletePathToRoot 866 status_t 867 VolumeManager::CompletePathToRoot(Directory* directory) 868 { 869 if (!directory) 870 return B_BAD_VALUE; 871 872 while (directory != GetRootDirectory()) { 873 // if the dir has a valid entry referring to it, we've nothing to do 874 if (directory->GetActualReferringEntry()) 875 return B_OK; 876 877 // get a proper entry_ref 878 BEntry bEntry; 879 entry_ref entryRef(directory->GetVolumeID(), directory->GetID(), "."); 880 status_t error = FDManager::SetEntry(&bEntry, &entryRef); 881 if (error == B_OK) 882 error = bEntry.GetRef(&entryRef); 883 if (error != B_OK) 884 return error; 885 886 // if the entry is already loaded, we're done 887 if (GetEntry(entryRef)) 888 return B_OK; 889 890 // the entry is not yet known -- load it 891 Entry* entry; 892 error = LoadEntry(entryRef.device, entryRef.directory, entryRef.name, 893 true, &entry); 894 if (error != B_OK) 895 return error; 896 897 // get the entry's parent dir and enter the next round 898 directory = entry->GetDirectory(); 899 } 900 901 return B_OK; 902 } 903 904 // GetPath 905 status_t 906 VolumeManager::GetPath(Entry* entry, Path* path) 907 { 908 // get directory path 909 status_t error = GetPath(entry->GetDirectory(), path); 910 if (error != B_OK) 911 return error; 912 913 // append the entry name 914 return path->Append(entry->GetName()); 915 } 916 917 // GetPath 918 status_t 919 VolumeManager::GetPath(Node* node, Path* path) 920 { 921 if (node == GetRootDirectory()) 922 return path->SetTo("/"); 923 924 // get an entry referring to the node 925 Entry* entry = node->GetActualReferringEntry(); 926 if (!entry) { 927 // if the node is a directory, we complete the path to the root and 928 // try again 929 if (Directory* directory = dynamic_cast<Directory*>(node)) { 930 CompletePathToRoot(directory); 931 entry = node->GetActualReferringEntry(); 932 } 933 934 if (!entry) 935 return B_ERROR; 936 } 937 938 return GetPath(entry, path); 939 } 940 941 // DirectoryContains 942 bool 943 VolumeManager::DirectoryContains(Directory* directory, Entry* entry) 944 { 945 if (!directory || !entry) 946 return false; 947 948 return DirectoryContains(directory, entry->GetDirectory(), true); 949 } 950 951 // DirectoryContains 952 bool 953 VolumeManager::DirectoryContains(Directory* directory, Directory* descendant, 954 bool reflexive) 955 { 956 if (!directory || !descendant) 957 return false; 958 959 // a directory contains itself, just as defined by the caller 960 if (directory == descendant) 961 return reflexive; 962 963 // if the directory is the root directory, it contains everything 964 Directory* rootDir = GetRootDirectory(); 965 if (directory == rootDir) 966 return true; 967 968 // recursively get the descendant's parent dir until reaching the root dir 969 // or the given dir 970 while (descendant != rootDir) { 971 descendant = GetParentDirectory(descendant); 972 if (!descendant) 973 return false; 974 975 if (descendant == directory) 976 return true; 977 } 978 979 return false; 980 } 981 982 // DirectoryContains 983 bool 984 VolumeManager::DirectoryContains(Directory* directory, Node* descendant, 985 bool reflexive) 986 { 987 if (!directory || !descendant) 988 return false; 989 990 // if the node is a directory, let the other version do the job 991 if (Directory* dir = dynamic_cast<Directory*>(descendant)) 992 return DirectoryContains(directory, dir, reflexive); 993 994 // iterate through the referring entries and check, if the directory 995 // contains any of them 996 for (Entry* entry = descendant->GetFirstReferringEntry(); 997 entry; 998 entry = descendant->GetNextReferringEntry(entry)) { 999 if (DirectoryContains(directory, entry)) 1000 return true; 1001 } 1002 1003 return false; 1004 } 1005 1006 1007 // #pragma mark - 1008 1009 // ProcessNodeMonitoringEvent 1010 void 1011 VolumeManager::ProcessNodeMonitoringEvent(NodeMonitoringEvent* event) 1012 { 1013 if (fNodeMonitoringEvents.Push(event) != B_OK) 1014 delete event; 1015 } 1016 1017 // _AddVolume 1018 status_t 1019 VolumeManager::_AddVolume(dev_t volumeID, Volume** _volume) 1020 { 1021 if (GetVolume(volumeID)) 1022 return B_OK; 1023 1024 // create the volume 1025 Volume* volume = new(std::nothrow) Volume(volumeID); 1026 if (!volume) 1027 RETURN_ERROR(B_NO_MEMORY); 1028 ObjectDeleter<Volume> volumeDeleter(volume); 1029 status_t error = volume->Init(); 1030 if (error != B_OK) 1031 RETURN_ERROR(error); 1032 1033 // add it 1034 error = fVolumes->Put(volumeID, volume); 1035 if (error != B_OK) 1036 RETURN_ERROR(error); 1037 1038 // add the root node 1039 error = AddNode(volume->GetRootDirectory()); 1040 if (error != B_OK) { 1041 fVolumes->Remove(volumeID); 1042 RETURN_ERROR(error); 1043 } 1044 1045 // complete the root dir path 1046 CompletePathToRoot(volume->GetRootDirectory()); 1047 1048 volumeDeleter.Detach(); 1049 if (_volume) 1050 *_volume = volume; 1051 return B_OK; 1052 } 1053 1054 // _EntryCreated 1055 void 1056 VolumeManager::_EntryCreated(EntryCreatedEvent* event) 1057 { 1058 // get the directory 1059 Directory* directory = GetDirectory(event->volumeID, event->directoryID); 1060 if (!directory) 1061 return; 1062 1063 // check, if there is an earlier similar event 1064 bool notify = true; 1065 NoAllocEntryRef ref(event->volumeID, event->directoryID, 1066 event->name.GetString()); 1067 EntryCreatedEvent* oldEvent = fEntryCreatedEvents->Get(ref); 1068 1069 // remove the old event 1070 if (oldEvent) { 1071 fEntryCreatedEvents->Remove(ref); 1072 fRecentNodeMonitoringEvents.Remove(oldEvent); 1073 notify = !_IsRecentEvent(oldEvent); 1074 oldEvent->RemoveReference(); 1075 } 1076 1077 // add the new event 1078 if (fEntryCreatedEvents->Put(ref, event) == B_OK) { 1079 fRecentNodeMonitoringEvents.Insert(event); 1080 event->AddReference(); 1081 } 1082 1083 // if the directory is complete or at least has iterators attached to it, 1084 // we load the entry 1085 if (directory->IsComplete() || directory->HasDirIterators()) { 1086 Entry* entry; 1087 LoadEntry(ref.device, ref.directory, ref.name, false, &entry); 1088 } 1089 1090 // send notifications 1091 if (notify) { 1092 for (ClientVolumeMap::Iterator it = fClientVolumes->GetIterator(); 1093 it.HasNext();) { 1094 ClientVolume* clientVolume = it.Next().value; 1095 if (DirectoryContains(clientVolume->GetRootDirectory(), directory, 1096 true)) { 1097 clientVolume->ProcessNodeMonitoringEvent(event); 1098 } 1099 } 1100 } 1101 } 1102 1103 // _EntryRemoved 1104 void 1105 VolumeManager::_EntryRemoved(EntryRemovedEvent* event, bool keepNode) 1106 { 1107 // get node and directory 1108 Node* node = GetNode(event->nodeVolumeID, event->nodeID); 1109 Directory* directory = GetDirectory(event->volumeID, event->directoryID); 1110 if (!directory) 1111 return; 1112 1113 // find the entry 1114 Entry* entry = NULL; 1115 if (node) { 1116 if (event->name.GetLength() == 0) { 1117 for (entry = node->GetFirstReferringEntry(); 1118 entry; 1119 entry = node->GetNextReferringEntry(entry)) { 1120 if (!entry->Exists()) { 1121 event->name.SetTo(entry->GetName()); 1122 break; 1123 } 1124 } 1125 } else { 1126 entry = GetEntry(directory->GetVolumeID(), directory->GetID(), 1127 event->name.GetString()); 1128 } 1129 } 1130 1131 // check, if there is an earlier similar event 1132 bool notify = true; 1133 NoAllocEntryRef ref(event->volumeID, event->directoryID, 1134 event->name.GetString()); 1135 EntryRemovedEvent* oldEvent = fEntryRemovedEvents->Get(ref); 1136 // TODO: Under BeOS R5 the entry name is not encoded in the 1137 // "entry removed" node monitoring message. If we have seen the entry 1138 // before, we can get the entry nevertheless (see above). Usually we 1139 // get 2 "entry removed" events: One for watching the directory and one 1140 // for watching the node. After the first one has been processed, we've 1141 // forgotten everything about the entry and we won't be able to find out 1142 // the entry's name for the second one. Hence we will never find the 1143 // previous event in the fEntryRemovedEvents map. We should probably 1144 // fall back to using a NodeRef as key under BeOS R5. 1145 1146 // remove the old event 1147 if (oldEvent) { 1148 fEntryRemovedEvents->Remove(ref); 1149 fRecentNodeMonitoringEvents.Remove(oldEvent); 1150 notify = !_IsRecentEvent(oldEvent); 1151 oldEvent->RemoveReference(); 1152 } 1153 1154 // add the new event 1155 if (fEntryRemovedEvents->Put(ref, event) == B_OK) { 1156 fRecentNodeMonitoringEvents.Insert(event); 1157 event->AddReference(); 1158 } 1159 1160 // remove the entry 1161 if (entry) { 1162 RemoveEntry(entry); 1163 delete entry; 1164 } 1165 1166 // remove the node, if it doesn't have any more actual referring entries 1167 if (node && !keepNode && !node->GetActualReferringEntry()) { 1168 RemoveNode(node); 1169 if (node != node->GetVolume()->GetRootDirectory()) 1170 delete node; 1171 } 1172 1173 // send notifications 1174 if (notify) { 1175 NodeRef nodeRef(event->nodeVolumeID, event->nodeID); 1176 for (ClientVolumeMap::Iterator it = fClientVolumes->GetIterator(); 1177 it.HasNext();) { 1178 // We send a notification, if the client volume contains the entry, 1179 // but also, if the removed entry refers to the client volume's 1180 // root. The client connection has a special handling for this 1181 // case. 1182 ClientVolume* clientVolume = it.Next().value; 1183 Directory* rootDir = clientVolume->GetRootDirectory(); 1184 if (DirectoryContains(rootDir, directory, true) 1185 || clientVolume->GetRootNodeRef() == nodeRef) { 1186 clientVolume->ProcessNodeMonitoringEvent(event); 1187 } 1188 } 1189 } 1190 } 1191 1192 // _EntryMoved 1193 void 1194 VolumeManager::_EntryMoved(EntryMovedEvent* event) 1195 { 1196 _CheckVolumeRootMoved(event); 1197 1198 Directory* fromDirectory 1199 = GetDirectory(event->volumeID, event->fromDirectoryID); 1200 Directory* toDirectory 1201 = GetDirectory(event->volumeID, event->toDirectoryID); 1202 Node* node = GetNode(event->nodeVolumeID, event->nodeID); 1203 1204 // we should at least have one of the directories 1205 if (!fromDirectory && !toDirectory) 1206 return; 1207 1208 // find the old entry 1209 Entry* oldEntry = NULL; 1210 if (node) { 1211 if (event->fromName.GetLength() == 0) { 1212 for (oldEntry = node->GetFirstReferringEntry(); 1213 oldEntry; 1214 oldEntry = node->GetNextReferringEntry(oldEntry)) { 1215 if (!oldEntry->Exists()) { 1216 event->fromName.SetTo(oldEntry->GetName()); 1217 break; 1218 } 1219 } 1220 } else { 1221 oldEntry = GetEntry(event->volumeID, event->fromDirectoryID, 1222 event->fromName.GetString()); 1223 } 1224 } 1225 1226 // check, if there is an earlier similar event 1227 bool notify = true; 1228 if (event->fromName.GetLength() > 0) { 1229 EntryMovedEventKey key(event->volumeID, event->fromDirectoryID, 1230 event->fromName.GetString(), event->toDirectoryID, 1231 event->toName.GetString()); 1232 EntryMovedEvent* oldEvent = fEntryMovedEvents->Get(key); 1233 1234 // remove the old event 1235 if (oldEvent) { 1236 fEntryMovedEvents->Remove(key); 1237 fRecentNodeMonitoringEvents.Remove(oldEvent); 1238 notify = !_IsRecentEvent(oldEvent); 1239 oldEvent->RemoveReference(); 1240 } 1241 1242 // add the new event 1243 if (fEntryMovedEvents->Put(key, event) == B_OK) { 1244 fRecentNodeMonitoringEvents.Insert(event); 1245 event->AddReference(); 1246 } 1247 } 1248 1249 // remove the old entry 1250 if (oldEntry) { 1251 RemoveEntry(oldEntry); 1252 delete oldEntry; 1253 } 1254 1255 // If the to directory is complete or at least has iterators attached to it, 1256 // we load the new entry. We also load it, if the node is the root of a 1257 // volume. 1258 if (toDirectory 1259 && (toDirectory->IsComplete() || toDirectory->HasDirIterators() 1260 || (node && node == node->GetVolume()->GetRootDirectory()))) { 1261 Entry* newEntry; 1262 LoadEntry(event->volumeID, event->toDirectoryID, 1263 event->toName.GetString(), false, &newEntry); 1264 } 1265 1266 // remove the node, if it doesn't have any more actual referring entries 1267 if (node && !node->GetActualReferringEntry()) { 1268 RemoveNode(node); 1269 if (node != node->GetVolume()->GetRootDirectory()) 1270 delete node; 1271 } 1272 1273 // send notifications 1274 if (notify) { 1275 for (ClientVolumeMap::Iterator it = fClientVolumes->GetIterator(); 1276 it.HasNext();) { 1277 ClientVolume* clientVolume = it.Next().value; 1278 1279 // check, if it contains the from/to directories 1280 Directory* rootDir = clientVolume->GetRootDirectory(); 1281 bool containsFrom = DirectoryContains(rootDir, fromDirectory, true); 1282 bool containsTo = DirectoryContains(rootDir, toDirectory, true); 1283 1284 if (containsFrom) { 1285 if (containsTo) { 1286 // contains source and target dir 1287 clientVolume->ProcessNodeMonitoringEvent(event); 1288 } else { 1289 // contains only the source dir: generate an "entry removed" 1290 // event 1291 EntryRemovedEvent *removedEvent 1292 = new(std::nothrow) EntryRemovedEvent; 1293 if (!removedEvent) 1294 continue; 1295 removedEvent->opcode = B_ENTRY_REMOVED; 1296 removedEvent->time = event->time; 1297 removedEvent->volumeID = event->volumeID; 1298 removedEvent->directoryID = event->fromDirectoryID; 1299 removedEvent->nodeVolumeID = event->nodeVolumeID; 1300 removedEvent->nodeID = event->nodeID; 1301 if (event->fromName.GetLength() > 0) 1302 removedEvent->name = event->fromName; 1303 clientVolume->ProcessNodeMonitoringEvent(removedEvent); 1304 removedEvent->RemoveReference(); 1305 } 1306 } else if (containsTo) { 1307 // contains only the target directory: generate an 1308 // "entry created" event 1309 EntryCreatedEvent *createdEvent 1310 = new(std::nothrow) EntryCreatedEvent; 1311 if (!createdEvent) 1312 continue; 1313 createdEvent->opcode = B_ENTRY_CREATED; 1314 createdEvent->time = event->time; 1315 createdEvent->volumeID = event->volumeID; 1316 createdEvent->directoryID = event->toDirectoryID; 1317 createdEvent->name = event->toName; 1318 clientVolume->ProcessNodeMonitoringEvent(createdEvent); 1319 createdEvent->RemoveReference(); 1320 } 1321 } 1322 } 1323 } 1324 1325 // _NodeStatChanged 1326 void 1327 VolumeManager::_NodeStatChanged(StatChangedEvent* event) 1328 { 1329 // get the node 1330 Node* node = GetNode(event->volumeID, event->nodeID); 1331 if (!node) 1332 return; 1333 1334 // check, if there is an earlier similar event 1335 bool notify = true; 1336 NodeRef ref(event->volumeID, event->nodeID); 1337 StatChangedEvent* oldEvent = fNodeStatChangedEvents->Get(ref); 1338 1339 // remove the old event 1340 if (oldEvent) { 1341 fNodeStatChangedEvents->Remove(ref); 1342 fRecentNodeMonitoringEvents.Remove(oldEvent); 1343 notify = !_IsRecentEvent(oldEvent); 1344 oldEvent->RemoveReference(); 1345 } 1346 1347 // add the new event 1348 if (fNodeStatChangedEvents->Put(ref, event) == B_OK) { 1349 fRecentNodeMonitoringEvents.Insert(event); 1350 event->AddReference(); 1351 } 1352 1353 if (notify) { 1354 // update the cached node stat 1355 node->UpdateStat(); 1356 1357 // send notifications 1358 for (ClientVolumeMap::Iterator it = fClientVolumes->GetIterator(); 1359 it.HasNext();) { 1360 ClientVolume* clientVolume = it.Next().value; 1361 if (DirectoryContains(clientVolume->GetRootDirectory(), node, true)) 1362 clientVolume->ProcessNodeMonitoringEvent(event); 1363 } 1364 } 1365 } 1366 1367 // _NodeAttributeChanged 1368 void 1369 VolumeManager::_NodeAttributeChanged(AttributeChangedEvent* event) 1370 { 1371 // get the node 1372 Node* node = GetNode(event->volumeID, event->nodeID); 1373 if (!node) 1374 return; 1375 1376 // check, if there is an earlier similar event 1377 bool notify = true; 1378 AttributeRef ref(event->volumeID, event->nodeID, 1379 event->attribute.GetString()); 1380 AttributeChangedEvent* oldEvent = fNodeAttributeChangedEvents->Get(ref); 1381 1382 // remove the old event 1383 if (oldEvent) { 1384 fNodeAttributeChangedEvents->Remove(ref); 1385 fRecentNodeMonitoringEvents.Remove(oldEvent); 1386 notify = !_IsRecentEvent(oldEvent); 1387 oldEvent->RemoveReference(); 1388 } 1389 1390 // add the new event 1391 if (fNodeAttributeChangedEvents->Put(ref, event) == B_OK) { 1392 fRecentNodeMonitoringEvents.Insert(event); 1393 event->AddReference(); 1394 } 1395 1396 // send notifications 1397 if (notify) { 1398 for (ClientVolumeMap::Iterator it = fClientVolumes->GetIterator(); 1399 it.HasNext();) { 1400 ClientVolume* clientVolume = it.Next().value; 1401 if (DirectoryContains(clientVolume->GetRootDirectory(), node, true)) 1402 clientVolume->ProcessNodeMonitoringEvent(event); 1403 } 1404 } 1405 } 1406 1407 // _VolumeMounted 1408 void 1409 VolumeManager::_VolumeMounted(VolumeMountedEvent* event) 1410 { 1411 entry_ref rootRef; 1412 bool rootRefInitialized = false; 1413 1414 // remove the entry referring to the covered directory 1415 Directory* coveredDirectory = GetDirectory(event->volumeID, 1416 event->directoryID); 1417 if (coveredDirectory) { 1418 if (Entry* entry = coveredDirectory->GetActualReferringEntry()) { 1419 // get an entry for later 1420 rootRef = entry->GetEntryRef(); 1421 rootRefInitialized = true; 1422 1423 // send the "entry removed" event 1424 EntryRemovedEvent* event; 1425 if (_GenerateEntryRemovedEvent(entry, system_time(), 1426 &event) == B_OK) { 1427 _EntryRemoved(event, true); 1428 event->RemoveReference(); 1429 } else { 1430 RemoveEntry(entry); 1431 delete entry; 1432 } 1433 } 1434 } 1435 1436 // add the volume 1437 _AddVolume(event->newVolumeID); 1438 1439 // generate an "entry created" event for the root dir entry 1440 if (rootRefInitialized) 1441 _GenerateEntryCreatedEvent(rootRef, event->time); 1442 } 1443 1444 // _VolumeUnmounted 1445 void 1446 VolumeManager::_VolumeUnmounted(VolumeUnmountedEvent* event) 1447 { 1448 // get the volume 1449 Volume* volume = GetVolume(event->volumeID); 1450 if (!volume) 1451 return; 1452 1453 entry_ref rootRef; 1454 bool rootRefInitialized = false; 1455 1456 // remove all actual entries referring to the root directory (should only 1457 // be one) 1458 if (Directory* rootDir = volume->GetRootDirectory()) { 1459 // get an entry ref for the root dir 1460 if (Entry* entry = rootDir->GetActualReferringEntry()) { 1461 rootRef = entry->GetEntryRef(); 1462 rootRefInitialized = true; 1463 } 1464 1465 Entry* entry = rootDir->GetFirstReferringEntry(); 1466 while (entry) { 1467 Entry* nextEntry = rootDir->GetNextReferringEntry(entry); 1468 1469 if (entry->IsActualEntry()) { 1470 EntryRemovedEvent* removedEvent; 1471 if (_GenerateEntryRemovedEvent(entry, event->time, 1472 &removedEvent) == B_OK) { 1473 _EntryRemoved(removedEvent, true); 1474 removedEvent->RemoveReference(); 1475 } else { 1476 RemoveEntry(entry); 1477 delete entry; 1478 } 1479 } 1480 1481 entry = nextEntry; 1482 } 1483 } 1484 1485 // remove all entries of the volume 1486 while (Entry* entry = volume->GetFirstEntry()) { 1487 bool remove = true; 1488 if (entry->IsActualEntry()) { 1489 if (_GenerateEntryRemovedEvent(entry, event->time) != B_OK) 1490 remove = false; 1491 } 1492 1493 if (remove) { 1494 RemoveEntry(entry); 1495 delete entry; 1496 } 1497 } 1498 1499 // remove all nodes 1500 while (Node* node = volume->GetFirstNode()) { 1501 RemoveNode(node); 1502 if (node != volume->GetRootDirectory()) 1503 delete node; 1504 } 1505 1506 // remove the volume 1507 fVolumes->Remove(volume->GetID()); 1508 delete volume; 1509 1510 // generate an "entry created" event for the covered node 1511 if (rootRefInitialized) 1512 _GenerateEntryCreatedEvent(rootRef, event->time); 1513 } 1514 1515 // _QueryEntryCreated 1516 void 1517 VolumeManager::_QueryEntryCreated(EntryCreatedEvent* event) 1518 { 1519 // get the query handler 1520 QueryHandler* queryHandler 1521 = dynamic_cast<QueryHandler*>(event->queryHandler); 1522 if (!queryHandler) 1523 return; 1524 1525 // load the entry (just to make sure that it really exists) 1526 Entry* entry = NULL; 1527 status_t error = LoadEntry(event->volumeID, event->directoryID, 1528 event->name.GetString(), true, &entry); 1529 if (error != B_OK) 1530 return; 1531 1532 // get remote port and token 1533 if (!queryHandler->LockLooper()) 1534 return; 1535 1536 QueryHandle* queryHandle = queryHandler->GetQueryHandle(); 1537 event->remotePort = queryHandle->GetRemotePort(); 1538 event->remoteToken = queryHandle->GetRemoteToken(); 1539 queryHandler->UnlockLooper(); 1540 1541 // send a notification to the client volume 1542 queryHandler->GetQueryDomain()->ProcessQueryEvent(event); 1543 } 1544 1545 // _QueryEntryRemoved 1546 void 1547 VolumeManager::_QueryEntryRemoved(EntryRemovedEvent* event) 1548 { 1549 // get the query handler 1550 QueryHandler* queryHandler 1551 = dynamic_cast<QueryHandler*>(event->queryHandler); 1552 if (!queryHandler) 1553 return; 1554 1555 // load the directory (just to make sure that it really exists) 1556 Directory* directory = NULL; 1557 status_t error = LoadDirectory(event->volumeID, event->directoryID, 1558 &directory); 1559 if (error != B_OK) 1560 return; 1561 1562 // get remote port and token 1563 if (!queryHandler->LockLooper()) 1564 return; 1565 QueryHandle* queryHandle = queryHandler->GetQueryHandle(); 1566 event->remotePort = queryHandle->GetRemotePort(); 1567 event->remoteToken = queryHandle->GetRemoteToken(); 1568 queryHandler->UnlockLooper(); 1569 1570 // send a notification to the client volume 1571 queryHandler->GetQueryDomain()->ProcessQueryEvent(event); 1572 } 1573 1574 // _QueryEntryMoved 1575 void 1576 VolumeManager::_QueryEntryMoved(EntryMovedEvent* event) 1577 { 1578 // we simply split the event into a `removed' and a `created' event 1579 1580 // allocate the events 1581 EntryRemovedEvent* removedEvent = new(std::nothrow) EntryRemovedEvent; 1582 EntryCreatedEvent* createdEvent = new(std::nothrow) EntryCreatedEvent; 1583 if (!removedEvent || !createdEvent) { 1584 delete removedEvent; 1585 delete createdEvent; 1586 return; 1587 } 1588 1589 // init the removed event 1590 removedEvent->opcode = B_ENTRY_REMOVED; 1591 removedEvent->time = event->time; 1592 removedEvent->queryHandler = event->queryHandler; 1593 removedEvent->queryHandler->AddReference(); 1594 removedEvent->volumeID = event->volumeID; 1595 removedEvent->directoryID = event->fromDirectoryID; 1596 removedEvent->nodeVolumeID = event->volumeID; 1597 removedEvent->nodeID = event->nodeID; 1598 removedEvent->name = event->fromName; 1599 1600 // init the created event 1601 createdEvent->opcode = B_ENTRY_CREATED; 1602 createdEvent->time = event->time; 1603 createdEvent->queryHandler = event->queryHandler; 1604 createdEvent->queryHandler->AddReference(); 1605 createdEvent->volumeID = event->volumeID; 1606 createdEvent->directoryID = event->toDirectoryID; 1607 createdEvent->nodeID = event->nodeID; 1608 createdEvent->name = event->toName; 1609 1610 // send them 1611 _QueryEntryRemoved(removedEvent); 1612 removedEvent->RemoveReference(); 1613 _QueryEntryCreated(createdEvent); 1614 createdEvent->RemoveReference(); 1615 } 1616 1617 // _IsRecentEvent 1618 bool 1619 VolumeManager::_IsRecentEvent(NodeMonitoringEvent* event) const 1620 { 1621 return (event && system_time() < event->time + kRecentEventLifeTime); 1622 } 1623 1624 // _GenerateEntryCreatedEvent 1625 status_t 1626 VolumeManager::_GenerateEntryCreatedEvent(const entry_ref& ref, bigtime_t time, 1627 EntryCreatedEvent** _event) 1628 { 1629 // load the entry 1630 Entry* entry; 1631 status_t error = LoadEntry(ref.device, ref.directory, ref.name, true, 1632 &entry); 1633 if (error != B_OK) 1634 return error; 1635 1636 // create the event 1637 EntryCreatedEvent* event = new(std::nothrow) EntryCreatedEvent; 1638 if (!event) 1639 return B_NO_MEMORY; 1640 1641 // fill in the fields 1642 event->opcode = B_ENTRY_CREATED; 1643 event->time = time; 1644 event->volumeID = entry->GetVolumeID(); 1645 event->directoryID = entry->GetDirectoryID(); 1646 event->nodeID = entry->GetNode()->GetID(); 1647 event->name.SetTo(entry->GetName()); 1648 1649 if (_event) { 1650 *_event = event; 1651 } else { 1652 _EntryCreated(event); 1653 event->RemoveReference(); 1654 } 1655 1656 return B_OK; 1657 } 1658 1659 // _GenerateEntryRemovedEvent 1660 status_t 1661 VolumeManager::_GenerateEntryRemovedEvent(Entry* entry, bigtime_t time, 1662 EntryRemovedEvent** _event) 1663 { 1664 if (!entry) 1665 return B_BAD_VALUE; 1666 1667 // create the event 1668 EntryRemovedEvent* event = new(std::nothrow) EntryRemovedEvent; 1669 if (!event) 1670 return B_NO_MEMORY; 1671 1672 // fill in the fields 1673 event->opcode = B_ENTRY_REMOVED; 1674 event->time = time; 1675 event->volumeID = entry->GetVolumeID(); 1676 event->directoryID = entry->GetDirectoryID(); 1677 event->nodeVolumeID = entry->GetNode()->GetVolumeID(); 1678 event->nodeID = entry->GetNode()->GetID(); 1679 event->name.SetTo(entry->GetName()); 1680 1681 if (_event) { 1682 *_event = event; 1683 } else { 1684 _EntryRemoved(event, false); 1685 event->RemoveReference(); 1686 } 1687 1688 return B_OK; 1689 } 1690 1691 // _CheckVolumeRootMoved 1692 void 1693 VolumeManager::_CheckVolumeRootMoved(EntryMovedEvent* event) 1694 { 1695 // If a volume root is moved, the sent node monitoring message does 1696 // unforunately contain the node_ref of the covered node, not that of the 1697 // volume root -- a BeOS R5 VFS bug. Since we have the entry_ref of the 1698 // new entry, we can stat the node. 1699 1700 // check whether the node is the root of a volume 1701 NoAllocEntryRef ref(event->volumeID, event->toDirectoryID, 1702 event->toName.GetString()); 1703 BEntry entry; 1704 struct stat st; 1705 if (FDManager::SetEntry(&entry, &ref) == B_OK 1706 && entry.GetStat(&st) == B_OK) { 1707 event->nodeVolumeID = st.st_dev; 1708 event->nodeID = st.st_ino; 1709 if (Volume* volume = GetVolume(st.st_dev)) { 1710 if (volume->GetRootID() == st.st_ino) { 1711 PRINT(("Mount point for volume %ld renamed\n", 1712 volume->GetID())); 1713 } 1714 } 1715 } 1716 } 1717 1718 // _NodeMonitoringProcessorEntry 1719 int32 1720 VolumeManager::_NodeMonitoringProcessorEntry(void* data) 1721 { 1722 return ((VolumeManager*)data)->_NodeMonitoringProcessor(); 1723 } 1724 1725 // _NodeMonitoryProcessor 1726 int32 1727 VolumeManager::_NodeMonitoringProcessor() 1728 { 1729 do { 1730 NodeMonitoringEvent* event = NULL; 1731 status_t error = fNodeMonitoringEvents.Pop(&event); 1732 1733 VolumeManagerLocker managerLocker; 1734 1735 while (error == B_OK) { 1736 if (event->queryHandler) { 1737 switch (event->opcode) { 1738 case B_ENTRY_CREATED: 1739 _QueryEntryCreated( 1740 dynamic_cast<EntryCreatedEvent*>(event)); 1741 break; 1742 case B_ENTRY_REMOVED: 1743 _QueryEntryRemoved( 1744 dynamic_cast<EntryRemovedEvent*>(event)); 1745 break; 1746 case B_ENTRY_MOVED: 1747 _QueryEntryMoved(dynamic_cast<EntryMovedEvent*>(event)); 1748 break; 1749 } 1750 } else { 1751 switch (event->opcode) { 1752 case B_ENTRY_CREATED: 1753 _EntryCreated(dynamic_cast<EntryCreatedEvent*>(event)); 1754 break; 1755 case B_ENTRY_REMOVED: 1756 _EntryRemoved(dynamic_cast<EntryRemovedEvent*>(event), 1757 false); 1758 break; 1759 case B_ENTRY_MOVED: 1760 _EntryMoved(dynamic_cast<EntryMovedEvent*>(event)); 1761 break; 1762 case B_STAT_CHANGED: 1763 _NodeStatChanged( 1764 dynamic_cast<StatChangedEvent*>(event)); 1765 break; 1766 case B_ATTR_CHANGED: 1767 _NodeAttributeChanged( 1768 dynamic_cast<AttributeChangedEvent*>(event)); 1769 break; 1770 case B_DEVICE_MOUNTED: 1771 _VolumeMounted(dynamic_cast<VolumeMountedEvent*>(event)); 1772 break; 1773 case B_DEVICE_UNMOUNTED: 1774 _VolumeUnmounted( 1775 dynamic_cast<VolumeUnmountedEvent*>(event)); 1776 break; 1777 } 1778 } 1779 event->RemoveReference(); 1780 1781 // If there is another event available, get it as long as we 1782 // have the VolumeManager lock. 1783 error = fNodeMonitoringEvents.Pop(&event, 0); 1784 } 1785 } while (!fTerminating); 1786 1787 return 0; 1788 } 1789 1790 1791 // sManager 1792 VolumeManager* VolumeManager::sManager = NULL; 1793