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