1 /* 2 * Copyright 2001-2009, Ingo Weinhold, ingo_weinhold@gmx.de. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 #include "FUSEVolume.h" 7 8 #include <dirent.h> 9 #include <file_systems/mime_ext_table.h> 10 11 #include <algorithm> 12 13 #include <fs_info.h> 14 #include <NodeMonitor.h> 15 16 #include <AutoDeleter.h> 17 #include <StackOrHeapArray.h> 18 19 #include "FUSELowLevel.h" 20 21 #include "../IORequestInfo.h" 22 #include "../kernel_emu.h" 23 #include "../RequestThread.h" 24 25 #define ROUNDUP(a, b) (((a) + ((b)-1)) & ~((b)-1)) 26 27 28 // TODO: For remote/shared file systems (sshfs, nfs, etc.) we need to notice 29 // that entries have been added/removed, so that we can (1) update our 30 // FUSEEntry/FUSENode objects and (2) send out node monitoring messages. 31 32 33 // The maximal node tree hierarchy levels we support. 34 static const uint32 kMaxNodeTreeDepth = 1024; 35 36 37 struct FUSEVolume::DirEntryCache { 38 DirEntryCache() 39 : 40 fEntries(NULL), 41 fNames(NULL), 42 fEntryCount(0), 43 fEntryCapacity(0), 44 fNamesSize(0), 45 fNamesCapacity(0) 46 { 47 } 48 49 ~DirEntryCache() 50 { 51 free(fEntries); 52 free(fNames); 53 } 54 55 status_t AddEntry(ino_t nodeID, const char* name) 56 { 57 // resize entries array, if full 58 if (fEntryCount == fEntryCapacity) { 59 // entries array full -- resize 60 uint32 newCapacity = std::max(fEntryCapacity * 2, (uint32)8); 61 Entry* newEntries = (Entry*)realloc(fEntries, 62 newCapacity * sizeof(Entry)); 63 if (newEntries == NULL) 64 return B_NO_MEMORY; 65 66 fEntries = newEntries; 67 fEntryCapacity = newCapacity; 68 } 69 70 // resize names buffer, if full 71 size_t nameSize = strlen(name) + 1; 72 if (fNamesSize + nameSize > fNamesCapacity) { 73 size_t newCapacity = std::max(fNamesCapacity * 2, (size_t)256); 74 while (newCapacity < fNamesSize + nameSize) 75 newCapacity *= 2; 76 77 char* names = (char*)realloc(fNames, newCapacity); 78 if (names == NULL) 79 return B_NO_MEMORY; 80 81 fNames = names; 82 fNamesCapacity = newCapacity; 83 } 84 85 // add the entry 86 fEntries[fEntryCount].nodeID = nodeID; 87 fEntries[fEntryCount].nameOffset = fNamesSize; 88 fEntries[fEntryCount].nameSize = nameSize; 89 fEntryCount++; 90 91 memcpy(fNames + fNamesSize, name, nameSize); 92 fNamesSize += nameSize; 93 94 return B_OK; 95 } 96 97 uint32 CountEntries() const 98 { 99 return fEntryCount; 100 } 101 102 bool ReadDirent(uint32 index, dev_t volumeID, bool align, dirent* buffer, 103 size_t bufferSize) const 104 { 105 if (index >= fEntryCount) 106 return false; 107 108 const Entry& entry = fEntries[index]; 109 110 // get and check the size 111 size_t size = offsetof(struct dirent, d_name) + entry.nameSize; 112 if (size > bufferSize) 113 return false; 114 115 // align the size, if requested 116 if (align) 117 size = std::min(bufferSize, ROUNDUP(size, 8)); 118 119 // fill in the dirent 120 buffer->d_dev = volumeID; 121 buffer->d_ino = entry.nodeID; 122 memcpy(buffer->d_name, fNames + entry.nameOffset, entry.nameSize); 123 buffer->d_reclen = size; 124 125 return true; 126 } 127 128 private: 129 struct Entry { 130 ino_t nodeID; 131 uint32 nameOffset; 132 uint32 nameSize; 133 }; 134 135 private: 136 Entry* fEntries; 137 char* fNames; 138 uint32 fEntryCount; 139 uint32 fEntryCapacity; 140 size_t fNamesSize; 141 size_t fNamesCapacity; 142 }; 143 144 145 struct FUSEVolume::DirCookie : fuse_file_info, RWLockable { 146 union { 147 off_t currentEntryOffset; 148 uint32 currentEntryIndex; 149 }; 150 DirEntryCache* entryCache; 151 bool getdirInterface; 152 153 DirCookie() 154 : 155 currentEntryOffset(0), 156 entryCache(NULL), 157 getdirInterface(false) 158 { 159 flags = 0; 160 fh_old = 0; 161 writepage = 0; 162 direct_io = 0; 163 keep_cache = 0; 164 flush = 0; 165 fh = 0; 166 lock_owner = 0; 167 } 168 169 ~DirCookie() 170 { 171 delete entryCache; 172 } 173 }; 174 175 176 struct FUSEVolume::FileCookie : fuse_file_info, RWLockable { 177 FileCookie(int openMode) 178 { 179 flags = openMode; 180 fh_old = 0; 181 writepage = 0; 182 direct_io = 0; 183 keep_cache = 0; 184 flush = 0; 185 fh = 0; 186 lock_owner = 0; 187 } 188 }; 189 190 191 struct FUSEVolume::AttrDirCookie : RWLockable { 192 AttrDirCookie() 193 : 194 fAttributes(NULL), 195 fAttributesSize(0), 196 fCurrentOffset(0), 197 fValid(false) 198 { 199 } 200 201 ~AttrDirCookie() 202 { 203 Clear(); 204 } 205 206 void Clear() 207 { 208 free(fAttributes); 209 fAttributes = NULL; 210 fAttributesSize = 0; 211 fCurrentOffset = 0; 212 fValid = false; 213 } 214 215 status_t Allocate(size_t size) 216 { 217 Clear(); 218 219 if (size == 0) 220 return B_OK; 221 222 fAttributes = (char*)malloc(size); 223 if (fAttributes == NULL) 224 return B_NO_MEMORY; 225 226 fAttributesSize = size; 227 return B_OK; 228 } 229 230 bool IsValid() const 231 { 232 return fValid; 233 } 234 235 void SetValid(bool valid) 236 { 237 fValid = valid; 238 } 239 240 char* AttributesBuffer() const 241 { 242 return fAttributes; 243 } 244 245 bool ReadNextEntry(dev_t volumeID, ino_t nodeID, bool align, 246 dirent* buffer, size_t bufferSize) 247 { 248 if (fCurrentOffset >= fAttributesSize) 249 return false; 250 251 const char* name = fAttributes + fCurrentOffset; 252 size_t nameLen = strlen(name); 253 254 // get and check the size 255 size_t size = offsetof(struct dirent, d_name) + nameLen + 1; 256 if (size > bufferSize) 257 return false; 258 259 // align the size, if requested 260 if (align) 261 size = std::min(bufferSize, ROUNDUP(size, 8)); 262 263 // fill in the dirent 264 buffer->d_dev = volumeID; 265 buffer->d_ino = nodeID; 266 memcpy(buffer->d_name, name, nameLen + 1); 267 buffer->d_reclen = size; 268 269 fCurrentOffset += nameLen + 1; 270 271 return true; 272 } 273 274 private: 275 char* fAttributes; 276 size_t fAttributesSize; 277 size_t fCurrentOffset; 278 bool fValid; 279 }; 280 281 282 struct FUSEVolume::AttrCookie : RWLockable { 283 public: 284 AttrCookie(const char* name) 285 : 286 fValue(NULL), 287 fSize(0), 288 fType(0) 289 { 290 _SetType(name); 291 } 292 293 AttrCookie(const char* name, const char* value) 294 : 295 fValue(strdup(value)), 296 fSize(strlen(value) + 1), 297 fType(0) 298 { 299 _SetType(name); 300 } 301 302 ~AttrCookie() 303 { 304 free(fValue); 305 } 306 307 bool IsValid() const 308 { 309 return fValue != NULL; 310 } 311 312 uint32 Type() const 313 { 314 return fType; 315 } 316 317 status_t Allocate(size_t size) 318 { 319 fValue = (char*)malloc(size); 320 if (fValue == NULL) { 321 fSize = 0; 322 return B_NO_MEMORY; 323 } 324 fSize = size; 325 return B_OK; 326 } 327 328 void Read(void* buffer, size_t bufferSize, off_t pos, 329 size_t* bytesRead) const 330 { 331 if (pos < 0 || (uint64)pos > SIZE_MAX || (size_t)pos > fSize - 1) { 332 *bytesRead = 0; 333 return; 334 } 335 size_t copySize = fSize - pos; 336 if (copySize > bufferSize) 337 copySize = bufferSize; 338 strlcpy((char*)buffer, &fValue[pos], bufferSize); 339 *bytesRead = copySize; 340 } 341 342 char* Buffer() 343 { 344 return fValue; 345 } 346 347 size_t Size() const 348 { 349 return fSize; 350 } 351 352 private: 353 void _SetType(const char* name) 354 { 355 if (strcmp(name, kAttrMimeTypeName) == 0) 356 fType = B_MIME_STRING_TYPE; 357 else 358 fType = B_RAW_TYPE; 359 } 360 361 private: 362 char* fValue; 363 size_t fSize; 364 uint32 fType; 365 }; 366 367 368 struct FUSEVolume::ReadDirBuffer { 369 FUSEVolume* volume; 370 FUSENode* directory; 371 DirCookie* cookie; 372 void* buffer; 373 size_t bufferSize; 374 size_t usedSize; 375 uint32 entriesRead; 376 uint32 maxEntries; 377 status_t error; 378 379 ReadDirBuffer(FUSEVolume* volume, FUSENode* directory, DirCookie* cookie, 380 void* buffer, size_t bufferSize, uint32 maxEntries) 381 : 382 volume(volume), 383 directory(directory), 384 cookie(cookie), 385 buffer(buffer), 386 bufferSize(bufferSize), 387 usedSize(0), 388 entriesRead(0), 389 maxEntries(maxEntries), 390 error(B_OK) 391 { 392 } 393 }; 394 395 396 struct FUSEVolume::LockIterator { 397 FUSEVolume* volume; 398 FUSENode* firstNode; 399 FUSENode* lastLockedNode; 400 FUSENode* nextNode; 401 FUSENode* stopBeforeNode; 402 bool writeLock; 403 404 LockIterator(FUSEVolume* volume, FUSENode* node, bool writeLock, 405 FUSENode* stopBeforeNode) 406 : 407 volume(volume), 408 firstNode(node), 409 lastLockedNode(NULL), 410 nextNode(node), 411 stopBeforeNode(stopBeforeNode), 412 writeLock(writeLock) 413 { 414 } 415 416 ~LockIterator() 417 { 418 Unlock(); 419 } 420 421 void SetTo(FUSEVolume* volume, FUSENode* node, bool writeLock, 422 FUSENode* stopBeforeNode) 423 { 424 Unlock(); 425 426 this->volume = volume; 427 this->firstNode = node; 428 this->lastLockedNode = NULL; 429 this->nextNode = node; 430 this->stopBeforeNode = stopBeforeNode; 431 this->writeLock = writeLock; 432 } 433 434 status_t LockNext(bool* _done, bool* _volumeUnlocked) 435 { 436 // increment the ref count first 437 nextNode->refCount++; 438 439 if (volume->fLockManager.TryGenericLock( 440 nextNode == firstNode && writeLock, nextNode)) { 441 // got the lock 442 *_volumeUnlocked = false; 443 } else { 444 // node is locked -- we need to unlock the volume and wait for 445 // the lock 446 volume->fLock.Unlock(); 447 status_t error = volume->fLockManager.GenericLock( 448 nextNode == firstNode && writeLock, nextNode); 449 volume->fLock.Lock(); 450 451 *_volumeUnlocked = false; 452 453 if (error != B_OK) { 454 volume->_PutNode(nextNode); 455 return error; 456 } 457 } 458 459 lastLockedNode = nextNode; 460 461 // get the parent node 462 FUSENode* parent = nextNode->Parent(); 463 if (parent == stopBeforeNode || parent == nextNode) { 464 if (parent == nextNode) 465 parent = NULL; 466 *_done = true; 467 } else 468 *_done = false; 469 470 nextNode = parent; 471 472 return B_OK; 473 } 474 475 void Unlock() 476 { 477 if (lastLockedNode == NULL) 478 return; 479 480 volume->_UnlockNodeChainInternal(firstNode, writeLock, lastLockedNode, 481 NULL); 482 483 lastLockedNode = NULL; 484 nextNode = firstNode; 485 } 486 487 void SetStopBeforeNode(FUSENode* stopBeforeNode) 488 { 489 this->stopBeforeNode = stopBeforeNode; 490 } 491 492 void Detach() 493 { 494 lastLockedNode = NULL; 495 nextNode = firstNode; 496 } 497 }; 498 499 500 struct FUSEVolume::RWLockableReadLocking { 501 RWLockableReadLocking(FUSEVolume* volume) 502 : 503 fLockManager(volume != NULL ? &volume->fLockManager : NULL) 504 { 505 } 506 507 RWLockableReadLocking(RWLockManager* lockManager) 508 : 509 fLockManager(lockManager) 510 { 511 } 512 513 RWLockableReadLocking(const RWLockableReadLocking& other) 514 : 515 fLockManager(other.fLockManager) 516 { 517 } 518 519 inline bool Lock(RWLockable* lockable) 520 { 521 return fLockManager != NULL && fLockManager->ReadLock(lockable); 522 } 523 524 inline void Unlock(RWLockable* lockable) 525 { 526 if (fLockManager != NULL) 527 fLockManager->ReadUnlock(lockable); 528 } 529 530 private: 531 RWLockManager* fLockManager; 532 }; 533 534 535 struct FUSEVolume::RWLockableWriteLocking { 536 RWLockableWriteLocking(FUSEVolume* volume) 537 : 538 fLockManager(volume != NULL ? &volume->fLockManager : NULL) 539 { 540 } 541 542 RWLockableWriteLocking(RWLockManager* lockManager) 543 : 544 fLockManager(lockManager) 545 { 546 } 547 548 RWLockableWriteLocking(const RWLockableWriteLocking& other) 549 : 550 fLockManager(other.fLockManager) 551 { 552 } 553 554 inline bool Lock(RWLockable* lockable) 555 { 556 return fLockManager != NULL && fLockManager->WriteLock(lockable); 557 } 558 559 inline void Unlock(RWLockable* lockable) 560 { 561 if (fLockManager != NULL) 562 fLockManager->WriteUnlock(lockable); 563 } 564 565 private: 566 RWLockManager* fLockManager; 567 }; 568 569 570 struct FUSEVolume::RWLockableReadLocker 571 : public AutoLocker<RWLockable, RWLockableReadLocking> { 572 573 RWLockableReadLocker(FUSEVolume* volume, RWLockable* lockable) 574 : 575 AutoLocker<RWLockable, RWLockableReadLocking>( 576 RWLockableReadLocking(volume)) 577 { 578 SetTo(lockable, false); 579 } 580 }; 581 582 583 struct FUSEVolume::RWLockableWriteLocker 584 : public AutoLocker<RWLockable, RWLockableWriteLocking> { 585 586 RWLockableWriteLocker(FUSEVolume* volume, RWLockable* lockable) 587 : 588 AutoLocker<RWLockable, RWLockableWriteLocking>( 589 RWLockableWriteLocking(volume)) 590 { 591 SetTo(lockable, false); 592 } 593 }; 594 595 596 struct FUSEVolume::NodeLocker { 597 NodeLocker(FUSEVolume* volume, FUSENode* node, bool parent, bool writeLock) 598 : 599 fVolume(volume), 600 fNode(NULL), 601 fParent(parent), 602 fWriteLock(writeLock) 603 { 604 fStatus = volume->_LockNodeChain(node, parent, writeLock); 605 if (fStatus == B_OK) 606 fNode = node; 607 } 608 609 ~NodeLocker() 610 { 611 if (fNode != NULL) 612 fVolume->_UnlockNodeChain(fNode, fParent, fWriteLock); 613 } 614 615 status_t Status() const 616 { 617 return fStatus; 618 } 619 620 private: 621 FUSEVolume* fVolume; 622 FUSENode* fNode; 623 status_t fStatus; 624 bool fParent; 625 bool fWriteLock; 626 }; 627 628 629 struct FUSEVolume::NodeReadLocker : NodeLocker { 630 NodeReadLocker(FUSEVolume* volume, FUSENode* node, bool parent) 631 : 632 NodeLocker(volume, node, parent, false) 633 { 634 } 635 }; 636 637 638 struct FUSEVolume::NodeWriteLocker : NodeLocker { 639 NodeWriteLocker(FUSEVolume* volume, FUSENode* node, bool parent) 640 : 641 NodeLocker(volume, node, parent, true) 642 { 643 } 644 }; 645 646 647 struct FUSEVolume::MultiNodeLocker { 648 MultiNodeLocker(FUSEVolume* volume, FUSENode* node1, bool lockParent1, 649 bool writeLock1, FUSENode* node2, bool lockParent2, bool writeLock2) 650 : 651 fVolume(volume), 652 fNode1(NULL), 653 fNode2(NULL), 654 fLockParent1(lockParent1), 655 fWriteLock1(writeLock1), 656 fLockParent2(lockParent2), 657 fWriteLock2(writeLock2) 658 { 659 fStatus = volume->_LockNodeChains(node1, lockParent1, writeLock1, node2, 660 lockParent2, writeLock2); 661 if (fStatus == B_OK) { 662 fNode1 = node1; 663 fNode2 = node2; 664 } 665 } 666 667 ~MultiNodeLocker() 668 { 669 if (fNode1 != NULL) { 670 fVolume->_UnlockNodeChains(fNode1, fLockParent1, fWriteLock1, 671 fNode2, fLockParent2, fWriteLock2); 672 } 673 } 674 675 status_t Status() const 676 { 677 return fStatus; 678 } 679 680 private: 681 FUSEVolume* fVolume; 682 FUSENode* fNode1; 683 FUSENode* fNode2; 684 bool fLockParent1; 685 bool fWriteLock1; 686 bool fLockParent2; 687 bool fWriteLock2; 688 status_t fStatus; 689 }; 690 691 692 // #pragma mark - 693 694 695 FUSEVolume::FUSEVolume(FUSEFileSystem* fileSystem, dev_t id) 696 : 697 Volume(fileSystem, id), 698 fFS(NULL), 699 fRootNode(NULL), 700 fNextNodeID(FUSE_ROOT_ID + 1), 701 fUseNodeIDs(false) 702 { 703 } 704 705 706 FUSEVolume::~FUSEVolume() 707 { 708 } 709 710 711 status_t 712 FUSEVolume::Init() 713 { 714 // init lock manager 715 status_t error = fLockManager.Init(); 716 if (error != B_OK) 717 return error; 718 719 // init entry and node tables 720 error = fEntries.Init(); 721 if (error != B_OK) 722 return error; 723 724 error = fNodes.Init(); 725 if (error != B_OK) 726 return error; 727 728 // check lock 729 error = fLock.InitCheck(); 730 if (error != B_OK) 731 return error; 732 733 return B_OK; 734 } 735 736 737 // #pragma mark - FS 738 739 740 status_t 741 FUSEVolume::Mount(const char* device, uint32 flags, const char* parameters, 742 ino_t* rootID) 743 { 744 printf("FUSEVolume::Mount()\n"); 745 status_t error = _FileSystem()->InitClientFS(parameters); 746 if (error != B_OK) 747 RETURN_ERROR(error); 748 749 fOps = _FileSystem()->GetLowlevelOps(); 750 if (fOps == NULL) 751 fFS = _FileSystem()->GetFS(); 752 _FileSystem()->GetVolumeCapabilities(fCapabilities); 753 754 const fuse_config& config = _FileSystem()->GetFUSEConfig(); 755 fUseNodeIDs = config.use_ino; 756 757 // update the fuse_context::private_data field before calling into the FS 758 if (fFS != NULL) { 759 fuse_context* context = (fuse_context*)RequestThread::GetCurrentThread() 760 ->GetContext()->GetFSData(); 761 762 context->private_data = fFS->userData; 763 } 764 765 // get the root node 766 struct stat st; 767 768 int fuseError; 769 if (fOps != NULL) 770 fuseError = fuse_ll_getattr(fOps, FUSE_ROOT_ID, &st); 771 else 772 fuseError = fuse_fs_getattr(fFS, "/", &st); 773 774 if (fuseError != 0) 775 RETURN_ERROR(fuseError); 776 777 if (!fUseNodeIDs) 778 st.st_ino = FUSE_ROOT_ID; 779 780 // create a node and an entry object for the root node 781 AutoLocker<Locker> _(fLock); 782 FUSENode* node = new(std::nothrow) FUSENode(st.st_ino, st.st_mode & S_IFMT); 783 FUSEEntry* entry = node != NULL ? FUSEEntry::Create(node, "/", node) : NULL; 784 if (node == NULL || entry == NULL) { 785 delete node; 786 delete entry; 787 _FileSystem()->ExitClientFS(B_NO_MEMORY); 788 RETURN_ERROR(B_NO_MEMORY); 789 } 790 791 node->refCount++; // for the entry 792 node->entries.Add(entry); 793 fRootNode = node; 794 795 // insert the node and the entry 796 fNodes.Insert(node); 797 fEntries.Insert(entry); 798 799 // init the volume name 800 snprintf(fName, sizeof(fName), "%s Volume", _FileSystem()->GetName()); 801 802 // publish the root node 803 error = UserlandFS::KernelEmu::publish_vnode(fID, node->id, node, 804 node->type, 0, _FileSystem()->GetNodeCapabilities()); 805 if (error != B_OK) { 806 _FileSystem()->ExitClientFS(B_NO_MEMORY); 807 RETURN_ERROR(error); 808 } 809 810 *rootID = node->id; 811 812 return B_OK; 813 } 814 815 816 status_t 817 FUSEVolume::Unmount() 818 { 819 printf("FUSEVolume::Unmount()\n"); 820 _FileSystem()->ExitClientFS(B_OK); 821 return B_OK; 822 } 823 824 825 status_t 826 FUSEVolume::Sync() 827 { 828 PRINT(("FUSEVolume::Sync()\n")); 829 830 // There's no FUSE hook for sync'ing the whole FS. We need to individually 831 // fsync all nodes that have been marked dirty. To keep things simple, we 832 // hold the volume lock the whole time. That's a concurrency killer, but 833 // usually sync isn't invoked that often. 834 835 AutoLocker<Locker> _(fLock); 836 837 // iterate through all nodes 838 FUSENodeTable::Iterator it = fNodes.GetIterator(); 839 while (FUSENode* node = it.Next()) { 840 if (!node->dirty) 841 continue; 842 843 // node is dirty -- we have to sync it 844 845 int fuseError; 846 if (fOps != NULL) { 847 fuse_file_info cookie; 848 fuseError = fuse_ll_open(fOps, node->id, &cookie); 849 if (fuseError == 0) { 850 fuse_ll_fsync(fOps, node->id, 0, &cookie); 851 // full sync, not only data 852 fuse_ll_flush(fOps, node->id, &cookie); 853 fuse_ll_release(fOps, node->id, &cookie); 854 } 855 } else { 856 // get a path for the node 857 char path[B_PATH_NAME_LENGTH]; 858 size_t pathLen; 859 status_t error = _BuildPath(node, path, pathLen); 860 if (error != B_OK) 861 continue; 862 863 // open, sync, and close the node 864 FileCookie cookie(O_RDONLY); 865 fuseError = fuse_fs_open(fFS, path, &cookie); 866 if (fuseError == 0) { 867 fuseError = fuse_fs_fsync(fFS, path, 0, &cookie); 868 // full sync, not only data 869 fuse_fs_flush(fFS, path, &cookie); 870 fuse_fs_release(fFS, path, &cookie); 871 } 872 } 873 874 if (fuseError == 0) { 875 // sync'ing successful -- mark the node not dirty 876 node->dirty = false; 877 } 878 } 879 880 return B_OK; 881 } 882 883 884 status_t 885 FUSEVolume::ReadFSInfo(fs_info* info) 886 { 887 if (_FileSystem()->HasHaikuFuseExtensions() && fFS->ops.ioctl != NULL) { 888 int fuseError = fuse_fs_ioctl(fFS, "/", FUSE_HAIKU_GET_DRIVE_INFO, info, NULL, 889 sizeof(fs_info), NULL); 890 if (fuseError != 0) 891 return fuseError; 892 return B_OK; 893 } 894 895 // No Haiku FUSE extensions, so our knowledge is limited: use some values 896 // from statfs and make reasonable guesses for the rest of them. 897 struct statvfs st; 898 int fuseError; 899 900 if (fOps != NULL) { 901 fuseError = fuse_ll_statfs(fOps, FUSE_ROOT_ID, &st); 902 } else { 903 if (fFS->ops.statfs == NULL) 904 return B_UNSUPPORTED; 905 906 fuseError = fuse_fs_statfs(fFS, "/", &st); 907 } 908 909 if (fuseError != 0) 910 return fuseError; 911 912 memset(info, 0, sizeof(*info)); 913 info->flags = B_FS_IS_PERSISTENT; // assume the FS is persistent 914 info->block_size = st.f_bsize; 915 info->io_size = 64 * 1024; // some value 916 info->total_blocks = st.f_blocks; 917 info->free_blocks = st.f_bfree; 918 info->total_nodes = st.f_files; 919 info->free_nodes = 100; // st.f_favail is ignored by statfs() 920 strlcpy(info->volume_name, fName, sizeof(info->volume_name)); 921 // no way to get the real name (if any) 922 923 return B_OK; 924 } 925 926 927 // #pragma mark - vnodes 928 929 930 status_t 931 FUSEVolume::Lookup(void* _dir, const char* entryName, ino_t* vnid) 932 { 933 FUSENode* dir = (FUSENode*)_dir; 934 935 // lock the directory 936 NodeReadLocker nodeLocker(this, dir, false); 937 if (nodeLocker.Status() != B_OK) 938 RETURN_ERROR(nodeLocker.Status()); 939 940 // look the node up 941 FUSENode* node; 942 status_t error = _GetNode(dir, entryName, &node); 943 if (error != B_OK) 944 return error; 945 946 *vnid = node->id; 947 948 return B_OK; 949 } 950 951 952 status_t 953 FUSEVolume::GetVNodeName(void* _node, char* buffer, size_t bufferSize) 954 { 955 FUSENode* node = (FUSENode*)_node; 956 957 AutoLocker<Locker> _(fLock); 958 959 // get one of the node's entries and return its name 960 FUSEEntry* entry = node->entries.Head(); 961 if (entry == NULL) 962 RETURN_ERROR(B_ENTRY_NOT_FOUND); 963 964 if (entry->name == NULL || entry->name[0] == '\0') 965 RETURN_ERROR(B_BAD_DATA); 966 967 if (strlcpy(buffer, entry->name, bufferSize) >= bufferSize) 968 RETURN_ERROR(B_NAME_TOO_LONG); 969 970 return B_OK; 971 } 972 973 974 status_t 975 FUSEVolume::ReadVNode(ino_t vnid, bool reenter, void** _node, int* type, 976 uint32* flags, FSVNodeCapabilities* _capabilities) 977 { 978 AutoLocker<Locker> _(fLock); 979 980 FUSENode* node = fNodes.Lookup(vnid); 981 if (node == NULL) 982 RETURN_ERROR(B_ENTRY_NOT_FOUND); 983 984 node->refCount++; 985 986 *_node = node; 987 *type = node->type; 988 *flags = 0; 989 *_capabilities = _FileSystem()->GetNodeCapabilities(); 990 991 return B_OK; 992 } 993 994 995 status_t 996 FUSEVolume::WriteVNode(void* _node, bool reenter) 997 { 998 FUSENode* node = (FUSENode*)_node; 999 1000 AutoLocker<Locker> _(fLock); 1001 1002 _PutNode(node); 1003 1004 return B_OK; 1005 } 1006 1007 1008 status_t 1009 FUSEVolume::RemoveVNode(void* node, bool reenter) 1010 { 1011 // TODO: Implement for real! 1012 return WriteVNode(node, reenter); 1013 } 1014 1015 1016 // #pragma mark - asynchronous I/O 1017 1018 1019 status_t 1020 FUSEVolume::DoIO(void* _node, void* _cookie, const IORequestInfo& requestInfo) 1021 { 1022 FUSENode* node = (FUSENode*)_node; 1023 FileCookie* cookie = (FileCookie*)_cookie; 1024 1025 NodeReadLocker nodeLocker(this, node, true); 1026 if (nodeLocker.Status() != B_OK) 1027 RETURN_ERROR(nodeLocker.Status()); 1028 1029 if (!S_ISREG(node->type)) 1030 RETURN_ERROR(B_BAD_VALUE); 1031 1032 BStackOrHeapArray<char, B_PAGE_SIZE> buffer(requestInfo.length); 1033 1034 char path[B_PATH_NAME_LENGTH]; 1035 size_t pathLen; 1036 1037 int fuseError = 0; 1038 status_t error = B_OK; 1039 1040 FileCookie alternativeCookie(requestInfo.isWrite ? O_WRONLY : O_RDONLY); 1041 if (cookie == NULL) { 1042 cookie = &alternativeCookie; 1043 1044 if (fOps != NULL) { 1045 fuseError = fuse_ll_open(fOps, node->id, cookie); 1046 } else { 1047 AutoLocker<Locker> locker(fLock); 1048 1049 error = _BuildPath(node, path, pathLen); 1050 if (error != B_OK) 1051 RETURN_ERROR(error); 1052 1053 locker.Unlock(); 1054 1055 fuseError = fuse_fs_open(fFS, path, cookie); 1056 } 1057 } 1058 1059 if (fuseError != 0) 1060 RETURN_ERROR(fuseError); 1061 1062 RWLockableReadLocker cookieLocker(this, cookie); 1063 1064 if (requestInfo.isWrite) { 1065 error = UserlandFS::KernelEmu::read_from_io_request(GetID(), requestInfo.id, buffer, 1066 requestInfo.length); 1067 } 1068 1069 size_t bytes = 0; 1070 if (error == B_OK) { 1071 bytes = requestInfo.length; 1072 error = _InternalIO(node, cookie, path, requestInfo.offset, 1073 buffer, bytes, requestInfo.isWrite); 1074 } 1075 1076 if (error == B_OK && !requestInfo.isWrite) { 1077 error = UserlandFS::KernelEmu::write_to_io_request(GetID(), requestInfo.id, 1078 buffer, bytes); 1079 } 1080 1081 UserlandFS::KernelEmu::notify_io_request(GetID(), requestInfo.id, error); 1082 1083 if (cookie == &alternativeCookie) { 1084 if (fOps != NULL) 1085 fuse_ll_release(fOps, node->id, cookie); 1086 else 1087 fuse_fs_release(fFS, path, cookie); 1088 } 1089 1090 if (error != B_OK) 1091 RETURN_ERROR(error); 1092 1093 return B_OK; 1094 } 1095 1096 1097 // #pragma mark - nodes 1098 1099 1100 status_t 1101 FUSEVolume::SetFlags(void* _node, void* _cookie, int flags) 1102 { 1103 FileCookie* cookie = (FileCookie*)_cookie; 1104 1105 RWLockableWriteLocker cookieLocker(this, cookie); 1106 1107 const int settableFlags = O_APPEND | O_NONBLOCK | O_SYNC | O_RSYNC 1108 | O_DSYNC | O_DIRECT; 1109 1110 cookie->flags = (cookie->flags & ~settableFlags) | (flags & settableFlags); 1111 1112 return B_OK; 1113 } 1114 1115 1116 status_t 1117 FUSEVolume::FSync(void* _node) 1118 { 1119 FUSENode* node = (FUSENode*)_node; 1120 1121 // lock the directory 1122 NodeReadLocker nodeLocker(this, node, true); 1123 if (nodeLocker.Status() != B_OK) 1124 RETURN_ERROR(nodeLocker.Status()); 1125 1126 int fuseError; 1127 bool dirty; 1128 AutoLocker<Locker> locker(fLock); 1129 if (fOps != NULL) { 1130 // mark the node not dirty 1131 dirty = node->dirty; 1132 node->dirty = false; 1133 1134 locker.Unlock(); 1135 1136 fuse_file_info cookie; 1137 fuseError = fuse_ll_open(fOps, node->id, &cookie); 1138 if (fuseError == 0) { 1139 fuseError = fuse_ll_fsync(fOps, node->id, 0, &cookie); 1140 // full sync, not only data 1141 fuse_ll_flush(fOps, node->id, &cookie); 1142 fuse_ll_release(fOps, node->id, &cookie); 1143 } 1144 } else { 1145 // get a path for the node 1146 char path[B_PATH_NAME_LENGTH]; 1147 size_t pathLen; 1148 status_t error = _BuildPath(node, path, pathLen); 1149 if (error != B_OK) 1150 RETURN_ERROR(error); 1151 1152 // mark the node not dirty 1153 dirty = node->dirty; 1154 node->dirty = false; 1155 1156 locker.Unlock(); 1157 1158 // open, sync, and close the node 1159 FileCookie cookie(O_RDONLY); 1160 fuseError = fuse_fs_open(fFS, path, &cookie); 1161 if (fuseError == 0) { 1162 fuseError = fuse_fs_fsync(fFS, path, 0, &cookie); 1163 // full sync, not only data 1164 fuse_fs_flush(fFS, path, &cookie); 1165 fuse_fs_release(fFS, path, &cookie); 1166 } 1167 } 1168 1169 if (fuseError != 0) { 1170 // sync'ing failed -- mark the node dirty again 1171 locker.Lock(); 1172 node->dirty |= dirty; 1173 RETURN_ERROR(fuseError); 1174 } 1175 1176 return B_OK; 1177 } 1178 1179 1180 status_t 1181 FUSEVolume::ReadSymlink(void* _node, char* buffer, size_t bufferSize, 1182 size_t* _bytesRead) 1183 { 1184 FUSENode* node = (FUSENode*)_node; 1185 1186 // lock the directory 1187 NodeReadLocker nodeLocker(this, node, true); 1188 if (nodeLocker.Status() != B_OK) 1189 RETURN_ERROR(nodeLocker.Status()); 1190 1191 int fuseError; 1192 if (fOps != NULL) { 1193 fuseError = fuse_ll_readlink(fOps, node->id, buffer, bufferSize); 1194 if (fuseError != 0) { 1195 *_bytesRead = 0; 1196 return fuseError; 1197 } 1198 // fuse_ll_readlink returns the actual size (even if the data didn't fit the buffer) 1199 *_bytesRead = fuseError; 1200 } else { 1201 AutoLocker<Locker> locker(fLock); 1202 1203 // get a path for the node 1204 char path[B_PATH_NAME_LENGTH]; 1205 size_t pathLen; 1206 status_t error = _BuildPath(node, path, pathLen); 1207 if (error != B_OK) 1208 RETURN_ERROR(error); 1209 1210 locker.Unlock(); 1211 1212 // read the symlink 1213 int fuseError = fuse_fs_readlink(fFS, path, buffer, bufferSize); 1214 if (fuseError != 0) { 1215 *_bytesRead = 0; 1216 return fuseError; 1217 } 1218 1219 // fuse_fs_readlink() is supposed to return a NULL-terminated string, which 1220 // the Haiku interface doesn't require. We have to return the string length, 1221 // though. 1222 *_bytesRead = strnlen(buffer, bufferSize); 1223 } 1224 1225 return B_OK; 1226 } 1227 1228 1229 status_t 1230 FUSEVolume::CreateSymlink(void* _dir, const char* name, const char* target, 1231 int mode) 1232 { 1233 FUSENode* dir = (FUSENode*)_dir; 1234 PRINT(("FUSEVolume::CreateSymlink(%p (%" B_PRId64 "), \"%s\" -> \"%s\", " 1235 "%#x)\n", dir, dir->id, name, target, mode)); 1236 1237 // lock the directory 1238 NodeWriteLocker nodeLocker(this, dir, false); 1239 if (nodeLocker.Status() != B_OK) 1240 RETURN_ERROR(nodeLocker.Status()); 1241 1242 int fuseError; 1243 if (fOps != NULL) { 1244 fuseError = fuse_ll_symlink(fOps, target, dir->id, name); 1245 } else { 1246 AutoLocker<Locker> locker(fLock); 1247 1248 // get a path for the entry 1249 char path[B_PATH_NAME_LENGTH]; 1250 size_t pathLen; 1251 status_t error = _BuildPath(dir, name, path, pathLen); 1252 if (error != B_OK) 1253 RETURN_ERROR(error); 1254 1255 locker.Unlock(); 1256 1257 // create the symlink 1258 fuseError = fuse_fs_symlink(fFS, target, path); 1259 } 1260 1261 if (fuseError != 0) 1262 RETURN_ERROR(fuseError); 1263 1264 // TODO: Set the mode?! 1265 1266 // mark the dir dirty 1267 AutoLocker<Locker> locker(fLock); 1268 dir->dirty = true; 1269 locker.Unlock(); 1270 1271 // send node monitoring message 1272 ino_t nodeID; 1273 if (_GetNodeID(dir, name, &nodeID)) { 1274 UserlandFS::KernelEmu::notify_listener(B_ENTRY_CREATED, 0, fID, 0, 1275 dir->id, nodeID, NULL, name); 1276 } 1277 1278 return B_OK; 1279 } 1280 1281 1282 status_t 1283 FUSEVolume::Link(void* _dir, const char* name, void* _node) 1284 { 1285 FUSENode* dir = (FUSENode*)_dir; 1286 FUSENode* node = (FUSENode*)_node; 1287 PRINT(("FUSEVolume::Link(%p (%" B_PRId64 "), \"%s\" -> %p (%" B_PRId64 1288 "))\n", dir, dir->id, name, node, node->id)); 1289 1290 // lock the directories -- the target directory for writing, the node's 1291 // parent for reading 1292 MultiNodeLocker nodeLocker(this, dir, false, true, node, true, false); 1293 if (nodeLocker.Status() != B_OK) 1294 RETURN_ERROR(nodeLocker.Status()); 1295 1296 int fuseError; 1297 if (fOps != NULL) { 1298 fuseError = fuse_ll_link(fOps, node->id, dir->id, name); 1299 } else { 1300 AutoLocker<Locker> locker(fLock); 1301 1302 // get a path for the entries 1303 char oldPath[B_PATH_NAME_LENGTH]; 1304 size_t oldPathLen; 1305 status_t error = _BuildPath(node, oldPath, oldPathLen); 1306 if (error != B_OK) 1307 RETURN_ERROR(error); 1308 1309 char newPath[B_PATH_NAME_LENGTH]; 1310 size_t newPathLen; 1311 error = _BuildPath(dir, name, newPath, newPathLen); 1312 if (error != B_OK) 1313 RETURN_ERROR(error); 1314 1315 locker.Unlock(); 1316 1317 // link 1318 fuseError = fuse_fs_link(fFS, oldPath, newPath); 1319 } 1320 if (fuseError != 0) 1321 RETURN_ERROR(fuseError); 1322 1323 // mark the dir and the node dirty 1324 AutoLocker<Locker> locker(fLock); 1325 dir->dirty = true; 1326 node->dirty = true; 1327 locker.Unlock(); 1328 1329 // send node monitoring message 1330 UserlandFS::KernelEmu::notify_listener(B_ENTRY_CREATED, 0, fID, 0, dir->id, 1331 node->id, NULL, name); 1332 1333 return B_OK; 1334 } 1335 1336 1337 status_t 1338 FUSEVolume::Unlink(void* _dir, const char* name) 1339 { 1340 FUSENode* dir = (FUSENode*)_dir; 1341 PRINT(("FUSEVolume::Unlink(%p (%" B_PRId64 "), \"%s\")\n", dir, dir->id, 1342 name)); 1343 1344 // lock the directory 1345 NodeWriteLocker nodeLocker(this, dir, false); 1346 if (nodeLocker.Status() != B_OK) 1347 RETURN_ERROR(nodeLocker.Status()); 1348 1349 // get the node ID (for the node monitoring message) 1350 ino_t nodeID; 1351 bool doNodeMonitoring = _GetNodeID(dir, name, &nodeID); 1352 1353 int fuseError; 1354 if (fOps != NULL) { 1355 fuseError = fuse_ll_unlink(fOps, dir->id, name); 1356 } else { 1357 AutoLocker<Locker> locker(fLock); 1358 1359 // get a path for the entry 1360 char path[B_PATH_NAME_LENGTH]; 1361 size_t pathLen; 1362 status_t error = _BuildPath(dir, name, path, pathLen); 1363 if (error != B_OK) 1364 RETURN_ERROR(error); 1365 1366 locker.Unlock(); 1367 1368 // unlink 1369 fuseError = fuse_fs_unlink(fFS, path); 1370 } 1371 if (fuseError != 0) 1372 RETURN_ERROR(fuseError); 1373 1374 // remove the entry 1375 AutoLocker<Locker> locker(fLock); 1376 _RemoveEntry(dir, name); 1377 1378 // mark the dir dirty 1379 dir->dirty = true; 1380 locker.Unlock(); 1381 1382 // send node monitoring message 1383 if (doNodeMonitoring) { 1384 UserlandFS::KernelEmu::notify_listener(B_ENTRY_REMOVED, 0, fID, 0, 1385 dir->id, nodeID, NULL, name); 1386 } 1387 1388 return B_OK; 1389 } 1390 1391 1392 status_t 1393 FUSEVolume::Rename(void* _oldDir, const char* oldName, void* _newDir, 1394 const char* newName) 1395 { 1396 FUSENode* oldDir = (FUSENode*)_oldDir; 1397 FUSENode* newDir = (FUSENode*)_newDir; 1398 PRINT(("FUSEVolume::Rename(%p (%" B_PRId64 "), \"%s\", %p (%" B_PRId64 1399 "), \"%s\")\n", oldDir, oldDir->id, oldName, newDir, newDir->id, 1400 newName)); 1401 1402 // lock the directories 1403 MultiNodeLocker nodeLocker(this, oldDir, false, true, newDir, false, true); 1404 if (nodeLocker.Status() != B_OK) 1405 RETURN_ERROR(nodeLocker.Status()); 1406 1407 int fuseError; 1408 if (fOps != NULL) { 1409 fuseError = fuse_ll_rename(fOps, oldDir->id, oldName, newDir->id, newName); 1410 } else { 1411 AutoLocker<Locker> locker(fLock); 1412 1413 // get a path for the entries 1414 char oldPath[B_PATH_NAME_LENGTH]; 1415 size_t oldPathLen; 1416 status_t error = _BuildPath(oldDir, oldName, oldPath, oldPathLen); 1417 if (error != B_OK) 1418 RETURN_ERROR(error); 1419 1420 char newPath[B_PATH_NAME_LENGTH]; 1421 size_t newPathLen; 1422 error = _BuildPath(newDir, newName, newPath, newPathLen); 1423 if (error != B_OK) 1424 RETURN_ERROR(error); 1425 1426 locker.Unlock(); 1427 1428 // rename 1429 fuseError = fuse_fs_rename(fFS, oldPath, newPath); 1430 } 1431 if (fuseError != 0) 1432 RETURN_ERROR(fuseError); 1433 1434 // rename the entry 1435 AutoLocker<Locker> locker(fLock); 1436 _RenameEntry(oldDir, oldName, newDir, newName); 1437 1438 // mark the dirs dirty 1439 oldDir->dirty = true; 1440 newDir->dirty = true; 1441 1442 // send node monitoring message 1443 ino_t nodeID; 1444 if (_GetNodeID(newDir, newName, &nodeID)) { 1445 UserlandFS::KernelEmu::notify_listener(B_ENTRY_MOVED, 0, fID, 1446 oldDir->id, newDir->id, nodeID, oldName, newName); 1447 } 1448 1449 return B_OK; 1450 } 1451 1452 1453 status_t 1454 FUSEVolume::Access(void* _node, int mode) 1455 { 1456 FUSENode* node = (FUSENode*)_node; 1457 1458 // lock the directory 1459 NodeReadLocker nodeLocker(this, node, true); 1460 if (nodeLocker.Status() != B_OK) 1461 RETURN_ERROR(nodeLocker.Status()); 1462 1463 int fuseError; 1464 if (fOps != NULL) { 1465 fuseError = fuse_ll_access(fOps, node->id, mode); 1466 } else { 1467 AutoLocker<Locker> locker(fLock); 1468 1469 // get a path for the node 1470 char path[B_PATH_NAME_LENGTH]; 1471 size_t pathLen; 1472 status_t error = _BuildPath(node, path, pathLen); 1473 if (error != B_OK) 1474 RETURN_ERROR(error); 1475 1476 locker.Unlock(); 1477 1478 // call the access hook on the path 1479 fuseError = fuse_fs_access(fFS, path, mode); 1480 } 1481 1482 if (fuseError != 0) 1483 return fuseError; 1484 1485 return B_OK; 1486 } 1487 1488 1489 status_t 1490 FUSEVolume::ReadStat(void* _node, struct stat* st) 1491 { 1492 FUSENode* node = (FUSENode*)_node; 1493 PRINT(("FUSEVolume::ReadStat(%p (%" B_PRId64 "), %p)\n", node, node->id, 1494 st)); 1495 1496 // lock the directory 1497 NodeReadLocker nodeLocker(this, node, true); 1498 if (nodeLocker.Status() != B_OK) 1499 RETURN_ERROR(nodeLocker.Status()); 1500 1501 st->st_dev = GetID(); 1502 st->st_ino = node->id; 1503 st->st_blksize = 2048; 1504 st->st_type = 0; 1505 1506 int fuseError; 1507 if (fOps != NULL) { 1508 fuseError = fuse_ll_getattr(fOps, node->id, st); 1509 } else { 1510 AutoLocker<Locker> locker(fLock); 1511 1512 // get a path for the node 1513 char path[B_PATH_NAME_LENGTH]; 1514 size_t pathLen; 1515 status_t error = _BuildPath(node, path, pathLen); 1516 if (error != B_OK) 1517 RETURN_ERROR(error); 1518 1519 locker.Unlock(); 1520 1521 // stat the path 1522 fuseError = fuse_fs_getattr(fFS, path, st); 1523 } 1524 if (fuseError != 0) 1525 return fuseError; 1526 1527 return B_OK; 1528 } 1529 1530 1531 status_t 1532 FUSEVolume::WriteStat(void* _node, const struct stat* st, uint32 mask) 1533 { 1534 FUSENode* node = (FUSENode*)_node; 1535 PRINT(("FUSEVolume::WriteStat(%p (%" B_PRId64 "), %p, %#" B_PRIx32 ")\n", 1536 node, node->id, st, mask)); 1537 1538 // lock the directory 1539 NodeReadLocker nodeLocker(this, node, true); 1540 if (nodeLocker.Status() != B_OK) 1541 RETURN_ERROR(nodeLocker.Status()); 1542 1543 if (fOps != NULL) { 1544 int fuseError = fuse_ll_setattr(fOps, node->id, st, mask); 1545 if (fuseError != 0) 1546 RETURN_ERROR(fuseError); 1547 } else { 1548 AutoLocker<Locker> locker(fLock); 1549 1550 // get a path for the node 1551 char path[B_PATH_NAME_LENGTH]; 1552 size_t pathLen; 1553 status_t error = _BuildPath(node, path, pathLen); 1554 if (error != B_OK) 1555 RETURN_ERROR(error); 1556 1557 locker.Unlock(); 1558 1559 // permissions 1560 if ((mask & B_STAT_MODE) != 0) { 1561 int fuseError = fuse_fs_chmod(fFS, path, st->st_mode); 1562 if (fuseError != 0) 1563 RETURN_ERROR(fuseError); 1564 } 1565 1566 // owner 1567 if ((mask & (B_STAT_UID | B_STAT_GID)) != 0) { 1568 uid_t uid = (mask & B_STAT_UID) != 0 ? st->st_uid : (uid_t)-1; 1569 gid_t gid = (mask & B_STAT_GID) != 0 ? st->st_gid : (gid_t)-1; 1570 int fuseError = fuse_fs_chown(fFS, path, uid, gid); 1571 if (fuseError != 0) 1572 RETURN_ERROR(fuseError); 1573 } 1574 1575 // size 1576 if ((mask & B_STAT_SIZE) != 0) { 1577 // truncate 1578 int fuseError = fuse_fs_truncate(fFS, path, st->st_size); 1579 if (fuseError != 0) 1580 RETURN_ERROR(fuseError); 1581 } 1582 1583 // access/modification time 1584 if ((mask & (B_STAT_ACCESS_TIME | B_STAT_MODIFICATION_TIME)) != 0) { 1585 timespec tv[2] = { 1586 {st->st_atime, 0}, 1587 {st->st_mtime, 0} 1588 }; 1589 1590 // If either time is not specified, we need to stat the file to get the 1591 // current value. 1592 if ((mask & (B_STAT_ACCESS_TIME | B_STAT_MODIFICATION_TIME)) 1593 != (B_STAT_ACCESS_TIME | B_STAT_MODIFICATION_TIME)) { 1594 struct stat currentStat; 1595 int fuseError = fuse_fs_getattr(fFS, path, ¤tStat); 1596 if (fuseError != 0) 1597 RETURN_ERROR(fuseError); 1598 1599 if ((mask & B_STAT_ACCESS_TIME) == 0) 1600 tv[0].tv_sec = currentStat.st_atime; 1601 else 1602 tv[1].tv_sec = currentStat.st_mtime; 1603 } 1604 1605 int fuseError = fuse_fs_utimens(fFS, path, tv); 1606 if (fuseError != 0) 1607 RETURN_ERROR(fuseError); 1608 } 1609 } 1610 1611 // mark the node dirty 1612 AutoLocker<Locker> locker(fLock); 1613 node->dirty = true; 1614 1615 // send node monitoring message 1616 uint32 changedFields = mask & 1617 (B_STAT_MODE | B_STAT_UID | B_STAT_GID | B_STAT_SIZE 1618 | B_STAT_ACCESS_TIME | B_STAT_MODIFICATION_TIME); 1619 1620 if (changedFields != 0) { 1621 UserlandFS::KernelEmu::notify_listener(B_STAT_CHANGED, changedFields, 1622 fID, 0, 0, node->id, NULL, NULL); 1623 } 1624 1625 return B_OK; 1626 } 1627 1628 1629 // #pragma mark - files 1630 1631 1632 status_t 1633 FUSEVolume::Create(void* _dir, const char* name, int openMode, int mode, 1634 void** _cookie, ino_t* _vnid) 1635 { 1636 FUSENode* dir = (FUSENode*)_dir; 1637 PRINT(("FUSEVolume::Create(%p (%" B_PRId64 "), \"%s\", %#x, %#x)\n", dir, 1638 dir->id, name, openMode, mode)); 1639 1640 // lock the directory 1641 NodeWriteLocker nodeLocker(this, dir, false); 1642 if (nodeLocker.Status() != B_OK) 1643 RETURN_ERROR(nodeLocker.Status()); 1644 1645 // allocate a file cookie 1646 FileCookie* cookie = new(std::nothrow) FileCookie(openMode); 1647 if (cookie == NULL) 1648 RETURN_ERROR(B_NO_MEMORY); 1649 ObjectDeleter<FileCookie> cookieDeleter(cookie); 1650 1651 FUSENode* node; 1652 int fuseError; 1653 if (fOps) { 1654 fuse_file_info cookie; 1655 fuse_ino_t ino; 1656 fuseError = fuse_ll_create(fOps, dir->id, name, mode, &cookie, ino); 1657 if (fuseError != 0) 1658 RETURN_ERROR(fuseError); 1659 1660 // get the node 1661 // TODO do we really need it? 1662 status_t error = _GetNode(dir, name, &node); 1663 if (error != B_OK) { 1664 // This is bad. We've create the file successfully, but couldn't get 1665 // the node. Delete the entry. 1666 // We can't close the file because we don't know its inode. 1667 fuse_ll_flush(fOps, ino, &cookie); 1668 fuse_ll_release(fOps, ino, &cookie); 1669 fuse_ll_unlink(fOps, dir->id, name); 1670 RETURN_ERROR(error); 1671 } 1672 } else { 1673 AutoLocker<Locker> locker(fLock); 1674 1675 // get a path for the node 1676 char path[B_PATH_NAME_LENGTH]; 1677 size_t pathLen; 1678 status_t error = _BuildPath(dir, name, path, pathLen); 1679 if (error != B_OK) 1680 RETURN_ERROR(error); 1681 1682 locker.Unlock(); 1683 1684 // create the file 1685 fuseError = fuse_fs_create(fFS, path, mode, cookie); 1686 if (fuseError != 0) 1687 RETURN_ERROR(fuseError); 1688 1689 // get the node 1690 error = _GetNode(dir, name, &node); 1691 if (error != B_OK) { 1692 // This is bad. We've create the file successfully, but couldn't get 1693 // the node. Close the file and delete the entry. 1694 fuse_fs_flush(fFS, path, cookie); 1695 fuse_fs_release(fFS, path, cookie); 1696 fuse_fs_unlink(fFS, path); 1697 RETURN_ERROR(error); 1698 } 1699 } 1700 1701 // mark the dir and the node dirty 1702 AutoLocker<Locker> locker(fLock); 1703 dir->dirty = true; 1704 node->dirty = true; 1705 locker.Unlock(); 1706 1707 // send node monitoring message 1708 UserlandFS::KernelEmu::notify_listener(B_ENTRY_CREATED, 0, fID, 0, dir->id, 1709 node->id, NULL, name); 1710 1711 cookieDeleter.Detach(); 1712 *_cookie = cookie; 1713 *_vnid = node->id; 1714 1715 return B_OK; 1716 } 1717 1718 1719 status_t 1720 FUSEVolume::Open(void* _node, int openMode, void** _cookie) 1721 { 1722 FUSENode* node = (FUSENode*)_node; 1723 PRINT(("FUSEVolume::Open(%p (%" B_PRId64 "), %#x)\n", node, node->id, 1724 openMode)); 1725 1726 // lock the directory 1727 NodeReadLocker nodeLocker(this, node, true); 1728 if (nodeLocker.Status() != B_OK) 1729 RETURN_ERROR(nodeLocker.Status()); 1730 1731 bool truncate = (openMode & O_TRUNC) != 0; 1732 openMode &= ~O_TRUNC; 1733 1734 // allocate a file cookie 1735 FileCookie* cookie = new(std::nothrow) FileCookie(openMode); 1736 if (cookie == NULL) 1737 RETURN_ERROR(B_NO_MEMORY); 1738 ObjectDeleter<FileCookie> cookieDeleter(cookie); 1739 1740 char path[B_PATH_NAME_LENGTH]; 1741 size_t pathLen; 1742 1743 int fuseError; 1744 struct fuse_file_info llCookie = { 0 }; 1745 // FIXME store this in the FileCookie for lowlevel streams, we'll need it in read, write... 1746 if (fOps != NULL) { 1747 llCookie.flags = openMode; 1748 if (S_ISDIR(node->type)) 1749 fuseError = fuse_ll_opendir(fOps, node->id, &llCookie); 1750 else 1751 fuseError = fuse_ll_open(fOps, node->id, &llCookie); 1752 } else { 1753 AutoLocker<Locker> locker(fLock); 1754 1755 // get a path for the node 1756 status_t error = _BuildPath(node, path, pathLen); 1757 if (error != B_OK) 1758 RETURN_ERROR(error); 1759 1760 locker.Unlock(); 1761 1762 // open the file 1763 fuseError = fuse_fs_open(fFS, path, cookie); 1764 } 1765 1766 if (fuseError != 0) 1767 RETURN_ERROR(fuseError); 1768 1769 // truncate the file, if requested 1770 if (truncate) { 1771 if (fOps != NULL) { 1772 struct stat st; 1773 st.st_size = 0; 1774 fuseError = fuse_ll_setattr(fOps, node->id, &st, FUSE_SET_ATTR_SIZE); 1775 1776 if (fuseError != 0) { 1777 fuse_ll_flush(fOps, node->id, &llCookie); 1778 fuse_ll_release(fOps, node->id, &llCookie); 1779 RETURN_ERROR(fuseError); 1780 } 1781 } else { 1782 fuseError = fuse_fs_ftruncate(fFS, path, 0, cookie); 1783 if (fuseError == ENOSYS) { 1784 // Fallback to truncate if ftruncate is not implemented 1785 fuseError = fuse_fs_truncate(fFS, path, 0); 1786 } 1787 if (fuseError != 0) { 1788 fuse_fs_flush(fFS, path, cookie); 1789 fuse_fs_release(fFS, path, cookie); 1790 RETURN_ERROR(fuseError); 1791 } 1792 } 1793 1794 // mark the node dirty 1795 AutoLocker<Locker> locker(fLock); 1796 node->dirty = true; 1797 1798 // send node monitoring message 1799 UserlandFS::KernelEmu::notify_listener(B_STAT_CHANGED, 1800 B_STAT_SIZE | B_STAT_MODIFICATION_TIME, fID, 0, 0, node->id, NULL, 1801 NULL); 1802 } 1803 1804 if (S_ISREG(node->type)) { 1805 // The caching logic does not seem to work quite right with many 1806 // filesystems (e.g. sshfs is one such): read past the end of a file 1807 // returns errors instead of no data, for instance. 1808 #if 0 1809 if (cookie->direct_io || llCookie.direct_io) { 1810 if (node->cacheCount > 0) { 1811 // In some very rare cases, for the same node, the first `open` 1812 // indicates that caching is allowed (by not setting `direct_io`), 1813 // but a subsequent `open` on the same node indicates that it is 1814 // NOT allowed (by setting `direct_io` to 1). 1815 debugger("FUSEVolume::Open(): inconsistent direct_io flags!"); 1816 UserlandFS::KernelEmu::file_cache_delete(GetID(), node->id); 1817 node->cacheCount = 0; 1818 } 1819 } else { 1820 if (node->cacheCount == 0) { 1821 struct stat st; 1822 if (fOps != NULL) { 1823 fuseError = fuse_ll_getattr(fOps, node->id, &st); 1824 } else { 1825 fuseError = fuse_fs_getattr(fFS, path, &st); 1826 } 1827 if (fuseError != 0) { 1828 RETURN_ERROR(fuseError); 1829 } 1830 status_t error = UserlandFS::KernelEmu::file_cache_create(GetID(), node->id, st.st_size); 1831 if (error != B_OK) { 1832 RETURN_ERROR(error); 1833 } 1834 } 1835 // Increment cacheCount by extra 1 if the cache is kept to prevent 1836 // the cache from being deleted at close(). 1837 node->cacheCount += 1 + cookie->keep_cache + llCookie.keep_cache; 1838 } 1839 #endif 1840 } 1841 1842 cookieDeleter.Detach(); 1843 *_cookie = cookie; 1844 1845 return B_OK; 1846 } 1847 1848 1849 status_t 1850 FUSEVolume::Close(void* _node, void* _cookie) 1851 { 1852 FUSENode* node = (FUSENode*)_node; 1853 FileCookie* cookie = (FileCookie*)_cookie; 1854 1855 RWLockableReadLocker cookieLocker(this, cookie); 1856 1857 // lock the directory 1858 NodeReadLocker nodeLocker(this, node, true); 1859 if (nodeLocker.Status() != B_OK) 1860 RETURN_ERROR(nodeLocker.Status()); 1861 1862 int fuseError; 1863 1864 if (fOps != NULL) { 1865 fuseError = fuse_ll_flush(fOps, node->id, cookie); 1866 } else { 1867 AutoLocker<Locker> locker(fLock); 1868 1869 // get a path for the node 1870 char path[B_PATH_NAME_LENGTH]; 1871 size_t pathLen; 1872 status_t error = _BuildPath(node, path, pathLen); 1873 if (error != B_OK) 1874 RETURN_ERROR(error); 1875 1876 locker.Unlock(); 1877 1878 // flush the file 1879 fuseError = fuse_fs_flush(fFS, path, cookie); 1880 } 1881 if (fuseError != 0) 1882 return fuseError; 1883 1884 if (S_ISREG(node->type) && node->cacheCount > 0) { 1885 --node->cacheCount; 1886 if (node->cacheCount == 0) { 1887 UserlandFS::KernelEmu::file_cache_delete(GetID(), node->id); 1888 } 1889 } 1890 1891 return B_OK; 1892 } 1893 1894 1895 status_t 1896 FUSEVolume::FreeCookie(void* _node, void* _cookie) 1897 { 1898 FUSENode* node = (FUSENode*)_node; 1899 FileCookie* cookie = (FileCookie*)_cookie; 1900 1901 // no need to lock the cookie here, as no-one else uses it anymore 1902 1903 // lock the directory 1904 NodeReadLocker nodeLocker(this, node, true); 1905 if (nodeLocker.Status() != B_OK) 1906 RETURN_ERROR(nodeLocker.Status()); 1907 1908 ObjectDeleter<FileCookie> cookieDeleter(cookie); 1909 1910 int fuseError; 1911 if (fOps) { 1912 fuseError = fuse_ll_release(fOps, node->id, cookie); 1913 } else { 1914 AutoLocker<Locker> locker(fLock); 1915 1916 // get a path for the node 1917 char path[B_PATH_NAME_LENGTH]; 1918 size_t pathLen; 1919 status_t error = _BuildPath(node, path, pathLen); 1920 if (error != B_OK) 1921 RETURN_ERROR(error); 1922 1923 locker.Unlock(); 1924 1925 // release the file 1926 fuseError = fuse_fs_release(fFS, path, cookie); 1927 } 1928 1929 if (fuseError != 0) 1930 return fuseError; 1931 1932 return B_OK; 1933 } 1934 1935 1936 status_t 1937 FUSEVolume::Read(void* _node, void* _cookie, off_t pos, void* buffer, 1938 size_t bufferSize, size_t* _bytesRead) 1939 { 1940 FUSENode* node = (FUSENode*)_node; 1941 FileCookie* cookie = (FileCookie*)_cookie; 1942 1943 RWLockableReadLocker cookieLocker(this, cookie); 1944 1945 // lock the directory 1946 NodeReadLocker nodeLocker(this, node, true); 1947 if (nodeLocker.Status() != B_OK) 1948 RETURN_ERROR(nodeLocker.Status()); 1949 1950 *_bytesRead = bufferSize; 1951 status_t error = B_OK; 1952 1953 if (S_ISREG(node->type) && node->cacheCount > 0) { 1954 error = UserlandFS::KernelEmu::file_cache_read(GetID(), node->id, cookie, pos, 1955 buffer, _bytesRead); 1956 } else 1957 error = _InternalIO(node, cookie, NULL, pos, (char *)buffer, *_bytesRead, false); 1958 1959 if (error != B_OK) 1960 RETURN_ERROR(error); 1961 1962 return B_OK; 1963 } 1964 1965 1966 status_t 1967 FUSEVolume::Write(void* _node, void* _cookie, off_t pos, const void* buffer, 1968 size_t bufferSize, size_t* _bytesWritten) 1969 { 1970 FUSENode* node = (FUSENode*)_node; 1971 FileCookie* cookie = (FileCookie*)_cookie; 1972 1973 RWLockableReadLocker cookieLocker(this, cookie); 1974 1975 *_bytesWritten = 0; 1976 1977 // lock the directory 1978 NodeReadLocker nodeLocker(this, node, true); 1979 if (nodeLocker.Status() != B_OK) 1980 RETURN_ERROR(nodeLocker.Status()); 1981 1982 *_bytesWritten = bufferSize; 1983 status_t error = B_OK; 1984 1985 if (S_ISREG(node->type) && node->cacheCount > 0) { 1986 error = UserlandFS::KernelEmu::file_cache_write(GetID(), node->id, cookie, pos, 1987 buffer, _bytesWritten); 1988 } else 1989 error = _InternalIO(node, cookie, NULL, pos, (char *)buffer, *_bytesWritten, true); 1990 1991 if (error != B_OK) 1992 RETURN_ERROR(error); 1993 1994 // mark the node dirty 1995 AutoLocker<Locker> locker(fLock); 1996 node->dirty = true; 1997 1998 // send node monitoring message 1999 UserlandFS::KernelEmu::notify_listener(B_STAT_CHANGED, 2000 B_STAT_SIZE | B_STAT_MODIFICATION_TIME, fID, 0, 0, node->id, NULL, 2001 NULL); 2002 // TODO: The size possibly doesn't change. 2003 // TODO: Avoid message flooding -- use a timeout and set the 2004 // B_STAT_INTERIM_UPDATE flag. 2005 2006 return B_OK; 2007 } 2008 2009 2010 // #pragma mark - directories 2011 2012 2013 status_t 2014 FUSEVolume::CreateDir(void* _dir, const char* name, int mode) 2015 { 2016 FUSENode* dir = (FUSENode*)_dir; 2017 PRINT(("FUSEVolume::CreateDir(%p (%" B_PRId64 "), \"%s\", %#x)\n", dir, 2018 dir->id, name, mode)); 2019 2020 // lock the directory 2021 NodeWriteLocker nodeLocker(this, dir, false); 2022 if (nodeLocker.Status() != B_OK) 2023 RETURN_ERROR(nodeLocker.Status()); 2024 2025 int fuseError; 2026 if (fOps != NULL) { 2027 fuseError = fuse_ll_mkdir(fOps, dir->id, name, mode); 2028 } else { 2029 AutoLocker<Locker> locker(fLock); 2030 2031 // get a path for the entry 2032 char path[B_PATH_NAME_LENGTH]; 2033 size_t pathLen; 2034 status_t error = _BuildPath(dir, name, path, pathLen); 2035 if (error != B_OK) 2036 RETURN_ERROR(error); 2037 2038 locker.Unlock(); 2039 2040 // create the dir 2041 fuseError = fuse_fs_mkdir(fFS, path, mode); 2042 } 2043 if (fuseError != 0) 2044 RETURN_ERROR(fuseError); 2045 2046 // mark the dir dirty 2047 AutoLocker<Locker> locker(fLock); 2048 dir->dirty = true; 2049 2050 // send node monitoring message 2051 ino_t nodeID; 2052 if (_GetNodeID(dir, name, &nodeID)) { 2053 UserlandFS::KernelEmu::notify_listener(B_ENTRY_CREATED, 0, fID, 0, 2054 dir->id, nodeID, NULL, name); 2055 } 2056 2057 return B_OK; 2058 } 2059 2060 2061 status_t 2062 FUSEVolume::RemoveDir(void* _dir, const char* name) 2063 { 2064 FUSENode* dir = (FUSENode*)_dir; 2065 PRINT(("FUSEVolume::RemoveDir(%p (%" B_PRId64 "), \"%s\")\n", dir, dir->id, 2066 name)); 2067 2068 // lock the directory 2069 NodeWriteLocker nodeLocker(this, dir, false); 2070 if (nodeLocker.Status() != B_OK) 2071 RETURN_ERROR(nodeLocker.Status()); 2072 2073 // get the node ID (for the node monitoring message) 2074 ino_t nodeID; 2075 bool doNodeMonitoring = _GetNodeID(dir, name, &nodeID); 2076 2077 int fuseError; 2078 if (fOps != NULL) { 2079 fuseError = fuse_ll_rmdir(fOps, dir->id, name); 2080 } else { 2081 AutoLocker<Locker> locker(fLock); 2082 2083 // get a path for the entry 2084 char path[B_PATH_NAME_LENGTH]; 2085 size_t pathLen; 2086 status_t error = _BuildPath(dir, name, path, pathLen); 2087 if (error != B_OK) 2088 RETURN_ERROR(error); 2089 2090 locker.Unlock(); 2091 2092 // remove the dir 2093 fuseError = fuse_fs_rmdir(fFS, path); 2094 } 2095 if (fuseError != 0) 2096 RETURN_ERROR(fuseError); 2097 2098 // remove the entry 2099 AutoLocker<Locker> locker(fLock); 2100 _RemoveEntry(dir, name); 2101 2102 // mark the parent dir dirty 2103 dir->dirty = true; 2104 2105 // send node monitoring message 2106 if (doNodeMonitoring) { 2107 UserlandFS::KernelEmu::notify_listener(B_ENTRY_REMOVED, 0, fID, 0, 2108 dir->id, nodeID, NULL, name); 2109 } 2110 2111 return B_OK; 2112 } 2113 2114 2115 status_t 2116 FUSEVolume::OpenDir(void* _node, void** _cookie) 2117 { 2118 FUSENode* node = (FUSENode*)_node; 2119 PRINT(("FUSEVolume::OpenDir(%p (%" B_PRId64 "), %p)\n", node, node->id, 2120 _cookie)); 2121 2122 // lock the parent directory 2123 NodeReadLocker nodeLocker(this, node, true); 2124 if (nodeLocker.Status() != B_OK) 2125 RETURN_ERROR(nodeLocker.Status()); 2126 2127 // allocate a dir cookie 2128 DirCookie* cookie = new(std::nothrow) DirCookie; 2129 if (cookie == NULL) 2130 RETURN_ERROR(B_NO_MEMORY); 2131 ObjectDeleter<DirCookie> cookieDeleter(cookie); 2132 2133 if (fOps) { 2134 int fuseError = fuse_ll_opendir(fOps, node->id, cookie); 2135 if (fuseError != 0) 2136 return fuseError; 2137 } else { 2138 AutoLocker<Locker> locker(fLock); 2139 2140 // get a path for the node 2141 char path[B_PATH_NAME_LENGTH]; 2142 size_t pathLen; 2143 status_t error = _BuildPath(node, path, pathLen); 2144 if (error != B_OK) 2145 RETURN_ERROR(error); 2146 2147 locker.Unlock(); 2148 2149 if (fFS->ops.readdir == NULL && fFS->ops.getdir != NULL) { 2150 // no open call -- the FS only supports the deprecated getdir() 2151 // interface 2152 cookie->getdirInterface = true; 2153 } else { 2154 // open the dir 2155 int fuseError = fuse_fs_opendir(fFS, path, cookie); 2156 if (fuseError != 0) 2157 return fuseError; 2158 } 2159 } 2160 2161 cookieDeleter.Detach(); 2162 *_cookie = cookie; 2163 2164 return B_OK; 2165 } 2166 2167 2168 status_t 2169 FUSEVolume::CloseDir(void* node, void* _cookie) 2170 { 2171 return B_OK; 2172 } 2173 2174 2175 status_t 2176 FUSEVolume::FreeDirCookie(void* _node, void* _cookie) 2177 { 2178 FUSENode* node = (FUSENode*)_node; 2179 DirCookie* cookie = (DirCookie*)_cookie; 2180 2181 // lock the parent directory 2182 NodeReadLocker nodeLocker(this, node, true); 2183 if (nodeLocker.Status() != B_OK) 2184 RETURN_ERROR(nodeLocker.Status()); 2185 2186 ObjectDeleter<DirCookie> cookieDeleter(cookie); 2187 2188 int fuseError; 2189 if (fOps != NULL) { 2190 fuseError = fuse_ll_releasedir(fOps, node->id, cookie); 2191 } else { 2192 if (cookie->getdirInterface) 2193 return B_OK; 2194 2195 AutoLocker<Locker> locker(fLock); 2196 2197 // get a path for the node 2198 char path[B_PATH_NAME_LENGTH]; 2199 size_t pathLen; 2200 status_t error = _BuildPath(node, path, pathLen); 2201 if (error != B_OK) 2202 RETURN_ERROR(error); 2203 2204 locker.Unlock(); 2205 2206 // release the dir 2207 fuseError = fuse_fs_releasedir(fFS, path, cookie); 2208 } 2209 2210 if (fuseError != 0) 2211 return fuseError; 2212 2213 return B_OK; 2214 } 2215 2216 2217 status_t 2218 FUSEVolume::ReadDir(void* _node, void* _cookie, void* buffer, size_t bufferSize, 2219 uint32 count, uint32* _countRead) 2220 { 2221 PRINT(("FUSEVolume::ReadDir(%p, %p, %p, %" B_PRIuSIZE ", %" B_PRId32 ")\n", 2222 _node, _cookie, buffer, bufferSize, count)); 2223 *_countRead = 0; 2224 2225 FUSENode* node = (FUSENode*)_node; 2226 DirCookie* cookie = (DirCookie*)_cookie; 2227 2228 RWLockableWriteLocker cookieLocker(this, cookie); 2229 2230 uint32 countRead = 0; 2231 status_t readDirError = B_OK; 2232 2233 AutoLocker<Locker> locker(fLock); 2234 2235 if (cookie->entryCache == NULL) { 2236 // We don't have an entry cache (yet), so we need to ask the client 2237 // file system to read the directory. 2238 2239 locker.Unlock(); 2240 2241 // lock the directory 2242 NodeReadLocker nodeLocker(this, node, false); 2243 if (nodeLocker.Status() != B_OK) 2244 RETURN_ERROR(nodeLocker.Status()); 2245 2246 locker.Lock(); 2247 2248 ReadDirBuffer readDirBuffer(this, node, cookie, buffer, bufferSize, count); 2249 off_t offset = cookie->currentEntryOffset; 2250 2251 // read the dir 2252 int fuseError; 2253 if (fOps != NULL) { 2254 locker.Unlock(); 2255 2256 // TODO pass the cookie from opendir here instead of NULL 2257 fuseError = fuse_ll_readdir(fOps, node->id, &readDirBuffer, (char*)buffer, bufferSize, 2258 &_AddReadDirEntryLowLevel, offset, NULL); 2259 2260 // The request filler may or may not be used. If the filesystem decides that it has 2261 // already cached the directory, it can reply with an already filled buffer from a 2262 // previous run. So, we can't rely on any updates done to the cookie by that function. 2263 // So we need to check the number of entries in the buffer, and advance the 2264 // currentEntryOffset. 2265 if (fuseError > 0) { 2266 struct dirent* dirent = (struct dirent*)buffer; 2267 while (countRead < count 2268 && (char*)dirent + dirent->d_reclen <= (char*)buffer + fuseError) { 2269 countRead++; 2270 dirent = (struct dirent*)(((char*)dirent) + dirent->d_reclen); 2271 if (dirent->d_reclen == 0) 2272 break; 2273 } 2274 cookie->currentEntryOffset += (char*)dirent - (char*)buffer; 2275 2276 fuseError = 0; 2277 } 2278 readDirError = 0; 2279 } else { 2280 // get a path for the node 2281 char path[B_PATH_NAME_LENGTH]; 2282 size_t pathLen; 2283 status_t error = _BuildPath(node, path, pathLen); 2284 if (error != B_OK) 2285 RETURN_ERROR(error); 2286 2287 locker.Unlock(); 2288 2289 if (cookie->getdirInterface) { 2290 PRINT((" using getdir() interface\n")); 2291 fuseError = fFS->ops.getdir(path, (fuse_dirh_t)&readDirBuffer, 2292 &_AddReadDirEntryGetDir); 2293 } else { 2294 PRINT((" using readdir() interface\n")); 2295 fuseError = fuse_fs_readdir(fFS, path, &readDirBuffer, 2296 &_AddReadDirEntry, offset, cookie); 2297 } 2298 2299 countRead = readDirBuffer.entriesRead; 2300 readDirError = readDirBuffer.error; 2301 } 2302 2303 if (fuseError != 0) 2304 return fuseError; 2305 2306 locker.Lock(); 2307 2308 } 2309 2310 if (cookie->entryCache != NULL) { 2311 // we're using an entry cache -- read into the buffer what we can 2312 dirent* entryBuffer = (dirent*)buffer; 2313 while (countRead < count 2314 && cookie->entryCache->ReadDirent(cookie->currentEntryIndex, fID, 2315 countRead + 1 < count, entryBuffer, bufferSize)) { 2316 countRead++; 2317 cookie->currentEntryIndex++; 2318 bufferSize -= entryBuffer->d_reclen; 2319 entryBuffer 2320 = (dirent*)((uint8*)entryBuffer + entryBuffer->d_reclen); 2321 } 2322 } 2323 2324 *_countRead = countRead; 2325 return countRead > 0 ? B_OK : readDirError; 2326 } 2327 2328 2329 status_t 2330 FUSEVolume::RewindDir(void* _node, void* _cookie) 2331 { 2332 PRINT(("FUSEVolume::RewindDir(%p, %p)\n", _node, _cookie)); 2333 DirCookie* cookie = (DirCookie*)_cookie; 2334 2335 RWLockableWriteLocker cookieLocker(this, cookie); 2336 2337 if (cookie->getdirInterface) { 2338 delete cookie->entryCache; 2339 cookie->entryCache = NULL; 2340 cookie->currentEntryIndex = 0; 2341 } else { 2342 cookie->currentEntryOffset = 0; 2343 } 2344 2345 return B_OK; 2346 } 2347 2348 2349 // #pragma mark - attribute directories 2350 2351 2352 // OpenAttrDir 2353 status_t 2354 FUSEVolume::OpenAttrDir(void* _node, void** _cookie) 2355 { 2356 // allocate an attribute directory cookie 2357 AttrDirCookie* cookie = new(std::nothrow) AttrDirCookie; 2358 if (cookie == NULL) 2359 RETURN_ERROR(B_NO_MEMORY); 2360 2361 *_cookie = cookie; 2362 2363 return B_OK; 2364 } 2365 2366 2367 // CloseAttrDir 2368 status_t 2369 FUSEVolume::CloseAttrDir(void* node, void* cookie) 2370 { 2371 return B_OK; 2372 } 2373 2374 2375 // FreeAttrDirCookie 2376 status_t 2377 FUSEVolume::FreeAttrDirCookie(void* _node, void* _cookie) 2378 { 2379 delete (AttrDirCookie*)_cookie; 2380 return B_OK; 2381 } 2382 2383 2384 // ReadAttrDir 2385 status_t 2386 FUSEVolume::ReadAttrDir(void* _node, void* _cookie, void* buffer, 2387 size_t bufferSize, uint32 count, uint32* _countRead) 2388 { 2389 FUSENode* node = (FUSENode*)_node; 2390 AttrDirCookie* cookie = (AttrDirCookie*)_cookie; 2391 2392 RWLockableWriteLocker cookieLocker(this, cookie); 2393 2394 *_countRead = 0; 2395 2396 // lock the directory 2397 NodeReadLocker nodeLocker(this, node, true); 2398 if (nodeLocker.Status() != B_OK) 2399 RETURN_ERROR(nodeLocker.Status()); 2400 2401 char path[B_PATH_NAME_LENGTH]; 2402 size_t pathLen; 2403 if (fOps == NULL) { 2404 AutoLocker<Locker> locker(fLock); 2405 2406 // get a path for the node 2407 status_t error = _BuildPath(node, path, pathLen); 2408 if (error != B_OK) 2409 RETURN_ERROR(error); 2410 2411 locker.Unlock(); 2412 } 2413 2414 if (!cookie->IsValid()) { 2415 // cookie not yet valid -- get the length of the list 2416 int listSize; 2417 if (fOps != NULL) 2418 listSize = fuse_ll_listxattr(fOps, node->id, NULL, 0); 2419 else 2420 listSize = fuse_fs_listxattr(fFS, path, NULL, 0); 2421 2422 if (listSize < 0) 2423 RETURN_ERROR(listSize); 2424 2425 while (true) { 2426 // allocate space for the listing 2427 status_t error = cookie->Allocate(listSize); 2428 if (error != B_OK) 2429 RETURN_ERROR(error); 2430 2431 // read the listing 2432 int bytesRead; 2433 if (fOps != NULL) { 2434 bytesRead = fuse_ll_listxattr(fOps, node->id, cookie->AttributesBuffer(), 2435 listSize); 2436 } else 2437 bytesRead = fuse_fs_listxattr(fFS, path, cookie->AttributesBuffer(), listSize); 2438 if (bytesRead < 0) 2439 RETURN_ERROR(bytesRead); 2440 2441 if (bytesRead == listSize) 2442 break; 2443 2444 // attributes listing changed -- reread it 2445 listSize = bytesRead; 2446 } 2447 2448 cookie->SetValid(true); 2449 } 2450 2451 // we have a valid cookie now -- get the next entries from the cookie 2452 uint32 countRead = 0; 2453 dirent* entryBuffer = (dirent*)buffer; 2454 while (countRead < count 2455 && cookie->ReadNextEntry(fID, node->id, countRead + 1 < count, 2456 entryBuffer, bufferSize)) { 2457 countRead++; 2458 bufferSize -= entryBuffer->d_reclen; 2459 entryBuffer = (dirent*)((uint8*)entryBuffer + entryBuffer->d_reclen); 2460 } 2461 2462 *_countRead = countRead; 2463 return B_OK; 2464 } 2465 2466 2467 // RewindAttrDir 2468 status_t 2469 FUSEVolume::RewindAttrDir(void* _node, void* _cookie) 2470 { 2471 AttrDirCookie* cookie = (AttrDirCookie*)_cookie; 2472 2473 RWLockableWriteLocker cookieLocker(this, cookie); 2474 2475 cookie->Clear(); 2476 2477 return B_OK; 2478 } 2479 2480 2481 // #pragma mark - attributes 2482 2483 2484 status_t 2485 FUSEVolume::OpenAttr(void* _node, const char* name, int openMode, 2486 void** _cookie) 2487 { 2488 FUSENode* node = (FUSENode*)_node; 2489 2490 // lock the node 2491 NodeReadLocker nodeLocker(this, node, true); 2492 if (nodeLocker.Status() != B_OK) 2493 RETURN_ERROR(nodeLocker.Status()); 2494 2495 if (openMode != O_RDONLY) { 2496 // Write support currently not implemented 2497 RETURN_ERROR(B_UNSUPPORTED); 2498 } 2499 2500 char path[B_PATH_NAME_LENGTH]; 2501 size_t pathLen; 2502 status_t error; 2503 int attrSize; 2504 if (fOps != NULL) { 2505 attrSize = fuse_ll_getxattr(fOps, node->id, name, NULL, 0); 2506 } else { 2507 AutoLocker<Locker> locker(fLock); 2508 2509 // get a path for the node 2510 error = _BuildPath(node, path, pathLen); 2511 if (error != B_OK) 2512 RETURN_ERROR(error); 2513 2514 locker.Unlock(); 2515 2516 attrSize = fuse_fs_getxattr(fFS, path, name, NULL, 0); 2517 } 2518 2519 if (attrSize < 0) { 2520 if (strcmp(name, kAttrMimeTypeName) == 0) { 2521 // Return a fake MIME type attribute based on the file extension 2522 const char* mimeType = NULL; 2523 error = set_mime(&mimeType, S_ISDIR(node->type) ? NULL : &path[0]); 2524 if (error != B_OK) 2525 return error; 2526 *_cookie = new(std::nothrow)AttrCookie(name, mimeType); 2527 return B_OK; 2528 } 2529 2530 // Reading attribute failed 2531 return attrSize; 2532 } 2533 2534 AttrCookie* cookie = new(std::nothrow)AttrCookie(name); 2535 if (cookie == NULL) 2536 RETURN_ERROR(B_NO_MEMORY); 2537 error = cookie->Allocate(attrSize); 2538 if (error != B_OK) { 2539 delete cookie; 2540 RETURN_ERROR(error); 2541 } 2542 2543 int bytesRead; 2544 if (fOps != NULL) 2545 bytesRead = fuse_ll_getxattr(fOps, node->id, name, cookie->Buffer(), attrSize); 2546 else 2547 bytesRead = fuse_fs_getxattr(fFS, path, name, cookie->Buffer(), attrSize); 2548 2549 if (bytesRead < 0) { 2550 delete cookie; 2551 return bytesRead; 2552 } 2553 2554 *_cookie = cookie; 2555 2556 return B_OK; 2557 } 2558 2559 2560 status_t 2561 FUSEVolume::CloseAttr(void* _node, void* _cookie) 2562 { 2563 return B_OK; 2564 } 2565 2566 2567 status_t 2568 FUSEVolume::FreeAttrCookie(void* _node, void* _cookie) 2569 { 2570 delete (AttrCookie*)_cookie; 2571 return B_OK; 2572 } 2573 2574 2575 status_t 2576 FUSEVolume::ReadAttr(void* _node, void* _cookie, off_t pos, void* buffer, 2577 size_t bufferSize, size_t* bytesRead) 2578 { 2579 AttrCookie* cookie = (AttrCookie*)_cookie; 2580 2581 RWLockableWriteLocker cookieLocker(this, cookie); 2582 2583 if (!cookie->IsValid()) 2584 RETURN_ERROR(B_BAD_VALUE); 2585 2586 cookie->Read(buffer, bufferSize, pos, bytesRead); 2587 2588 return B_OK; 2589 } 2590 2591 2592 status_t 2593 FUSEVolume::ReadAttrStat(void* _node, void* _cookie, struct stat* st) 2594 { 2595 AttrCookie* cookie = (AttrCookie*)_cookie; 2596 2597 RWLockableWriteLocker cookieLocker(this, cookie); 2598 2599 if (!cookie->IsValid()) 2600 RETURN_ERROR(B_BAD_VALUE); 2601 2602 st->st_size = cookie->Size(); 2603 st->st_type = cookie->Type(); 2604 2605 return B_OK; 2606 } 2607 2608 2609 // #pragma mark - 2610 2611 2612 ino_t 2613 FUSEVolume::_GenerateNodeID() 2614 { 2615 ino_t id; 2616 do { 2617 id = fNextNodeID++; 2618 } while (fNodes.Lookup(id) != NULL); 2619 2620 return id; 2621 } 2622 2623 2624 /*! Gets the ID of the node the entry specified by \a dir and \a entryName 2625 refers to. The ID is returned via \a _nodeID. The caller doesn't get a 2626 reference to the node. 2627 */ 2628 bool 2629 FUSEVolume::_GetNodeID(FUSENode* dir, const char* entryName, ino_t* _nodeID) 2630 { 2631 while (true) { 2632 AutoLocker<Locker> locker(fLock); 2633 2634 FUSENode* node; 2635 status_t error = _InternalGetNode(dir, entryName, &node, locker); 2636 if (error != B_OK) 2637 return false; 2638 2639 if (node == NULL) 2640 continue; 2641 2642 *_nodeID = node->id; 2643 _PutNode(node); 2644 2645 return true; 2646 } 2647 } 2648 2649 2650 /*! Gets the node the entry specified by \a dir and \a entryName refers to. The 2651 found node is returned via \a _node. The caller gets a reference to the node 2652 as well as a vnode reference. 2653 */ 2654 status_t 2655 FUSEVolume::_GetNode(FUSENode* dir, const char* entryName, FUSENode** _node) 2656 { 2657 while (true) { 2658 AutoLocker<Locker> locker(fLock); 2659 2660 FUSENode* node; 2661 status_t error = _InternalGetNode(dir, entryName, &node, locker); 2662 if (error != B_OK) 2663 return error; 2664 2665 if (node == NULL) 2666 continue; 2667 2668 ino_t nodeID = node->id; 2669 2670 locker.Unlock(); 2671 2672 // get a reference for the caller 2673 void* privateNode; 2674 error = UserlandFS::KernelEmu::get_vnode(fID, nodeID, &privateNode); 2675 if (error != B_OK) 2676 RETURN_ERROR(error); 2677 2678 locker.Lock(); 2679 2680 if (privateNode != node) { 2681 // weird, the node changed! 2682 ERROR(("FUSEVolume::_GetNode(): cookie for node %" B_PRId64 2683 " changed: expected: %p, got: %p\n", nodeID, node, 2684 privateNode)); 2685 UserlandFS::KernelEmu::put_vnode(fID, nodeID); 2686 _PutNode(node); 2687 continue; 2688 } 2689 2690 // Put the node reference we got from _InternalGetNode. We've now got 2691 // a reference from get_vnode(). 2692 _PutNode(node); 2693 2694 *_node = node; 2695 return B_OK; 2696 } 2697 } 2698 2699 2700 status_t 2701 FUSEVolume::_InternalGetNode(FUSENode* dir, const char* entryName, 2702 FUSENode** _node, AutoLocker<Locker>& locker) 2703 { 2704 // handle special cases 2705 if (strcmp(entryName, ".") == 0) { 2706 // same directory 2707 if (!S_ISDIR(dir->type)) 2708 RETURN_ERROR(B_NOT_A_DIRECTORY); 2709 2710 dir->refCount++; 2711 *_node = dir; 2712 return B_OK; 2713 } 2714 2715 if (strcmp(entryName, "..") == 0) { 2716 // parent directory 2717 if (!S_ISDIR(dir->type)) 2718 RETURN_ERROR(B_NOT_A_DIRECTORY); 2719 2720 FUSEEntry* entry = dir->entries.Head(); 2721 if (entry == NULL) 2722 RETURN_ERROR(B_ENTRY_NOT_FOUND); 2723 2724 entry->parent->refCount++; 2725 *_node = entry->parent; 2726 return B_OK; 2727 } 2728 2729 // lookup the entry in the table 2730 FUSEEntry* entry = fEntries.Lookup(FUSEEntryRef(dir->id, entryName)); 2731 if (entry != NULL) { 2732 entry->node->refCount++; 2733 *_node = entry->node; 2734 return B_OK; 2735 } 2736 2737 int fuseError; 2738 struct stat st; 2739 if (fOps != NULL) { 2740 fuseError = fuse_ll_lookup(fOps, dir->id, entryName, &st); 2741 } else { 2742 // construct a path for the entry 2743 char path[B_PATH_NAME_LENGTH]; 2744 size_t pathLen = 0; 2745 status_t error = _BuildPath(dir, entryName, path, pathLen); 2746 if (error != B_OK) 2747 return error; 2748 2749 locker.Unlock(); 2750 2751 // stat the path 2752 fuseError = fuse_fs_getattr(fFS, path, &st); 2753 } 2754 if (fuseError != 0) 2755 return fuseError; 2756 2757 // lookup the entry in the table again 2758 entry = fEntries.Lookup(FUSEEntryRef(dir->id, entryName)); 2759 if (entry != NULL) { 2760 // check whether the node still matches 2761 if (entry->node->id == st.st_ino) { 2762 entry->node->refCount++; 2763 *_node = entry->node; 2764 } else { 2765 // nope, something changed -- return a NULL node and let the caller 2766 // call us again 2767 *_node = NULL; 2768 } 2769 2770 return B_OK; 2771 } 2772 2773 // lookup the node in the table 2774 FUSENode* node = NULL; 2775 if (fUseNodeIDs) 2776 node = fNodes.Lookup(st.st_ino); 2777 else 2778 st.st_ino = _GenerateNodeID(); 2779 2780 if (node == NULL) { 2781 // no node yet -- create one 2782 node = new(std::nothrow) FUSENode(st.st_ino, st.st_mode & S_IFMT); 2783 if (node == NULL) 2784 RETURN_ERROR(B_NO_MEMORY); 2785 2786 fNodes.Insert(node); 2787 } else { 2788 // get a node reference for the entry 2789 node->refCount++; 2790 } 2791 2792 // create the entry 2793 entry = FUSEEntry::Create(dir, entryName, node); 2794 if (entry == NULL) { 2795 _PutNode(node); 2796 RETURN_ERROR(B_NO_MEMORY); 2797 } 2798 2799 dir->refCount++; 2800 // dir reference for the entry 2801 2802 fEntries.Insert(entry); 2803 node->entries.Add(entry); 2804 2805 locker.Unlock(); 2806 2807 // get a reference for the caller 2808 node->refCount++; 2809 2810 *_node = node; 2811 return B_OK; 2812 } 2813 2814 2815 void 2816 FUSEVolume::_PutNode(FUSENode* node) 2817 { 2818 if (--node->refCount == 0) { 2819 fNodes.Remove(node); 2820 if (node->cacheCount != 0) 2821 UserlandFS::KernelEmu::file_cache_delete(GetID(), node->id); 2822 delete node; 2823 } 2824 } 2825 2826 2827 void 2828 FUSEVolume::_PutNodes(FUSENode* const* nodes, int32 count) 2829 { 2830 for (int32 i = 0; i < count; i++) 2831 _PutNode(nodes[i]); 2832 } 2833 2834 2835 /*! Volume must be locked. The entry's directory must be write locked. 2836 */ 2837 void 2838 FUSEVolume::_RemoveEntry(FUSEEntry* entry) 2839 { 2840 fEntries.Remove(entry); 2841 entry->node->entries.Remove(entry); 2842 _PutNode(entry->node); 2843 _PutNode(entry->parent); 2844 delete entry; 2845 } 2846 2847 2848 /*! Volume must be locked. The directory must be write locked. 2849 */ 2850 status_t 2851 FUSEVolume::_RemoveEntry(FUSENode* dir, const char* name) 2852 { 2853 FUSEEntry* entry = fEntries.Lookup(FUSEEntryRef(dir->id, name)); 2854 if (entry == NULL) 2855 return B_ENTRY_NOT_FOUND; 2856 2857 _RemoveEntry(entry); 2858 return B_OK; 2859 } 2860 2861 2862 /*! Volume must be locked. The directories must be write locked. 2863 */ 2864 status_t 2865 FUSEVolume::_RenameEntry(FUSENode* oldDir, const char* oldName, 2866 FUSENode* newDir, const char* newName) 2867 { 2868 FUSEEntry* entry = fEntries.Lookup(FUSEEntryRef(oldDir->id, oldName)); 2869 if (entry == NULL) 2870 return B_ENTRY_NOT_FOUND; 2871 2872 // get a node reference for the new entry 2873 FUSENode* node = entry->node; 2874 node->refCount++; 2875 2876 // remove the old entry 2877 _RemoveEntry(entry); 2878 2879 // make sure there's no entry in our way 2880 _RemoveEntry(newDir, newName); 2881 2882 // create a new entry 2883 entry = FUSEEntry::Create(newDir, newName, node); 2884 if (entry == NULL) { 2885 _PutNode(node); 2886 RETURN_ERROR(B_NO_MEMORY); 2887 } 2888 2889 newDir->refCount++; 2890 // dir reference for the entry 2891 2892 fEntries.Insert(entry); 2893 node->entries.Add(entry); 2894 2895 return B_OK; 2896 } 2897 2898 2899 /*! Locks the given node and all of its ancestors up to the root. The given 2900 node is write-locked, if \a writeLock is \c true, read-locked otherwise. All 2901 ancestors are always read-locked in either case. 2902 2903 If \a lockParent is \c true, the given node itself is ignored, but locking 2904 starts with the parent node of the given node (\a writeLock applies to the 2905 parent node then). 2906 2907 If the method fails, none of the nodes is locked. 2908 2909 The volume lock must not be held. 2910 */ 2911 status_t 2912 FUSEVolume::_LockNodeChain(FUSENode* node, bool lockParent, bool writeLock) 2913 { 2914 AutoLocker<Locker> locker(fLock); 2915 2916 FUSENode* originalNode = node; 2917 2918 if (lockParent && node != NULL) 2919 node = node->Parent(); 2920 2921 if (node == NULL) 2922 RETURN_ERROR(B_ENTRY_NOT_FOUND); 2923 2924 LockIterator iterator(this, node, writeLock, NULL); 2925 2926 bool done; 2927 do { 2928 bool volumeUnlocked; 2929 status_t error = iterator.LockNext(&done, &volumeUnlocked); 2930 if (error != B_OK) 2931 RETURN_ERROR(error); 2932 2933 if (volumeUnlocked) { 2934 // check whether we're still locking the right node 2935 if (lockParent && originalNode->Parent() != node) { 2936 // We don't -- unlock everything and try again. 2937 node = originalNode->Parent(); 2938 iterator.SetTo(this, node, writeLock, NULL); 2939 } 2940 } 2941 } while (!done); 2942 2943 // Fail, if we couldn't lock all nodes up to the root. 2944 if (iterator.lastLockedNode != fRootNode) 2945 RETURN_ERROR(B_ENTRY_NOT_FOUND); 2946 2947 iterator.Detach(); 2948 return B_OK; 2949 } 2950 2951 2952 void 2953 FUSEVolume::_UnlockNodeChain(FUSENode* node, bool parent, bool writeLock) 2954 { 2955 AutoLocker<Locker> locker(fLock); 2956 2957 if (parent && node != NULL) 2958 node = node->Parent(); 2959 2960 _UnlockNodeChainInternal(node, writeLock, NULL, NULL); 2961 } 2962 2963 2964 /*! Unlocks all nodes from \a node up to (and including) \a stopNode (if 2965 \c NULL, it is ignored). If \a stopBeforeNode is given, the method stops 2966 before unlocking that node. 2967 The volume lock must be held. 2968 */ 2969 void 2970 FUSEVolume::_UnlockNodeChainInternal(FUSENode* node, bool writeLock, 2971 FUSENode* stopNode, FUSENode* stopBeforeNode) 2972 { 2973 FUSENode* originalNode = node; 2974 2975 while (node != NULL && node != stopBeforeNode) { 2976 FUSENode* parent = node->Parent(); 2977 2978 fLockManager.GenericUnlock(node == originalNode && writeLock, node); 2979 _PutNode(node); 2980 2981 if (node == stopNode || parent == node) 2982 break; 2983 2984 node = parent; 2985 } 2986 } 2987 2988 2989 status_t 2990 FUSEVolume::_LockNodeChains(FUSENode* node1, bool lockParent1, bool writeLock1, 2991 FUSENode* node2, bool lockParent2, bool writeLock2) 2992 { 2993 // Since in this case locking is more complicated, we use a helper method. 2994 // It does the main work, but simply returns telling us to retry when the 2995 // node hierarchy changes. 2996 bool retry; 2997 do { 2998 status_t error = _LockNodeChainsInternal(node1, lockParent1, writeLock1, 2999 node2, lockParent2, writeLock2, &retry); 3000 if (error != B_OK) 3001 return error; 3002 } while (retry); 3003 3004 return B_OK; 3005 } 3006 3007 3008 status_t 3009 FUSEVolume::_LockNodeChainsInternal(FUSENode* node1, bool lockParent1, 3010 bool writeLock1, FUSENode* node2, bool lockParent2, bool writeLock2, 3011 bool* _retry) 3012 { 3013 // Locking order: 3014 // * A child of a node has to be locked before its parent. 3015 // * Sibling nodes have to be locked in ascending node ID order. 3016 // 3017 // This implies the following locking algorithm: 3018 // * We find the closest common ancestor of the two given nodes (might even 3019 // be one of the given nodes). 3020 // * We lock all ancestors on one branch (the one with the lower common 3021 // ancestor child node ID), but not including the common ancestor. 3022 // * We lock all ancestors on the other branch, not including the common 3023 // ancestor. 3024 // * We lock the common ancestor and all of its ancestors up to the root 3025 // node. 3026 // 3027 // When the hierarchy changes while we're waiting for a lock, we recheck the 3028 // conditions and in doubt have to be restarted. 3029 3030 AutoLocker<Locker> locker(fLock); 3031 3032 FUSENode* originalNode1 = node1; 3033 FUSENode* originalNode2 = node2; 3034 3035 if (lockParent1 && node1 != NULL) 3036 node1 = node1->Parent(); 3037 3038 if (lockParent2 && node2 != NULL) 3039 node2 = node2->Parent(); 3040 3041 if (node1 == NULL || node2 == NULL) 3042 RETURN_ERROR(B_ENTRY_NOT_FOUND); 3043 3044 // find the first common ancestor 3045 FUSENode* commonAncestor; 3046 bool inverseLockingOrder; 3047 if (!_FindCommonAncestor(node1, node2, &commonAncestor, 3048 &inverseLockingOrder)) { 3049 RETURN_ERROR(B_ENTRY_NOT_FOUND); 3050 } 3051 3052 // lock the both node chains up to (but not including) the common ancestor 3053 LockIterator iterator1(this, node1, writeLock1, commonAncestor); 3054 LockIterator iterator2(this, node2, writeLock2, commonAncestor); 3055 3056 for (int i = 0; i < 2; i++) { 3057 LockIterator& iterator = (i == 0) != inverseLockingOrder 3058 ? iterator1 : iterator2; 3059 3060 // If the node is the common ancestor, don't enter the "do" loop, since 3061 // we don't have to lock anything here. 3062 if (iterator.firstNode == commonAncestor) 3063 continue; 3064 3065 bool done; 3066 do { 3067 bool volumeUnlocked; 3068 status_t error = iterator.LockNext(&done, &volumeUnlocked); 3069 if (error != B_OK) 3070 RETURN_ERROR(error); 3071 3072 if (volumeUnlocked) { 3073 // check whether we're still locking the right nodes 3074 if ((lockParent1 && originalNode1->Parent() != node1) 3075 || (lockParent2 && originalNode2->Parent() != node2)) { 3076 // We don't -- unlock everything and retry. 3077 *_retry = true; 3078 return B_OK; 3079 } 3080 3081 // also recheck the common ancestor 3082 FUSENode* newCommonParent; 3083 bool newInverseLockingOrder; 3084 if (!_FindCommonAncestor(node1, node2, &newCommonParent, 3085 &newInverseLockingOrder)) { 3086 RETURN_ERROR(B_ENTRY_NOT_FOUND); 3087 } 3088 3089 if (newCommonParent != commonAncestor 3090 || inverseLockingOrder != newInverseLockingOrder) { 3091 // Something changed -- unlock everything and retry. 3092 *_retry = true; 3093 return B_OK; 3094 } 3095 } 3096 } while (!done); 3097 } 3098 3099 // Continue locking from the common ancestor to the root. If one of the 3100 // given nodes is the common ancestor and shall be write locked, we need to 3101 // use the respective iterator. 3102 LockIterator& iterator = node2 == commonAncestor && writeLock2 3103 ? iterator2 : iterator1; 3104 iterator.SetStopBeforeNode(NULL); 3105 3106 bool done; 3107 do { 3108 bool volumeUnlocked; 3109 status_t error = iterator.LockNext(&done, &volumeUnlocked); 3110 if (error != B_OK) 3111 RETURN_ERROR(error); 3112 3113 if (volumeUnlocked) { 3114 // check whether we're still locking the right nodes 3115 if ((lockParent1 && originalNode1->Parent() != node1) 3116 || (lockParent2 && originalNode2->Parent() != node2)) { 3117 // We don't -- unlock everything and retry. 3118 *_retry = true; 3119 return B_OK; 3120 } 3121 3122 // Also recheck the common ancestor, if we have just locked it. 3123 // Otherwise we can just continue to lock, since nothing below the 3124 // previously locked node can have changed. 3125 if (iterator.lastLockedNode == commonAncestor) { 3126 FUSENode* newCommonParent; 3127 bool newInverseLockingOrder; 3128 if (!_FindCommonAncestor(node1, node2, &newCommonParent, 3129 &newInverseLockingOrder)) { 3130 RETURN_ERROR(B_ENTRY_NOT_FOUND); 3131 } 3132 3133 if (newCommonParent != commonAncestor 3134 || inverseLockingOrder != newInverseLockingOrder) { 3135 // Something changed -- unlock everything and retry. 3136 *_retry = true; 3137 return B_OK; 3138 } 3139 } 3140 } 3141 } while (!done); 3142 3143 // Fail, if we couldn't lock all nodes up to the root. 3144 if (iterator.lastLockedNode != fRootNode) 3145 RETURN_ERROR(B_ENTRY_NOT_FOUND); 3146 3147 // everything went fine 3148 iterator1.Detach(); 3149 iterator2.Detach(); 3150 3151 *_retry = false; 3152 return B_OK; 3153 } 3154 3155 3156 void 3157 FUSEVolume::_UnlockNodeChains(FUSENode* node1, bool lockParent1, 3158 bool writeLock1, FUSENode* node2, bool lockParent2, bool writeLock2) 3159 { 3160 AutoLocker<Locker> locker(fLock); 3161 3162 if (lockParent1 && node1 != NULL) 3163 node1 = node1->Parent(); 3164 3165 if (lockParent2 && node2 != NULL) 3166 node2 = node2->Parent(); 3167 3168 if (node1 == NULL || node2 == NULL) 3169 return; 3170 3171 // find the common ancestor 3172 FUSENode* commonAncestor; 3173 bool inverseLockingOrder; 3174 if (!_FindCommonAncestor(node1, node2, &commonAncestor, 3175 &inverseLockingOrder)) { 3176 return; 3177 } 3178 3179 // Unlock one branch up to the common ancestor and then the complete other 3180 // branch up to the root. If one of the given nodes is the common ancestor, 3181 // we need to make sure, we write-unlock it, if requested. 3182 if (node2 == commonAncestor && writeLock2) { 3183 _UnlockNodeChainInternal(node1, writeLock1, NULL, commonAncestor); 3184 _UnlockNodeChainInternal(node2, writeLock2, NULL, NULL); 3185 } else { 3186 _UnlockNodeChainInternal(node2, writeLock2, NULL, commonAncestor); 3187 _UnlockNodeChainInternal(node1, writeLock1, NULL, NULL); 3188 } 3189 } 3190 3191 3192 bool 3193 FUSEVolume::_FindCommonAncestor(FUSENode* node1, FUSENode* node2, 3194 FUSENode** _commonAncestor, bool* _inverseLockingOrder) 3195 { 3196 // handle trivial special case -- both nodes are the same 3197 if (node1 == node2) { 3198 *_commonAncestor = node1; 3199 *_inverseLockingOrder = false; 3200 return true; 3201 } 3202 3203 // get the ancestors of both nodes 3204 FUSENode* ancestors1[kMaxNodeTreeDepth]; 3205 FUSENode* ancestors2[kMaxNodeTreeDepth]; 3206 uint32 count1; 3207 uint32 count2; 3208 3209 if (!_GetNodeAncestors(node1, ancestors1, &count1) 3210 || !_GetNodeAncestors(node2, ancestors2, &count2)) { 3211 return false; 3212 } 3213 3214 // find the first ancestor not common to both nodes 3215 uint32 index = 0; 3216 for (; index < count1 && index < count2; index++) { 3217 FUSENode* ancestor1 = ancestors1[count1 - index - 1]; 3218 FUSENode* ancestor2 = ancestors2[count2 - index - 1]; 3219 if (ancestor1 != ancestor2) { 3220 *_commonAncestor = ancestors1[count1 - index]; 3221 *_inverseLockingOrder = ancestor1->id > ancestor2->id; 3222 return true; 3223 } 3224 } 3225 3226 // one node is an ancestor of the other 3227 *_commonAncestor = ancestors1[count1 - index]; 3228 *_inverseLockingOrder = index == count1; 3229 return true; 3230 } 3231 3232 3233 bool 3234 FUSEVolume::_GetNodeAncestors(FUSENode* node, FUSENode** ancestors, 3235 uint32* _count) 3236 { 3237 uint32 count = 0; 3238 while (node != NULL && count < kMaxNodeTreeDepth) { 3239 ancestors[count++] = node; 3240 3241 if (node == fRootNode) { 3242 *_count = count; 3243 return true; 3244 } 3245 3246 node = node->Parent(); 3247 } 3248 3249 // Either the node is not in the tree or we hit the array limit. 3250 return false; 3251 } 3252 3253 3254 status_t 3255 FUSEVolume::_BuildPath(FUSENode* dir, const char* entryName, char* path, 3256 size_t& pathLen) 3257 { 3258 // get the directory path 3259 status_t error = _BuildPath(dir, path, pathLen); 3260 if (error != B_OK) 3261 return error; 3262 3263 if (path[pathLen - 1] != '/') { 3264 path[pathLen++] = '/'; 3265 if (pathLen == B_PATH_NAME_LENGTH) 3266 RETURN_ERROR(B_NAME_TOO_LONG); 3267 } 3268 3269 // append the entry name 3270 size_t len = strlen(entryName); 3271 if (pathLen + len >= B_PATH_NAME_LENGTH) 3272 RETURN_ERROR(B_NAME_TOO_LONG); 3273 3274 memcpy(path + pathLen, entryName, len + 1); 3275 pathLen += len; 3276 3277 return B_OK; 3278 } 3279 3280 3281 status_t 3282 FUSEVolume::_BuildPath(FUSENode* node, char* path, size_t& pathLen) 3283 { 3284 if (node == fRootNode) { 3285 // we hit the root 3286 strcpy(path, "/"); 3287 pathLen = 1; 3288 return B_OK; 3289 } 3290 3291 // get an entry for the node and get its path 3292 FUSEEntry* entry = node->entries.Head(); 3293 if (entry == NULL) 3294 RETURN_ERROR(B_ENTRY_NOT_FOUND); 3295 3296 return _BuildPath(entry->parent, entry->name, path, pathLen); 3297 } 3298 3299 3300 /*static*/ int 3301 FUSEVolume::_AddReadDirEntryLowLevel(void* _buffer, char* buf, size_t bufsize, const char* name, 3302 const struct stat* st, off_t offset) 3303 { 3304 ReadDirBuffer* buffer = (ReadDirBuffer*)_buffer; 3305 3306 ino_t nodeID = st != NULL ? st->st_ino : 0; 3307 int type = st != NULL ? st->st_mode & S_IFMT : 0; 3308 return buffer->volume->_AddReadDirEntryLowLevel(buffer, buf, bufsize, name, type, nodeID, offset); 3309 } 3310 3311 3312 /*static*/ int 3313 FUSEVolume::_AddReadDirEntry(void* _buffer, const char* name, 3314 const struct stat* st, off_t offset) 3315 { 3316 ReadDirBuffer* buffer = (ReadDirBuffer*)_buffer; 3317 3318 ino_t nodeID = st != NULL ? st->st_ino : 0; 3319 int type = st != NULL ? st->st_mode & S_IFMT : 0; 3320 return buffer->volume->_AddReadDirEntry(buffer, name, type, nodeID, offset); 3321 } 3322 3323 3324 /*static*/ int 3325 FUSEVolume::_AddReadDirEntryGetDir(fuse_dirh_t handle, const char* name, 3326 int type, ino_t nodeID) 3327 { 3328 ReadDirBuffer* buffer = (ReadDirBuffer*)handle; 3329 return buffer->volume->_AddReadDirEntry(buffer, name, type << 12, nodeID, 0); 3330 } 3331 3332 3333 int 3334 FUSEVolume::_AddReadDirEntryLowLevel(ReadDirBuffer* buffer, char* buf, size_t bufsize, const char* name, 3335 int type, ino_t nodeID, off_t offset) 3336 { 3337 PRINT(("FUSEVolume::_AddReadDirEntryLowLevel(%p, \"%s\", %#x, %" B_PRId64 ", %" 3338 B_PRId64 "\n", buffer, name, type, nodeID, offset)); 3339 3340 AutoLocker<Locker> locker(fLock); 3341 3342 size_t entryLen = 0; 3343 3344 // create a node and an entry, if necessary 3345 ino_t dirID = buffer->directory->id; 3346 FUSEEntry* entry; 3347 if (strcmp(name, ".") == 0) { 3348 // current dir entry 3349 nodeID = dirID; 3350 type = S_IFDIR; 3351 } else if (strcmp(name, "..") == 0) { 3352 // parent dir entry 3353 FUSEEntry* parentEntry = buffer->directory->entries.Head(); 3354 if (parentEntry == NULL) { 3355 ERROR(("FUSEVolume::_AddReadDirEntry(): dir %" B_PRId64 3356 " has no entry!\n", dirID)); 3357 return 0; 3358 } 3359 nodeID = parentEntry->parent->id; 3360 type = S_IFDIR; 3361 } else if ((entry = fEntries.Lookup(FUSEEntryRef(dirID, name))) == NULL) { 3362 // get the node 3363 FUSENode* node = NULL; 3364 if (fUseNodeIDs) 3365 node = fNodes.Lookup(nodeID); 3366 else 3367 nodeID = _GenerateNodeID(); 3368 3369 if (node == NULL) { 3370 // no node yet -- create one 3371 3372 // If we don't have a valid type, we need to stat the node first. 3373 if (type == 0) { 3374 struct stat st; 3375 int fuseError; 3376 if (fOps != NULL) { 3377 fuseError = fuse_ll_getattr(fOps, node->id, &st); 3378 } else { 3379 char path[B_PATH_NAME_LENGTH]; 3380 size_t pathLen; 3381 status_t error = _BuildPath(buffer->directory, name, path, 3382 pathLen); 3383 if (error != B_OK) { 3384 buffer->error = error; 3385 return 0; 3386 } 3387 3388 locker.Unlock(); 3389 3390 // stat the path 3391 fuseError = fuse_fs_getattr(fFS, path, &st); 3392 } 3393 3394 locker.Lock(); 3395 3396 if (fuseError != 0) { 3397 buffer->error = fuseError; 3398 return 0; 3399 } 3400 3401 type = st.st_mode & S_IFMT; 3402 } 3403 3404 node = new(std::nothrow) FUSENode(nodeID, type); 3405 if (node == NULL) { 3406 buffer->error = B_NO_MEMORY; 3407 return 1; 3408 } 3409 PRINT((" -> create node: %p, id: %" B_PRId64 "\n", node, nodeID)); 3410 3411 fNodes.Insert(node); 3412 } else { 3413 // get a node reference for the entry 3414 node->refCount++; 3415 } 3416 3417 // create the entry 3418 entry = FUSEEntry::Create(buffer->directory, name, node); 3419 if (entry == NULL) { 3420 _PutNode(node); 3421 buffer->error = B_NO_MEMORY; 3422 return 1; 3423 } 3424 3425 buffer->directory->refCount++; 3426 // dir reference for the entry 3427 3428 fEntries.Insert(entry); 3429 node->entries.Add(entry); 3430 } else { 3431 // TODO: Check whether the node's ID matches the one we got (if any)! 3432 nodeID = entry->node->id; 3433 type = entry->node->type; 3434 } 3435 3436 // fill in the dirent 3437 dirent* dirEntry = (dirent*)(buf); 3438 dirEntry->d_dev = fID; 3439 dirEntry->d_ino = nodeID; 3440 strcpy(dirEntry->d_name, name); 3441 3442 // align the entry length, so the next dirent will be aligned 3443 entryLen = offsetof(struct dirent, d_name) + strlen(name) + 1; 3444 entryLen = ROUNDUP(entryLen, 8); 3445 3446 dirEntry->d_reclen = entryLen; 3447 3448 // update the buffer 3449 buffer->usedSize += entryLen; 3450 3451 return 0; 3452 } 3453 3454 3455 int 3456 FUSEVolume::_AddReadDirEntry(ReadDirBuffer* buffer, const char* name, 3457 int type, ino_t nodeID, off_t offset) 3458 { 3459 PRINT(("FUSEVolume::_AddReadDirEntry(%p, \"%s\", %#x, %" B_PRId64 ", %" 3460 B_PRId64 "\n", buffer, name, type, nodeID, offset)); 3461 3462 AutoLocker<Locker> locker(fLock); 3463 3464 size_t entryLen = 0; 3465 if (offset != 0) { 3466 // does the caller want more entries? 3467 if (buffer->entriesRead == buffer->maxEntries) 3468 return 1; 3469 3470 // compute the entry length and check whether the entry still fits 3471 entryLen = offsetof(struct dirent, d_name) + strlen(name) + 1; 3472 if (buffer->usedSize + entryLen > buffer->bufferSize) 3473 return 1; 3474 } 3475 3476 // create a node and an entry, if necessary 3477 ino_t dirID = buffer->directory->id; 3478 FUSEEntry* entry; 3479 if (strcmp(name, ".") == 0) { 3480 // current dir entry 3481 nodeID = dirID; 3482 type = S_IFDIR; 3483 } else if (strcmp(name, "..") == 0) { 3484 // parent dir entry 3485 FUSEEntry* parentEntry = buffer->directory->entries.Head(); 3486 if (parentEntry == NULL) { 3487 ERROR(("FUSEVolume::_AddReadDirEntry(): dir %" B_PRId64 3488 " has no entry!\n", dirID)); 3489 return 0; 3490 } 3491 nodeID = parentEntry->parent->id; 3492 type = S_IFDIR; 3493 } else if ((entry = fEntries.Lookup(FUSEEntryRef(dirID, name))) == NULL) { 3494 // get the node 3495 FUSENode* node = NULL; 3496 if (fUseNodeIDs) 3497 node = fNodes.Lookup(nodeID); 3498 else 3499 nodeID = _GenerateNodeID(); 3500 3501 if (node == NULL) { 3502 // no node yet -- create one 3503 3504 // If we don't have a valid type, we need to stat the node first. 3505 if (type == 0) { 3506 struct stat st; 3507 int fuseError; 3508 if (fOps != NULL) { 3509 fuseError = fuse_ll_getattr(fOps, node->id, &st); 3510 } else { 3511 char path[B_PATH_NAME_LENGTH]; 3512 size_t pathLen; 3513 status_t error = _BuildPath(buffer->directory, name, path, 3514 pathLen); 3515 if (error != B_OK) { 3516 buffer->error = error; 3517 return 0; 3518 } 3519 3520 locker.Unlock(); 3521 3522 // stat the path 3523 fuseError = fuse_fs_getattr(fFS, path, &st); 3524 } 3525 3526 locker.Lock(); 3527 3528 if (fuseError != 0) { 3529 buffer->error = fuseError; 3530 return 0; 3531 } 3532 3533 type = st.st_mode & S_IFMT; 3534 } 3535 3536 node = new(std::nothrow) FUSENode(nodeID, type); 3537 if (node == NULL) { 3538 buffer->error = B_NO_MEMORY; 3539 return 1; 3540 } 3541 PRINT((" -> create node: %p, id: %" B_PRId64 "\n", node, nodeID)); 3542 3543 fNodes.Insert(node); 3544 } else { 3545 // get a node reference for the entry 3546 node->refCount++; 3547 } 3548 3549 // create the entry 3550 entry = FUSEEntry::Create(buffer->directory, name, node); 3551 if (entry == NULL) { 3552 _PutNode(node); 3553 buffer->error = B_NO_MEMORY; 3554 return 1; 3555 } 3556 3557 buffer->directory->refCount++; 3558 // dir reference for the entry 3559 3560 fEntries.Insert(entry); 3561 node->entries.Add(entry); 3562 } else { 3563 // TODO: Check whether the node's ID matches the one we got (if any)! 3564 nodeID = entry->node->id; 3565 type = entry->node->type; 3566 } 3567 3568 if (offset == 0) { 3569 // cache the entry 3570 if (buffer->cookie->entryCache == NULL) { 3571 // no cache yet -- create it 3572 buffer->cookie->entryCache = new(std::nothrow) DirEntryCache; 3573 if (buffer->cookie->entryCache == NULL) { 3574 buffer->error = B_NO_MEMORY; 3575 return 1; 3576 } 3577 } 3578 3579 status_t error = buffer->cookie->entryCache->AddEntry(nodeID, name); 3580 if (error != B_OK) { 3581 buffer->error = error; 3582 return 1; 3583 } 3584 } else { 3585 // fill in the dirent 3586 dirent* dirEntry = (dirent*)((uint8*)buffer->buffer + buffer->usedSize); 3587 dirEntry->d_dev = fID; 3588 dirEntry->d_ino = nodeID; 3589 strcpy(dirEntry->d_name, name); 3590 3591 if (buffer->entriesRead + 1 < buffer->maxEntries) { 3592 // align the entry length, so the next dirent will be aligned 3593 entryLen = ROUNDUP(entryLen, 8); 3594 entryLen = std::min(entryLen, 3595 buffer->bufferSize - buffer->usedSize); 3596 } 3597 3598 dirEntry->d_reclen = entryLen; 3599 3600 // update the buffer 3601 buffer->usedSize += entryLen; 3602 buffer->entriesRead++; 3603 buffer->cookie->currentEntryOffset = offset; 3604 } 3605 3606 return 0; 3607 } 3608 3609 3610 status_t 3611 FUSEVolume::_InternalIO(FUSENode* node, FileCookie* cookie, const char* path, 3612 off_t pos, char* buffer, size_t& length, bool write) 3613 { 3614 PRINT(("FUSEVolume::_InternalIO(%p, %p, %s, %" B_PRIdOFF ", %p, %" B_PRIuSIZE ", %d)\n", 3615 node, cookie, path, pos, buffer, length, write)); 3616 3617 int fuseError; 3618 if (fOps != NULL) { 3619 if (write) 3620 fuseError = fuse_ll_write(fOps, node->id, buffer, length, pos, cookie); 3621 else 3622 fuseError = fuse_ll_read(fOps, node->id, buffer, length, pos, cookie); 3623 } else { 3624 char pathBuf[B_PATH_NAME_LENGTH]; 3625 3626 if (path == NULL) { 3627 AutoLocker<Locker> locker(fLock); 3628 3629 size_t pathLen; 3630 status_t error = _BuildPath(node, pathBuf, pathLen); 3631 if (error != B_OK) 3632 RETURN_ERROR(error); 3633 3634 locker.Unlock(); 3635 3636 path = pathBuf; 3637 } 3638 3639 if (write) 3640 fuseError = fuse_fs_write(fFS, path, buffer, length, pos, cookie); 3641 else 3642 fuseError = fuse_fs_read(fFS, path, buffer, length, pos, cookie); 3643 } 3644 3645 if (fuseError < 0) 3646 return fuseError; 3647 3648 length = fuseError; 3649 return B_OK; 3650 } 3651