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