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 19VirtualVolume::VirtualVolume(VolumeManager* volumeManager) 20 : Volume(volumeManager), 21 fRootNode(NULL) 22{ 23} 24 25// destructor 26VirtualVolume::~VirtualVolume() 27{ 28 delete fRootNode; 29} 30 31// Init 32status_t 33VirtualVolume::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 63void 64VirtualVolume::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 76Node* 77VirtualVolume::GetRootNode() const 78{ 79 return fRootNode; 80} 81 82// PrepareToUnmount 83void 84VirtualVolume::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 125status_t 126VirtualVolume::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 163void 164VirtualVolume::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 201Volume* 202VirtualVolume::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 223status_t 224VirtualVolume::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 263status_t 264VirtualVolume::Unmount() 265{ 266 return B_OK; 267} 268 269// Sync 270status_t 271VirtualVolume::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 282status_t 283VirtualVolume::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 299status_t 300VirtualVolume::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 316status_t 317VirtualVolume::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 336status_t 337VirtualVolume::FSync(Node* node) 338{ 339 return B_OK; 340} 341 342// ReadStat 343status_t 344VirtualVolume::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 366status_t 367VirtualVolume::WriteStat(Node* node, struct stat *st, uint32 mask) 368{ 369 return B_NOT_ALLOWED; 370} 371 372// Access 373status_t 374VirtualVolume::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 384status_t 385VirtualVolume::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 392status_t 393VirtualVolume::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 412status_t 413VirtualVolume::Close(Node* node, void* cookie) 414{ 415 // no-op: FreeCookie() does the job 416 return B_OK; 417} 418 419// FreeCookie 420status_t 421VirtualVolume::FreeCookie(Node* node, void* cookie) 422{ 423 // nothing to do: we didn't allocate anything 424 return B_OK; 425} 426 427// Read 428status_t 429VirtualVolume::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 437status_t 438VirtualVolume::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 446status_t 447VirtualVolume::IOCtl(Node* node, void* cookie, int cmd, void* buffer, 448 size_t bufferSize) 449{ 450 return B_BAD_VALUE; 451} 452 453// SetFlags 454status_t 455VirtualVolume::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 464status_t 465VirtualVolume::Link(Node* dir, const char* name, Node* node) 466{ 467 // we're read-only 468 return B_NOT_ALLOWED; 469} 470 471// Unlink 472status_t 473VirtualVolume::Unlink(Node* dir, const char* name) 474{ 475 // we're read-only 476 return B_NOT_ALLOWED; 477} 478 479// Symlink 480status_t 481VirtualVolume::Symlink(Node* dir, const char* name, const char* target) 482{ 483 // we're read-only 484 return B_NOT_ALLOWED; 485} 486 487// ReadLink 488status_t 489VirtualVolume::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 497status_t 498VirtualVolume::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 509status_t 510VirtualVolume::MkDir(Node* dir, const char* name, int mode) 511{ 512 // we're read-only 513 return B_NOT_ALLOWED; 514} 515 516// RmDir 517status_t 518VirtualVolume::RmDir(Node* dir, const char* name) 519{ 520 // we're read-only 521 return B_NOT_ALLOWED; 522} 523 524// OpenDir 525status_t 526VirtualVolume::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 543status_t 544VirtualVolume::CloseDir(Node* node, void* cookie) 545{ 546 // no-op: FreeDirCookie() does the job 547 return B_OK; 548} 549 550// FreeDirCookie 551status_t 552VirtualVolume::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 565status_t 566VirtualVolume::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 606status_t 607VirtualVolume::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 620status_t 621VirtualVolume::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 656status_t 657VirtualVolume::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 668status_t 669VirtualVolume::CloseAttrDir(Node* node, void* cookie) 670{ 671 // no-op: FreeAttrDirCookie() does the job 672 return B_OK; 673} 674 675// FreeAttrDirCookie 676status_t 677VirtualVolume::FreeAttrDirCookie(Node* node, void* _cookie) 678{ 679 // nothing to do: we didn't allocate anything 680 return B_OK; 681} 682 683// ReadAttrDir 684status_t 685VirtualVolume::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 694status_t 695VirtualVolume::RewindAttrDir(Node* node, void* _cookie) 696{ 697 return B_OK; 698} 699 700// ReadAttr 701status_t 702VirtualVolume::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 711status_t 712VirtualVolume::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 721status_t 722VirtualVolume::RemoveAttr(Node* node, const char* name) 723{ 724 return B_NOT_ALLOWED; 725} 726 727// RenameAttr 728status_t 729VirtualVolume::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 736status_t 737VirtualVolume::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 747status_t 748VirtualVolume::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 806void 807VirtualVolume::FreeQueryIterator(QueryIterator* iterator) 808{ 809 delete iterator; 810} 811 812// ReadQuery 813status_t 814VirtualVolume::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