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 return bytesRead; 2216 2217 *_cookie = cookie; 2218 2219 return B_OK; 2220 } 2221 2222 2223 status_t 2224 FUSEVolume::CloseAttr(void* _node, void* _cookie) 2225 { 2226 return B_OK; 2227 } 2228 2229 2230 status_t 2231 FUSEVolume::FreeAttrCookie(void* _node, void* _cookie) 2232 { 2233 delete (AttrCookie*)_cookie; 2234 return B_OK; 2235 } 2236 2237 2238 status_t 2239 FUSEVolume::ReadAttr(void* _node, void* _cookie, off_t pos, void* buffer, 2240 size_t bufferSize, size_t* bytesRead) 2241 { 2242 AttrCookie* cookie = (AttrCookie*)_cookie; 2243 2244 RWLockableWriteLocker cookieLocker(this, cookie); 2245 2246 if (!cookie->IsValid()) 2247 RETURN_ERROR(B_BAD_VALUE); 2248 2249 cookie->Read(buffer, bufferSize, pos, bytesRead); 2250 2251 return B_OK; 2252 } 2253 2254 2255 status_t 2256 FUSEVolume::ReadAttrStat(void* _node, void* _cookie, struct stat* st) 2257 { 2258 AttrCookie* cookie = (AttrCookie*)_cookie; 2259 2260 RWLockableWriteLocker cookieLocker(this, cookie); 2261 2262 if (!cookie->IsValid()) 2263 RETURN_ERROR(B_BAD_VALUE); 2264 2265 st->st_size = cookie->Size(); 2266 st->st_type = cookie->Type(); 2267 2268 return B_OK; 2269 } 2270 2271 2272 // #pragma mark - 2273 2274 2275 ino_t 2276 FUSEVolume::_GenerateNodeID() 2277 { 2278 ino_t id; 2279 do { 2280 id = fNextNodeID++; 2281 } while (fNodes.Lookup(id) != NULL); 2282 2283 return id; 2284 } 2285 2286 2287 /*! Gets the ID of the node the entry specified by \a dir and \a entryName 2288 refers to. The ID is returned via \a _nodeID. The caller doesn't get a 2289 reference to the node. 2290 */ 2291 bool 2292 FUSEVolume::_GetNodeID(FUSENode* dir, const char* entryName, ino_t* _nodeID) 2293 { 2294 while (true) { 2295 AutoLocker<Locker> locker(fLock); 2296 2297 FUSENode* node; 2298 status_t error = _InternalGetNode(dir, entryName, &node, locker); 2299 if (error != B_OK) 2300 return false; 2301 2302 if (node == NULL) 2303 continue; 2304 2305 *_nodeID = node->id; 2306 _PutNode(node); 2307 2308 return true; 2309 } 2310 } 2311 2312 2313 /*! Gets the node the entry specified by \a dir and \a entryName refers to. The 2314 found node is returned via \a _node. The caller gets a reference to the node 2315 as well as a vnode reference. 2316 */ 2317 status_t 2318 FUSEVolume::_GetNode(FUSENode* dir, const char* entryName, FUSENode** _node) 2319 { 2320 while (true) { 2321 AutoLocker<Locker> locker(fLock); 2322 2323 FUSENode* node; 2324 status_t error = _InternalGetNode(dir, entryName, &node, locker); 2325 if (error != B_OK) 2326 return error; 2327 2328 if (node == NULL) 2329 continue; 2330 2331 ino_t nodeID = node->id; 2332 2333 locker.Unlock(); 2334 2335 // get a reference for the caller 2336 void* privateNode; 2337 error = UserlandFS::KernelEmu::get_vnode(fID, nodeID, &privateNode); 2338 if (error != B_OK) 2339 RETURN_ERROR(error); 2340 2341 locker.Lock(); 2342 2343 if (privateNode != node) { 2344 // weird, the node changed! 2345 ERROR(("FUSEVolume::_GetNode(): cookie for node %" B_PRId64 2346 " changed: expected: %p, got: %p\n", nodeID, node, 2347 privateNode)); 2348 UserlandFS::KernelEmu::put_vnode(fID, nodeID); 2349 _PutNode(node); 2350 continue; 2351 } 2352 2353 // Put the node reference we got from _InternalGetNode. We've now got 2354 // a reference from get_vnode(). 2355 _PutNode(node); 2356 2357 *_node = node; 2358 return B_OK; 2359 } 2360 } 2361 2362 2363 status_t 2364 FUSEVolume::_InternalGetNode(FUSENode* dir, const char* entryName, 2365 FUSENode** _node, AutoLocker<Locker>& locker) 2366 { 2367 // handle special cases 2368 if (strcmp(entryName, ".") == 0) { 2369 // same directory 2370 if (!S_ISDIR(dir->type)) 2371 RETURN_ERROR(B_NOT_A_DIRECTORY); 2372 2373 dir->refCount++; 2374 *_node = dir; 2375 return B_OK; 2376 } 2377 2378 if (strcmp(entryName, "..") == 0) { 2379 // parent directory 2380 if (!S_ISDIR(dir->type)) 2381 RETURN_ERROR(B_NOT_A_DIRECTORY); 2382 2383 FUSEEntry* entry = dir->entries.Head(); 2384 if (entry == NULL) 2385 RETURN_ERROR(B_ENTRY_NOT_FOUND); 2386 2387 entry->parent->refCount++; 2388 *_node = entry->parent; 2389 return B_OK; 2390 } 2391 2392 // lookup the entry in the table 2393 FUSEEntry* entry = fEntries.Lookup(FUSEEntryRef(dir->id, entryName)); 2394 if (entry != NULL) { 2395 entry->node->refCount++; 2396 *_node = entry->node; 2397 return B_OK; 2398 } 2399 2400 // construct a path for the entry 2401 char path[B_PATH_NAME_LENGTH]; 2402 size_t pathLen = 0; 2403 status_t error = _BuildPath(dir, entryName, path, pathLen); 2404 if (error != B_OK) 2405 return error; 2406 2407 locker.Unlock(); 2408 2409 // stat the path 2410 struct stat st; 2411 int fuseError = fuse_fs_getattr(fFS, path, &st); 2412 if (fuseError != 0) 2413 return fuseError; 2414 2415 locker.Lock(); 2416 2417 // lookup the entry in the table again 2418 entry = fEntries.Lookup(FUSEEntryRef(dir->id, entryName)); 2419 if (entry != NULL) { 2420 // check whether the node still matches 2421 if (entry->node->id == st.st_ino) { 2422 entry->node->refCount++; 2423 *_node = entry->node; 2424 } else { 2425 // nope, something changed -- return a NULL node and let the caller 2426 // call us again 2427 *_node = NULL; 2428 } 2429 2430 return B_OK; 2431 } 2432 2433 // lookup the node in the table 2434 FUSENode* node = NULL; 2435 if (fUseNodeIDs) 2436 node = fNodes.Lookup(st.st_ino); 2437 else 2438 st.st_ino = _GenerateNodeID(); 2439 2440 if (node == NULL) { 2441 // no node yet -- create one 2442 node = new(std::nothrow) FUSENode(st.st_ino, st.st_mode & S_IFMT); 2443 if (node == NULL) 2444 RETURN_ERROR(B_NO_MEMORY); 2445 2446 fNodes.Insert(node); 2447 } else { 2448 // get a node reference for the entry 2449 node->refCount++; 2450 } 2451 2452 // create the entry 2453 entry = FUSEEntry::Create(dir, entryName, node); 2454 if (entry == NULL) { 2455 _PutNode(node); 2456 RETURN_ERROR(B_NO_MEMORY); 2457 } 2458 2459 dir->refCount++; 2460 // dir reference for the entry 2461 2462 fEntries.Insert(entry); 2463 node->entries.Add(entry); 2464 2465 locker.Unlock(); 2466 2467 // get a reference for the caller 2468 node->refCount++; 2469 2470 *_node = node; 2471 return B_OK; 2472 } 2473 2474 2475 void 2476 FUSEVolume::_PutNode(FUSENode* node) 2477 { 2478 if (--node->refCount == 0) { 2479 fNodes.Remove(node); 2480 delete node; 2481 } 2482 } 2483 2484 2485 void 2486 FUSEVolume::_PutNodes(FUSENode* const* nodes, int32 count) 2487 { 2488 for (int32 i = 0; i < count; i++) 2489 _PutNode(nodes[i]); 2490 } 2491 2492 2493 /*! Volume must be locked. The entry's directory must be write locked. 2494 */ 2495 void 2496 FUSEVolume::_RemoveEntry(FUSEEntry* entry) 2497 { 2498 fEntries.Remove(entry); 2499 entry->node->entries.Remove(entry); 2500 _PutNode(entry->node); 2501 _PutNode(entry->parent); 2502 delete entry; 2503 } 2504 2505 2506 /*! Volume must be locked. The directory must be write locked. 2507 */ 2508 status_t 2509 FUSEVolume::_RemoveEntry(FUSENode* dir, const char* name) 2510 { 2511 FUSEEntry* entry = fEntries.Lookup(FUSEEntryRef(dir->id, name)); 2512 if (entry == NULL) 2513 return B_ENTRY_NOT_FOUND; 2514 2515 _RemoveEntry(entry); 2516 return B_OK; 2517 } 2518 2519 2520 /*! Volume must be locked. The directories must be write locked. 2521 */ 2522 status_t 2523 FUSEVolume::_RenameEntry(FUSENode* oldDir, const char* oldName, 2524 FUSENode* newDir, const char* newName) 2525 { 2526 FUSEEntry* entry = fEntries.Lookup(FUSEEntryRef(oldDir->id, oldName)); 2527 if (entry == NULL) 2528 return B_ENTRY_NOT_FOUND; 2529 2530 // get a node reference for the new entry 2531 FUSENode* node = entry->node; 2532 node->refCount++; 2533 2534 // remove the old entry 2535 _RemoveEntry(entry); 2536 2537 // make sure there's no entry in our way 2538 _RemoveEntry(newDir, newName); 2539 2540 // create a new entry 2541 entry = FUSEEntry::Create(newDir, newName, node); 2542 if (entry == NULL) { 2543 _PutNode(node); 2544 RETURN_ERROR(B_NO_MEMORY); 2545 } 2546 2547 newDir->refCount++; 2548 // dir reference for the entry 2549 2550 fEntries.Insert(entry); 2551 node->entries.Add(entry); 2552 2553 return B_OK; 2554 } 2555 2556 2557 /*! Locks the given node and all of its ancestors up to the root. The given 2558 node is write-locked, if \a writeLock is \c true, read-locked otherwise. All 2559 ancestors are always read-locked in either case. 2560 2561 If \a lockParent is \c true, the given node itself is ignored, but locking 2562 starts with the parent node of the given node (\a writeLock applies to the 2563 parent node then). 2564 2565 If the method fails, none of the nodes is locked. 2566 2567 The volume lock must not be held. 2568 */ 2569 status_t 2570 FUSEVolume::_LockNodeChain(FUSENode* node, bool lockParent, bool writeLock) 2571 { 2572 AutoLocker<Locker> locker(fLock); 2573 2574 FUSENode* originalNode = node; 2575 2576 if (lockParent && node != NULL) 2577 node = node->Parent(); 2578 2579 if (node == NULL) 2580 RETURN_ERROR(B_ENTRY_NOT_FOUND); 2581 2582 LockIterator iterator(this, node, writeLock, NULL); 2583 2584 bool done; 2585 do { 2586 bool volumeUnlocked; 2587 status_t error = iterator.LockNext(&done, &volumeUnlocked); 2588 if (error != B_OK) 2589 RETURN_ERROR(error); 2590 2591 if (volumeUnlocked) { 2592 // check whether we're still locking the right node 2593 if (lockParent && originalNode->Parent() != node) { 2594 // We don't -- unlock everything and try again. 2595 node = originalNode->Parent(); 2596 iterator.SetTo(this, node, writeLock, NULL); 2597 } 2598 } 2599 } while (!done); 2600 2601 // Fail, if we couldn't lock all nodes up to the root. 2602 if (iterator.lastLockedNode != fRootNode) 2603 RETURN_ERROR(B_ENTRY_NOT_FOUND); 2604 2605 iterator.Detach(); 2606 return B_OK; 2607 } 2608 2609 2610 void 2611 FUSEVolume::_UnlockNodeChain(FUSENode* node, bool parent, bool writeLock) 2612 { 2613 AutoLocker<Locker> locker(fLock); 2614 2615 if (parent && node != NULL) 2616 node = node->Parent(); 2617 2618 _UnlockNodeChainInternal(node, writeLock, NULL, NULL); 2619 } 2620 2621 2622 /*! Unlocks all nodes from \a node up to (and including) \a stopNode (if 2623 \c NULL, it is ignored). If \a stopBeforeNode is given, the method stops 2624 before unlocking that node. 2625 The volume lock must be held. 2626 */ 2627 void 2628 FUSEVolume::_UnlockNodeChainInternal(FUSENode* node, bool writeLock, 2629 FUSENode* stopNode, FUSENode* stopBeforeNode) 2630 { 2631 FUSENode* originalNode = node; 2632 2633 while (node != NULL && node != stopBeforeNode) { 2634 FUSENode* parent = node->Parent(); 2635 2636 fLockManager.GenericUnlock(node == originalNode && writeLock, node); 2637 _PutNode(node); 2638 2639 if (node == stopNode || parent == node) 2640 break; 2641 2642 node = parent; 2643 } 2644 } 2645 2646 2647 status_t 2648 FUSEVolume::_LockNodeChains(FUSENode* node1, bool lockParent1, bool writeLock1, 2649 FUSENode* node2, bool lockParent2, bool writeLock2) 2650 { 2651 // Since in this case locking is more complicated, we use a helper method. 2652 // It does the main work, but simply returns telling us to retry when the 2653 // node hierarchy changes. 2654 bool retry; 2655 do { 2656 status_t error = _LockNodeChainsInternal(node1, lockParent1, writeLock1, 2657 node2, lockParent2, writeLock2, &retry); 2658 if (error != B_OK) 2659 return error; 2660 } while (retry); 2661 2662 return B_OK; 2663 } 2664 2665 2666 status_t 2667 FUSEVolume::_LockNodeChainsInternal(FUSENode* node1, bool lockParent1, 2668 bool writeLock1, FUSENode* node2, bool lockParent2, bool writeLock2, 2669 bool* _retry) 2670 { 2671 // Locking order: 2672 // * A child of a node has to be locked before its parent. 2673 // * Sibling nodes have to be locked in ascending node ID order. 2674 // 2675 // This implies the following locking algorithm: 2676 // * We find the closest common ancestor of the two given nodes (might even 2677 // be one of the given nodes). 2678 // * We lock all ancestors on one branch (the one with the lower common 2679 // ancestor child node ID), but not including the common ancestor. 2680 // * We lock all ancestors on the other branch, not including the common 2681 // ancestor. 2682 // * We lock the common ancestor and all of its ancestors up to the root 2683 // node. 2684 // 2685 // When the hierarchy changes while we're waiting for a lock, we recheck the 2686 // conditions and in doubt have to be restarted. 2687 2688 AutoLocker<Locker> locker(fLock); 2689 2690 FUSENode* originalNode1 = node1; 2691 FUSENode* originalNode2 = node2; 2692 2693 if (lockParent1 && node1 != NULL) 2694 node1 = node1->Parent(); 2695 2696 if (lockParent2 && node2 != NULL) 2697 node2 = node2->Parent(); 2698 2699 if (node1 == NULL || node2 == NULL) 2700 RETURN_ERROR(B_ENTRY_NOT_FOUND); 2701 2702 // find the first common ancestor 2703 FUSENode* commonAncestor; 2704 bool inverseLockingOrder; 2705 if (!_FindCommonAncestor(node1, node2, &commonAncestor, 2706 &inverseLockingOrder)) { 2707 RETURN_ERROR(B_ENTRY_NOT_FOUND); 2708 } 2709 2710 // lock the both node chains up to (but not including) the common ancestor 2711 LockIterator iterator1(this, node1, writeLock1, commonAncestor); 2712 LockIterator iterator2(this, node2, writeLock2, commonAncestor); 2713 2714 for (int i = 0; i < 2; i++) { 2715 LockIterator& iterator = (i == 0) != inverseLockingOrder 2716 ? iterator1 : iterator2; 2717 2718 // If the node is the common ancestor, don't enter the "do" loop, since 2719 // we don't have to lock anything here. 2720 if (iterator.firstNode == commonAncestor) 2721 continue; 2722 2723 bool done; 2724 do { 2725 bool volumeUnlocked; 2726 status_t error = iterator.LockNext(&done, &volumeUnlocked); 2727 if (error != B_OK) 2728 RETURN_ERROR(error); 2729 2730 if (volumeUnlocked) { 2731 // check whether we're still locking the right nodes 2732 if ((lockParent1 && originalNode1->Parent() != node1) 2733 || (lockParent2 && originalNode2->Parent() != node2)) { 2734 // We don't -- unlock everything and retry. 2735 *_retry = true; 2736 return B_OK; 2737 } 2738 2739 // also recheck the common ancestor 2740 FUSENode* newCommonParent; 2741 bool newInverseLockingOrder; 2742 if (!_FindCommonAncestor(node1, node2, &newCommonParent, 2743 &newInverseLockingOrder)) { 2744 RETURN_ERROR(B_ENTRY_NOT_FOUND); 2745 } 2746 2747 if (newCommonParent != commonAncestor 2748 || inverseLockingOrder != newInverseLockingOrder) { 2749 // Something changed -- unlock everything and retry. 2750 *_retry = true; 2751 return B_OK; 2752 } 2753 } 2754 } while (!done); 2755 } 2756 2757 // Continue locking from the common ancestor to the root. If one of the 2758 // given nodes is the common ancestor and shall be write locked, we need to 2759 // use the respective iterator. 2760 LockIterator& iterator = node2 == commonAncestor && writeLock2 2761 ? iterator2 : iterator1; 2762 iterator.SetStopBeforeNode(NULL); 2763 2764 bool done; 2765 do { 2766 bool volumeUnlocked; 2767 status_t error = iterator.LockNext(&done, &volumeUnlocked); 2768 if (error != B_OK) 2769 RETURN_ERROR(error); 2770 2771 if (volumeUnlocked) { 2772 // check whether we're still locking the right nodes 2773 if ((lockParent1 && originalNode1->Parent() != node1) 2774 || (lockParent2 && originalNode2->Parent() != node2)) { 2775 // We don't -- unlock everything and retry. 2776 *_retry = true; 2777 return B_OK; 2778 } 2779 2780 // Also recheck the common ancestor, if we have just locked it. 2781 // Otherwise we can just continue to lock, since nothing below the 2782 // previously locked node can have changed. 2783 if (iterator.lastLockedNode == commonAncestor) { 2784 FUSENode* newCommonParent; 2785 bool newInverseLockingOrder; 2786 if (!_FindCommonAncestor(node1, node2, &newCommonParent, 2787 &newInverseLockingOrder)) { 2788 RETURN_ERROR(B_ENTRY_NOT_FOUND); 2789 } 2790 2791 if (newCommonParent != commonAncestor 2792 || inverseLockingOrder != newInverseLockingOrder) { 2793 // Something changed -- unlock everything and retry. 2794 *_retry = true; 2795 return B_OK; 2796 } 2797 } 2798 } 2799 } while (!done); 2800 2801 // Fail, if we couldn't lock all nodes up to the root. 2802 if (iterator.lastLockedNode != fRootNode) 2803 RETURN_ERROR(B_ENTRY_NOT_FOUND); 2804 2805 // everything went fine 2806 iterator1.Detach(); 2807 iterator2.Detach(); 2808 2809 *_retry = false; 2810 return B_OK; 2811 } 2812 2813 2814 void 2815 FUSEVolume::_UnlockNodeChains(FUSENode* node1, bool lockParent1, 2816 bool writeLock1, FUSENode* node2, bool lockParent2, bool writeLock2) 2817 { 2818 AutoLocker<Locker> locker(fLock); 2819 2820 if (lockParent1 && node1 != NULL) 2821 node1 = node1->Parent(); 2822 2823 if (lockParent2 && node2 != NULL) 2824 node2 = node2->Parent(); 2825 2826 if (node1 == NULL || node2 == NULL) 2827 return; 2828 2829 // find the common ancestor 2830 FUSENode* commonAncestor; 2831 bool inverseLockingOrder; 2832 if (!_FindCommonAncestor(node1, node2, &commonAncestor, 2833 &inverseLockingOrder)) { 2834 return; 2835 } 2836 2837 // Unlock one branch up to the common ancestor and then the complete other 2838 // branch up to the root. If one of the given nodes is the common ancestor, 2839 // we need to make sure, we write-unlock it, if requested. 2840 if (node2 == commonAncestor && writeLock2) { 2841 _UnlockNodeChainInternal(node1, writeLock1, NULL, commonAncestor); 2842 _UnlockNodeChainInternal(node2, writeLock2, NULL, NULL); 2843 } else { 2844 _UnlockNodeChainInternal(node2, writeLock2, NULL, commonAncestor); 2845 _UnlockNodeChainInternal(node1, writeLock1, NULL, NULL); 2846 } 2847 } 2848 2849 2850 bool 2851 FUSEVolume::_FindCommonAncestor(FUSENode* node1, FUSENode* node2, 2852 FUSENode** _commonAncestor, bool* _inverseLockingOrder) 2853 { 2854 // handle trivial special case -- both nodes are the same 2855 if (node1 == node2) { 2856 *_commonAncestor = node1; 2857 *_inverseLockingOrder = false; 2858 return true; 2859 } 2860 2861 // get the ancestors of both nodes 2862 FUSENode* ancestors1[kMaxNodeTreeDepth]; 2863 FUSENode* ancestors2[kMaxNodeTreeDepth]; 2864 uint32 count1; 2865 uint32 count2; 2866 2867 if (!_GetNodeAncestors(node1, ancestors1, &count1) 2868 || !_GetNodeAncestors(node2, ancestors2, &count2)) { 2869 return false; 2870 } 2871 2872 // find the first ancestor not common to both nodes 2873 uint32 index = 0; 2874 for (; index < count1 && index < count2; index++) { 2875 FUSENode* ancestor1 = ancestors1[count1 - index - 1]; 2876 FUSENode* ancestor2 = ancestors2[count2 - index - 1]; 2877 if (ancestor1 != ancestor2) { 2878 *_commonAncestor = ancestors1[count1 - index]; 2879 *_inverseLockingOrder = ancestor1->id > ancestor2->id; 2880 return true; 2881 } 2882 } 2883 2884 // one node is an ancestor of the other 2885 *_commonAncestor = ancestors1[count1 - index]; 2886 *_inverseLockingOrder = index == count1; 2887 return true; 2888 } 2889 2890 2891 bool 2892 FUSEVolume::_GetNodeAncestors(FUSENode* node, FUSENode** ancestors, 2893 uint32* _count) 2894 { 2895 uint32 count = 0; 2896 while (node != NULL && count < kMaxNodeTreeDepth) { 2897 ancestors[count++] = node; 2898 2899 if (node == fRootNode) { 2900 *_count = count; 2901 return true; 2902 } 2903 2904 node = node->Parent(); 2905 } 2906 2907 // Either the node is not in the tree or we hit the array limit. 2908 return false; 2909 } 2910 2911 2912 status_t 2913 FUSEVolume::_BuildPath(FUSENode* dir, const char* entryName, char* path, 2914 size_t& pathLen) 2915 { 2916 // get the directory path 2917 status_t error = _BuildPath(dir, path, pathLen); 2918 if (error != B_OK) 2919 return error; 2920 2921 if (path[pathLen - 1] != '/') { 2922 path[pathLen++] = '/'; 2923 if (pathLen == B_PATH_NAME_LENGTH) 2924 RETURN_ERROR(B_NAME_TOO_LONG); 2925 } 2926 2927 // append the entry name 2928 size_t len = strlen(entryName); 2929 if (pathLen + len >= B_PATH_NAME_LENGTH) 2930 RETURN_ERROR(B_NAME_TOO_LONG); 2931 2932 memcpy(path + pathLen, entryName, len + 1); 2933 pathLen += len; 2934 2935 return B_OK; 2936 } 2937 2938 2939 status_t 2940 FUSEVolume::_BuildPath(FUSENode* node, char* path, size_t& pathLen) 2941 { 2942 if (node == fRootNode) { 2943 // we hit the root 2944 strcpy(path, "/"); 2945 pathLen = 1; 2946 return B_OK; 2947 } 2948 2949 // get an entry for the node and get its path 2950 FUSEEntry* entry = node->entries.Head(); 2951 if (entry == NULL) 2952 RETURN_ERROR(B_ENTRY_NOT_FOUND); 2953 2954 return _BuildPath(entry->parent, entry->name, path, pathLen); 2955 } 2956 2957 2958 /*static*/ int 2959 FUSEVolume::_AddReadDirEntry(void* _buffer, const char* name, 2960 const struct stat* st, off_t offset) 2961 { 2962 ReadDirBuffer* buffer = (ReadDirBuffer*)_buffer; 2963 2964 ino_t nodeID = st != NULL ? st->st_ino : 0; 2965 int type = st != NULL ? st->st_mode & S_IFMT : 0; 2966 return buffer->volume->_AddReadDirEntry(buffer, name, type, nodeID, offset); 2967 } 2968 2969 2970 /*static*/ int 2971 FUSEVolume::_AddReadDirEntryGetDir(fuse_dirh_t handle, const char* name, 2972 int type, ino_t nodeID) 2973 { 2974 ReadDirBuffer* buffer = (ReadDirBuffer*)handle; 2975 return buffer->volume->_AddReadDirEntry(buffer, name, type << 12, nodeID, 2976 0); 2977 } 2978 2979 2980 int 2981 FUSEVolume::_AddReadDirEntry(ReadDirBuffer* buffer, const char* name, int type, 2982 ino_t nodeID, off_t offset) 2983 { 2984 PRINT(("FUSEVolume::_AddReadDirEntry(%p, \"%s\", %#x, %" B_PRId64 ", %" 2985 B_PRId64 "\n", buffer, name, type, nodeID, offset)); 2986 2987 AutoLocker<Locker> locker(fLock); 2988 2989 size_t entryLen = 0; 2990 if (offset != 0) { 2991 // does the caller want more entries? 2992 if (buffer->entriesRead == buffer->maxEntries) 2993 return 1; 2994 2995 // compute the entry length and check whether the entry still fits 2996 entryLen = sizeof(dirent) + strlen(name); 2997 if (buffer->usedSize + entryLen > buffer->bufferSize) 2998 return 1; 2999 } 3000 3001 // create a node and an entry, if necessary 3002 ino_t dirID = buffer->directory->id; 3003 FUSEEntry* entry; 3004 if (strcmp(name, ".") == 0) { 3005 // current dir entry 3006 nodeID = dirID; 3007 type = S_IFDIR; 3008 } else if (strcmp(name, "..") == 0) { 3009 // parent dir entry 3010 FUSEEntry* parentEntry = buffer->directory->entries.Head(); 3011 if (parentEntry == NULL) { 3012 ERROR(("FUSEVolume::_AddReadDirEntry(): dir %" B_PRId64 3013 " has no entry!\n", dirID)); 3014 return 0; 3015 } 3016 nodeID = parentEntry->parent->id; 3017 type = S_IFDIR; 3018 } else if ((entry = fEntries.Lookup(FUSEEntryRef(dirID, name))) == NULL) { 3019 // get the node 3020 FUSENode* node = NULL; 3021 if (fUseNodeIDs) 3022 node = fNodes.Lookup(nodeID); 3023 else 3024 nodeID = _GenerateNodeID(); 3025 3026 if (node == NULL) { 3027 // no node yet -- create one 3028 3029 // If we don't have a valid type, we need to stat the node first. 3030 if (type == 0) { 3031 char path[B_PATH_NAME_LENGTH]; 3032 size_t pathLen; 3033 status_t error = _BuildPath(buffer->directory, name, path, 3034 pathLen); 3035 if (error != B_OK) { 3036 buffer->error = error; 3037 return 0; 3038 } 3039 3040 locker.Unlock(); 3041 3042 // stat the path 3043 struct stat st; 3044 int fuseError = fuse_fs_getattr(fFS, path, &st); 3045 3046 locker.Lock(); 3047 3048 if (fuseError != 0) { 3049 buffer->error = fuseError; 3050 return 0; 3051 } 3052 3053 type = st.st_mode & S_IFMT; 3054 } 3055 3056 node = new(std::nothrow) FUSENode(nodeID, type); 3057 if (node == NULL) { 3058 buffer->error = B_NO_MEMORY; 3059 return 1; 3060 } 3061 PRINT((" -> create node: %p, id: %" B_PRId64 "\n", node, nodeID)); 3062 3063 fNodes.Insert(node); 3064 } else { 3065 // get a node reference for the entry 3066 node->refCount++; 3067 } 3068 3069 // create the entry 3070 entry = FUSEEntry::Create(buffer->directory, name, node); 3071 if (entry == NULL) { 3072 _PutNode(node); 3073 buffer->error = B_NO_MEMORY; 3074 return 1; 3075 } 3076 3077 buffer->directory->refCount++; 3078 // dir reference for the entry 3079 3080 fEntries.Insert(entry); 3081 node->entries.Add(entry); 3082 } else { 3083 // TODO: Check whether the node's ID matches the one we got (if any)! 3084 nodeID = entry->node->id; 3085 type = entry->node->type; 3086 } 3087 3088 if (offset == 0) { 3089 // cache the entry 3090 if (buffer->cookie->entryCache == NULL) { 3091 // no cache yet -- create it 3092 buffer->cookie->entryCache = new(std::nothrow) DirEntryCache; 3093 if (buffer->cookie->entryCache == NULL) { 3094 buffer->error = B_NO_MEMORY; 3095 return 1; 3096 } 3097 } 3098 3099 status_t error = buffer->cookie->entryCache->AddEntry(nodeID, name); 3100 if (error != B_OK) { 3101 buffer->error = error; 3102 return 1; 3103 } 3104 } else { 3105 // fill in the dirent 3106 dirent* dirEntry = (dirent*)((uint8*)buffer->buffer + buffer->usedSize); 3107 dirEntry->d_dev = fID; 3108 dirEntry->d_ino = nodeID; 3109 strcpy(dirEntry->d_name, name); 3110 3111 if (buffer->entriesRead + 1 < buffer->maxEntries) { 3112 // align the entry length, so the next dirent will be aligned 3113 entryLen = (entryLen + 7) / 8 * 8; 3114 entryLen = std::min(entryLen, 3115 buffer->bufferSize - buffer->usedSize); 3116 } 3117 3118 dirEntry->d_reclen = entryLen; 3119 3120 // update the buffer 3121 buffer->usedSize += entryLen; 3122 buffer->entriesRead++; 3123 buffer->cookie->currentEntryOffset = offset; 3124 } 3125 3126 return 0; 3127 } 3128