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 if (error == B_OK) { 1070 size_t bytesTransferred = 0; 1071 while (bytesTransferred < requestInfo.length) { 1072 size_t bytes = requestInfo.length - bytesTransferred; 1073 error = _InternalIO(node, cookie, path, requestInfo.offset + bytesTransferred, 1074 buffer + bytesTransferred, bytes, requestInfo.isWrite); 1075 1076 if (error == B_OK) 1077 bytesTransferred += bytes; 1078 else 1079 break; 1080 } 1081 } 1082 1083 if (error == B_OK && !requestInfo.isWrite) { 1084 error = UserlandFS::KernelEmu::write_to_io_request(GetID(), requestInfo.id, buffer, 1085 requestInfo.length); 1086 } 1087 1088 UserlandFS::KernelEmu::notify_io_request(GetID(), requestInfo.id, error); 1089 1090 if (cookie == &alternativeCookie) { 1091 if (fOps != NULL) 1092 fuse_ll_release(fOps, node->id, cookie); 1093 else 1094 fuse_fs_release(fFS, path, cookie); 1095 } 1096 1097 if (error != B_OK) 1098 RETURN_ERROR(error); 1099 1100 return B_OK; 1101 } 1102 1103 1104 // #pragma mark - nodes 1105 1106 1107 status_t 1108 FUSEVolume::SetFlags(void* _node, void* _cookie, int flags) 1109 { 1110 FileCookie* cookie = (FileCookie*)_cookie; 1111 1112 RWLockableWriteLocker cookieLocker(this, cookie); 1113 1114 const int settableFlags = O_APPEND | O_NONBLOCK | O_SYNC | O_RSYNC 1115 | O_DSYNC | O_DIRECT; 1116 1117 cookie->flags = (cookie->flags & ~settableFlags) | (flags & settableFlags); 1118 1119 return B_OK; 1120 } 1121 1122 1123 status_t 1124 FUSEVolume::FSync(void* _node) 1125 { 1126 FUSENode* node = (FUSENode*)_node; 1127 1128 // lock the directory 1129 NodeReadLocker nodeLocker(this, node, true); 1130 if (nodeLocker.Status() != B_OK) 1131 RETURN_ERROR(nodeLocker.Status()); 1132 1133 int fuseError; 1134 bool dirty; 1135 AutoLocker<Locker> locker(fLock); 1136 if (fOps != NULL) { 1137 // mark the node not dirty 1138 dirty = node->dirty; 1139 node->dirty = false; 1140 1141 locker.Unlock(); 1142 1143 fuse_file_info cookie; 1144 fuseError = fuse_ll_open(fOps, node->id, &cookie); 1145 if (fuseError == 0) { 1146 fuseError = fuse_ll_fsync(fOps, node->id, 0, &cookie); 1147 // full sync, not only data 1148 fuse_ll_flush(fOps, node->id, &cookie); 1149 fuse_ll_release(fOps, node->id, &cookie); 1150 } 1151 } else { 1152 // get a path for the node 1153 char path[B_PATH_NAME_LENGTH]; 1154 size_t pathLen; 1155 status_t error = _BuildPath(node, path, pathLen); 1156 if (error != B_OK) 1157 RETURN_ERROR(error); 1158 1159 // mark the node not dirty 1160 dirty = node->dirty; 1161 node->dirty = false; 1162 1163 locker.Unlock(); 1164 1165 // open, sync, and close the node 1166 FileCookie cookie(O_RDONLY); 1167 fuseError = fuse_fs_open(fFS, path, &cookie); 1168 if (fuseError == 0) { 1169 fuseError = fuse_fs_fsync(fFS, path, 0, &cookie); 1170 // full sync, not only data 1171 fuse_fs_flush(fFS, path, &cookie); 1172 fuse_fs_release(fFS, path, &cookie); 1173 } 1174 } 1175 1176 if (fuseError != 0) { 1177 // sync'ing failed -- mark the node dirty again 1178 locker.Lock(); 1179 node->dirty |= dirty; 1180 RETURN_ERROR(fuseError); 1181 } 1182 1183 return B_OK; 1184 } 1185 1186 1187 status_t 1188 FUSEVolume::ReadSymlink(void* _node, char* buffer, size_t bufferSize, 1189 size_t* _bytesRead) 1190 { 1191 FUSENode* node = (FUSENode*)_node; 1192 1193 // lock the directory 1194 NodeReadLocker nodeLocker(this, node, true); 1195 if (nodeLocker.Status() != B_OK) 1196 RETURN_ERROR(nodeLocker.Status()); 1197 1198 int fuseError; 1199 if (fOps != NULL) { 1200 fuseError = fuse_ll_readlink(fOps, node->id, buffer, bufferSize); 1201 if (fuseError != 0) { 1202 *_bytesRead = 0; 1203 return fuseError; 1204 } 1205 // fuse_ll_readlink returns the actual size (even if the data didn't fit the buffer) 1206 *_bytesRead = fuseError; 1207 } else { 1208 AutoLocker<Locker> locker(fLock); 1209 1210 // get a path for the node 1211 char path[B_PATH_NAME_LENGTH]; 1212 size_t pathLen; 1213 status_t error = _BuildPath(node, path, pathLen); 1214 if (error != B_OK) 1215 RETURN_ERROR(error); 1216 1217 locker.Unlock(); 1218 1219 // read the symlink 1220 int fuseError = fuse_fs_readlink(fFS, path, buffer, bufferSize); 1221 if (fuseError != 0) { 1222 *_bytesRead = 0; 1223 return fuseError; 1224 } 1225 1226 // fuse_fs_readlink() is supposed to return a NULL-terminated string, which 1227 // the Haiku interface doesn't require. We have to return the string length, 1228 // though. 1229 *_bytesRead = strnlen(buffer, bufferSize); 1230 } 1231 1232 return B_OK; 1233 } 1234 1235 1236 status_t 1237 FUSEVolume::CreateSymlink(void* _dir, const char* name, const char* target, 1238 int mode) 1239 { 1240 FUSENode* dir = (FUSENode*)_dir; 1241 PRINT(("FUSEVolume::CreateSymlink(%p (%" B_PRId64 "), \"%s\" -> \"%s\", " 1242 "%#x)\n", dir, dir->id, name, target, mode)); 1243 1244 // lock the directory 1245 NodeWriteLocker nodeLocker(this, dir, false); 1246 if (nodeLocker.Status() != B_OK) 1247 RETURN_ERROR(nodeLocker.Status()); 1248 1249 int fuseError; 1250 if (fOps != NULL) { 1251 fuseError = fuse_ll_symlink(fOps, target, dir->id, name); 1252 } else { 1253 AutoLocker<Locker> locker(fLock); 1254 1255 // get a path for the entry 1256 char path[B_PATH_NAME_LENGTH]; 1257 size_t pathLen; 1258 status_t error = _BuildPath(dir, name, path, pathLen); 1259 if (error != B_OK) 1260 RETURN_ERROR(error); 1261 1262 locker.Unlock(); 1263 1264 // create the symlink 1265 fuseError = fuse_fs_symlink(fFS, target, path); 1266 } 1267 1268 if (fuseError != 0) 1269 RETURN_ERROR(fuseError); 1270 1271 // TODO: Set the mode?! 1272 1273 // mark the dir dirty 1274 AutoLocker<Locker> locker(fLock); 1275 dir->dirty = true; 1276 locker.Unlock(); 1277 1278 // send node monitoring message 1279 ino_t nodeID; 1280 if (_GetNodeID(dir, name, &nodeID)) { 1281 UserlandFS::KernelEmu::notify_listener(B_ENTRY_CREATED, 0, fID, 0, 1282 dir->id, nodeID, NULL, name); 1283 } 1284 1285 return B_OK; 1286 } 1287 1288 1289 status_t 1290 FUSEVolume::Link(void* _dir, const char* name, void* _node) 1291 { 1292 FUSENode* dir = (FUSENode*)_dir; 1293 FUSENode* node = (FUSENode*)_node; 1294 PRINT(("FUSEVolume::Link(%p (%" B_PRId64 "), \"%s\" -> %p (%" B_PRId64 1295 "))\n", dir, dir->id, name, node, node->id)); 1296 1297 // lock the directories -- the target directory for writing, the node's 1298 // parent for reading 1299 MultiNodeLocker nodeLocker(this, dir, false, true, node, true, false); 1300 if (nodeLocker.Status() != B_OK) 1301 RETURN_ERROR(nodeLocker.Status()); 1302 1303 int fuseError; 1304 if (fOps != NULL) { 1305 fuseError = fuse_ll_link(fOps, node->id, dir->id, name); 1306 } else { 1307 AutoLocker<Locker> locker(fLock); 1308 1309 // get a path for the entries 1310 char oldPath[B_PATH_NAME_LENGTH]; 1311 size_t oldPathLen; 1312 status_t error = _BuildPath(node, oldPath, oldPathLen); 1313 if (error != B_OK) 1314 RETURN_ERROR(error); 1315 1316 char newPath[B_PATH_NAME_LENGTH]; 1317 size_t newPathLen; 1318 error = _BuildPath(dir, name, newPath, newPathLen); 1319 if (error != B_OK) 1320 RETURN_ERROR(error); 1321 1322 locker.Unlock(); 1323 1324 // link 1325 fuseError = fuse_fs_link(fFS, oldPath, newPath); 1326 } 1327 if (fuseError != 0) 1328 RETURN_ERROR(fuseError); 1329 1330 // mark the dir and the node dirty 1331 AutoLocker<Locker> locker(fLock); 1332 dir->dirty = true; 1333 node->dirty = true; 1334 locker.Unlock(); 1335 1336 // send node monitoring message 1337 UserlandFS::KernelEmu::notify_listener(B_ENTRY_CREATED, 0, fID, 0, dir->id, 1338 node->id, NULL, name); 1339 1340 return B_OK; 1341 } 1342 1343 1344 status_t 1345 FUSEVolume::Unlink(void* _dir, const char* name) 1346 { 1347 FUSENode* dir = (FUSENode*)_dir; 1348 PRINT(("FUSEVolume::Unlink(%p (%" B_PRId64 "), \"%s\")\n", dir, dir->id, 1349 name)); 1350 1351 // lock the directory 1352 NodeWriteLocker nodeLocker(this, dir, false); 1353 if (nodeLocker.Status() != B_OK) 1354 RETURN_ERROR(nodeLocker.Status()); 1355 1356 // get the node ID (for the node monitoring message) 1357 ino_t nodeID; 1358 bool doNodeMonitoring = _GetNodeID(dir, name, &nodeID); 1359 1360 int fuseError; 1361 if (fOps != NULL) { 1362 fuseError = fuse_ll_unlink(fOps, dir->id, name); 1363 } else { 1364 AutoLocker<Locker> locker(fLock); 1365 1366 // get a path for the entry 1367 char path[B_PATH_NAME_LENGTH]; 1368 size_t pathLen; 1369 status_t error = _BuildPath(dir, name, path, pathLen); 1370 if (error != B_OK) 1371 RETURN_ERROR(error); 1372 1373 locker.Unlock(); 1374 1375 // unlink 1376 fuseError = fuse_fs_unlink(fFS, path); 1377 } 1378 if (fuseError != 0) 1379 RETURN_ERROR(fuseError); 1380 1381 // remove the entry 1382 AutoLocker<Locker> locker(fLock); 1383 _RemoveEntry(dir, name); 1384 1385 // mark the dir dirty 1386 dir->dirty = true; 1387 locker.Unlock(); 1388 1389 // send node monitoring message 1390 if (doNodeMonitoring) { 1391 UserlandFS::KernelEmu::notify_listener(B_ENTRY_REMOVED, 0, fID, 0, 1392 dir->id, nodeID, NULL, name); 1393 } 1394 1395 return B_OK; 1396 } 1397 1398 1399 status_t 1400 FUSEVolume::Rename(void* _oldDir, const char* oldName, void* _newDir, 1401 const char* newName) 1402 { 1403 FUSENode* oldDir = (FUSENode*)_oldDir; 1404 FUSENode* newDir = (FUSENode*)_newDir; 1405 PRINT(("FUSEVolume::Rename(%p (%" B_PRId64 "), \"%s\", %p (%" B_PRId64 1406 "), \"%s\")\n", oldDir, oldDir->id, oldName, newDir, newDir->id, 1407 newName)); 1408 1409 // lock the directories 1410 MultiNodeLocker nodeLocker(this, oldDir, false, true, newDir, false, true); 1411 if (nodeLocker.Status() != B_OK) 1412 RETURN_ERROR(nodeLocker.Status()); 1413 1414 int fuseError; 1415 if (fOps != NULL) { 1416 fuseError = fuse_ll_rename(fOps, oldDir->id, oldName, newDir->id, newName); 1417 } else { 1418 AutoLocker<Locker> locker(fLock); 1419 1420 // get a path for the entries 1421 char oldPath[B_PATH_NAME_LENGTH]; 1422 size_t oldPathLen; 1423 status_t error = _BuildPath(oldDir, oldName, oldPath, oldPathLen); 1424 if (error != B_OK) 1425 RETURN_ERROR(error); 1426 1427 char newPath[B_PATH_NAME_LENGTH]; 1428 size_t newPathLen; 1429 error = _BuildPath(newDir, newName, newPath, newPathLen); 1430 if (error != B_OK) 1431 RETURN_ERROR(error); 1432 1433 locker.Unlock(); 1434 1435 // rename 1436 fuseError = fuse_fs_rename(fFS, oldPath, newPath); 1437 } 1438 if (fuseError != 0) 1439 RETURN_ERROR(fuseError); 1440 1441 // rename the entry 1442 AutoLocker<Locker> locker(fLock); 1443 _RenameEntry(oldDir, oldName, newDir, newName); 1444 1445 // mark the dirs dirty 1446 oldDir->dirty = true; 1447 newDir->dirty = true; 1448 1449 // send node monitoring message 1450 ino_t nodeID; 1451 if (_GetNodeID(newDir, newName, &nodeID)) { 1452 UserlandFS::KernelEmu::notify_listener(B_ENTRY_MOVED, 0, fID, 1453 oldDir->id, newDir->id, nodeID, oldName, newName); 1454 } 1455 1456 return B_OK; 1457 } 1458 1459 1460 status_t 1461 FUSEVolume::Access(void* _node, int mode) 1462 { 1463 FUSENode* node = (FUSENode*)_node; 1464 1465 // lock the directory 1466 NodeReadLocker nodeLocker(this, node, true); 1467 if (nodeLocker.Status() != B_OK) 1468 RETURN_ERROR(nodeLocker.Status()); 1469 1470 int fuseError; 1471 if (fOps != NULL) { 1472 fuseError = fuse_ll_access(fOps, node->id, mode); 1473 } else { 1474 AutoLocker<Locker> locker(fLock); 1475 1476 // get a path for the node 1477 char path[B_PATH_NAME_LENGTH]; 1478 size_t pathLen; 1479 status_t error = _BuildPath(node, path, pathLen); 1480 if (error != B_OK) 1481 RETURN_ERROR(error); 1482 1483 locker.Unlock(); 1484 1485 // call the access hook on the path 1486 fuseError = fuse_fs_access(fFS, path, mode); 1487 } 1488 1489 if (fuseError != 0) 1490 return fuseError; 1491 1492 return B_OK; 1493 } 1494 1495 1496 status_t 1497 FUSEVolume::ReadStat(void* _node, struct stat* st) 1498 { 1499 FUSENode* node = (FUSENode*)_node; 1500 PRINT(("FUSEVolume::ReadStat(%p (%" B_PRId64 "), %p)\n", node, node->id, 1501 st)); 1502 1503 // lock the directory 1504 NodeReadLocker nodeLocker(this, node, true); 1505 if (nodeLocker.Status() != B_OK) 1506 RETURN_ERROR(nodeLocker.Status()); 1507 1508 st->st_dev = GetID(); 1509 st->st_ino = node->id; 1510 st->st_blksize = 2048; 1511 st->st_type = 0; 1512 1513 int fuseError; 1514 if (fOps != NULL) { 1515 fuseError = fuse_ll_getattr(fOps, node->id, st); 1516 } else { 1517 AutoLocker<Locker> locker(fLock); 1518 1519 // get a path for the node 1520 char path[B_PATH_NAME_LENGTH]; 1521 size_t pathLen; 1522 status_t error = _BuildPath(node, path, pathLen); 1523 if (error != B_OK) 1524 RETURN_ERROR(error); 1525 1526 locker.Unlock(); 1527 1528 // stat the path 1529 fuseError = fuse_fs_getattr(fFS, path, st); 1530 } 1531 if (fuseError != 0) 1532 return fuseError; 1533 1534 return B_OK; 1535 } 1536 1537 1538 status_t 1539 FUSEVolume::WriteStat(void* _node, const struct stat* st, uint32 mask) 1540 { 1541 FUSENode* node = (FUSENode*)_node; 1542 PRINT(("FUSEVolume::WriteStat(%p (%" B_PRId64 "), %p, %#" B_PRIx32 ")\n", 1543 node, node->id, st, mask)); 1544 1545 // lock the directory 1546 NodeReadLocker nodeLocker(this, node, true); 1547 if (nodeLocker.Status() != B_OK) 1548 RETURN_ERROR(nodeLocker.Status()); 1549 1550 if (fOps != NULL) { 1551 int fuseError = fuse_ll_setattr(fOps, node->id, st, mask); 1552 if (fuseError != 0) 1553 RETURN_ERROR(fuseError); 1554 } else { 1555 AutoLocker<Locker> locker(fLock); 1556 1557 // get a path for the node 1558 char path[B_PATH_NAME_LENGTH]; 1559 size_t pathLen; 1560 status_t error = _BuildPath(node, path, pathLen); 1561 if (error != B_OK) 1562 RETURN_ERROR(error); 1563 1564 locker.Unlock(); 1565 1566 // permissions 1567 if ((mask & B_STAT_MODE) != 0) { 1568 int fuseError = fuse_fs_chmod(fFS, path, st->st_mode); 1569 if (fuseError != 0) 1570 RETURN_ERROR(fuseError); 1571 } 1572 1573 // owner 1574 if ((mask & (B_STAT_UID | B_STAT_GID)) != 0) { 1575 uid_t uid = (mask & B_STAT_UID) != 0 ? st->st_uid : (uid_t)-1; 1576 gid_t gid = (mask & B_STAT_GID) != 0 ? st->st_gid : (gid_t)-1; 1577 int fuseError = fuse_fs_chown(fFS, path, uid, gid); 1578 if (fuseError != 0) 1579 RETURN_ERROR(fuseError); 1580 } 1581 1582 // size 1583 if ((mask & B_STAT_SIZE) != 0) { 1584 // truncate 1585 int fuseError = fuse_fs_truncate(fFS, path, st->st_size); 1586 if (fuseError != 0) 1587 RETURN_ERROR(fuseError); 1588 } 1589 1590 // access/modification time 1591 if ((mask & (B_STAT_ACCESS_TIME | B_STAT_MODIFICATION_TIME)) != 0) { 1592 timespec tv[2] = { 1593 {st->st_atime, 0}, 1594 {st->st_mtime, 0} 1595 }; 1596 1597 // If either time is not specified, we need to stat the file to get the 1598 // current value. 1599 if ((mask & (B_STAT_ACCESS_TIME | B_STAT_MODIFICATION_TIME)) 1600 != (B_STAT_ACCESS_TIME | B_STAT_MODIFICATION_TIME)) { 1601 struct stat currentStat; 1602 int fuseError = fuse_fs_getattr(fFS, path, ¤tStat); 1603 if (fuseError != 0) 1604 RETURN_ERROR(fuseError); 1605 1606 if ((mask & B_STAT_ACCESS_TIME) == 0) 1607 tv[0].tv_sec = currentStat.st_atime; 1608 else 1609 tv[1].tv_sec = currentStat.st_mtime; 1610 } 1611 1612 int fuseError = fuse_fs_utimens(fFS, path, tv); 1613 if (fuseError != 0) 1614 RETURN_ERROR(fuseError); 1615 } 1616 } 1617 1618 // mark the node dirty 1619 AutoLocker<Locker> locker(fLock); 1620 node->dirty = true; 1621 1622 // send node monitoring message 1623 uint32 changedFields = mask & 1624 (B_STAT_MODE | B_STAT_UID | B_STAT_GID | B_STAT_SIZE 1625 | B_STAT_ACCESS_TIME | B_STAT_MODIFICATION_TIME); 1626 1627 if (changedFields != 0) { 1628 UserlandFS::KernelEmu::notify_listener(B_STAT_CHANGED, changedFields, 1629 fID, 0, 0, node->id, NULL, NULL); 1630 } 1631 1632 return B_OK; 1633 } 1634 1635 1636 // #pragma mark - files 1637 1638 1639 status_t 1640 FUSEVolume::Create(void* _dir, const char* name, int openMode, int mode, 1641 void** _cookie, ino_t* _vnid) 1642 { 1643 FUSENode* dir = (FUSENode*)_dir; 1644 PRINT(("FUSEVolume::Create(%p (%" B_PRId64 "), \"%s\", %#x, %#x)\n", dir, 1645 dir->id, name, openMode, mode)); 1646 1647 // lock the directory 1648 NodeWriteLocker nodeLocker(this, dir, false); 1649 if (nodeLocker.Status() != B_OK) 1650 RETURN_ERROR(nodeLocker.Status()); 1651 1652 // allocate a file cookie 1653 FileCookie* cookie = new(std::nothrow) FileCookie(openMode); 1654 if (cookie == NULL) 1655 RETURN_ERROR(B_NO_MEMORY); 1656 ObjectDeleter<FileCookie> cookieDeleter(cookie); 1657 1658 FUSENode* node; 1659 int fuseError; 1660 if (fOps) { 1661 fuse_file_info cookie; 1662 fuse_ino_t ino; 1663 fuseError = fuse_ll_create(fOps, dir->id, name, mode, &cookie, ino); 1664 if (fuseError != 0) 1665 RETURN_ERROR(fuseError); 1666 1667 // get the node 1668 // TODO do we really need it? 1669 status_t error = _GetNode(dir, name, &node); 1670 if (error != B_OK) { 1671 // This is bad. We've create the file successfully, but couldn't get 1672 // the node. Delete the entry. 1673 // We can't close the file because we don't know its inode. 1674 fuse_ll_flush(fOps, ino, &cookie); 1675 fuse_ll_release(fOps, ino, &cookie); 1676 fuse_ll_unlink(fOps, dir->id, name); 1677 RETURN_ERROR(error); 1678 } 1679 } else { 1680 AutoLocker<Locker> locker(fLock); 1681 1682 // get a path for the node 1683 char path[B_PATH_NAME_LENGTH]; 1684 size_t pathLen; 1685 status_t error = _BuildPath(dir, name, path, pathLen); 1686 if (error != B_OK) 1687 RETURN_ERROR(error); 1688 1689 locker.Unlock(); 1690 1691 // create the file 1692 fuseError = fuse_fs_create(fFS, path, mode, cookie); 1693 if (fuseError != 0) 1694 RETURN_ERROR(fuseError); 1695 1696 // get the node 1697 error = _GetNode(dir, name, &node); 1698 if (error != B_OK) { 1699 // This is bad. We've create the file successfully, but couldn't get 1700 // the node. Close the file and delete the entry. 1701 fuse_fs_flush(fFS, path, cookie); 1702 fuse_fs_release(fFS, path, cookie); 1703 fuse_fs_unlink(fFS, path); 1704 RETURN_ERROR(error); 1705 } 1706 } 1707 1708 // mark the dir and the node dirty 1709 AutoLocker<Locker> locker(fLock); 1710 dir->dirty = true; 1711 node->dirty = true; 1712 locker.Unlock(); 1713 1714 // send node monitoring message 1715 UserlandFS::KernelEmu::notify_listener(B_ENTRY_CREATED, 0, fID, 0, dir->id, 1716 node->id, NULL, name); 1717 1718 cookieDeleter.Detach(); 1719 *_cookie = cookie; 1720 *_vnid = node->id; 1721 1722 return B_OK; 1723 } 1724 1725 1726 status_t 1727 FUSEVolume::Open(void* _node, int openMode, void** _cookie) 1728 { 1729 FUSENode* node = (FUSENode*)_node; 1730 PRINT(("FUSEVolume::Open(%p (%" B_PRId64 "), %#x)\n", node, node->id, 1731 openMode)); 1732 1733 // lock the directory 1734 NodeReadLocker nodeLocker(this, node, true); 1735 if (nodeLocker.Status() != B_OK) 1736 RETURN_ERROR(nodeLocker.Status()); 1737 1738 bool truncate = (openMode & O_TRUNC) != 0; 1739 openMode &= ~O_TRUNC; 1740 1741 // allocate a file cookie 1742 FileCookie* cookie = new(std::nothrow) FileCookie(openMode); 1743 if (cookie == NULL) 1744 RETURN_ERROR(B_NO_MEMORY); 1745 ObjectDeleter<FileCookie> cookieDeleter(cookie); 1746 1747 char path[B_PATH_NAME_LENGTH]; 1748 size_t pathLen; 1749 1750 int fuseError; 1751 struct fuse_file_info llCookie = { 0 }; 1752 // FIXME store this in the FileCookie for lowlevel streams, we'll need it in read, write... 1753 if (fOps != NULL) { 1754 llCookie.flags = openMode; 1755 if (S_ISDIR(node->type)) 1756 fuseError = fuse_ll_opendir(fOps, node->id, &llCookie); 1757 else 1758 fuseError = fuse_ll_open(fOps, node->id, &llCookie); 1759 } else { 1760 AutoLocker<Locker> locker(fLock); 1761 1762 // get a path for the node 1763 status_t error = _BuildPath(node, path, pathLen); 1764 if (error != B_OK) 1765 RETURN_ERROR(error); 1766 1767 locker.Unlock(); 1768 1769 // open the file 1770 fuseError = fuse_fs_open(fFS, path, cookie); 1771 } 1772 1773 if (fuseError != 0) 1774 RETURN_ERROR(fuseError); 1775 1776 // truncate the file, if requested 1777 if (truncate) { 1778 if (fOps != NULL) { 1779 struct stat st; 1780 st.st_size = 0; 1781 fuseError = fuse_ll_setattr(fOps, node->id, &st, FUSE_SET_ATTR_SIZE); 1782 1783 if (fuseError != 0) { 1784 fuse_ll_flush(fOps, node->id, &llCookie); 1785 fuse_ll_release(fOps, node->id, &llCookie); 1786 RETURN_ERROR(fuseError); 1787 } 1788 } else { 1789 fuseError = fuse_fs_ftruncate(fFS, path, 0, cookie); 1790 if (fuseError == ENOSYS) { 1791 // Fallback to truncate if ftruncate is not implemented 1792 fuseError = fuse_fs_truncate(fFS, path, 0); 1793 } 1794 if (fuseError != 0) { 1795 fuse_fs_flush(fFS, path, cookie); 1796 fuse_fs_release(fFS, path, cookie); 1797 RETURN_ERROR(fuseError); 1798 } 1799 } 1800 1801 // mark the node dirty 1802 AutoLocker<Locker> locker(fLock); 1803 node->dirty = true; 1804 1805 // send node monitoring message 1806 UserlandFS::KernelEmu::notify_listener(B_STAT_CHANGED, 1807 B_STAT_SIZE | B_STAT_MODIFICATION_TIME, fID, 0, 0, node->id, NULL, 1808 NULL); 1809 } 1810 1811 if (S_ISREG(node->type)) { 1812 if (cookie->direct_io || llCookie.direct_io) { 1813 if (node->cacheCount > 0) { 1814 // In some very rare cases, for the same node, the first `open` 1815 // indicates that caching is allowed (by not setting `direct_io`), 1816 // but a subsequent `open` on the same node indicates that it is 1817 // NOT allowed (by setting `direct_io` to 1). 1818 debugger("FUSEVolume::Open(): inconsistent direct_io flags!"); 1819 UserlandFS::KernelEmu::file_cache_delete(GetID(), node->id); 1820 node->cacheCount = 0; 1821 } 1822 } else { 1823 if (node->cacheCount == 0) { 1824 struct stat st; 1825 if (fOps != NULL) { 1826 fuseError = fuse_ll_getattr(fOps, node->id, &st); 1827 } else { 1828 fuseError = fuse_fs_getattr(fFS, path, &st); 1829 } 1830 if (fuseError != 0) { 1831 RETURN_ERROR(fuseError); 1832 } 1833 status_t error = UserlandFS::KernelEmu::file_cache_create(GetID(), node->id, st.st_size); 1834 if (error != B_OK) { 1835 RETURN_ERROR(error); 1836 } 1837 } 1838 // Increment cacheCount by extra 1 if the cache is kept to prevent 1839 // the cache from being deleted at close(). 1840 node->cacheCount += 1 + cookie->keep_cache + llCookie.keep_cache; 1841 } 1842 } 1843 1844 cookieDeleter.Detach(); 1845 *_cookie = cookie; 1846 1847 return B_OK; 1848 } 1849 1850 1851 status_t 1852 FUSEVolume::Close(void* _node, void* _cookie) 1853 { 1854 FUSENode* node = (FUSENode*)_node; 1855 FileCookie* cookie = (FileCookie*)_cookie; 1856 1857 RWLockableReadLocker cookieLocker(this, cookie); 1858 1859 // lock the directory 1860 NodeReadLocker nodeLocker(this, node, true); 1861 if (nodeLocker.Status() != B_OK) 1862 RETURN_ERROR(nodeLocker.Status()); 1863 1864 int fuseError; 1865 1866 if (fOps != NULL) { 1867 fuseError = fuse_ll_flush(fOps, node->id, cookie); 1868 } else { 1869 AutoLocker<Locker> locker(fLock); 1870 1871 // get a path for the node 1872 char path[B_PATH_NAME_LENGTH]; 1873 size_t pathLen; 1874 status_t error = _BuildPath(node, path, pathLen); 1875 if (error != B_OK) 1876 RETURN_ERROR(error); 1877 1878 locker.Unlock(); 1879 1880 // flush the file 1881 fuseError = fuse_fs_flush(fFS, path, cookie); 1882 } 1883 if (fuseError != 0) 1884 return fuseError; 1885 1886 if (S_ISREG(node->type) && node->cacheCount > 0) { 1887 --node->cacheCount; 1888 if (node->cacheCount == 0) { 1889 UserlandFS::KernelEmu::file_cache_delete(GetID(), node->id); 1890 } 1891 } 1892 1893 return B_OK; 1894 } 1895 1896 1897 status_t 1898 FUSEVolume::FreeCookie(void* _node, void* _cookie) 1899 { 1900 FUSENode* node = (FUSENode*)_node; 1901 FileCookie* cookie = (FileCookie*)_cookie; 1902 1903 // no need to lock the cookie here, as no-one else uses it anymore 1904 1905 // lock the directory 1906 NodeReadLocker nodeLocker(this, node, true); 1907 if (nodeLocker.Status() != B_OK) 1908 RETURN_ERROR(nodeLocker.Status()); 1909 1910 ObjectDeleter<FileCookie> cookieDeleter(cookie); 1911 1912 int fuseError; 1913 if (fOps) { 1914 fuseError = fuse_ll_release(fOps, node->id, cookie); 1915 } else { 1916 AutoLocker<Locker> locker(fLock); 1917 1918 // get a path for the node 1919 char path[B_PATH_NAME_LENGTH]; 1920 size_t pathLen; 1921 status_t error = _BuildPath(node, path, pathLen); 1922 if (error != B_OK) 1923 RETURN_ERROR(error); 1924 1925 locker.Unlock(); 1926 1927 // release the file 1928 fuseError = fuse_fs_release(fFS, path, cookie); 1929 } 1930 1931 if (fuseError != 0) 1932 return fuseError; 1933 1934 return B_OK; 1935 } 1936 1937 1938 status_t 1939 FUSEVolume::Read(void* _node, void* _cookie, off_t pos, void* buffer, 1940 size_t bufferSize, size_t* _bytesRead) 1941 { 1942 FUSENode* node = (FUSENode*)_node; 1943 FileCookie* cookie = (FileCookie*)_cookie; 1944 1945 RWLockableReadLocker cookieLocker(this, cookie); 1946 1947 // lock the directory 1948 NodeReadLocker nodeLocker(this, node, true); 1949 if (nodeLocker.Status() != B_OK) 1950 RETURN_ERROR(nodeLocker.Status()); 1951 1952 *_bytesRead = bufferSize; 1953 status_t error = B_OK; 1954 1955 if (S_ISREG(node->type) && node->cacheCount > 0) { 1956 error = UserlandFS::KernelEmu::file_cache_read(GetID(), node->id, cookie, pos, 1957 buffer, _bytesRead); 1958 } else 1959 error = _InternalIO(node, cookie, NULL, pos, (char *)buffer, *_bytesRead, false); 1960 1961 if (error != B_OK) 1962 RETURN_ERROR(error); 1963 1964 return B_OK; 1965 } 1966 1967 1968 status_t 1969 FUSEVolume::Write(void* _node, void* _cookie, off_t pos, const void* buffer, 1970 size_t bufferSize, size_t* _bytesWritten) 1971 { 1972 FUSENode* node = (FUSENode*)_node; 1973 FileCookie* cookie = (FileCookie*)_cookie; 1974 1975 RWLockableReadLocker cookieLocker(this, cookie); 1976 1977 *_bytesWritten = 0; 1978 1979 // lock the directory 1980 NodeReadLocker nodeLocker(this, node, true); 1981 if (nodeLocker.Status() != B_OK) 1982 RETURN_ERROR(nodeLocker.Status()); 1983 1984 *_bytesWritten = bufferSize; 1985 status_t error = B_OK; 1986 1987 if (S_ISREG(node->type) && node->cacheCount > 0) { 1988 error = UserlandFS::KernelEmu::file_cache_write(GetID(), node->id, cookie, pos, 1989 buffer, _bytesWritten); 1990 } else 1991 error = _InternalIO(node, cookie, NULL, pos, (char *)buffer, *_bytesWritten, true); 1992 1993 if (error != B_OK) 1994 RETURN_ERROR(error); 1995 1996 // mark the node dirty 1997 AutoLocker<Locker> locker(fLock); 1998 node->dirty = true; 1999 2000 // send node monitoring message 2001 UserlandFS::KernelEmu::notify_listener(B_STAT_CHANGED, 2002 B_STAT_SIZE | B_STAT_MODIFICATION_TIME, fID, 0, 0, node->id, NULL, 2003 NULL); 2004 // TODO: The size possibly doesn't change. 2005 // TODO: Avoid message flooding -- use a timeout and set the 2006 // B_STAT_INTERIM_UPDATE flag. 2007 2008 return B_OK; 2009 } 2010 2011 2012 // #pragma mark - directories 2013 2014 2015 status_t 2016 FUSEVolume::CreateDir(void* _dir, const char* name, int mode) 2017 { 2018 FUSENode* dir = (FUSENode*)_dir; 2019 PRINT(("FUSEVolume::CreateDir(%p (%" B_PRId64 "), \"%s\", %#x)\n", dir, 2020 dir->id, name, mode)); 2021 2022 // lock the directory 2023 NodeWriteLocker nodeLocker(this, dir, false); 2024 if (nodeLocker.Status() != B_OK) 2025 RETURN_ERROR(nodeLocker.Status()); 2026 2027 int fuseError; 2028 if (fOps != NULL) { 2029 fuseError = fuse_ll_mkdir(fOps, dir->id, name, mode); 2030 } else { 2031 AutoLocker<Locker> locker(fLock); 2032 2033 // get a path for the entry 2034 char path[B_PATH_NAME_LENGTH]; 2035 size_t pathLen; 2036 status_t error = _BuildPath(dir, name, path, pathLen); 2037 if (error != B_OK) 2038 RETURN_ERROR(error); 2039 2040 locker.Unlock(); 2041 2042 // create the dir 2043 fuseError = fuse_fs_mkdir(fFS, path, mode); 2044 } 2045 if (fuseError != 0) 2046 RETURN_ERROR(fuseError); 2047 2048 // mark the dir dirty 2049 AutoLocker<Locker> locker(fLock); 2050 dir->dirty = true; 2051 2052 // send node monitoring message 2053 ino_t nodeID; 2054 if (_GetNodeID(dir, name, &nodeID)) { 2055 UserlandFS::KernelEmu::notify_listener(B_ENTRY_CREATED, 0, fID, 0, 2056 dir->id, nodeID, NULL, name); 2057 } 2058 2059 return B_OK; 2060 } 2061 2062 2063 status_t 2064 FUSEVolume::RemoveDir(void* _dir, const char* name) 2065 { 2066 FUSENode* dir = (FUSENode*)_dir; 2067 PRINT(("FUSEVolume::RemoveDir(%p (%" B_PRId64 "), \"%s\")\n", dir, dir->id, 2068 name)); 2069 2070 // lock the directory 2071 NodeWriteLocker nodeLocker(this, dir, false); 2072 if (nodeLocker.Status() != B_OK) 2073 RETURN_ERROR(nodeLocker.Status()); 2074 2075 // get the node ID (for the node monitoring message) 2076 ino_t nodeID; 2077 bool doNodeMonitoring = _GetNodeID(dir, name, &nodeID); 2078 2079 int fuseError; 2080 if (fOps != NULL) { 2081 fuseError = fuse_ll_rmdir(fOps, dir->id, name); 2082 } else { 2083 AutoLocker<Locker> locker(fLock); 2084 2085 // get a path for the entry 2086 char path[B_PATH_NAME_LENGTH]; 2087 size_t pathLen; 2088 status_t error = _BuildPath(dir, name, path, pathLen); 2089 if (error != B_OK) 2090 RETURN_ERROR(error); 2091 2092 locker.Unlock(); 2093 2094 // remove the dir 2095 fuseError = fuse_fs_rmdir(fFS, path); 2096 } 2097 if (fuseError != 0) 2098 RETURN_ERROR(fuseError); 2099 2100 // remove the entry 2101 AutoLocker<Locker> locker(fLock); 2102 _RemoveEntry(dir, name); 2103 2104 // mark the parent dir dirty 2105 dir->dirty = true; 2106 2107 // send node monitoring message 2108 if (doNodeMonitoring) { 2109 UserlandFS::KernelEmu::notify_listener(B_ENTRY_REMOVED, 0, fID, 0, 2110 dir->id, nodeID, NULL, name); 2111 } 2112 2113 return B_OK; 2114 } 2115 2116 2117 status_t 2118 FUSEVolume::OpenDir(void* _node, void** _cookie) 2119 { 2120 FUSENode* node = (FUSENode*)_node; 2121 PRINT(("FUSEVolume::OpenDir(%p (%" B_PRId64 "), %p)\n", node, node->id, 2122 _cookie)); 2123 2124 // lock the parent directory 2125 NodeReadLocker nodeLocker(this, node, true); 2126 if (nodeLocker.Status() != B_OK) 2127 RETURN_ERROR(nodeLocker.Status()); 2128 2129 // allocate a dir cookie 2130 DirCookie* cookie = new(std::nothrow) DirCookie; 2131 if (cookie == NULL) 2132 RETURN_ERROR(B_NO_MEMORY); 2133 ObjectDeleter<DirCookie> cookieDeleter(cookie); 2134 2135 if (fOps) { 2136 int fuseError = fuse_ll_opendir(fOps, node->id, cookie); 2137 if (fuseError != 0) 2138 return fuseError; 2139 } else { 2140 AutoLocker<Locker> locker(fLock); 2141 2142 // get a path for the node 2143 char path[B_PATH_NAME_LENGTH]; 2144 size_t pathLen; 2145 status_t error = _BuildPath(node, path, pathLen); 2146 if (error != B_OK) 2147 RETURN_ERROR(error); 2148 2149 locker.Unlock(); 2150 2151 if (fFS->ops.readdir == NULL && fFS->ops.getdir != NULL) { 2152 // no open call -- the FS only supports the deprecated getdir() 2153 // interface 2154 cookie->getdirInterface = true; 2155 } else { 2156 // open the dir 2157 int fuseError = fuse_fs_opendir(fFS, path, cookie); 2158 if (fuseError != 0) 2159 return fuseError; 2160 } 2161 } 2162 2163 cookieDeleter.Detach(); 2164 *_cookie = cookie; 2165 2166 return B_OK; 2167 } 2168 2169 2170 status_t 2171 FUSEVolume::CloseDir(void* node, void* _cookie) 2172 { 2173 return B_OK; 2174 } 2175 2176 2177 status_t 2178 FUSEVolume::FreeDirCookie(void* _node, void* _cookie) 2179 { 2180 FUSENode* node = (FUSENode*)_node; 2181 DirCookie* cookie = (DirCookie*)_cookie; 2182 2183 // lock the parent directory 2184 NodeReadLocker nodeLocker(this, node, true); 2185 if (nodeLocker.Status() != B_OK) 2186 RETURN_ERROR(nodeLocker.Status()); 2187 2188 ObjectDeleter<DirCookie> cookieDeleter(cookie); 2189 2190 int fuseError; 2191 if (fOps != NULL) { 2192 fuseError = fuse_ll_releasedir(fOps, node->id, cookie); 2193 } else { 2194 if (cookie->getdirInterface) 2195 return B_OK; 2196 2197 AutoLocker<Locker> locker(fLock); 2198 2199 // get a path for the node 2200 char path[B_PATH_NAME_LENGTH]; 2201 size_t pathLen; 2202 status_t error = _BuildPath(node, path, pathLen); 2203 if (error != B_OK) 2204 RETURN_ERROR(error); 2205 2206 locker.Unlock(); 2207 2208 // release the dir 2209 fuseError = fuse_fs_releasedir(fFS, path, cookie); 2210 } 2211 2212 if (fuseError != 0) 2213 return fuseError; 2214 2215 return B_OK; 2216 } 2217 2218 2219 status_t 2220 FUSEVolume::ReadDir(void* _node, void* _cookie, void* buffer, size_t bufferSize, 2221 uint32 count, uint32* _countRead) 2222 { 2223 PRINT(("FUSEVolume::ReadDir(%p, %p, %p, %" B_PRIuSIZE ", %" B_PRId32 ")\n", 2224 _node, _cookie, buffer, bufferSize, count)); 2225 *_countRead = 0; 2226 2227 FUSENode* node = (FUSENode*)_node; 2228 DirCookie* cookie = (DirCookie*)_cookie; 2229 2230 RWLockableWriteLocker cookieLocker(this, cookie); 2231 2232 uint32 countRead = 0; 2233 status_t readDirError = B_OK; 2234 2235 AutoLocker<Locker> locker(fLock); 2236 2237 if (cookie->entryCache == NULL) { 2238 // We don't have an entry cache (yet), so we need to ask the client 2239 // file system to read the directory. 2240 2241 locker.Unlock(); 2242 2243 // lock the directory 2244 NodeReadLocker nodeLocker(this, node, false); 2245 if (nodeLocker.Status() != B_OK) 2246 RETURN_ERROR(nodeLocker.Status()); 2247 2248 locker.Lock(); 2249 2250 ReadDirBuffer readDirBuffer(this, node, cookie, buffer, bufferSize, count); 2251 off_t offset = cookie->currentEntryOffset; 2252 2253 // read the dir 2254 int fuseError; 2255 if (fOps != NULL) { 2256 locker.Unlock(); 2257 2258 // TODO pass the cookie from opendir here instead of NULL 2259 fuseError = fuse_ll_readdir(fOps, node->id, &readDirBuffer, (char*)buffer, bufferSize, 2260 &_AddReadDirEntryLowLevel, offset, NULL); 2261 2262 // The request filler may or may not be used. If the filesystem decides that it has 2263 // already cached the directory, it can reply with an already filled buffer from a 2264 // previous run. So, we can't rely on any updates done to the cookie by that function. 2265 // So we need to check the number of entries in the buffer, and advance the 2266 // currentEntryOffset. 2267 if (fuseError > 0) { 2268 struct dirent* dirent = (struct dirent*)buffer; 2269 while (countRead < count 2270 && (char*)dirent + dirent->d_reclen <= (char*)buffer + fuseError) { 2271 countRead++; 2272 dirent = (struct dirent*)(((char*)dirent) + dirent->d_reclen); 2273 if (dirent->d_reclen == 0) 2274 break; 2275 } 2276 cookie->currentEntryOffset += (char*)dirent - (char*)buffer; 2277 2278 fuseError = 0; 2279 } 2280 readDirError = 0; 2281 } else { 2282 // get a path for the node 2283 char path[B_PATH_NAME_LENGTH]; 2284 size_t pathLen; 2285 status_t error = _BuildPath(node, path, pathLen); 2286 if (error != B_OK) 2287 RETURN_ERROR(error); 2288 2289 locker.Unlock(); 2290 2291 if (cookie->getdirInterface) { 2292 PRINT((" using getdir() interface\n")); 2293 fuseError = fFS->ops.getdir(path, (fuse_dirh_t)&readDirBuffer, 2294 &_AddReadDirEntryGetDir); 2295 } else { 2296 PRINT((" using readdir() interface\n")); 2297 fuseError = fuse_fs_readdir(fFS, path, &readDirBuffer, 2298 &_AddReadDirEntry, offset, cookie); 2299 } 2300 2301 countRead = readDirBuffer.entriesRead; 2302 readDirError = readDirBuffer.error; 2303 } 2304 2305 if (fuseError != 0) 2306 return fuseError; 2307 2308 locker.Lock(); 2309 2310 } 2311 2312 if (cookie->entryCache != NULL) { 2313 // we're using an entry cache -- read into the buffer what we can 2314 dirent* entryBuffer = (dirent*)buffer; 2315 while (countRead < count 2316 && cookie->entryCache->ReadDirent(cookie->currentEntryIndex, fID, 2317 countRead + 1 < count, entryBuffer, bufferSize)) { 2318 countRead++; 2319 cookie->currentEntryIndex++; 2320 bufferSize -= entryBuffer->d_reclen; 2321 entryBuffer 2322 = (dirent*)((uint8*)entryBuffer + entryBuffer->d_reclen); 2323 } 2324 } 2325 2326 *_countRead = countRead; 2327 return countRead > 0 ? B_OK : readDirError; 2328 } 2329 2330 2331 status_t 2332 FUSEVolume::RewindDir(void* _node, void* _cookie) 2333 { 2334 PRINT(("FUSEVolume::RewindDir(%p, %p)\n", _node, _cookie)); 2335 DirCookie* cookie = (DirCookie*)_cookie; 2336 2337 RWLockableWriteLocker cookieLocker(this, cookie); 2338 2339 if (cookie->getdirInterface) { 2340 delete cookie->entryCache; 2341 cookie->entryCache = NULL; 2342 cookie->currentEntryIndex = 0; 2343 } else { 2344 cookie->currentEntryOffset = 0; 2345 } 2346 2347 return B_OK; 2348 } 2349 2350 2351 // #pragma mark - attribute directories 2352 2353 2354 // OpenAttrDir 2355 status_t 2356 FUSEVolume::OpenAttrDir(void* _node, void** _cookie) 2357 { 2358 // allocate an attribute directory cookie 2359 AttrDirCookie* cookie = new(std::nothrow) AttrDirCookie; 2360 if (cookie == NULL) 2361 RETURN_ERROR(B_NO_MEMORY); 2362 2363 *_cookie = cookie; 2364 2365 return B_OK; 2366 } 2367 2368 2369 // CloseAttrDir 2370 status_t 2371 FUSEVolume::CloseAttrDir(void* node, void* cookie) 2372 { 2373 return B_OK; 2374 } 2375 2376 2377 // FreeAttrDirCookie 2378 status_t 2379 FUSEVolume::FreeAttrDirCookie(void* _node, void* _cookie) 2380 { 2381 delete (AttrDirCookie*)_cookie; 2382 return B_OK; 2383 } 2384 2385 2386 // ReadAttrDir 2387 status_t 2388 FUSEVolume::ReadAttrDir(void* _node, void* _cookie, void* buffer, 2389 size_t bufferSize, uint32 count, uint32* _countRead) 2390 { 2391 FUSENode* node = (FUSENode*)_node; 2392 AttrDirCookie* cookie = (AttrDirCookie*)_cookie; 2393 2394 RWLockableWriteLocker cookieLocker(this, cookie); 2395 2396 *_countRead = 0; 2397 2398 // lock the directory 2399 NodeReadLocker nodeLocker(this, node, true); 2400 if (nodeLocker.Status() != B_OK) 2401 RETURN_ERROR(nodeLocker.Status()); 2402 2403 char path[B_PATH_NAME_LENGTH]; 2404 size_t pathLen; 2405 if (fOps == NULL) { 2406 AutoLocker<Locker> locker(fLock); 2407 2408 // get a path for the node 2409 status_t error = _BuildPath(node, path, pathLen); 2410 if (error != B_OK) 2411 RETURN_ERROR(error); 2412 2413 locker.Unlock(); 2414 } 2415 2416 if (!cookie->IsValid()) { 2417 // cookie not yet valid -- get the length of the list 2418 int listSize; 2419 if (fOps != NULL) 2420 listSize = fuse_ll_listxattr(fOps, node->id, NULL, 0); 2421 else 2422 listSize = fuse_fs_listxattr(fFS, path, NULL, 0); 2423 2424 if (listSize < 0) 2425 RETURN_ERROR(listSize); 2426 2427 while (true) { 2428 // allocate space for the listing 2429 status_t error = cookie->Allocate(listSize); 2430 if (error != B_OK) 2431 RETURN_ERROR(error); 2432 2433 // read the listing 2434 int bytesRead; 2435 if (fOps != NULL) { 2436 bytesRead = fuse_ll_listxattr(fOps, node->id, cookie->AttributesBuffer(), 2437 listSize); 2438 } else 2439 bytesRead = fuse_fs_listxattr(fFS, path, cookie->AttributesBuffer(), listSize); 2440 if (bytesRead < 0) 2441 RETURN_ERROR(bytesRead); 2442 2443 if (bytesRead == listSize) 2444 break; 2445 2446 // attributes listing changed -- reread it 2447 listSize = bytesRead; 2448 } 2449 2450 cookie->SetValid(true); 2451 } 2452 2453 // we have a valid cookie now -- get the next entries from the cookie 2454 uint32 countRead = 0; 2455 dirent* entryBuffer = (dirent*)buffer; 2456 while (countRead < count 2457 && cookie->ReadNextEntry(fID, node->id, countRead + 1 < count, 2458 entryBuffer, bufferSize)) { 2459 countRead++; 2460 bufferSize -= entryBuffer->d_reclen; 2461 entryBuffer = (dirent*)((uint8*)entryBuffer + entryBuffer->d_reclen); 2462 } 2463 2464 *_countRead = countRead; 2465 return B_OK; 2466 } 2467 2468 2469 // RewindAttrDir 2470 status_t 2471 FUSEVolume::RewindAttrDir(void* _node, void* _cookie) 2472 { 2473 AttrDirCookie* cookie = (AttrDirCookie*)_cookie; 2474 2475 RWLockableWriteLocker cookieLocker(this, cookie); 2476 2477 cookie->Clear(); 2478 2479 return B_OK; 2480 } 2481 2482 2483 // #pragma mark - attributes 2484 2485 2486 status_t 2487 FUSEVolume::OpenAttr(void* _node, const char* name, int openMode, 2488 void** _cookie) 2489 { 2490 FUSENode* node = (FUSENode*)_node; 2491 2492 // lock the node 2493 NodeReadLocker nodeLocker(this, node, true); 2494 if (nodeLocker.Status() != B_OK) 2495 RETURN_ERROR(nodeLocker.Status()); 2496 2497 if (openMode != O_RDONLY) { 2498 // Write support currently not implemented 2499 RETURN_ERROR(B_UNSUPPORTED); 2500 } 2501 2502 char path[B_PATH_NAME_LENGTH]; 2503 size_t pathLen; 2504 status_t error; 2505 int attrSize; 2506 if (fOps != NULL) { 2507 attrSize = fuse_ll_getxattr(fOps, node->id, name, NULL, 0); 2508 } else { 2509 AutoLocker<Locker> locker(fLock); 2510 2511 // get a path for the node 2512 error = _BuildPath(node, path, pathLen); 2513 if (error != B_OK) 2514 RETURN_ERROR(error); 2515 2516 locker.Unlock(); 2517 2518 attrSize = fuse_fs_getxattr(fFS, path, name, NULL, 0); 2519 } 2520 2521 if (attrSize < 0) { 2522 if (strcmp(name, kAttrMimeTypeName) == 0) { 2523 // Return a fake MIME type attribute based on the file extension 2524 const char* mimeType = NULL; 2525 error = set_mime(&mimeType, S_ISDIR(node->type) ? NULL : &path[0]); 2526 if (error != B_OK) 2527 return error; 2528 *_cookie = new(std::nothrow)AttrCookie(name, mimeType); 2529 return B_OK; 2530 } 2531 2532 // Reading attribute failed 2533 return attrSize; 2534 } 2535 2536 AttrCookie* cookie = new(std::nothrow)AttrCookie(name); 2537 if (cookie == NULL) 2538 RETURN_ERROR(B_NO_MEMORY); 2539 error = cookie->Allocate(attrSize); 2540 if (error != B_OK) { 2541 delete cookie; 2542 RETURN_ERROR(error); 2543 } 2544 2545 int bytesRead; 2546 if (fOps != NULL) 2547 bytesRead = fuse_ll_getxattr(fOps, node->id, name, cookie->Buffer(), attrSize); 2548 else 2549 bytesRead = fuse_fs_getxattr(fFS, path, name, cookie->Buffer(), attrSize); 2550 2551 if (bytesRead < 0) { 2552 delete cookie; 2553 return bytesRead; 2554 } 2555 2556 *_cookie = cookie; 2557 2558 return B_OK; 2559 } 2560 2561 2562 status_t 2563 FUSEVolume::CloseAttr(void* _node, void* _cookie) 2564 { 2565 return B_OK; 2566 } 2567 2568 2569 status_t 2570 FUSEVolume::FreeAttrCookie(void* _node, void* _cookie) 2571 { 2572 delete (AttrCookie*)_cookie; 2573 return B_OK; 2574 } 2575 2576 2577 status_t 2578 FUSEVolume::ReadAttr(void* _node, void* _cookie, off_t pos, void* buffer, 2579 size_t bufferSize, size_t* bytesRead) 2580 { 2581 AttrCookie* cookie = (AttrCookie*)_cookie; 2582 2583 RWLockableWriteLocker cookieLocker(this, cookie); 2584 2585 if (!cookie->IsValid()) 2586 RETURN_ERROR(B_BAD_VALUE); 2587 2588 cookie->Read(buffer, bufferSize, pos, bytesRead); 2589 2590 return B_OK; 2591 } 2592 2593 2594 status_t 2595 FUSEVolume::ReadAttrStat(void* _node, void* _cookie, struct stat* st) 2596 { 2597 AttrCookie* cookie = (AttrCookie*)_cookie; 2598 2599 RWLockableWriteLocker cookieLocker(this, cookie); 2600 2601 if (!cookie->IsValid()) 2602 RETURN_ERROR(B_BAD_VALUE); 2603 2604 st->st_size = cookie->Size(); 2605 st->st_type = cookie->Type(); 2606 2607 return B_OK; 2608 } 2609 2610 2611 // #pragma mark - 2612 2613 2614 ino_t 2615 FUSEVolume::_GenerateNodeID() 2616 { 2617 ino_t id; 2618 do { 2619 id = fNextNodeID++; 2620 } while (fNodes.Lookup(id) != NULL); 2621 2622 return id; 2623 } 2624 2625 2626 /*! Gets the ID of the node the entry specified by \a dir and \a entryName 2627 refers to. The ID is returned via \a _nodeID. The caller doesn't get a 2628 reference to the node. 2629 */ 2630 bool 2631 FUSEVolume::_GetNodeID(FUSENode* dir, const char* entryName, ino_t* _nodeID) 2632 { 2633 while (true) { 2634 AutoLocker<Locker> locker(fLock); 2635 2636 FUSENode* node; 2637 status_t error = _InternalGetNode(dir, entryName, &node, locker); 2638 if (error != B_OK) 2639 return false; 2640 2641 if (node == NULL) 2642 continue; 2643 2644 *_nodeID = node->id; 2645 _PutNode(node); 2646 2647 return true; 2648 } 2649 } 2650 2651 2652 /*! Gets the node the entry specified by \a dir and \a entryName refers to. The 2653 found node is returned via \a _node. The caller gets a reference to the node 2654 as well as a vnode reference. 2655 */ 2656 status_t 2657 FUSEVolume::_GetNode(FUSENode* dir, const char* entryName, FUSENode** _node) 2658 { 2659 while (true) { 2660 AutoLocker<Locker> locker(fLock); 2661 2662 FUSENode* node; 2663 status_t error = _InternalGetNode(dir, entryName, &node, locker); 2664 if (error != B_OK) 2665 return error; 2666 2667 if (node == NULL) 2668 continue; 2669 2670 ino_t nodeID = node->id; 2671 2672 locker.Unlock(); 2673 2674 // get a reference for the caller 2675 void* privateNode; 2676 error = UserlandFS::KernelEmu::get_vnode(fID, nodeID, &privateNode); 2677 if (error != B_OK) 2678 RETURN_ERROR(error); 2679 2680 locker.Lock(); 2681 2682 if (privateNode != node) { 2683 // weird, the node changed! 2684 ERROR(("FUSEVolume::_GetNode(): cookie for node %" B_PRId64 2685 " changed: expected: %p, got: %p\n", nodeID, node, 2686 privateNode)); 2687 UserlandFS::KernelEmu::put_vnode(fID, nodeID); 2688 _PutNode(node); 2689 continue; 2690 } 2691 2692 // Put the node reference we got from _InternalGetNode. We've now got 2693 // a reference from get_vnode(). 2694 _PutNode(node); 2695 2696 *_node = node; 2697 return B_OK; 2698 } 2699 } 2700 2701 2702 status_t 2703 FUSEVolume::_InternalGetNode(FUSENode* dir, const char* entryName, 2704 FUSENode** _node, AutoLocker<Locker>& locker) 2705 { 2706 // handle special cases 2707 if (strcmp(entryName, ".") == 0) { 2708 // same directory 2709 if (!S_ISDIR(dir->type)) 2710 RETURN_ERROR(B_NOT_A_DIRECTORY); 2711 2712 dir->refCount++; 2713 *_node = dir; 2714 return B_OK; 2715 } 2716 2717 if (strcmp(entryName, "..") == 0) { 2718 // parent directory 2719 if (!S_ISDIR(dir->type)) 2720 RETURN_ERROR(B_NOT_A_DIRECTORY); 2721 2722 FUSEEntry* entry = dir->entries.Head(); 2723 if (entry == NULL) 2724 RETURN_ERROR(B_ENTRY_NOT_FOUND); 2725 2726 entry->parent->refCount++; 2727 *_node = entry->parent; 2728 return B_OK; 2729 } 2730 2731 // lookup the entry in the table 2732 FUSEEntry* entry = fEntries.Lookup(FUSEEntryRef(dir->id, entryName)); 2733 if (entry != NULL) { 2734 entry->node->refCount++; 2735 *_node = entry->node; 2736 return B_OK; 2737 } 2738 2739 int fuseError; 2740 struct stat st; 2741 if (fOps != NULL) { 2742 fuseError = fuse_ll_lookup(fOps, dir->id, entryName, &st); 2743 } else { 2744 // construct a path for the entry 2745 char path[B_PATH_NAME_LENGTH]; 2746 size_t pathLen = 0; 2747 status_t error = _BuildPath(dir, entryName, path, pathLen); 2748 if (error != B_OK) 2749 return error; 2750 2751 locker.Unlock(); 2752 2753 // stat the path 2754 fuseError = fuse_fs_getattr(fFS, path, &st); 2755 } 2756 if (fuseError != 0) 2757 return fuseError; 2758 2759 // lookup the entry in the table again 2760 entry = fEntries.Lookup(FUSEEntryRef(dir->id, entryName)); 2761 if (entry != NULL) { 2762 // check whether the node still matches 2763 if (entry->node->id == st.st_ino) { 2764 entry->node->refCount++; 2765 *_node = entry->node; 2766 } else { 2767 // nope, something changed -- return a NULL node and let the caller 2768 // call us again 2769 *_node = NULL; 2770 } 2771 2772 return B_OK; 2773 } 2774 2775 // lookup the node in the table 2776 FUSENode* node = NULL; 2777 if (fUseNodeIDs) 2778 node = fNodes.Lookup(st.st_ino); 2779 else 2780 st.st_ino = _GenerateNodeID(); 2781 2782 if (node == NULL) { 2783 // no node yet -- create one 2784 node = new(std::nothrow) FUSENode(st.st_ino, st.st_mode & S_IFMT); 2785 if (node == NULL) 2786 RETURN_ERROR(B_NO_MEMORY); 2787 2788 fNodes.Insert(node); 2789 } else { 2790 // get a node reference for the entry 2791 node->refCount++; 2792 } 2793 2794 // create the entry 2795 entry = FUSEEntry::Create(dir, entryName, node); 2796 if (entry == NULL) { 2797 _PutNode(node); 2798 RETURN_ERROR(B_NO_MEMORY); 2799 } 2800 2801 dir->refCount++; 2802 // dir reference for the entry 2803 2804 fEntries.Insert(entry); 2805 node->entries.Add(entry); 2806 2807 locker.Unlock(); 2808 2809 // get a reference for the caller 2810 node->refCount++; 2811 2812 *_node = node; 2813 return B_OK; 2814 } 2815 2816 2817 void 2818 FUSEVolume::_PutNode(FUSENode* node) 2819 { 2820 if (--node->refCount == 0) { 2821 fNodes.Remove(node); 2822 if (node->cacheCount != 0) 2823 UserlandFS::KernelEmu::file_cache_delete(GetID(), node->id); 2824 delete node; 2825 } 2826 } 2827 2828 2829 void 2830 FUSEVolume::_PutNodes(FUSENode* const* nodes, int32 count) 2831 { 2832 for (int32 i = 0; i < count; i++) 2833 _PutNode(nodes[i]); 2834 } 2835 2836 2837 /*! Volume must be locked. The entry's directory must be write locked. 2838 */ 2839 void 2840 FUSEVolume::_RemoveEntry(FUSEEntry* entry) 2841 { 2842 fEntries.Remove(entry); 2843 entry->node->entries.Remove(entry); 2844 _PutNode(entry->node); 2845 _PutNode(entry->parent); 2846 delete entry; 2847 } 2848 2849 2850 /*! Volume must be locked. The directory must be write locked. 2851 */ 2852 status_t 2853 FUSEVolume::_RemoveEntry(FUSENode* dir, const char* name) 2854 { 2855 FUSEEntry* entry = fEntries.Lookup(FUSEEntryRef(dir->id, name)); 2856 if (entry == NULL) 2857 return B_ENTRY_NOT_FOUND; 2858 2859 _RemoveEntry(entry); 2860 return B_OK; 2861 } 2862 2863 2864 /*! Volume must be locked. The directories must be write locked. 2865 */ 2866 status_t 2867 FUSEVolume::_RenameEntry(FUSENode* oldDir, const char* oldName, 2868 FUSENode* newDir, const char* newName) 2869 { 2870 FUSEEntry* entry = fEntries.Lookup(FUSEEntryRef(oldDir->id, oldName)); 2871 if (entry == NULL) 2872 return B_ENTRY_NOT_FOUND; 2873 2874 // get a node reference for the new entry 2875 FUSENode* node = entry->node; 2876 node->refCount++; 2877 2878 // remove the old entry 2879 _RemoveEntry(entry); 2880 2881 // make sure there's no entry in our way 2882 _RemoveEntry(newDir, newName); 2883 2884 // create a new entry 2885 entry = FUSEEntry::Create(newDir, newName, node); 2886 if (entry == NULL) { 2887 _PutNode(node); 2888 RETURN_ERROR(B_NO_MEMORY); 2889 } 2890 2891 newDir->refCount++; 2892 // dir reference for the entry 2893 2894 fEntries.Insert(entry); 2895 node->entries.Add(entry); 2896 2897 return B_OK; 2898 } 2899 2900 2901 /*! Locks the given node and all of its ancestors up to the root. The given 2902 node is write-locked, if \a writeLock is \c true, read-locked otherwise. All 2903 ancestors are always read-locked in either case. 2904 2905 If \a lockParent is \c true, the given node itself is ignored, but locking 2906 starts with the parent node of the given node (\a writeLock applies to the 2907 parent node then). 2908 2909 If the method fails, none of the nodes is locked. 2910 2911 The volume lock must not be held. 2912 */ 2913 status_t 2914 FUSEVolume::_LockNodeChain(FUSENode* node, bool lockParent, bool writeLock) 2915 { 2916 AutoLocker<Locker> locker(fLock); 2917 2918 FUSENode* originalNode = node; 2919 2920 if (lockParent && node != NULL) 2921 node = node->Parent(); 2922 2923 if (node == NULL) 2924 RETURN_ERROR(B_ENTRY_NOT_FOUND); 2925 2926 LockIterator iterator(this, node, writeLock, NULL); 2927 2928 bool done; 2929 do { 2930 bool volumeUnlocked; 2931 status_t error = iterator.LockNext(&done, &volumeUnlocked); 2932 if (error != B_OK) 2933 RETURN_ERROR(error); 2934 2935 if (volumeUnlocked) { 2936 // check whether we're still locking the right node 2937 if (lockParent && originalNode->Parent() != node) { 2938 // We don't -- unlock everything and try again. 2939 node = originalNode->Parent(); 2940 iterator.SetTo(this, node, writeLock, NULL); 2941 } 2942 } 2943 } while (!done); 2944 2945 // Fail, if we couldn't lock all nodes up to the root. 2946 if (iterator.lastLockedNode != fRootNode) 2947 RETURN_ERROR(B_ENTRY_NOT_FOUND); 2948 2949 iterator.Detach(); 2950 return B_OK; 2951 } 2952 2953 2954 void 2955 FUSEVolume::_UnlockNodeChain(FUSENode* node, bool parent, bool writeLock) 2956 { 2957 AutoLocker<Locker> locker(fLock); 2958 2959 if (parent && node != NULL) 2960 node = node->Parent(); 2961 2962 _UnlockNodeChainInternal(node, writeLock, NULL, NULL); 2963 } 2964 2965 2966 /*! Unlocks all nodes from \a node up to (and including) \a stopNode (if 2967 \c NULL, it is ignored). If \a stopBeforeNode is given, the method stops 2968 before unlocking that node. 2969 The volume lock must be held. 2970 */ 2971 void 2972 FUSEVolume::_UnlockNodeChainInternal(FUSENode* node, bool writeLock, 2973 FUSENode* stopNode, FUSENode* stopBeforeNode) 2974 { 2975 FUSENode* originalNode = node; 2976 2977 while (node != NULL && node != stopBeforeNode) { 2978 FUSENode* parent = node->Parent(); 2979 2980 fLockManager.GenericUnlock(node == originalNode && writeLock, node); 2981 _PutNode(node); 2982 2983 if (node == stopNode || parent == node) 2984 break; 2985 2986 node = parent; 2987 } 2988 } 2989 2990 2991 status_t 2992 FUSEVolume::_LockNodeChains(FUSENode* node1, bool lockParent1, bool writeLock1, 2993 FUSENode* node2, bool lockParent2, bool writeLock2) 2994 { 2995 // Since in this case locking is more complicated, we use a helper method. 2996 // It does the main work, but simply returns telling us to retry when the 2997 // node hierarchy changes. 2998 bool retry; 2999 do { 3000 status_t error = _LockNodeChainsInternal(node1, lockParent1, writeLock1, 3001 node2, lockParent2, writeLock2, &retry); 3002 if (error != B_OK) 3003 return error; 3004 } while (retry); 3005 3006 return B_OK; 3007 } 3008 3009 3010 status_t 3011 FUSEVolume::_LockNodeChainsInternal(FUSENode* node1, bool lockParent1, 3012 bool writeLock1, FUSENode* node2, bool lockParent2, bool writeLock2, 3013 bool* _retry) 3014 { 3015 // Locking order: 3016 // * A child of a node has to be locked before its parent. 3017 // * Sibling nodes have to be locked in ascending node ID order. 3018 // 3019 // This implies the following locking algorithm: 3020 // * We find the closest common ancestor of the two given nodes (might even 3021 // be one of the given nodes). 3022 // * We lock all ancestors on one branch (the one with the lower common 3023 // ancestor child node ID), but not including the common ancestor. 3024 // * We lock all ancestors on the other branch, not including the common 3025 // ancestor. 3026 // * We lock the common ancestor and all of its ancestors up to the root 3027 // node. 3028 // 3029 // When the hierarchy changes while we're waiting for a lock, we recheck the 3030 // conditions and in doubt have to be restarted. 3031 3032 AutoLocker<Locker> locker(fLock); 3033 3034 FUSENode* originalNode1 = node1; 3035 FUSENode* originalNode2 = node2; 3036 3037 if (lockParent1 && node1 != NULL) 3038 node1 = node1->Parent(); 3039 3040 if (lockParent2 && node2 != NULL) 3041 node2 = node2->Parent(); 3042 3043 if (node1 == NULL || node2 == NULL) 3044 RETURN_ERROR(B_ENTRY_NOT_FOUND); 3045 3046 // find the first common ancestor 3047 FUSENode* commonAncestor; 3048 bool inverseLockingOrder; 3049 if (!_FindCommonAncestor(node1, node2, &commonAncestor, 3050 &inverseLockingOrder)) { 3051 RETURN_ERROR(B_ENTRY_NOT_FOUND); 3052 } 3053 3054 // lock the both node chains up to (but not including) the common ancestor 3055 LockIterator iterator1(this, node1, writeLock1, commonAncestor); 3056 LockIterator iterator2(this, node2, writeLock2, commonAncestor); 3057 3058 for (int i = 0; i < 2; i++) { 3059 LockIterator& iterator = (i == 0) != inverseLockingOrder 3060 ? iterator1 : iterator2; 3061 3062 // If the node is the common ancestor, don't enter the "do" loop, since 3063 // we don't have to lock anything here. 3064 if (iterator.firstNode == commonAncestor) 3065 continue; 3066 3067 bool done; 3068 do { 3069 bool volumeUnlocked; 3070 status_t error = iterator.LockNext(&done, &volumeUnlocked); 3071 if (error != B_OK) 3072 RETURN_ERROR(error); 3073 3074 if (volumeUnlocked) { 3075 // check whether we're still locking the right nodes 3076 if ((lockParent1 && originalNode1->Parent() != node1) 3077 || (lockParent2 && originalNode2->Parent() != node2)) { 3078 // We don't -- unlock everything and retry. 3079 *_retry = true; 3080 return B_OK; 3081 } 3082 3083 // also recheck the common ancestor 3084 FUSENode* newCommonParent; 3085 bool newInverseLockingOrder; 3086 if (!_FindCommonAncestor(node1, node2, &newCommonParent, 3087 &newInverseLockingOrder)) { 3088 RETURN_ERROR(B_ENTRY_NOT_FOUND); 3089 } 3090 3091 if (newCommonParent != commonAncestor 3092 || inverseLockingOrder != newInverseLockingOrder) { 3093 // Something changed -- unlock everything and retry. 3094 *_retry = true; 3095 return B_OK; 3096 } 3097 } 3098 } while (!done); 3099 } 3100 3101 // Continue locking from the common ancestor to the root. If one of the 3102 // given nodes is the common ancestor and shall be write locked, we need to 3103 // use the respective iterator. 3104 LockIterator& iterator = node2 == commonAncestor && writeLock2 3105 ? iterator2 : iterator1; 3106 iterator.SetStopBeforeNode(NULL); 3107 3108 bool done; 3109 do { 3110 bool volumeUnlocked; 3111 status_t error = iterator.LockNext(&done, &volumeUnlocked); 3112 if (error != B_OK) 3113 RETURN_ERROR(error); 3114 3115 if (volumeUnlocked) { 3116 // check whether we're still locking the right nodes 3117 if ((lockParent1 && originalNode1->Parent() != node1) 3118 || (lockParent2 && originalNode2->Parent() != node2)) { 3119 // We don't -- unlock everything and retry. 3120 *_retry = true; 3121 return B_OK; 3122 } 3123 3124 // Also recheck the common ancestor, if we have just locked it. 3125 // Otherwise we can just continue to lock, since nothing below the 3126 // previously locked node can have changed. 3127 if (iterator.lastLockedNode == commonAncestor) { 3128 FUSENode* newCommonParent; 3129 bool newInverseLockingOrder; 3130 if (!_FindCommonAncestor(node1, node2, &newCommonParent, 3131 &newInverseLockingOrder)) { 3132 RETURN_ERROR(B_ENTRY_NOT_FOUND); 3133 } 3134 3135 if (newCommonParent != commonAncestor 3136 || inverseLockingOrder != newInverseLockingOrder) { 3137 // Something changed -- unlock everything and retry. 3138 *_retry = true; 3139 return B_OK; 3140 } 3141 } 3142 } 3143 } while (!done); 3144 3145 // Fail, if we couldn't lock all nodes up to the root. 3146 if (iterator.lastLockedNode != fRootNode) 3147 RETURN_ERROR(B_ENTRY_NOT_FOUND); 3148 3149 // everything went fine 3150 iterator1.Detach(); 3151 iterator2.Detach(); 3152 3153 *_retry = false; 3154 return B_OK; 3155 } 3156 3157 3158 void 3159 FUSEVolume::_UnlockNodeChains(FUSENode* node1, bool lockParent1, 3160 bool writeLock1, FUSENode* node2, bool lockParent2, bool writeLock2) 3161 { 3162 AutoLocker<Locker> locker(fLock); 3163 3164 if (lockParent1 && node1 != NULL) 3165 node1 = node1->Parent(); 3166 3167 if (lockParent2 && node2 != NULL) 3168 node2 = node2->Parent(); 3169 3170 if (node1 == NULL || node2 == NULL) 3171 return; 3172 3173 // find the common ancestor 3174 FUSENode* commonAncestor; 3175 bool inverseLockingOrder; 3176 if (!_FindCommonAncestor(node1, node2, &commonAncestor, 3177 &inverseLockingOrder)) { 3178 return; 3179 } 3180 3181 // Unlock one branch up to the common ancestor and then the complete other 3182 // branch up to the root. If one of the given nodes is the common ancestor, 3183 // we need to make sure, we write-unlock it, if requested. 3184 if (node2 == commonAncestor && writeLock2) { 3185 _UnlockNodeChainInternal(node1, writeLock1, NULL, commonAncestor); 3186 _UnlockNodeChainInternal(node2, writeLock2, NULL, NULL); 3187 } else { 3188 _UnlockNodeChainInternal(node2, writeLock2, NULL, commonAncestor); 3189 _UnlockNodeChainInternal(node1, writeLock1, NULL, NULL); 3190 } 3191 } 3192 3193 3194 bool 3195 FUSEVolume::_FindCommonAncestor(FUSENode* node1, FUSENode* node2, 3196 FUSENode** _commonAncestor, bool* _inverseLockingOrder) 3197 { 3198 // handle trivial special case -- both nodes are the same 3199 if (node1 == node2) { 3200 *_commonAncestor = node1; 3201 *_inverseLockingOrder = false; 3202 return true; 3203 } 3204 3205 // get the ancestors of both nodes 3206 FUSENode* ancestors1[kMaxNodeTreeDepth]; 3207 FUSENode* ancestors2[kMaxNodeTreeDepth]; 3208 uint32 count1; 3209 uint32 count2; 3210 3211 if (!_GetNodeAncestors(node1, ancestors1, &count1) 3212 || !_GetNodeAncestors(node2, ancestors2, &count2)) { 3213 return false; 3214 } 3215 3216 // find the first ancestor not common to both nodes 3217 uint32 index = 0; 3218 for (; index < count1 && index < count2; index++) { 3219 FUSENode* ancestor1 = ancestors1[count1 - index - 1]; 3220 FUSENode* ancestor2 = ancestors2[count2 - index - 1]; 3221 if (ancestor1 != ancestor2) { 3222 *_commonAncestor = ancestors1[count1 - index]; 3223 *_inverseLockingOrder = ancestor1->id > ancestor2->id; 3224 return true; 3225 } 3226 } 3227 3228 // one node is an ancestor of the other 3229 *_commonAncestor = ancestors1[count1 - index]; 3230 *_inverseLockingOrder = index == count1; 3231 return true; 3232 } 3233 3234 3235 bool 3236 FUSEVolume::_GetNodeAncestors(FUSENode* node, FUSENode** ancestors, 3237 uint32* _count) 3238 { 3239 uint32 count = 0; 3240 while (node != NULL && count < kMaxNodeTreeDepth) { 3241 ancestors[count++] = node; 3242 3243 if (node == fRootNode) { 3244 *_count = count; 3245 return true; 3246 } 3247 3248 node = node->Parent(); 3249 } 3250 3251 // Either the node is not in the tree or we hit the array limit. 3252 return false; 3253 } 3254 3255 3256 status_t 3257 FUSEVolume::_BuildPath(FUSENode* dir, const char* entryName, char* path, 3258 size_t& pathLen) 3259 { 3260 // get the directory path 3261 status_t error = _BuildPath(dir, path, pathLen); 3262 if (error != B_OK) 3263 return error; 3264 3265 if (path[pathLen - 1] != '/') { 3266 path[pathLen++] = '/'; 3267 if (pathLen == B_PATH_NAME_LENGTH) 3268 RETURN_ERROR(B_NAME_TOO_LONG); 3269 } 3270 3271 // append the entry name 3272 size_t len = strlen(entryName); 3273 if (pathLen + len >= B_PATH_NAME_LENGTH) 3274 RETURN_ERROR(B_NAME_TOO_LONG); 3275 3276 memcpy(path + pathLen, entryName, len + 1); 3277 pathLen += len; 3278 3279 return B_OK; 3280 } 3281 3282 3283 status_t 3284 FUSEVolume::_BuildPath(FUSENode* node, char* path, size_t& pathLen) 3285 { 3286 if (node == fRootNode) { 3287 // we hit the root 3288 strcpy(path, "/"); 3289 pathLen = 1; 3290 return B_OK; 3291 } 3292 3293 // get an entry for the node and get its path 3294 FUSEEntry* entry = node->entries.Head(); 3295 if (entry == NULL) 3296 RETURN_ERROR(B_ENTRY_NOT_FOUND); 3297 3298 return _BuildPath(entry->parent, entry->name, path, pathLen); 3299 } 3300 3301 3302 /*static*/ int 3303 FUSEVolume::_AddReadDirEntryLowLevel(void* _buffer, char* buf, size_t bufsize, const char* name, 3304 const struct stat* st, off_t offset) 3305 { 3306 ReadDirBuffer* buffer = (ReadDirBuffer*)_buffer; 3307 3308 ino_t nodeID = st != NULL ? st->st_ino : 0; 3309 int type = st != NULL ? st->st_mode & S_IFMT : 0; 3310 return buffer->volume->_AddReadDirEntryLowLevel(buffer, buf, bufsize, name, type, nodeID, offset); 3311 } 3312 3313 3314 /*static*/ int 3315 FUSEVolume::_AddReadDirEntry(void* _buffer, const char* name, 3316 const struct stat* st, off_t offset) 3317 { 3318 ReadDirBuffer* buffer = (ReadDirBuffer*)_buffer; 3319 3320 ino_t nodeID = st != NULL ? st->st_ino : 0; 3321 int type = st != NULL ? st->st_mode & S_IFMT : 0; 3322 return buffer->volume->_AddReadDirEntry(buffer, name, type, nodeID, offset); 3323 } 3324 3325 3326 /*static*/ int 3327 FUSEVolume::_AddReadDirEntryGetDir(fuse_dirh_t handle, const char* name, 3328 int type, ino_t nodeID) 3329 { 3330 ReadDirBuffer* buffer = (ReadDirBuffer*)handle; 3331 return buffer->volume->_AddReadDirEntry(buffer, name, type << 12, nodeID, 0); 3332 } 3333 3334 3335 int 3336 FUSEVolume::_AddReadDirEntryLowLevel(ReadDirBuffer* buffer, char* buf, size_t bufsize, const char* name, 3337 int type, ino_t nodeID, off_t offset) 3338 { 3339 PRINT(("FUSEVolume::_AddReadDirEntryLowLevel(%p, \"%s\", %#x, %" B_PRId64 ", %" 3340 B_PRId64 "\n", buffer, name, type, nodeID, offset)); 3341 3342 AutoLocker<Locker> locker(fLock); 3343 3344 size_t entryLen = 0; 3345 3346 // create a node and an entry, if necessary 3347 ino_t dirID = buffer->directory->id; 3348 FUSEEntry* entry; 3349 if (strcmp(name, ".") == 0) { 3350 // current dir entry 3351 nodeID = dirID; 3352 type = S_IFDIR; 3353 } else if (strcmp(name, "..") == 0) { 3354 // parent dir entry 3355 FUSEEntry* parentEntry = buffer->directory->entries.Head(); 3356 if (parentEntry == NULL) { 3357 ERROR(("FUSEVolume::_AddReadDirEntry(): dir %" B_PRId64 3358 " has no entry!\n", dirID)); 3359 return 0; 3360 } 3361 nodeID = parentEntry->parent->id; 3362 type = S_IFDIR; 3363 } else if ((entry = fEntries.Lookup(FUSEEntryRef(dirID, name))) == NULL) { 3364 // get the node 3365 FUSENode* node = NULL; 3366 if (fUseNodeIDs) 3367 node = fNodes.Lookup(nodeID); 3368 else 3369 nodeID = _GenerateNodeID(); 3370 3371 if (node == NULL) { 3372 // no node yet -- create one 3373 3374 // If we don't have a valid type, we need to stat the node first. 3375 if (type == 0) { 3376 struct stat st; 3377 int fuseError; 3378 if (fOps != NULL) { 3379 fuseError = fuse_ll_getattr(fOps, node->id, &st); 3380 } else { 3381 char path[B_PATH_NAME_LENGTH]; 3382 size_t pathLen; 3383 status_t error = _BuildPath(buffer->directory, name, path, 3384 pathLen); 3385 if (error != B_OK) { 3386 buffer->error = error; 3387 return 0; 3388 } 3389 3390 locker.Unlock(); 3391 3392 // stat the path 3393 fuseError = fuse_fs_getattr(fFS, path, &st); 3394 } 3395 3396 locker.Lock(); 3397 3398 if (fuseError != 0) { 3399 buffer->error = fuseError; 3400 return 0; 3401 } 3402 3403 type = st.st_mode & S_IFMT; 3404 } 3405 3406 node = new(std::nothrow) FUSENode(nodeID, type); 3407 if (node == NULL) { 3408 buffer->error = B_NO_MEMORY; 3409 return 1; 3410 } 3411 PRINT((" -> create node: %p, id: %" B_PRId64 "\n", node, nodeID)); 3412 3413 fNodes.Insert(node); 3414 } else { 3415 // get a node reference for the entry 3416 node->refCount++; 3417 } 3418 3419 // create the entry 3420 entry = FUSEEntry::Create(buffer->directory, name, node); 3421 if (entry == NULL) { 3422 _PutNode(node); 3423 buffer->error = B_NO_MEMORY; 3424 return 1; 3425 } 3426 3427 buffer->directory->refCount++; 3428 // dir reference for the entry 3429 3430 fEntries.Insert(entry); 3431 node->entries.Add(entry); 3432 } else { 3433 // TODO: Check whether the node's ID matches the one we got (if any)! 3434 nodeID = entry->node->id; 3435 type = entry->node->type; 3436 } 3437 3438 // fill in the dirent 3439 dirent* dirEntry = (dirent*)(buf); 3440 dirEntry->d_dev = fID; 3441 dirEntry->d_ino = nodeID; 3442 strcpy(dirEntry->d_name, name); 3443 3444 // align the entry length, so the next dirent will be aligned 3445 entryLen = offsetof(struct dirent, d_name) + strlen(name) + 1; 3446 entryLen = ROUNDUP(entryLen, 8); 3447 3448 dirEntry->d_reclen = entryLen; 3449 3450 // update the buffer 3451 buffer->usedSize += entryLen; 3452 3453 return 0; 3454 } 3455 3456 3457 int 3458 FUSEVolume::_AddReadDirEntry(ReadDirBuffer* buffer, const char* name, 3459 int type, ino_t nodeID, off_t offset) 3460 { 3461 PRINT(("FUSEVolume::_AddReadDirEntry(%p, \"%s\", %#x, %" B_PRId64 ", %" 3462 B_PRId64 "\n", buffer, name, type, nodeID, offset)); 3463 3464 AutoLocker<Locker> locker(fLock); 3465 3466 size_t entryLen = 0; 3467 if (offset != 0) { 3468 // does the caller want more entries? 3469 if (buffer->entriesRead == buffer->maxEntries) 3470 return 1; 3471 3472 // compute the entry length and check whether the entry still fits 3473 entryLen = offsetof(struct dirent, d_name) + strlen(name) + 1; 3474 if (buffer->usedSize + entryLen > buffer->bufferSize) 3475 return 1; 3476 } 3477 3478 // create a node and an entry, if necessary 3479 ino_t dirID = buffer->directory->id; 3480 FUSEEntry* entry; 3481 if (strcmp(name, ".") == 0) { 3482 // current dir entry 3483 nodeID = dirID; 3484 type = S_IFDIR; 3485 } else if (strcmp(name, "..") == 0) { 3486 // parent dir entry 3487 FUSEEntry* parentEntry = buffer->directory->entries.Head(); 3488 if (parentEntry == NULL) { 3489 ERROR(("FUSEVolume::_AddReadDirEntry(): dir %" B_PRId64 3490 " has no entry!\n", dirID)); 3491 return 0; 3492 } 3493 nodeID = parentEntry->parent->id; 3494 type = S_IFDIR; 3495 } else if ((entry = fEntries.Lookup(FUSEEntryRef(dirID, name))) == NULL) { 3496 // get the node 3497 FUSENode* node = NULL; 3498 if (fUseNodeIDs) 3499 node = fNodes.Lookup(nodeID); 3500 else 3501 nodeID = _GenerateNodeID(); 3502 3503 if (node == NULL) { 3504 // no node yet -- create one 3505 3506 // If we don't have a valid type, we need to stat the node first. 3507 if (type == 0) { 3508 struct stat st; 3509 int fuseError; 3510 if (fOps != NULL) { 3511 fuseError = fuse_ll_getattr(fOps, node->id, &st); 3512 } else { 3513 char path[B_PATH_NAME_LENGTH]; 3514 size_t pathLen; 3515 status_t error = _BuildPath(buffer->directory, name, path, 3516 pathLen); 3517 if (error != B_OK) { 3518 buffer->error = error; 3519 return 0; 3520 } 3521 3522 locker.Unlock(); 3523 3524 // stat the path 3525 fuseError = fuse_fs_getattr(fFS, path, &st); 3526 } 3527 3528 locker.Lock(); 3529 3530 if (fuseError != 0) { 3531 buffer->error = fuseError; 3532 return 0; 3533 } 3534 3535 type = st.st_mode & S_IFMT; 3536 } 3537 3538 node = new(std::nothrow) FUSENode(nodeID, type); 3539 if (node == NULL) { 3540 buffer->error = B_NO_MEMORY; 3541 return 1; 3542 } 3543 PRINT((" -> create node: %p, id: %" B_PRId64 "\n", node, nodeID)); 3544 3545 fNodes.Insert(node); 3546 } else { 3547 // get a node reference for the entry 3548 node->refCount++; 3549 } 3550 3551 // create the entry 3552 entry = FUSEEntry::Create(buffer->directory, name, node); 3553 if (entry == NULL) { 3554 _PutNode(node); 3555 buffer->error = B_NO_MEMORY; 3556 return 1; 3557 } 3558 3559 buffer->directory->refCount++; 3560 // dir reference for the entry 3561 3562 fEntries.Insert(entry); 3563 node->entries.Add(entry); 3564 } else { 3565 // TODO: Check whether the node's ID matches the one we got (if any)! 3566 nodeID = entry->node->id; 3567 type = entry->node->type; 3568 } 3569 3570 if (offset == 0) { 3571 // cache the entry 3572 if (buffer->cookie->entryCache == NULL) { 3573 // no cache yet -- create it 3574 buffer->cookie->entryCache = new(std::nothrow) DirEntryCache; 3575 if (buffer->cookie->entryCache == NULL) { 3576 buffer->error = B_NO_MEMORY; 3577 return 1; 3578 } 3579 } 3580 3581 status_t error = buffer->cookie->entryCache->AddEntry(nodeID, name); 3582 if (error != B_OK) { 3583 buffer->error = error; 3584 return 1; 3585 } 3586 } else { 3587 // fill in the dirent 3588 dirent* dirEntry = (dirent*)((uint8*)buffer->buffer + buffer->usedSize); 3589 dirEntry->d_dev = fID; 3590 dirEntry->d_ino = nodeID; 3591 strcpy(dirEntry->d_name, name); 3592 3593 if (buffer->entriesRead + 1 < buffer->maxEntries) { 3594 // align the entry length, so the next dirent will be aligned 3595 entryLen = ROUNDUP(entryLen, 8); 3596 entryLen = std::min(entryLen, 3597 buffer->bufferSize - buffer->usedSize); 3598 } 3599 3600 dirEntry->d_reclen = entryLen; 3601 3602 // update the buffer 3603 buffer->usedSize += entryLen; 3604 buffer->entriesRead++; 3605 buffer->cookie->currentEntryOffset = offset; 3606 } 3607 3608 return 0; 3609 } 3610 3611 3612 status_t 3613 FUSEVolume::_InternalIO(FUSENode* node, FileCookie* cookie, const char* path, 3614 off_t pos, char* buffer, size_t& length, bool write) 3615 { 3616 PRINT(("FUSEVolume::_InternalIO(%p, %p, %s, %" B_PRIdOFF ", %p, %" B_PRIuSIZE ", %d)\n", 3617 node, cookie, path, pos, buffer, length, write)); 3618 3619 int fuseError; 3620 if (fOps != NULL) { 3621 if (write) 3622 fuseError = fuse_ll_write(fOps, node->id, buffer, length, pos, cookie); 3623 else 3624 fuseError = fuse_ll_read(fOps, node->id, buffer, length, pos, cookie); 3625 } else { 3626 char pathBuf[B_PATH_NAME_LENGTH]; 3627 3628 if (path == NULL) { 3629 AutoLocker<Locker> locker(fLock); 3630 3631 size_t pathLen; 3632 status_t error = _BuildPath(node, pathBuf, pathLen); 3633 if (error != B_OK) 3634 RETURN_ERROR(error); 3635 3636 locker.Unlock(); 3637 3638 path = pathBuf; 3639 } 3640 3641 if (write) 3642 fuseError = fuse_fs_write(fFS, path, buffer, length, pos, cookie); 3643 else 3644 fuseError = fuse_fs_read(fFS, path, buffer, length, pos, cookie); 3645 } 3646 3647 if (fuseError < 0) 3648 return fuseError; 3649 3650 length = fuseError; 3651 return B_OK; 3652 } 3653