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