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 (%lld), \"%s\" -> \"%s\", %#x)\n", dir, 1003 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 (%lld), \"%s\" -> %p (%lld))\n", dir, dir->id, name, 1050 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 (%lld), \"%s\")\n", dir, dir->id, name)); 1099 1100 // lock the directory 1101 NodeWriteLocker nodeLocker(this, dir, false); 1102 if (nodeLocker.Status() != B_OK) 1103 RETURN_ERROR(nodeLocker.Status()); 1104 1105 // get the node ID (for the node monitoring message) 1106 ino_t nodeID; 1107 bool doNodeMonitoring = _GetNodeID(dir, name, &nodeID); 1108 1109 AutoLocker<Locker> locker(fLock); 1110 1111 // get a path for the entry 1112 char path[B_PATH_NAME_LENGTH]; 1113 size_t pathLen; 1114 status_t error = _BuildPath(dir, name, path, pathLen); 1115 if (error != B_OK) 1116 RETURN_ERROR(error); 1117 1118 locker.Unlock(); 1119 1120 // unlink 1121 int fuseError = fuse_fs_unlink(fFS, path); 1122 if (fuseError != 0) 1123 RETURN_ERROR(fuseError); 1124 1125 // remove the entry 1126 locker.Lock(); 1127 _RemoveEntry(dir, name); 1128 1129 // mark the dir dirty 1130 dir->dirty = true; 1131 locker.Unlock(); 1132 1133 // send node monitoring message 1134 if (doNodeMonitoring) { 1135 UserlandFS::KernelEmu::notify_listener(B_ENTRY_REMOVED, 0, fID, 0, 1136 dir->id, nodeID, NULL, name); 1137 } 1138 1139 return B_OK; 1140 } 1141 1142 1143 status_t 1144 FUSEVolume::Rename(void* _oldDir, const char* oldName, void* _newDir, 1145 const char* newName) 1146 { 1147 FUSENode* oldDir = (FUSENode*)_oldDir; 1148 FUSENode* newDir = (FUSENode*)_newDir; 1149 PRINT(("FUSEVolume::Rename(%p (%lld), \"%s\", %p (%lld), \"%s\")\n", oldDir, 1150 oldDir->id, oldName, newDir, newDir->id, newName)); 1151 1152 // lock the directories 1153 MultiNodeLocker nodeLocker(this, oldDir, false, true, newDir, false, true); 1154 if (nodeLocker.Status() != B_OK) 1155 RETURN_ERROR(nodeLocker.Status()); 1156 1157 AutoLocker<Locker> locker(fLock); 1158 1159 // get a path for the entries 1160 char oldPath[B_PATH_NAME_LENGTH]; 1161 size_t oldPathLen; 1162 status_t error = _BuildPath(oldDir, oldName, oldPath, oldPathLen); 1163 if (error != B_OK) 1164 RETURN_ERROR(error); 1165 1166 char newPath[B_PATH_NAME_LENGTH]; 1167 size_t newPathLen; 1168 error = _BuildPath(newDir, newName, newPath, newPathLen); 1169 if (error != B_OK) 1170 RETURN_ERROR(error); 1171 1172 locker.Unlock(); 1173 1174 // rename 1175 int fuseError = fuse_fs_rename(fFS, oldPath, newPath); 1176 if (fuseError != 0) 1177 RETURN_ERROR(fuseError); 1178 1179 // rename the entry 1180 locker.Lock(); 1181 _RenameEntry(oldDir, oldName, newDir, newName); 1182 1183 // mark the dirs dirty 1184 oldDir->dirty = true; 1185 newDir->dirty = true; 1186 1187 // send node monitoring message 1188 ino_t nodeID; 1189 if (_GetNodeID(newDir, newName, &nodeID)) { 1190 UserlandFS::KernelEmu::notify_listener(B_ENTRY_MOVED, 0, fID, 1191 oldDir->id, newDir->id, nodeID, oldName, newName); 1192 } 1193 1194 return B_OK; 1195 } 1196 1197 1198 status_t 1199 FUSEVolume::Access(void* _node, int mode) 1200 { 1201 FUSENode* node = (FUSENode*)_node; 1202 1203 // lock the directory 1204 NodeReadLocker nodeLocker(this, node, true); 1205 if (nodeLocker.Status() != B_OK) 1206 RETURN_ERROR(nodeLocker.Status()); 1207 1208 AutoLocker<Locker> locker(fLock); 1209 1210 // get a path for the node 1211 char path[B_PATH_NAME_LENGTH]; 1212 size_t pathLen; 1213 status_t error = _BuildPath(node, path, pathLen); 1214 if (error != B_OK) 1215 RETURN_ERROR(error); 1216 1217 locker.Unlock(); 1218 1219 // call the access hook on the path 1220 int fuseError = fuse_fs_access(fFS, path, mode); 1221 if (fuseError != 0) 1222 return fuseError; 1223 1224 return B_OK; 1225 } 1226 1227 1228 status_t 1229 FUSEVolume::ReadStat(void* _node, struct stat* st) 1230 { 1231 FUSENode* node = (FUSENode*)_node; 1232 PRINT(("FUSEVolume::ReadStat(%p (%lld), %p)\n", node, node->id, st)); 1233 1234 // lock the directory 1235 NodeReadLocker nodeLocker(this, node, true); 1236 if (nodeLocker.Status() != B_OK) 1237 RETURN_ERROR(nodeLocker.Status()); 1238 1239 AutoLocker<Locker> locker(fLock); 1240 1241 // get a path for the node 1242 char path[B_PATH_NAME_LENGTH]; 1243 size_t pathLen; 1244 status_t error = _BuildPath(node, path, pathLen); 1245 if (error != B_OK) 1246 RETURN_ERROR(error); 1247 1248 locker.Unlock(); 1249 1250 // stat the path 1251 int fuseError = fuse_fs_getattr(fFS, path, st); 1252 if (fuseError != 0) 1253 return fuseError; 1254 1255 return B_OK; 1256 } 1257 1258 1259 status_t 1260 FUSEVolume::WriteStat(void* _node, const struct stat* st, uint32 mask) 1261 { 1262 FUSENode* node = (FUSENode*)_node; 1263 PRINT(("FUSEVolume::WriteStat(%p (%lld), %p, %#lx)\n", node, node->id, st, 1264 mask)); 1265 1266 // lock the directory 1267 NodeReadLocker nodeLocker(this, node, true); 1268 if (nodeLocker.Status() != B_OK) 1269 RETURN_ERROR(nodeLocker.Status()); 1270 1271 AutoLocker<Locker> locker(fLock); 1272 1273 // get a path for the node 1274 char path[B_PATH_NAME_LENGTH]; 1275 size_t pathLen; 1276 status_t error = _BuildPath(node, path, pathLen); 1277 if (error != B_OK) 1278 RETURN_ERROR(error); 1279 1280 locker.Unlock(); 1281 1282 // permissions 1283 if ((mask & B_STAT_MODE) != 0) { 1284 int fuseError = fuse_fs_chmod(fFS, path, st->st_mode); 1285 if (fuseError != 0) 1286 RETURN_ERROR(fuseError); 1287 } 1288 1289 // owner 1290 if ((mask & (B_STAT_UID | B_STAT_GID)) != 0) { 1291 uid_t uid = (mask & B_STAT_UID) != 0 ? st->st_uid : (uid_t)-1; 1292 gid_t gid = (mask & B_STAT_GID) != 0 ? st->st_gid : (gid_t)-1; 1293 int fuseError = fuse_fs_chown(fFS, path, uid, gid); 1294 if (fuseError != 0) 1295 RETURN_ERROR(fuseError); 1296 } 1297 1298 // size 1299 if ((mask & B_STAT_SIZE) != 0) { 1300 // truncate 1301 int fuseError = fuse_fs_truncate(fFS, path, st->st_size); 1302 if (fuseError != 0) 1303 RETURN_ERROR(fuseError); 1304 } 1305 1306 // access/modification time 1307 if ((mask & (B_STAT_ACCESS_TIME | B_STAT_MODIFICATION_TIME)) != 0) { 1308 timespec tv[2] = { 1309 {st->st_atime, 0}, 1310 {st->st_mtime, 0} 1311 }; 1312 1313 // If either time is not specified, we need to stat the file to get the 1314 // current value. 1315 if ((mask & (B_STAT_ACCESS_TIME | B_STAT_MODIFICATION_TIME)) 1316 != (B_STAT_ACCESS_TIME | B_STAT_MODIFICATION_TIME)) { 1317 struct stat currentStat; 1318 int fuseError = fuse_fs_getattr(fFS, path, ¤tStat); 1319 if (fuseError != 0) 1320 RETURN_ERROR(fuseError); 1321 1322 if ((mask & B_STAT_ACCESS_TIME) == 0) 1323 tv[0].tv_sec = currentStat.st_atime; 1324 else 1325 tv[1].tv_sec = currentStat.st_mtime; 1326 } 1327 1328 int fuseError = fuse_fs_utimens(fFS, path, tv); 1329 if (fuseError != 0) 1330 RETURN_ERROR(fuseError); 1331 } 1332 1333 // mark the node dirty 1334 locker.Lock(); 1335 node->dirty = true; 1336 1337 // send node monitoring message 1338 uint32 changedFields = mask & 1339 (B_STAT_MODE | B_STAT_UID | B_STAT_GID | B_STAT_SIZE 1340 | B_STAT_ACCESS_TIME | B_STAT_MODIFICATION_TIME); 1341 1342 if (changedFields != 0) { 1343 UserlandFS::KernelEmu::notify_listener(B_STAT_CHANGED, changedFields, 1344 fID, 0, 0, node->id, NULL, NULL); 1345 } 1346 1347 return B_OK; 1348 } 1349 1350 1351 // #pragma mark - files 1352 1353 1354 status_t 1355 FUSEVolume::Create(void* _dir, const char* name, int openMode, int mode, 1356 void** _cookie, ino_t* _vnid) 1357 { 1358 FUSENode* dir = (FUSENode*)_dir; 1359 PRINT(("FUSEVolume::Create(%p (%lld), \"%s\", %#x, %#x)\n", dir, dir->id, name, 1360 openMode, mode)); 1361 1362 // lock the directory 1363 NodeWriteLocker nodeLocker(this, dir, false); 1364 if (nodeLocker.Status() != B_OK) 1365 RETURN_ERROR(nodeLocker.Status()); 1366 1367 // allocate a file cookie 1368 FileCookie* cookie = new(std::nothrow) FileCookie(openMode); 1369 if (cookie == NULL) 1370 RETURN_ERROR(B_NO_MEMORY); 1371 ObjectDeleter<FileCookie> cookieDeleter(cookie); 1372 1373 AutoLocker<Locker> locker(fLock); 1374 1375 // get a path for the node 1376 char path[B_PATH_NAME_LENGTH]; 1377 size_t pathLen; 1378 status_t error = _BuildPath(dir, name, path, pathLen); 1379 if (error != B_OK) 1380 RETURN_ERROR(error); 1381 1382 locker.Unlock(); 1383 1384 // create the file 1385 int fuseError = fuse_fs_create(fFS, path, mode, cookie); 1386 if (fuseError != 0) 1387 RETURN_ERROR(fuseError); 1388 1389 // get the node 1390 FUSENode* node; 1391 error = _GetNode(dir, name, &node); 1392 if (error != B_OK) { 1393 // This is bad. We've create the file successfully, but couldn't get 1394 // the node. Close the file and delete the entry. 1395 fuse_fs_flush(fFS, path, cookie); 1396 fuse_fs_release(fFS, path, cookie); 1397 fuse_fs_unlink(fFS, path); 1398 RETURN_ERROR(error); 1399 } 1400 1401 // mark the dir and the node dirty 1402 locker.Lock(); 1403 dir->dirty = true; 1404 node->dirty = true; 1405 locker.Unlock(); 1406 1407 // send node monitoring message 1408 UserlandFS::KernelEmu::notify_listener(B_ENTRY_CREATED, 0, fID, 0, dir->id, 1409 node->id, NULL, name); 1410 1411 cookieDeleter.Detach(); 1412 *_cookie = cookie; 1413 *_vnid = node->id; 1414 1415 return B_OK; 1416 } 1417 1418 1419 status_t 1420 FUSEVolume::Open(void* _node, int openMode, void** _cookie) 1421 { 1422 FUSENode* node = (FUSENode*)_node; 1423 PRINT(("FUSEVolume::Open(%p (%lld), %#x)\n", node, node->id, openMode)); 1424 1425 // lock the directory 1426 NodeReadLocker nodeLocker(this, node, true); 1427 if (nodeLocker.Status() != B_OK) 1428 RETURN_ERROR(nodeLocker.Status()); 1429 1430 bool truncate = (openMode & O_TRUNC) != 0; 1431 openMode &= ~O_TRUNC; 1432 1433 // allocate a file cookie 1434 FileCookie* cookie = new(std::nothrow) FileCookie(openMode); 1435 if (cookie == NULL) 1436 RETURN_ERROR(B_NO_MEMORY); 1437 ObjectDeleter<FileCookie> cookieDeleter(cookie); 1438 1439 AutoLocker<Locker> locker(fLock); 1440 1441 // get a path for the node 1442 char path[B_PATH_NAME_LENGTH]; 1443 size_t pathLen; 1444 status_t error = _BuildPath(node, path, pathLen); 1445 if (error != B_OK) 1446 RETURN_ERROR(error); 1447 1448 locker.Unlock(); 1449 1450 // open the file 1451 int fuseError = fuse_fs_open(fFS, path, cookie); 1452 if (fuseError != 0) 1453 RETURN_ERROR(fuseError); 1454 1455 // truncate the file, if requested 1456 if (truncate) { 1457 fuseError = fuse_fs_ftruncate(fFS, path, 0, cookie); 1458 if (fuseError != 0) { 1459 fuse_fs_flush(fFS, path, cookie); 1460 fuse_fs_release(fFS, path, cookie); 1461 RETURN_ERROR(fuseError); 1462 } 1463 1464 // mark the node dirty 1465 locker.Lock(); 1466 node->dirty = true; 1467 1468 // send node monitoring message 1469 UserlandFS::KernelEmu::notify_listener(B_STAT_CHANGED, 1470 B_STAT_SIZE | B_STAT_MODIFICATION_TIME, fID, 0, 0, node->id, NULL, 1471 NULL); 1472 } 1473 1474 cookieDeleter.Detach(); 1475 *_cookie = cookie; 1476 1477 return B_OK; 1478 } 1479 1480 1481 status_t 1482 FUSEVolume::Close(void* _node, void* _cookie) 1483 { 1484 FUSENode* node = (FUSENode*)_node; 1485 FileCookie* cookie = (FileCookie*)_cookie; 1486 1487 RWLockableReadLocker cookieLocker(this, cookie); 1488 1489 // lock the directory 1490 NodeReadLocker nodeLocker(this, node, true); 1491 if (nodeLocker.Status() != B_OK) 1492 RETURN_ERROR(nodeLocker.Status()); 1493 1494 AutoLocker<Locker> locker(fLock); 1495 1496 // get a path for the node 1497 char path[B_PATH_NAME_LENGTH]; 1498 size_t pathLen; 1499 status_t error = _BuildPath(node, path, pathLen); 1500 if (error != B_OK) 1501 RETURN_ERROR(error); 1502 1503 locker.Unlock(); 1504 1505 // flush the file 1506 int fuseError = fuse_fs_flush(fFS, path, cookie); 1507 if (fuseError != 0) 1508 return fuseError; 1509 1510 return B_OK; 1511 } 1512 1513 1514 status_t 1515 FUSEVolume::FreeCookie(void* _node, void* _cookie) 1516 { 1517 FUSENode* node = (FUSENode*)_node; 1518 FileCookie* cookie = (FileCookie*)_cookie; 1519 1520 // no need to lock the cookie here, as no-one else uses it anymore 1521 1522 // lock the directory 1523 NodeReadLocker nodeLocker(this, node, true); 1524 if (nodeLocker.Status() != B_OK) 1525 RETURN_ERROR(nodeLocker.Status()); 1526 1527 ObjectDeleter<FileCookie> cookieDeleter(cookie); 1528 1529 AutoLocker<Locker> locker(fLock); 1530 1531 // get a path for the node 1532 char path[B_PATH_NAME_LENGTH]; 1533 size_t pathLen; 1534 status_t error = _BuildPath(node, path, pathLen); 1535 if (error != B_OK) 1536 RETURN_ERROR(error); 1537 1538 locker.Unlock(); 1539 1540 // release the file 1541 int fuseError = fuse_fs_release(fFS, path, cookie); 1542 if (fuseError != 0) 1543 return fuseError; 1544 1545 return B_OK; 1546 } 1547 1548 1549 status_t 1550 FUSEVolume::Read(void* _node, void* _cookie, off_t pos, void* buffer, 1551 size_t bufferSize, size_t* _bytesRead) 1552 { 1553 FUSENode* node = (FUSENode*)_node; 1554 FileCookie* cookie = (FileCookie*)_cookie; 1555 1556 RWLockableReadLocker cookieLocker(this, cookie); 1557 1558 *_bytesRead = 0; 1559 1560 // lock the directory 1561 NodeReadLocker nodeLocker(this, node, true); 1562 if (nodeLocker.Status() != B_OK) 1563 RETURN_ERROR(nodeLocker.Status()); 1564 1565 AutoLocker<Locker> locker(fLock); 1566 1567 // get a path for the node 1568 char path[B_PATH_NAME_LENGTH]; 1569 size_t pathLen; 1570 status_t error = _BuildPath(node, path, pathLen); 1571 if (error != B_OK) 1572 RETURN_ERROR(error); 1573 1574 locker.Unlock(); 1575 1576 // read the file 1577 int bytesRead = fuse_fs_read(fFS, path, (char*)buffer, bufferSize, pos, 1578 cookie); 1579 if (bytesRead < 0) 1580 return bytesRead; 1581 1582 *_bytesRead = bytesRead; 1583 return B_OK; 1584 } 1585 1586 1587 status_t 1588 FUSEVolume::Write(void* _node, void* _cookie, off_t pos, const void* buffer, 1589 size_t bufferSize, size_t* _bytesWritten) 1590 { 1591 FUSENode* node = (FUSENode*)_node; 1592 FileCookie* cookie = (FileCookie*)_cookie; 1593 1594 RWLockableReadLocker cookieLocker(this, cookie); 1595 1596 *_bytesWritten = 0; 1597 1598 // lock the directory 1599 NodeReadLocker nodeLocker(this, node, true); 1600 if (nodeLocker.Status() != B_OK) 1601 RETURN_ERROR(nodeLocker.Status()); 1602 1603 AutoLocker<Locker> locker(fLock); 1604 1605 // get a path for the node 1606 char path[B_PATH_NAME_LENGTH]; 1607 size_t pathLen; 1608 status_t error = _BuildPath(node, path, pathLen); 1609 if (error != B_OK) 1610 RETURN_ERROR(error); 1611 1612 locker.Unlock(); 1613 1614 // write the file 1615 int bytesWritten = fuse_fs_write(fFS, path, (const char*)buffer, bufferSize, 1616 pos, cookie); 1617 if (bytesWritten < 0) 1618 return bytesWritten; 1619 1620 // mark the node dirty 1621 locker.Lock(); 1622 node->dirty = true; 1623 1624 // send node monitoring message 1625 UserlandFS::KernelEmu::notify_listener(B_STAT_CHANGED, 1626 B_STAT_SIZE | B_STAT_MODIFICATION_TIME, fID, 0, 0, node->id, NULL, 1627 NULL); 1628 // TODO: The size possibly doesn't change. 1629 // TODO: Avoid message flooding -- use a timeout and set the 1630 // B_STAT_INTERIM_UPDATE flag. 1631 1632 *_bytesWritten = bytesWritten; 1633 return B_OK; 1634 } 1635 1636 1637 // #pragma mark - directories 1638 1639 1640 status_t 1641 FUSEVolume::CreateDir(void* _dir, const char* name, int mode) 1642 { 1643 FUSENode* dir = (FUSENode*)_dir; 1644 PRINT(("FUSEVolume::CreateDir(%p (%lld), \"%s\", %#x)\n", dir, dir->id, name, 1645 mode)); 1646 1647 // lock the directory 1648 NodeWriteLocker nodeLocker(this, dir, false); 1649 if (nodeLocker.Status() != B_OK) 1650 RETURN_ERROR(nodeLocker.Status()); 1651 1652 AutoLocker<Locker> locker(fLock); 1653 1654 // get a path for the entry 1655 char path[B_PATH_NAME_LENGTH]; 1656 size_t pathLen; 1657 status_t error = _BuildPath(dir, name, path, pathLen); 1658 if (error != B_OK) 1659 RETURN_ERROR(error); 1660 1661 locker.Unlock(); 1662 1663 // create the dir 1664 int fuseError = fuse_fs_mkdir(fFS, path, mode); 1665 if (fuseError != 0) 1666 RETURN_ERROR(fuseError); 1667 1668 // mark the dir dirty 1669 locker.Lock(); 1670 dir->dirty = true; 1671 1672 // send node monitoring message 1673 ino_t nodeID; 1674 if (_GetNodeID(dir, name, &nodeID)) { 1675 UserlandFS::KernelEmu::notify_listener(B_ENTRY_CREATED, 0, fID, 0, 1676 dir->id, nodeID, NULL, name); 1677 } 1678 1679 return B_OK; 1680 } 1681 1682 1683 status_t 1684 FUSEVolume::RemoveDir(void* _dir, const char* name) 1685 { 1686 FUSENode* dir = (FUSENode*)_dir; 1687 PRINT(("FUSEVolume::RemoveDir(%p (%lld), \"%s\")\n", dir, dir->id, name)); 1688 1689 // lock the directory 1690 NodeWriteLocker nodeLocker(this, dir, false); 1691 if (nodeLocker.Status() != B_OK) 1692 RETURN_ERROR(nodeLocker.Status()); 1693 1694 // get the node ID (for the node monitoring message) 1695 ino_t nodeID; 1696 bool doNodeMonitoring = _GetNodeID(dir, name, &nodeID); 1697 1698 AutoLocker<Locker> locker(fLock); 1699 1700 // get a path for the entry 1701 char path[B_PATH_NAME_LENGTH]; 1702 size_t pathLen; 1703 status_t error = _BuildPath(dir, name, path, pathLen); 1704 if (error != B_OK) 1705 RETURN_ERROR(error); 1706 1707 locker.Unlock(); 1708 1709 // remove the dir 1710 int fuseError = fuse_fs_rmdir(fFS, path); 1711 if (fuseError != 0) 1712 RETURN_ERROR(fuseError); 1713 1714 // remove the entry 1715 locker.Lock(); 1716 _RemoveEntry(dir, name); 1717 1718 // mark the parent dir dirty 1719 dir->dirty = true; 1720 1721 // send node monitoring message 1722 if (doNodeMonitoring) { 1723 UserlandFS::KernelEmu::notify_listener(B_ENTRY_REMOVED, 0, fID, 0, 1724 dir->id, nodeID, NULL, name); 1725 } 1726 1727 return B_OK; 1728 } 1729 1730 1731 status_t 1732 FUSEVolume::OpenDir(void* _node, void** _cookie) 1733 { 1734 FUSENode* node = (FUSENode*)_node; 1735 PRINT(("FUSEVolume::OpenDir(%p (%lld), %p)\n", node, node->id, _cookie)); 1736 1737 // lock the parent directory 1738 NodeReadLocker nodeLocker(this, node, true); 1739 if (nodeLocker.Status() != B_OK) 1740 RETURN_ERROR(nodeLocker.Status()); 1741 1742 // allocate a dir cookie 1743 DirCookie* cookie = new(std::nothrow) DirCookie; 1744 if (cookie == NULL) 1745 RETURN_ERROR(B_NO_MEMORY); 1746 ObjectDeleter<DirCookie> cookieDeleter(cookie); 1747 1748 AutoLocker<Locker> locker(fLock); 1749 1750 // get a path for the node 1751 char path[B_PATH_NAME_LENGTH]; 1752 size_t pathLen; 1753 status_t error = _BuildPath(node, path, pathLen); 1754 if (error != B_OK) 1755 RETURN_ERROR(error); 1756 1757 locker.Unlock(); 1758 1759 if (fFS->ops.readdir == NULL && fFS->ops.getdir != NULL) { 1760 // no open call -- the FS only supports the deprecated getdir() 1761 // interface 1762 cookie->getdirInterface = true; 1763 } else { 1764 // open the dir 1765 int fuseError = fuse_fs_opendir(fFS, path, cookie); 1766 if (fuseError != 0) 1767 return fuseError; 1768 } 1769 1770 cookieDeleter.Detach(); 1771 *_cookie = cookie; 1772 1773 return B_OK; 1774 } 1775 1776 1777 status_t 1778 FUSEVolume::CloseDir(void* node, void* _cookie) 1779 { 1780 return B_OK; 1781 } 1782 1783 1784 status_t 1785 FUSEVolume::FreeDirCookie(void* _node, void* _cookie) 1786 { 1787 FUSENode* node = (FUSENode*)_node; 1788 DirCookie* cookie = (DirCookie*)_cookie; 1789 1790 // lock the parent directory 1791 NodeReadLocker nodeLocker(this, node, true); 1792 if (nodeLocker.Status() != B_OK) 1793 RETURN_ERROR(nodeLocker.Status()); 1794 1795 ObjectDeleter<DirCookie> cookieDeleter(cookie); 1796 1797 if (cookie->getdirInterface) 1798 return B_OK; 1799 1800 AutoLocker<Locker> locker(fLock); 1801 1802 // get a path for the node 1803 char path[B_PATH_NAME_LENGTH]; 1804 size_t pathLen; 1805 status_t error = _BuildPath(node, path, pathLen); 1806 if (error != B_OK) 1807 RETURN_ERROR(error); 1808 1809 locker.Unlock(); 1810 1811 // release the dir 1812 int fuseError = fuse_fs_releasedir(fFS, path, cookie); 1813 if (fuseError != 0) 1814 return fuseError; 1815 1816 return B_OK; 1817 } 1818 1819 1820 status_t 1821 FUSEVolume::ReadDir(void* _node, void* _cookie, void* buffer, size_t bufferSize, 1822 uint32 count, uint32* _countRead) 1823 { 1824 PRINT(("FUSEVolume::ReadDir(%p, %p, %p, %lu, %ld)\n", _node, _cookie, buffer, 1825 bufferSize, count)); 1826 *_countRead = 0; 1827 1828 FUSENode* node = (FUSENode*)_node; 1829 DirCookie* cookie = (DirCookie*)_cookie; 1830 1831 RWLockableWriteLocker cookieLocker(this, cookie); 1832 1833 uint32 countRead = 0; 1834 status_t readDirError = B_OK; 1835 1836 AutoLocker<Locker> locker(fLock); 1837 1838 if (cookie->entryCache == NULL) { 1839 // We don't have an entry cache (yet), so we need to ask the client 1840 // file system to read the directory. 1841 1842 locker.Unlock(); 1843 1844 // lock the directory 1845 NodeReadLocker nodeLocker(this, node, false); 1846 if (nodeLocker.Status() != B_OK) 1847 RETURN_ERROR(nodeLocker.Status()); 1848 1849 locker.Lock(); 1850 1851 ReadDirBuffer readDirBuffer(this, node, cookie, buffer, bufferSize, 1852 count); 1853 1854 // get a path for the node 1855 char path[B_PATH_NAME_LENGTH]; 1856 size_t pathLen; 1857 status_t error = _BuildPath(node, path, pathLen); 1858 if (error != B_OK) 1859 RETURN_ERROR(error); 1860 1861 off_t offset = cookie->currentEntryOffset; 1862 1863 locker.Unlock(); 1864 1865 // read the dir 1866 int fuseError; 1867 if (cookie->getdirInterface) { 1868 PRINT((" using getdir() interface\n")); 1869 fuseError = fFS->ops.getdir(path, (fuse_dirh_t)&readDirBuffer, 1870 &_AddReadDirEntryGetDir); 1871 } else { 1872 PRINT((" using readdir() interface\n")); 1873 fuseError = fuse_fs_readdir(fFS, path, &readDirBuffer, 1874 &_AddReadDirEntry, offset, cookie); 1875 } 1876 if (fuseError != 0) 1877 return fuseError; 1878 1879 locker.Lock(); 1880 1881 countRead = readDirBuffer.entriesRead; 1882 readDirError = readDirBuffer.error; 1883 } 1884 1885 if (cookie->entryCache != NULL) { 1886 // we're using an entry cache -- read into the buffer what we can 1887 dirent* entryBuffer = (dirent*)buffer; 1888 while (countRead < count 1889 && cookie->entryCache->ReadDirent(cookie->currentEntryIndex, fID, 1890 countRead + 1 < count, entryBuffer, bufferSize)) { 1891 countRead++; 1892 cookie->currentEntryIndex++; 1893 bufferSize -= entryBuffer->d_reclen; 1894 entryBuffer 1895 = (dirent*)((uint8*)entryBuffer + entryBuffer->d_reclen); 1896 } 1897 } 1898 1899 *_countRead = countRead; 1900 return countRead > 0 ? B_OK : readDirError; 1901 } 1902 1903 1904 status_t 1905 FUSEVolume::RewindDir(void* _node, void* _cookie) 1906 { 1907 PRINT(("FUSEVolume::RewindDir(%p, %p)\n", _node, _cookie)); 1908 DirCookie* cookie = (DirCookie*)_cookie; 1909 1910 RWLockableWriteLocker cookieLocker(this, cookie); 1911 1912 if (cookie->getdirInterface) { 1913 delete cookie->entryCache; 1914 cookie->entryCache = NULL; 1915 cookie->currentEntryIndex = 0; 1916 } else { 1917 cookie->currentEntryOffset = 0; 1918 } 1919 1920 return B_OK; 1921 } 1922 1923 1924 // #pragma mark - attribute directories 1925 1926 1927 // OpenAttrDir 1928 status_t 1929 FUSEVolume::OpenAttrDir(void* _node, void** _cookie) 1930 { 1931 // allocate an attribute directory cookie 1932 AttrDirCookie* cookie = new(std::nothrow) AttrDirCookie; 1933 if (cookie == NULL) 1934 RETURN_ERROR(B_NO_MEMORY); 1935 1936 *_cookie = cookie; 1937 1938 return B_OK; 1939 } 1940 1941 1942 // CloseAttrDir 1943 status_t 1944 FUSEVolume::CloseAttrDir(void* node, void* cookie) 1945 { 1946 return B_OK; 1947 } 1948 1949 1950 // FreeAttrDirCookie 1951 status_t 1952 FUSEVolume::FreeAttrDirCookie(void* _node, void* _cookie) 1953 { 1954 delete (AttrDirCookie*)_cookie; 1955 return B_OK; 1956 } 1957 1958 1959 // ReadAttrDir 1960 status_t 1961 FUSEVolume::ReadAttrDir(void* _node, void* _cookie, void* buffer, 1962 size_t bufferSize, uint32 count, uint32* _countRead) 1963 { 1964 FUSENode* node = (FUSENode*)_node; 1965 AttrDirCookie* cookie = (AttrDirCookie*)_cookie; 1966 1967 RWLockableWriteLocker cookieLocker(this, cookie); 1968 1969 *_countRead = 0; 1970 1971 // lock the directory 1972 NodeReadLocker nodeLocker(this, node, true); 1973 if (nodeLocker.Status() != B_OK) 1974 RETURN_ERROR(nodeLocker.Status()); 1975 1976 AutoLocker<Locker> locker(fLock); 1977 1978 // get a path for the node 1979 char path[B_PATH_NAME_LENGTH]; 1980 size_t pathLen; 1981 status_t error = _BuildPath(node, path, pathLen); 1982 if (error != B_OK) 1983 RETURN_ERROR(error); 1984 1985 locker.Unlock(); 1986 1987 if (!cookie->IsValid()) { 1988 // cookie not yet valid -- get the length of the list 1989 int listSize = fuse_fs_listxattr(fFS, path, NULL, 0); 1990 if (listSize < 0) 1991 RETURN_ERROR(listSize); 1992 1993 while (true) { 1994 // allocate space for the listing 1995 error = cookie->Allocate(listSize); 1996 if (error != B_OK) 1997 RETURN_ERROR(error); 1998 1999 // read the listing 2000 int bytesRead = fuse_fs_listxattr(fFS, path, 2001 cookie->AttributesBuffer(), listSize); 2002 if (bytesRead < 0) 2003 RETURN_ERROR(bytesRead); 2004 2005 if (bytesRead == listSize) 2006 break; 2007 2008 // attributes listing changed -- reread it 2009 listSize = bytesRead; 2010 } 2011 2012 cookie->SetValid(true); 2013 } 2014 2015 // we have a valid cookie now -- get the next entries from the cookie 2016 uint32 countRead = 0; 2017 dirent* entryBuffer = (dirent*)buffer; 2018 while (countRead < count 2019 && cookie->ReadNextEntry(fID, node->id, countRead + 1 < count, 2020 entryBuffer, bufferSize)) { 2021 countRead++; 2022 bufferSize -= entryBuffer->d_reclen; 2023 entryBuffer = (dirent*)((uint8*)entryBuffer + entryBuffer->d_reclen); 2024 } 2025 2026 *_countRead = countRead; 2027 return B_OK; 2028 } 2029 2030 2031 // RewindAttrDir 2032 status_t 2033 FUSEVolume::RewindAttrDir(void* _node, void* _cookie) 2034 { 2035 AttrDirCookie* cookie = (AttrDirCookie*)_cookie; 2036 2037 RWLockableWriteLocker cookieLocker(this, cookie); 2038 2039 cookie->Clear(); 2040 2041 return B_OK; 2042 } 2043 2044 2045 // #pragma mark - 2046 2047 2048 ino_t 2049 FUSEVolume::_GenerateNodeID() 2050 { 2051 ino_t id; 2052 do { 2053 id = fNextNodeID++; 2054 } while (fNodes.Lookup(id) != NULL); 2055 2056 return id; 2057 } 2058 2059 2060 /*! Gets the ID of the node the entry specified by \a dir and \a entryName 2061 refers to. The ID is returned via \a _nodeID. The caller doesn't get a 2062 reference to the node. 2063 */ 2064 bool 2065 FUSEVolume::_GetNodeID(FUSENode* dir, const char* entryName, ino_t* _nodeID) 2066 { 2067 while (true) { 2068 AutoLocker<Locker> locker(fLock); 2069 2070 FUSENode* node; 2071 status_t error = _InternalGetNode(dir, entryName, &node, locker); 2072 if (error != B_OK) 2073 return false; 2074 2075 if (node == NULL) 2076 continue; 2077 2078 *_nodeID = node->id; 2079 _PutNode(node); 2080 2081 return true; 2082 } 2083 } 2084 2085 2086 /*! Gets the node the entry specified by \a dir and \a entryName refers to. The 2087 found node is returned via \a _node. The caller gets a reference to the node 2088 as well as a vnode reference. 2089 */ 2090 status_t 2091 FUSEVolume::_GetNode(FUSENode* dir, const char* entryName, FUSENode** _node) 2092 { 2093 while (true) { 2094 AutoLocker<Locker> locker(fLock); 2095 2096 FUSENode* node; 2097 status_t error = _InternalGetNode(dir, entryName, &node, locker); 2098 if (error != B_OK) 2099 return error; 2100 2101 if (node == NULL) 2102 continue; 2103 2104 ino_t nodeID = node->id; 2105 2106 locker.Unlock(); 2107 2108 // get a reference for the caller 2109 void* privateNode; 2110 error = UserlandFS::KernelEmu::get_vnode(fID, nodeID, &privateNode); 2111 if (error != B_OK) 2112 RETURN_ERROR(error); 2113 2114 locker.Lock(); 2115 2116 if (privateNode != node) { 2117 // weird, the node changed! 2118 ERROR(("FUSEVolume::_GetNode(): cookie for node %lld changed: " 2119 "expected: %p, got: %p\n", nodeID, node, privateNode)); 2120 UserlandFS::KernelEmu::put_vnode(fID, nodeID); 2121 _PutNode(node); 2122 continue; 2123 } 2124 2125 // Put the node reference we got from _InternalGetNode. We've now got 2126 // a reference from get_vnode(). 2127 _PutNode(node); 2128 2129 *_node = node; 2130 return B_OK; 2131 } 2132 } 2133 2134 2135 status_t 2136 FUSEVolume::_InternalGetNode(FUSENode* dir, const char* entryName, 2137 FUSENode** _node, AutoLocker<Locker>& locker) 2138 { 2139 // handle special cases 2140 if (strcmp(entryName, ".") == 0) { 2141 // same directory 2142 if (!S_ISDIR(dir->type)) 2143 RETURN_ERROR(B_NOT_A_DIRECTORY); 2144 2145 dir->refCount++; 2146 *_node = dir; 2147 return B_OK; 2148 } 2149 2150 if (strcmp(entryName, "..") == 0) { 2151 // parent directory 2152 if (!S_ISDIR(dir->type)) 2153 RETURN_ERROR(B_NOT_A_DIRECTORY); 2154 2155 FUSEEntry* entry = dir->entries.Head(); 2156 if (entry == NULL) 2157 RETURN_ERROR(B_ENTRY_NOT_FOUND); 2158 2159 entry->parent->refCount++; 2160 *_node = entry->parent; 2161 return B_OK; 2162 } 2163 2164 // lookup the entry in the table 2165 FUSEEntry* entry = fEntries.Lookup(FUSEEntryRef(dir->id, entryName)); 2166 if (entry != NULL) { 2167 entry->node->refCount++; 2168 *_node = entry->node; 2169 return B_OK; 2170 } 2171 2172 // construct a path for the entry 2173 char path[B_PATH_NAME_LENGTH]; 2174 size_t pathLen = 0; 2175 status_t error = _BuildPath(dir, entryName, path, pathLen); 2176 if (error != B_OK) 2177 return error; 2178 2179 locker.Unlock(); 2180 2181 // stat the path 2182 struct stat st; 2183 int fuseError = fuse_fs_getattr(fFS, path, &st); 2184 if (fuseError != 0) 2185 return fuseError; 2186 2187 locker.Lock(); 2188 2189 // lookup the entry in the table again 2190 entry = fEntries.Lookup(FUSEEntryRef(dir->id, entryName)); 2191 if (entry != NULL) { 2192 // check whether the node still matches 2193 if (entry->node->id == st.st_ino) { 2194 entry->node->refCount++; 2195 *_node = entry->node; 2196 } else { 2197 // nope, something changed -- return a NULL node and let the caller 2198 // call us again 2199 *_node = NULL; 2200 } 2201 2202 return B_OK; 2203 } 2204 2205 // lookup the node in the table 2206 FUSENode* node = NULL; 2207 if (fUseNodeIDs) 2208 node = fNodes.Lookup(st.st_ino); 2209 else 2210 st.st_ino = _GenerateNodeID(); 2211 2212 if (node == NULL) { 2213 // no node yet -- create one 2214 node = new(std::nothrow) FUSENode(st.st_ino, st.st_mode & S_IFMT); 2215 if (node == NULL) 2216 RETURN_ERROR(B_NO_MEMORY); 2217 2218 fNodes.Insert(node); 2219 } else { 2220 // get a node reference for the entry 2221 node->refCount++; 2222 } 2223 2224 // create the entry 2225 entry = FUSEEntry::Create(dir, entryName, node); 2226 if (entry == NULL) { 2227 _PutNode(node); 2228 RETURN_ERROR(B_NO_MEMORY); 2229 } 2230 2231 dir->refCount++; 2232 // dir reference for the entry 2233 2234 fEntries.Insert(entry); 2235 node->entries.Add(entry); 2236 2237 locker.Unlock(); 2238 2239 // get a reference for the caller 2240 node->refCount++; 2241 2242 *_node = node; 2243 return B_OK; 2244 } 2245 2246 2247 void 2248 FUSEVolume::_PutNode(FUSENode* node) 2249 { 2250 if (--node->refCount == 0) { 2251 fNodes.Remove(node); 2252 delete node; 2253 } 2254 } 2255 2256 2257 void 2258 FUSEVolume::_PutNodes(FUSENode* const* nodes, int32 count) 2259 { 2260 for (int32 i = 0; i < count; i++) 2261 _PutNode(nodes[i]); 2262 } 2263 2264 2265 /*! Volume must be locked. The entry's directory must be write locked. 2266 */ 2267 void 2268 FUSEVolume::_RemoveEntry(FUSEEntry* entry) 2269 { 2270 fEntries.Remove(entry); 2271 entry->node->entries.Remove(entry); 2272 _PutNode(entry->node); 2273 _PutNode(entry->parent); 2274 delete entry; 2275 } 2276 2277 2278 /*! Volume must be locked. The directory must be write locked. 2279 */ 2280 status_t 2281 FUSEVolume::_RemoveEntry(FUSENode* dir, const char* name) 2282 { 2283 FUSEEntry* entry = fEntries.Lookup(FUSEEntryRef(dir->id, name)); 2284 if (entry == NULL) 2285 return B_ENTRY_NOT_FOUND; 2286 2287 _RemoveEntry(entry); 2288 return B_OK; 2289 } 2290 2291 2292 /*! Volume must be locked. The directories must be write locked. 2293 */ 2294 status_t 2295 FUSEVolume::_RenameEntry(FUSENode* oldDir, const char* oldName, 2296 FUSENode* newDir, const char* newName) 2297 { 2298 FUSEEntry* entry = fEntries.Lookup(FUSEEntryRef(oldDir->id, oldName)); 2299 if (entry == NULL) 2300 return B_ENTRY_NOT_FOUND; 2301 2302 // get a node reference for the new entry 2303 FUSENode* node = entry->node; 2304 node->refCount++; 2305 2306 // remove the old entry 2307 _RemoveEntry(entry); 2308 2309 // make sure there's no entry in our way 2310 _RemoveEntry(newDir, newName); 2311 2312 // create a new entry 2313 entry = FUSEEntry::Create(newDir, newName, node); 2314 if (entry == NULL) { 2315 _PutNode(node); 2316 RETURN_ERROR(B_NO_MEMORY); 2317 } 2318 2319 newDir->refCount++; 2320 // dir reference for the entry 2321 2322 fEntries.Insert(entry); 2323 node->entries.Add(entry); 2324 2325 return B_OK; 2326 } 2327 2328 2329 /*! Locks the given node and all of its ancestors up to the root. The given 2330 node is write-locked, if \a writeLock is \c true, read-locked otherwise. All 2331 ancestors are always read-locked in either case. 2332 2333 If \a lockParent is \c true, the given node itself is ignored, but locking 2334 starts with the parent node of the given node (\a writeLock applies to the 2335 parent node then). 2336 2337 If the method fails, none of the nodes is locked. 2338 2339 The volume lock must not be held. 2340 */ 2341 status_t 2342 FUSEVolume::_LockNodeChain(FUSENode* node, bool lockParent, bool writeLock) 2343 { 2344 AutoLocker<Locker> locker(fLock); 2345 2346 FUSENode* originalNode = node; 2347 2348 if (lockParent && node != NULL) 2349 node = node->Parent(); 2350 2351 if (node == NULL) 2352 RETURN_ERROR(B_ENTRY_NOT_FOUND); 2353 2354 LockIterator iterator(this, node, writeLock, NULL); 2355 2356 bool done; 2357 do { 2358 bool volumeUnlocked; 2359 status_t error = iterator.LockNext(&done, &volumeUnlocked); 2360 if (error != B_OK) 2361 RETURN_ERROR(error); 2362 2363 if (volumeUnlocked) { 2364 // check whether we're still locking the right node 2365 if (lockParent && originalNode->Parent() != node) { 2366 // We don't -- unlock everything and try again. 2367 node = originalNode->Parent(); 2368 iterator.SetTo(this, node, writeLock, NULL); 2369 } 2370 } 2371 } while (!done); 2372 2373 // Fail, if we couldn't lock all nodes up to the root. 2374 if (iterator.lastLockedNode != fRootNode) 2375 RETURN_ERROR(B_ENTRY_NOT_FOUND); 2376 2377 iterator.Detach(); 2378 return B_OK; 2379 } 2380 2381 2382 void 2383 FUSEVolume::_UnlockNodeChain(FUSENode* node, bool parent, bool writeLock) 2384 { 2385 AutoLocker<Locker> locker(fLock); 2386 2387 if (parent && node != NULL) 2388 node = node->Parent(); 2389 2390 _UnlockNodeChainInternal(node, writeLock, NULL, NULL); 2391 } 2392 2393 2394 /*! Unlocks all nodes from \a node up to (and including) \a stopNode (if 2395 \c NULL, it is ignored). If \a stopBeforeNode is given, the method stops 2396 before unlocking that node. 2397 The volume lock must be held. 2398 */ 2399 void 2400 FUSEVolume::_UnlockNodeChainInternal(FUSENode* node, bool writeLock, 2401 FUSENode* stopNode, FUSENode* stopBeforeNode) 2402 { 2403 FUSENode* originalNode = node; 2404 2405 while (node != NULL && node != stopBeforeNode) { 2406 FUSENode* parent = node->Parent(); 2407 2408 fLockManager.GenericUnlock(node == originalNode && writeLock, node); 2409 _PutNode(node); 2410 2411 if (node == stopNode || parent == node) 2412 break; 2413 2414 node = parent; 2415 } 2416 } 2417 2418 2419 status_t 2420 FUSEVolume::_LockNodeChains(FUSENode* node1, bool lockParent1, bool writeLock1, 2421 FUSENode* node2, bool lockParent2, bool writeLock2) 2422 { 2423 // Since in this case locking is more complicated, we use a helper method. 2424 // It does the main work, but simply returns telling us to retry when the 2425 // node hierarchy changes. 2426 bool retry; 2427 do { 2428 status_t error = _LockNodeChainsInternal(node1, lockParent1, writeLock1, 2429 node2, lockParent2, writeLock2, &retry); 2430 if (error != B_OK) 2431 return error; 2432 } while (retry); 2433 2434 return B_OK; 2435 } 2436 2437 2438 status_t 2439 FUSEVolume::_LockNodeChainsInternal(FUSENode* node1, bool lockParent1, 2440 bool writeLock1, FUSENode* node2, bool lockParent2, bool writeLock2, 2441 bool* _retry) 2442 { 2443 // Locking order: 2444 // * A child of a node has to be locked before its parent. 2445 // * Sibling nodes have to be locked in ascending node ID order. 2446 // 2447 // This implies the following locking algorithm: 2448 // * We find the closest common ancestor of the two given nodes (might even 2449 // be one of the given nodes). 2450 // * We lock all ancestors on one branch (the one with the lower common 2451 // ancestor child node ID), but not including the common ancestor. 2452 // * We lock all ancestors on the other branch, not including the common 2453 // ancestor. 2454 // * We lock the common ancestor and all of its ancestors up to the root 2455 // node. 2456 // 2457 // When the hierarchy changes while we're waiting for a lock, we recheck the 2458 // conditions and in doubt have to be restarted. 2459 2460 AutoLocker<Locker> locker(fLock); 2461 2462 FUSENode* originalNode1 = node1; 2463 FUSENode* originalNode2 = node2; 2464 2465 if (lockParent1 && node1 != NULL) 2466 node1 = node1->Parent(); 2467 2468 if (lockParent2 && node2 != NULL) 2469 node2 = node2->Parent(); 2470 2471 if (node1 == NULL || node2 == NULL) 2472 RETURN_ERROR(B_ENTRY_NOT_FOUND); 2473 2474 // find the first common ancestor 2475 FUSENode* commonAncestor; 2476 bool inverseLockingOrder; 2477 if (!_FindCommonAncestor(node1, node2, &commonAncestor, 2478 &inverseLockingOrder)) { 2479 RETURN_ERROR(B_ENTRY_NOT_FOUND); 2480 } 2481 2482 // lock the both node chains up to (but not including) the common ancestor 2483 LockIterator iterator1(this, node1, writeLock1, commonAncestor); 2484 LockIterator iterator2(this, node2, writeLock2, commonAncestor); 2485 2486 for (int i = 0; i < 2; i++) { 2487 LockIterator& iterator = (i == 0) != inverseLockingOrder 2488 ? iterator1 : iterator2; 2489 2490 // If the node is the common ancestor, don't enter the "do" loop, since 2491 // we don't have to lock anything here. 2492 if (iterator.firstNode == commonAncestor) 2493 continue; 2494 2495 bool done; 2496 do { 2497 bool volumeUnlocked; 2498 status_t error = iterator.LockNext(&done, &volumeUnlocked); 2499 if (error != B_OK) 2500 RETURN_ERROR(error); 2501 2502 if (volumeUnlocked) { 2503 // check whether we're still locking the right nodes 2504 if ((lockParent1 && originalNode1->Parent() != node1) 2505 || (lockParent2 && originalNode2->Parent() != node2)) { 2506 // We don't -- unlock everything and retry. 2507 *_retry = true; 2508 return B_OK; 2509 } 2510 2511 // also recheck the common ancestor 2512 FUSENode* newCommonParent; 2513 bool newInverseLockingOrder; 2514 if (!_FindCommonAncestor(node1, node2, &newCommonParent, 2515 &newInverseLockingOrder)) { 2516 RETURN_ERROR(B_ENTRY_NOT_FOUND); 2517 } 2518 2519 if (newCommonParent != commonAncestor 2520 || inverseLockingOrder != newInverseLockingOrder) { 2521 // Something changed -- unlock everything and retry. 2522 *_retry = true; 2523 return B_OK; 2524 } 2525 } 2526 } while (!done); 2527 } 2528 2529 // Continue locking from the common ancestor to the root. If one of the 2530 // given nodes is the common ancestor and shall be write locked, we need to 2531 // use the respective iterator. 2532 LockIterator& iterator = node2 == commonAncestor && writeLock2 2533 ? iterator2 : iterator1; 2534 iterator.SetStopBeforeNode(NULL); 2535 2536 bool done; 2537 do { 2538 bool volumeUnlocked; 2539 status_t error = iterator.LockNext(&done, &volumeUnlocked); 2540 if (error != B_OK) 2541 RETURN_ERROR(error); 2542 2543 if (volumeUnlocked) { 2544 // check whether we're still locking the right nodes 2545 if ((lockParent1 && originalNode1->Parent() != node1) 2546 || (lockParent2 && originalNode2->Parent() != node2)) { 2547 // We don't -- unlock everything and retry. 2548 *_retry = true; 2549 return B_OK; 2550 } 2551 2552 // Also recheck the common ancestor, if we have just locked it. 2553 // Otherwise we can just continue to lock, since nothing below the 2554 // previously locked node can have changed. 2555 if (iterator.lastLockedNode == commonAncestor) { 2556 FUSENode* newCommonParent; 2557 bool newInverseLockingOrder; 2558 if (!_FindCommonAncestor(node1, node2, &newCommonParent, 2559 &newInverseLockingOrder)) { 2560 RETURN_ERROR(B_ENTRY_NOT_FOUND); 2561 } 2562 2563 if (newCommonParent != commonAncestor 2564 || inverseLockingOrder != newInverseLockingOrder) { 2565 // Something changed -- unlock everything and retry. 2566 *_retry = true; 2567 return B_OK; 2568 } 2569 } 2570 } 2571 } while (!done); 2572 2573 // Fail, if we couldn't lock all nodes up to the root. 2574 if (iterator.lastLockedNode != fRootNode) 2575 RETURN_ERROR(B_ENTRY_NOT_FOUND); 2576 2577 // everything went fine 2578 iterator1.Detach(); 2579 iterator2.Detach(); 2580 2581 *_retry = false; 2582 return B_OK; 2583 } 2584 2585 2586 void 2587 FUSEVolume::_UnlockNodeChains(FUSENode* node1, bool lockParent1, 2588 bool writeLock1, FUSENode* node2, bool lockParent2, bool writeLock2) 2589 { 2590 AutoLocker<Locker> locker(fLock); 2591 2592 if (lockParent1 && node1 != NULL) 2593 node1 = node1->Parent(); 2594 2595 if (lockParent2 && node2 != NULL) 2596 node2 = node2->Parent(); 2597 2598 if (node1 == NULL || node2 == NULL) 2599 return; 2600 2601 // find the common ancestor 2602 FUSENode* commonAncestor; 2603 bool inverseLockingOrder; 2604 if (!_FindCommonAncestor(node1, node2, &commonAncestor, 2605 &inverseLockingOrder)) { 2606 return; 2607 } 2608 2609 // Unlock one branch up to the common ancestor and then the complete other 2610 // branch up to the root. If one of the given nodes is the common ancestor, 2611 // we need to make sure, we write-unlock it, if requested. 2612 if (node2 == commonAncestor && writeLock2) { 2613 _UnlockNodeChainInternal(node1, writeLock1, NULL, commonAncestor); 2614 _UnlockNodeChainInternal(node2, writeLock2, NULL, NULL); 2615 } else { 2616 _UnlockNodeChainInternal(node2, writeLock2, NULL, commonAncestor); 2617 _UnlockNodeChainInternal(node1, writeLock1, NULL, NULL); 2618 } 2619 } 2620 2621 2622 bool 2623 FUSEVolume::_FindCommonAncestor(FUSENode* node1, FUSENode* node2, 2624 FUSENode** _commonAncestor, bool* _inverseLockingOrder) 2625 { 2626 // handle trivial special case -- both nodes are the same 2627 if (node1 == node2) { 2628 *_commonAncestor = node1; 2629 *_inverseLockingOrder = false; 2630 return true; 2631 } 2632 2633 // get the ancestors of both nodes 2634 FUSENode* ancestors1[kMaxNodeTreeDepth]; 2635 FUSENode* ancestors2[kMaxNodeTreeDepth]; 2636 uint32 count1; 2637 uint32 count2; 2638 2639 if (!_GetNodeAncestors(node1, ancestors1, &count1) 2640 || !_GetNodeAncestors(node2, ancestors2, &count2)) { 2641 return false; 2642 } 2643 2644 // find the first ancestor not common to both nodes 2645 uint32 index = 0; 2646 for (; index < count1 && index < count2; index++) { 2647 FUSENode* ancestor1 = ancestors1[count1 - index - 1]; 2648 FUSENode* ancestor2 = ancestors2[count2 - index - 1]; 2649 if (ancestor1 != ancestor2) { 2650 *_commonAncestor = ancestors1[count1 - index]; 2651 *_inverseLockingOrder = ancestor1->id > ancestor2->id; 2652 return true; 2653 } 2654 } 2655 2656 // one node is an ancestor of the other 2657 *_commonAncestor = ancestors1[count1 - index]; 2658 *_inverseLockingOrder = index == count1; 2659 return true; 2660 } 2661 2662 2663 bool 2664 FUSEVolume::_GetNodeAncestors(FUSENode* node, FUSENode** ancestors, 2665 uint32* _count) 2666 { 2667 uint32 count = 0; 2668 while (node != NULL && count < kMaxNodeTreeDepth) { 2669 ancestors[count++] = node; 2670 2671 if (node == fRootNode) { 2672 *_count = count; 2673 return true; 2674 } 2675 2676 node = node->Parent(); 2677 } 2678 2679 // Either the node is not in the tree or we hit the array limit. 2680 return false; 2681 } 2682 2683 2684 status_t 2685 FUSEVolume::_BuildPath(FUSENode* dir, const char* entryName, char* path, 2686 size_t& pathLen) 2687 { 2688 // get the directory path 2689 status_t error = _BuildPath(dir, path, pathLen); 2690 if (error != B_OK) 2691 return error; 2692 2693 if (path[pathLen - 1] != '/') { 2694 path[pathLen++] = '/'; 2695 if (pathLen == B_PATH_NAME_LENGTH) 2696 RETURN_ERROR(B_NAME_TOO_LONG); 2697 } 2698 2699 // append the entry name 2700 size_t len = strlen(entryName); 2701 if (pathLen + len >= B_PATH_NAME_LENGTH) 2702 RETURN_ERROR(B_NAME_TOO_LONG); 2703 2704 memcpy(path + pathLen, entryName, len + 1); 2705 pathLen += len; 2706 2707 return B_OK; 2708 } 2709 2710 2711 status_t 2712 FUSEVolume::_BuildPath(FUSENode* node, char* path, size_t& pathLen) 2713 { 2714 if (node == fRootNode) { 2715 // we hit the root 2716 strcpy(path, "/"); 2717 pathLen = 1; 2718 return B_OK; 2719 } 2720 2721 // get an entry for the node and get its path 2722 FUSEEntry* entry = node->entries.Head(); 2723 if (entry == NULL) 2724 RETURN_ERROR(B_ENTRY_NOT_FOUND); 2725 2726 return _BuildPath(entry->parent, entry->name, path, pathLen); 2727 } 2728 2729 2730 /*static*/ int 2731 FUSEVolume::_AddReadDirEntry(void* _buffer, const char* name, 2732 const struct stat* st, off_t offset) 2733 { 2734 ReadDirBuffer* buffer = (ReadDirBuffer*)_buffer; 2735 2736 ino_t nodeID = st != NULL ? st->st_ino : 0; 2737 int type = st != NULL ? st->st_mode & S_IFMT : 0; 2738 return buffer->volume->_AddReadDirEntry(buffer, name, type, nodeID, offset); 2739 } 2740 2741 2742 /*static*/ int 2743 FUSEVolume::_AddReadDirEntryGetDir(fuse_dirh_t handle, const char* name, 2744 int type, ino_t nodeID) 2745 { 2746 ReadDirBuffer* buffer = (ReadDirBuffer*)handle; 2747 return buffer->volume->_AddReadDirEntry(buffer, name, type << 12, nodeID, 2748 0); 2749 } 2750 2751 2752 int 2753 FUSEVolume::_AddReadDirEntry(ReadDirBuffer* buffer, const char* name, int type, 2754 ino_t nodeID, off_t offset) 2755 { 2756 PRINT(("FUSEVolume::_AddReadDirEntry(%p, \"%s\", %#x, %lld, %lld\n", buffer, 2757 name, type, nodeID, offset)); 2758 2759 AutoLocker<Locker> locker(fLock); 2760 2761 size_t entryLen; 2762 if (offset != 0) { 2763 // does the caller want more entries? 2764 if (buffer->entriesRead == buffer->maxEntries) 2765 return 1; 2766 2767 // compute the entry length and check whether the entry still fits 2768 entryLen = sizeof(dirent) + strlen(name); 2769 if (buffer->usedSize + entryLen > buffer->bufferSize) 2770 return 1; 2771 } 2772 2773 // create a node and an entry, if necessary 2774 ino_t dirID = buffer->directory->id; 2775 FUSEEntry* entry; 2776 if (strcmp(name, ".") == 0) { 2777 // current dir entry 2778 nodeID = dirID; 2779 type = S_IFDIR; 2780 } else if (strcmp(name, "..") == 0) { 2781 // parent dir entry 2782 FUSEEntry* parentEntry = buffer->directory->entries.Head(); 2783 if (parentEntry == NULL) { 2784 ERROR(("FUSEVolume::_AddReadDirEntry(): dir %lld has no entry!\n", 2785 dirID)); 2786 return 0; 2787 } 2788 nodeID = parentEntry->parent->id; 2789 type = S_IFDIR; 2790 } else if ((entry = fEntries.Lookup(FUSEEntryRef(dirID, name))) == NULL) { 2791 // get the node 2792 FUSENode* node = NULL; 2793 if (fUseNodeIDs) 2794 node = fNodes.Lookup(nodeID); 2795 else 2796 nodeID = _GenerateNodeID(); 2797 2798 if (node == NULL) { 2799 // no node yet -- create one 2800 2801 // If we don't have a valid type, we need to stat the node first. 2802 if (type == 0) { 2803 char path[B_PATH_NAME_LENGTH]; 2804 size_t pathLen; 2805 status_t error = _BuildPath(buffer->directory, name, path, 2806 pathLen); 2807 if (error != B_OK) { 2808 buffer->error = error; 2809 return 0; 2810 } 2811 2812 locker.Unlock(); 2813 2814 // stat the path 2815 struct stat st; 2816 int fuseError = fuse_fs_getattr(fFS, path, &st); 2817 2818 locker.Lock(); 2819 2820 if (fuseError != 0) { 2821 buffer->error = fuseError; 2822 return 0; 2823 } 2824 2825 type = st.st_mode & S_IFMT; 2826 } 2827 2828 node = new(std::nothrow) FUSENode(nodeID, type); 2829 if (node == NULL) { 2830 buffer->error = B_NO_MEMORY; 2831 return 1; 2832 } 2833 PRINT((" -> create node: %p, id: %lld\n", node, nodeID)); 2834 2835 fNodes.Insert(node); 2836 } else { 2837 // get a node reference for the entry 2838 node->refCount++; 2839 } 2840 2841 // create the entry 2842 entry = FUSEEntry::Create(buffer->directory, name, node); 2843 if (entry == NULL) { 2844 _PutNode(node); 2845 buffer->error = B_NO_MEMORY; 2846 return 1; 2847 } 2848 2849 buffer->directory->refCount++; 2850 // dir reference for the entry 2851 2852 fEntries.Insert(entry); 2853 node->entries.Add(entry); 2854 } else { 2855 // TODO: Check whether the node's ID matches the one we got (if any)! 2856 nodeID = entry->node->id; 2857 type = entry->node->type; 2858 } 2859 2860 if (offset == 0) { 2861 // cache the entry 2862 if (buffer->cookie->entryCache == NULL) { 2863 // no cache yet -- create it 2864 buffer->cookie->entryCache = new(std::nothrow) DirEntryCache; 2865 if (buffer->cookie->entryCache == NULL) { 2866 buffer->error = B_NO_MEMORY; 2867 return 1; 2868 } 2869 } 2870 2871 status_t error = buffer->cookie->entryCache->AddEntry(nodeID, name); 2872 if (error != B_OK) { 2873 buffer->error = error; 2874 return 1; 2875 } 2876 } else { 2877 // fill in the dirent 2878 dirent* dirEntry = (dirent*)((uint8*)buffer->buffer + buffer->usedSize); 2879 dirEntry->d_dev = fID; 2880 dirEntry->d_ino = nodeID; 2881 strcpy(dirEntry->d_name, name); 2882 2883 if (buffer->entriesRead + 1 < buffer->maxEntries) { 2884 // align the entry length, so the next dirent will be aligned 2885 entryLen = (entryLen + 7) / 8 * 8; 2886 entryLen = std::min(entryLen, 2887 buffer->bufferSize - buffer->usedSize); 2888 } 2889 2890 dirEntry->d_reclen = entryLen; 2891 2892 // update the buffer 2893 buffer->usedSize += entryLen; 2894 buffer->entriesRead++; 2895 buffer->cookie->currentEntryOffset = offset; 2896 } 2897 2898 return 0; 2899 } 2900