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