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