1 // VirtualVolume.cpp 2 3 #include <new> 4 #include <stdio.h> 5 #include <string.h> 6 7 #include <AutoLocker.h> 8 9 #include "Compatibility.h" 10 #include "DebugSupport.h" 11 #include "QueryIterator.h" 12 #include "QueryManager.h" 13 #include "VirtualDir.h" 14 #include "VirtualVolume.h" 15 #include "VolumeManager.h" 16 #include "VolumeSupport.h" 17 18 // constructor 19 VirtualVolume::VirtualVolume(VolumeManager* volumeManager) 20 : Volume(volumeManager), 21 fRootNode(NULL) 22 { 23 } 24 25 // destructor 26 VirtualVolume::~VirtualVolume() 27 { 28 delete fRootNode; 29 } 30 31 // Init 32 status_t 33 VirtualVolume::Init(const char* name) 34 { 35 status_t error = Volume::Init(name); 36 if (error != B_OK) 37 return error; 38 39 // get an ID for the root node 40 vnode_id rootNodeID = fVolumeManager->NewNodeID(this); 41 if (rootNodeID < 0) { 42 Uninit(); 43 return rootNodeID; 44 } 45 46 // create the root node 47 fRootNode = new(std::nothrow) VirtualDir(this, rootNodeID); 48 if (!fRootNode) { 49 Uninit(); 50 fVolumeManager->RemoveNodeID(rootNodeID); 51 return B_NO_MEMORY; 52 } 53 error = fRootNode->InitCheck(); 54 if (error != B_OK) { 55 Uninit(); 56 return error; 57 } 58 59 return B_OK; 60 } 61 62 // Uninit 63 void 64 VirtualVolume::Uninit() 65 { 66 if (fRootNode) { 67 fVolumeManager->RemoveNodeID(fRootNode->GetID()); 68 delete fRootNode; 69 fRootNode = NULL; 70 } 71 72 Volume::Uninit(); 73 } 74 75 // GetRootNode 76 Node* 77 VirtualVolume::GetRootNode() const 78 { 79 return fRootNode; 80 } 81 82 // PrepareToUnmount 83 void 84 VirtualVolume::PrepareToUnmount() 85 { 86 Volume::PrepareToUnmount(); 87 88 // remove all child volumes 89 90 // init a directory iterator 91 fLock.Lock(); 92 VirtualDirIterator iterator; 93 iterator.SetDirectory(fRootNode, true); 94 95 // iterate through the directory 96 const char* name; 97 Node* node; 98 while (iterator.GetCurrentEntry(&name, &node)) { 99 iterator.NextEntry(); 100 Volume* volume = fVolumeManager->GetVolume(node->GetID()); 101 fLock.Unlock(); 102 if (volume) { 103 RemoveChildVolume(volume); 104 volume->SetUnmounting(true); 105 volume->PutVolume(); 106 } 107 fLock.Lock(); 108 } 109 110 // uninit the directory iterator 111 iterator.SetDirectory(NULL); 112 113 // remove the our root node 114 vnode_id rootNodeID = fRootNode->GetID(); 115 116 fLock.Unlock(); 117 118 if (GetVNode(rootNodeID, &node) == B_OK) { 119 Volume::RemoveVNode(rootNodeID); 120 PutVNode(rootNodeID); 121 } 122 } 123 124 // AddChildVolume 125 status_t 126 VirtualVolume::AddChildVolume(Volume* volume) 127 { 128 if (!volume) 129 return B_BAD_VALUE; 130 131 // don't add anything, if we are already unmounting 132 AutoLocker<Locker> locker(fLock); 133 if (fUnmounting) 134 return B_BAD_VALUE; 135 136 // get and check the node name 137 char name[B_FILE_NAME_LENGTH]; 138 int32 nameLen = strlen(volume->GetName()); 139 if (nameLen == 0 || nameLen >= B_FILE_NAME_LENGTH) 140 return B_BAD_VALUE; 141 strcpy(name, volume->GetName()); 142 143 // add the volume's root node 144 status_t error = fRootNode->AddEntry(name, volume->GetRootNode()); 145 if (error != B_OK) 146 return error; 147 148 // set the volume's parent volume 149 volume->SetParentVolume(this); 150 AcquireReference(); 151 152 // send out a notification 153 vnode_id dirID = fRootNode->GetID(); 154 vnode_id nodeID = volume->GetRootID(); 155 locker.Unlock(); 156 NotifyListener(B_ENTRY_CREATED, fVolumeManager->GetID(), dirID, 0, nodeID, 157 name); 158 159 return B_OK; 160 } 161 162 // RemoveChildVolume 163 void 164 VirtualVolume::RemoveChildVolume(Volume* volume) 165 { 166 if (!volume) 167 return; 168 169 // check, if the volume's root node is a child of our root node 170 AutoLocker<Locker> locker(fLock); 171 Node* node = fRootNode->GetChildNode(volume->GetName()); 172 if (!node) 173 return; 174 if (node != volume->GetRootNode()) 175 return; 176 177 // remove it 178 fRootNode->RemoveEntry(volume->GetName()); 179 volume->SetParentVolume(NULL); 180 181 // get the data needed for the node monitoring notification 182 vnode_id dirID = fRootNode->GetID(); 183 vnode_id nodeID = volume->GetRootID(); 184 char name[B_FILE_NAME_LENGTH]; 185 strcpy(name, volume->GetName()); 186 187 // surrender the child volumes reference to us 188 // Since the caller of this method must have a valid reference to us, 189 // this is unproblematic, even if fLock is being held, while this method 190 // is invoked. 191 locker.Unlock(); 192 PutVolume(); 193 194 // send out a notification 195 locker.Unlock(); 196 NotifyListener(B_ENTRY_REMOVED, fVolumeManager->GetID(), dirID, 0, nodeID, 197 name); 198 } 199 200 // GetChildVolume 201 Volume* 202 VirtualVolume::GetChildVolume(const char* name) 203 { 204 if (!name) 205 return NULL; 206 207 // get the child node of the root node 208 AutoLocker<Locker> locker(fLock); 209 Node* node = fRootNode->GetChildNode(name); 210 if (!node) 211 return NULL; 212 213 // get its volume, if it is the root volume 214 Volume* volume = node->GetVolume(); 215 if (volume->GetRootNode() != node) 216 return NULL; 217 locker.Unlock(); 218 219 return fVolumeManager->GetVolume(node->GetID()); 220 } 221 222 // GetUniqueEntryName 223 status_t 224 VirtualVolume::GetUniqueEntryName(const char* baseName, char* buffer) 225 { 226 if (!baseName || !buffer) 227 return B_BAD_VALUE; 228 229 // check the base name len 230 int32 baseLen = strlen(baseName); 231 if (baseLen == 0 || baseLen >= B_FILE_NAME_LENGTH) 232 return B_BAD_VALUE; 233 234 strcpy(buffer, baseName); 235 236 AutoLocker<Locker> _(fLock); 237 238 // adjust the name, if necessary 239 int32 suffixNumber = 2; 240 while (fRootNode->GetChildNode(baseName)) { 241 // create a suffix 242 char suffix[13]; 243 sprintf(suffix, " %" B_PRId32, suffixNumber); 244 suffixNumber++; 245 246 // check the len 247 int32 suffixLen = strlen(suffix); 248 if (baseLen + suffixLen >= B_FILE_NAME_LENGTH) 249 return B_NAME_TOO_LONG; 250 251 // compose the final name 252 strcpy(buffer + baseLen, suffix); 253 } 254 255 return B_OK; 256 } 257 258 259 // #pragma mark - 260 // #pragma mark ----- FS ----- 261 262 // Unmount 263 status_t 264 VirtualVolume::Unmount() 265 { 266 return B_OK; 267 } 268 269 // Sync 270 status_t 271 VirtualVolume::Sync() 272 { 273 // TODO: Recursively call Sync(). 274 return B_BAD_VALUE; 275 } 276 277 278 // #pragma mark - 279 // #pragma mark ----- vnodes ----- 280 281 // ReadVNode 282 status_t 283 VirtualVolume::ReadVNode(vnode_id vnid, char reenter, Node** node) 284 { 285 if (vnid != GetRootID()) 286 return B_BAD_VALUE; 287 288 AutoLocker<Locker> _(fLock); 289 fRootNode->SetKnownToVFS(true); 290 *node = fRootNode; 291 292 // add a volume reference for the node 293 AcquireReference(); 294 295 return B_OK; 296 } 297 298 // WriteVNode 299 status_t 300 VirtualVolume::WriteVNode(Node* node, char reenter) 301 { 302 if (node != fRootNode) 303 return B_BAD_VALUE; 304 305 AutoLocker<Locker> locker(fLock); 306 fRootNode->SetKnownToVFS(false); 307 308 // surrender the volume reference of the node 309 locker.Unlock(); 310 PutVolume(); 311 312 return B_OK; 313 } 314 315 // RemoveVNode 316 status_t 317 VirtualVolume::RemoveVNode(Node* node, char reenter) 318 { 319 if (node != fRootNode) 320 return B_BAD_VALUE; 321 322 AutoLocker<Locker> locker(fLock); 323 fRootNode->SetKnownToVFS(false); 324 325 // surrender the volume reference of the node 326 locker.Unlock(); 327 PutVolume(); 328 329 return B_OK; 330 } 331 332 // #pragma mark - 333 // #pragma mark ----- nodes ----- 334 335 // FSync 336 status_t 337 VirtualVolume::FSync(Node* node) 338 { 339 return B_OK; 340 } 341 342 // ReadStat 343 status_t 344 VirtualVolume::ReadStat(Node* node, struct stat* st) 345 { 346 if (node != fRootNode) 347 return B_BAD_VALUE; 348 349 AutoLocker<Locker> _(fLock); 350 st->st_ino = node->GetID(); 351 st->st_mode = S_IFDIR | S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH 352 | S_IXOTH; 353 st->st_nlink = 1; 354 st->st_size = 1; 355 st->st_blksize = 1024; 356 st->st_crtime = fRootNode->GetCreationTime(); 357 st->st_ctime = st->st_mtime = st->st_atime = st->st_crtime; 358 st->st_dev = fVolumeManager->GetID(); 359 // we set the UID/GID fields to the one who mounted the FS 360 st->st_uid = fVolumeManager->GetMountUID(); 361 st->st_gid = fVolumeManager->GetMountGID(); 362 return B_OK; 363 } 364 365 // WriteStat 366 status_t 367 VirtualVolume::WriteStat(Node* node, struct stat *st, uint32 mask) 368 { 369 return B_NOT_ALLOWED; 370 } 371 372 // Access 373 status_t 374 VirtualVolume::Access(Node* node, int mode) 375 { 376 // TODO: Implement! 377 return B_OK; 378 } 379 380 // #pragma mark - 381 // #pragma mark ----- files ----- 382 383 // Create 384 status_t 385 VirtualVolume::Create(Node* dir, const char* name, int openMode, int mode, 386 vnode_id* vnid, void** cookie) 387 { 388 return B_NOT_ALLOWED; 389 } 390 391 // Open 392 status_t 393 VirtualVolume::Open(Node* node, int openMode, void** cookie) 394 { 395 if (node != fRootNode) 396 return B_BAD_VALUE; 397 398 // we're read-only 399 if ((openMode & O_RWMASK) == O_WRONLY) 400 return B_PERMISSION_DENIED; 401 if (openMode & O_TRUNC) 402 return B_PERMISSION_DENIED; 403 if ((openMode & O_RWMASK) == O_RDWR) 404 openMode = (openMode & ~O_RWMASK) | O_RDONLY; 405 406 // set the result 407 *cookie = NULL; 408 return B_OK; 409 } 410 411 // Close 412 status_t 413 VirtualVolume::Close(Node* node, void* cookie) 414 { 415 // no-op: FreeCookie() does the job 416 return B_OK; 417 } 418 419 // FreeCookie 420 status_t 421 VirtualVolume::FreeCookie(Node* node, void* cookie) 422 { 423 // nothing to do: we didn't allocate anything 424 return B_OK; 425 } 426 427 // Read 428 status_t 429 VirtualVolume::Read(Node* node, void* cookie, off_t pos, void* _buffer, 430 size_t bufferSize, size_t* _bytesRead) 431 { 432 // can't read() directories 433 return B_NOT_ALLOWED; 434 } 435 436 // Write 437 status_t 438 VirtualVolume::Write(Node* node, void* cookie, off_t pos, const void* _buffer, 439 size_t bufferSize, size_t* bytesWritten) 440 { 441 // can't write() directories 442 return B_NOT_ALLOWED; 443 } 444 445 // IOCtl 446 status_t 447 VirtualVolume::IOCtl(Node* node, void* cookie, int cmd, void* buffer, 448 size_t bufferSize) 449 { 450 return B_BAD_VALUE; 451 } 452 453 // SetFlags 454 status_t 455 VirtualVolume::SetFlags(Node* node, void* cookie, int flags) 456 { 457 return B_BAD_VALUE; 458 } 459 460 // #pragma mark - 461 // #pragma mark ----- hard links / symlinks ----- 462 463 // Link 464 status_t 465 VirtualVolume::Link(Node* dir, const char* name, Node* node) 466 { 467 // we're read-only 468 return B_NOT_ALLOWED; 469 } 470 471 // Unlink 472 status_t 473 VirtualVolume::Unlink(Node* dir, const char* name) 474 { 475 // we're read-only 476 return B_NOT_ALLOWED; 477 } 478 479 // Symlink 480 status_t 481 VirtualVolume::Symlink(Node* dir, const char* name, const char* target) 482 { 483 // we're read-only 484 return B_NOT_ALLOWED; 485 } 486 487 // ReadLink 488 status_t 489 VirtualVolume::ReadLink(Node* node, char* buffer, size_t bufferSize, 490 size_t* bytesRead) 491 { 492 // there are no symlinks 493 return B_BAD_VALUE; 494 } 495 496 // Rename 497 status_t 498 VirtualVolume::Rename(Node* oldDir, const char* oldName, Node* newDir, 499 const char* newName) 500 { 501 // we're read-only 502 return B_NOT_ALLOWED; 503 } 504 505 // #pragma mark - 506 // #pragma mark ----- directories ----- 507 508 // MkDir 509 status_t 510 VirtualVolume::MkDir(Node* dir, const char* name, int mode) 511 { 512 // we're read-only 513 return B_NOT_ALLOWED; 514 } 515 516 // RmDir 517 status_t 518 VirtualVolume::RmDir(Node* dir, const char* name) 519 { 520 // we're read-only 521 return B_NOT_ALLOWED; 522 } 523 524 // OpenDir 525 status_t 526 VirtualVolume::OpenDir(Node* node, void** cookie) 527 { 528 if (node != fRootNode) 529 return B_BAD_VALUE; 530 531 // allocate an iterator 532 VirtualDirIterator* iterator = new(std::nothrow) VirtualDirIterator; 533 if (!iterator) 534 return B_NO_MEMORY; 535 536 AutoLocker<Locker> locker(fLock); 537 iterator->SetDirectory(fRootNode); 538 *cookie = iterator; 539 return B_OK; 540 } 541 542 // CloseDir 543 status_t 544 VirtualVolume::CloseDir(Node* node, void* cookie) 545 { 546 // no-op: FreeDirCookie() does the job 547 return B_OK; 548 } 549 550 // FreeDirCookie 551 status_t 552 VirtualVolume::FreeDirCookie(Node* node, void* cookie) 553 { 554 if (node != fRootNode) 555 return B_BAD_VALUE; 556 557 // delete the iterator 558 AutoLocker<Locker> locker(fLock); 559 VirtualDirIterator* iterator = (VirtualDirIterator*)cookie; 560 delete iterator; 561 return B_OK; 562 } 563 564 // ReadDir 565 status_t 566 VirtualVolume::ReadDir(Node* node, void* cookie, struct dirent* buffer, 567 size_t bufferSize, int32 count, int32* countRead) 568 { 569 if (node != fRootNode) 570 return B_BAD_VALUE; 571 572 *countRead = 0; 573 574 AutoLocker<Locker> locker(fLock); 575 VirtualDirIterator* iterator = (VirtualDirIterator*)cookie; 576 577 // get the current entry 578 const char* name; 579 Node* child; 580 if (!iterator->GetCurrentEntry(&name, &child)) 581 return B_OK; 582 583 // set the name: this also checks the size of the buffer 584 status_t error = set_dirent_name(buffer, bufferSize, name, strlen(name)); 585 if (error != B_OK) 586 RETURN_ERROR(error); 587 588 // fill in the other fields 589 buffer->d_pdev = fVolumeManager->GetID(); 590 buffer->d_pino = fRootNode->GetID(); 591 buffer->d_dev = fVolumeManager->GetID(); 592 buffer->d_ino = child->GetID(); 593 *countRead = 1; 594 595 // fix d_ino, if this is the parent of the root node 596 if (strcmp(name, "..") == 0) { 597 if (Volume* parentVolume = GetParentVolume()) 598 buffer->d_ino = parentVolume->GetRootID(); 599 } 600 601 iterator->NextEntry(); 602 return B_OK; 603 } 604 605 // RewindDir 606 status_t 607 VirtualVolume::RewindDir(Node* node, void* cookie) 608 { 609 if (node != fRootNode) 610 return B_BAD_VALUE; 611 612 AutoLocker<Locker> locker(fLock); 613 VirtualDirIterator* iterator = (VirtualDirIterator*)cookie; 614 iterator->Rewind(); 615 616 return B_OK; 617 } 618 619 // Walk 620 status_t 621 VirtualVolume::Walk(Node* dir, const char* entryName, char** resolvedPath, 622 vnode_id* vnid) 623 { 624 if (dir != fRootNode) 625 return B_BAD_VALUE; 626 627 // get the referred to node ID 628 AutoLocker<Locker> locker(fLock); 629 if (strcmp(entryName, ".") == 0) { 630 *vnid = dir->GetID(); 631 } else if (strcmp(entryName, "..") == 0) { 632 if (Volume* parentVolume = GetParentVolume()) 633 *vnid = parentVolume->GetRootID(); 634 else 635 *vnid = dir->GetID(); 636 } else { 637 Node* node = fRootNode->GetChildNode(entryName); 638 if (!node) 639 return B_ENTRY_NOT_FOUND; 640 *vnid = node->GetID(); 641 } 642 locker.Unlock(); 643 644 // get a VFS node reference 645 Node* dummyNode; 646 status_t error = GetVNode(*vnid, &dummyNode); 647 if (error != B_OK) 648 return error; 649 return B_OK; 650 } 651 652 // #pragma mark - 653 // #pragma mark ----- attributes ----- 654 655 // OpenAttrDir 656 status_t 657 VirtualVolume::OpenAttrDir(Node* node, void** cookie) 658 { 659 if (node != fRootNode) 660 return B_BAD_VALUE; 661 662 // we support no attributes at this time 663 *cookie = NULL; 664 return B_OK; 665 } 666 667 // CloseAttrDir 668 status_t 669 VirtualVolume::CloseAttrDir(Node* node, void* cookie) 670 { 671 // no-op: FreeAttrDirCookie() does the job 672 return B_OK; 673 } 674 675 // FreeAttrDirCookie 676 status_t 677 VirtualVolume::FreeAttrDirCookie(Node* node, void* _cookie) 678 { 679 // nothing to do: we didn't allocate anything 680 return B_OK; 681 } 682 683 // ReadAttrDir 684 status_t 685 VirtualVolume::ReadAttrDir(Node* node, void* _cookie, struct dirent* buffer, 686 size_t bufferSize, int32 count, int32* countRead) 687 { 688 // no attributes for the time being 689 *countRead = 0; 690 return B_OK; 691 } 692 693 // RewindAttrDir 694 status_t 695 VirtualVolume::RewindAttrDir(Node* node, void* _cookie) 696 { 697 return B_OK; 698 } 699 700 // ReadAttr 701 status_t 702 VirtualVolume::ReadAttr(Node* node, const char* name, int type, off_t pos, 703 void* _buffer, size_t bufferSize, size_t* bytesRead) 704 { 705 // no attributes for the time being 706 *bytesRead = 0; 707 return B_ENTRY_NOT_FOUND; 708 } 709 710 // WriteAttr 711 status_t 712 VirtualVolume::WriteAttr(Node* node, const char* name, int type, off_t pos, 713 const void* _buffer, size_t bufferSize, size_t* bytesWritten) 714 { 715 // no attributes for the time being 716 *bytesWritten = 0; 717 return B_NOT_ALLOWED; 718 } 719 720 // RemoveAttr 721 status_t 722 VirtualVolume::RemoveAttr(Node* node, const char* name) 723 { 724 return B_NOT_ALLOWED; 725 } 726 727 // RenameAttr 728 status_t 729 VirtualVolume::RenameAttr(Node* node, const char* oldName, const char* newName) 730 { 731 // no attributes for the time being 732 return B_ENTRY_NOT_FOUND; 733 } 734 735 // StatAttr 736 status_t 737 VirtualVolume::StatAttr(Node* node, const char* name, struct attr_info* attrInfo) 738 { 739 // no attributes for the time being 740 return B_ENTRY_NOT_FOUND; 741 } 742 743 // #pragma mark - 744 // #pragma mark ----- queries ----- 745 746 // OpenQuery 747 status_t 748 VirtualVolume::OpenQuery(const char* queryString, uint32 flags, port_id port, 749 int32 token, QueryIterator** _iterator) 750 { 751 QueryManager* queryManager = fVolumeManager->GetQueryManager(); 752 753 // allocate a hierarchical iterator 754 HierarchicalQueryIterator* iterator 755 = new(std::nothrow) HierarchicalQueryIterator(this); 756 if (!iterator) 757 return B_NO_MEMORY; 758 759 // add it to the query manager 760 status_t error = queryManager->AddIterator(iterator); 761 if (error != B_OK) { 762 delete iterator; 763 return error; 764 } 765 QueryIteratorPutter iteratorPutter(queryManager, iterator); 766 767 // iterate through the child volumes and open subqueries for them 768 // init a directory iterator 769 fLock.Lock(); 770 VirtualDirIterator dirIterator; 771 dirIterator.SetDirectory(fRootNode, true); 772 773 // iterate through the directory 774 const char* name; 775 Node* node; 776 while (dirIterator.GetCurrentEntry(&name, &node)) { 777 dirIterator.NextEntry(); 778 Volume* volume = fVolumeManager->GetVolume(node->GetID()); 779 fLock.Unlock(); 780 781 // open the subquery 782 QueryIterator* subIterator; 783 if (volume->OpenQuery(queryString, flags, port, token, 784 &subIterator) == B_OK) { 785 // add the subiterator 786 if (queryManager->AddSubIterator(iterator, subIterator) != B_OK) 787 queryManager->PutIterator(subIterator); 788 } 789 volume->PutVolume(); 790 791 fLock.Lock(); 792 } 793 794 // uninit the directory iterator 795 dirIterator.SetDirectory(NULL); 796 fLock.Unlock(); 797 798 // return the result 799 *_iterator = iterator; 800 iteratorPutter.Detach(); 801 802 return B_OK; 803 } 804 805 // FreeQueryIterator 806 void 807 VirtualVolume::FreeQueryIterator(QueryIterator* iterator) 808 { 809 delete iterator; 810 } 811 812 // ReadQuery 813 status_t 814 VirtualVolume::ReadQuery(QueryIterator* _iterator, struct dirent* buffer, 815 size_t bufferSize, int32 count, int32* countRead) 816 { 817 HierarchicalQueryIterator* iterator 818 = dynamic_cast<HierarchicalQueryIterator*>(_iterator); 819 820 QueryManager* queryManager = fVolumeManager->GetQueryManager(); 821 while (QueryIterator* subIterator 822 = queryManager->GetCurrentSubIterator(iterator)) { 823 QueryIteratorPutter _(queryManager, subIterator); 824 825 status_t error = subIterator->GetVolume()->ReadQuery(subIterator, 826 buffer, bufferSize, count, countRead); 827 if (error != B_OK) 828 return error; 829 830 if (*countRead > 0) 831 return B_OK; 832 833 queryManager->NextSubIterator(iterator, subIterator); 834 } 835 836 *countRead = 0; 837 return B_OK; 838 } 839 840