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