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