xref: /haiku/src/add-ons/kernel/file_systems/userlandfs/server/fuse/FUSEVolume.cpp (revision 445d4fd926c569e7b9ae28017da86280aaecbae2)
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 {
38 	DirEntryCache()
39 		:
40 		fEntries(NULL),
41 		fNames(NULL),
42 		fEntryCount(0),
43 		fEntryCapacity(0),
44 		fNamesSize(0),
45 		fNamesCapacity(0)
46 	{
47 	}
48 
49 	~DirEntryCache()
50 	{
51 		free(fEntries);
52 		free(fNames);
53 	}
54 
55 	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 
97 	uint32 CountEntries() const
98 	{
99 		return fEntryCount;
100 	}
101 
102 	bool ReadDirent(uint32 index, dev_t volumeID, bool align, dirent* buffer,
103 		size_t bufferSize) const
104 	{
105 		if (index >= fEntryCount)
106 			return false;
107 
108 		const Entry& entry = fEntries[index];
109 
110 		// get and check the size
111 		size_t size = 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 
153 	DirCookie()
154 		:
155 		currentEntryOffset(0),
156 		entryCache(NULL),
157 		getdirInterface(false)
158 	{
159 		flags = 0;
160 		fh_old = 0;
161 		writepage = 0;
162 		direct_io = 0;
163 		keep_cache = 0;
164 		flush = 0;
165 		fh = 0;
166 		lock_owner = 0;
167 	}
168 
169 	~DirCookie()
170 	{
171 		delete entryCache;
172 	}
173 };
174 
175 
176 struct FUSEVolume::FileCookie : fuse_file_info, RWLockable {
177 	FileCookie(int openMode)
178 	{
179 		flags = openMode;
180 		fh_old = 0;
181 		writepage = 0;
182 		direct_io = 0;
183 		keep_cache = 0;
184 		flush = 0;
185 		fh = 0;
186 		lock_owner = 0;
187 	}
188 };
189 
190 
191 struct FUSEVolume::AttrDirCookie : RWLockable {
192 	AttrDirCookie()
193 		:
194 		fAttributes(NULL),
195 		fAttributesSize(0),
196 		fCurrentOffset(0),
197 		fValid(false)
198 	{
199 	}
200 
201 	~AttrDirCookie()
202 	{
203 		Clear();
204 	}
205 
206 	void Clear()
207 	{
208 		free(fAttributes);
209 		fAttributes = NULL;
210 		fAttributesSize = 0;
211 		fCurrentOffset = 0;
212 		fValid = false;
213 	}
214 
215 	status_t Allocate(size_t size)
216 	{
217 		Clear();
218 
219 		if (size == 0)
220 			return B_OK;
221 
222 		fAttributes = (char*)malloc(size);
223 		if (fAttributes == NULL)
224 			return B_NO_MEMORY;
225 
226 		fAttributesSize = size;
227 		return B_OK;
228 	}
229 
230 	bool IsValid() const
231 	{
232 		return fValid;
233 	}
234 
235 	void SetValid(bool valid)
236 	{
237 		fValid = valid;
238 	}
239 
240 	char* AttributesBuffer() const
241 	{
242 		return fAttributes;
243 	}
244 
245 	bool ReadNextEntry(dev_t volumeID, ino_t nodeID, bool align,
246 		dirent* buffer, size_t bufferSize)
247 	{
248 		if (fCurrentOffset >= fAttributesSize)
249 			return false;
250 
251 		const char* name = fAttributes + fCurrentOffset;
252 		size_t nameLen = strlen(name);
253 
254 		// get and check the size
255 		size_t size = 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:
284 	AttrCookie(const char* name)
285 		:
286 		fValue(NULL),
287 		fSize(0),
288 		fType(0)
289 	{
290 		_SetType(name);
291 	}
292 
293 	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 
302 	~AttrCookie()
303 	{
304 		free(fValue);
305 	}
306 
307 	bool IsValid() const
308 	{
309 		return fValue != NULL;
310 	}
311 
312 	uint32 Type() const
313 	{
314 		return fType;
315 	}
316 
317 	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 
328 	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 
342 	char* Buffer()
343 	{
344 		return fValue;
345 	}
346 
347 	size_t Size() const
348 	{
349 		return fSize;
350 	}
351 
352 private:
353 	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 
379 	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 
404 	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 
416 	~LockIterator()
417 	{
418 		Unlock();
419 	}
420 
421 	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 
434 	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 
475 	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 
487 	void SetStopBeforeNode(FUSENode* stopBeforeNode)
488 	{
489 		this->stopBeforeNode = stopBeforeNode;
490 	}
491 
492 	void Detach()
493 	{
494 		lastLockedNode = NULL;
495 		nextNode = firstNode;
496 	}
497 };
498 
499 
500 struct FUSEVolume::RWLockableReadLocking {
501 	RWLockableReadLocking(FUSEVolume* volume)
502 		:
503 		fLockManager(volume != NULL ? &volume->fLockManager : NULL)
504 	{
505 	}
506 
507 	RWLockableReadLocking(RWLockManager* lockManager)
508 		:
509 		fLockManager(lockManager)
510 	{
511 	}
512 
513 	RWLockableReadLocking(const RWLockableReadLocking& other)
514 		:
515 		fLockManager(other.fLockManager)
516 	{
517 	}
518 
519 	inline bool Lock(RWLockable* lockable)
520 	{
521 		return fLockManager != NULL && fLockManager->ReadLock(lockable);
522 	}
523 
524 	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 {
536 	RWLockableWriteLocking(FUSEVolume* volume)
537 		:
538 		fLockManager(volume != NULL ? &volume->fLockManager : NULL)
539 	{
540 	}
541 
542 	RWLockableWriteLocking(RWLockManager* lockManager)
543 		:
544 		fLockManager(lockManager)
545 	{
546 	}
547 
548 	RWLockableWriteLocking(const RWLockableWriteLocking& other)
549 		:
550 		fLockManager(other.fLockManager)
551 	{
552 	}
553 
554 	inline bool Lock(RWLockable* lockable)
555 	{
556 		return fLockManager != NULL && fLockManager->WriteLock(lockable);
557 	}
558 
559 	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 
573 	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 
586 	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 {
597 	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 
609 	~NodeLocker()
610 	{
611 		if (fNode != NULL)
612 			fVolume->_UnlockNodeChain(fNode, fParent, fWriteLock);
613 	}
614 
615 	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 {
630 	NodeReadLocker(FUSEVolume* volume, FUSENode* node, bool parent)
631 		:
632 		NodeLocker(volume, node, parent, false)
633 	{
634 	}
635 };
636 
637 
638 struct FUSEVolume::NodeWriteLocker : NodeLocker {
639 	NodeWriteLocker(FUSEVolume* volume, FUSENode* node, bool parent)
640 		:
641 		NodeLocker(volume, node, parent, true)
642 	{
643 	}
644 };
645 
646 
647 struct FUSEVolume::MultiNodeLocker {
648 	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 
667 	~MultiNodeLocker()
668 	{
669 		if (fNode1 != NULL) {
670 			fVolume->_UnlockNodeChains(fNode1, fLockParent1, fWriteLock1,
671 				fNode2, fLockParent2, fWriteLock2);
672 		}
673 	}
674 
675 	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 
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 
706 FUSEVolume::~FUSEVolume()
707 {
708 }
709 
710 
711 status_t
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
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
817 FUSEVolume::Unmount()
818 {
819 printf("FUSEVolume::Unmount()\n");
820 	_FileSystem()->ExitClientFS(B_OK);
821 	return B_OK;
822 }
823 
824 
825 status_t
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
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
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
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
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
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
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
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 	if (error == B_OK) {
1070 		size_t bytesTransferred = 0;
1071 		while (bytesTransferred < requestInfo.length) {
1072 			size_t bytes = requestInfo.length - bytesTransferred;
1073 			error = _InternalIO(node, cookie, path, requestInfo.offset + bytesTransferred,
1074 				buffer + bytesTransferred, bytes, requestInfo.isWrite);
1075 
1076 			if (error == B_OK)
1077 				bytesTransferred += bytes;
1078 			else
1079 				break;
1080 		}
1081 	}
1082 
1083 	if (error == B_OK && !requestInfo.isWrite) {
1084 		error = UserlandFS::KernelEmu::write_to_io_request(GetID(), requestInfo.id, buffer,
1085 			requestInfo.length);
1086 	}
1087 
1088 	UserlandFS::KernelEmu::notify_io_request(GetID(), requestInfo.id, error);
1089 
1090 	if (cookie == &alternativeCookie) {
1091 		if (fOps != NULL)
1092 			fuse_ll_release(fOps, node->id, cookie);
1093 		else
1094 			fuse_fs_release(fFS, path, cookie);
1095 	}
1096 
1097 	if (error != B_OK)
1098 		RETURN_ERROR(error);
1099 
1100 	return B_OK;
1101 }
1102 
1103 
1104 // #pragma mark - nodes
1105 
1106 
1107 status_t
1108 FUSEVolume::SetFlags(void* _node, void* _cookie, int flags)
1109 {
1110 	FileCookie* cookie = (FileCookie*)_cookie;
1111 
1112 	RWLockableWriteLocker cookieLocker(this, cookie);
1113 
1114 	const int settableFlags = O_APPEND | O_NONBLOCK | O_SYNC | O_RSYNC
1115 		| O_DSYNC | O_DIRECT;
1116 
1117 	cookie->flags = (cookie->flags & ~settableFlags) | (flags & settableFlags);
1118 
1119 	return B_OK;
1120 }
1121 
1122 
1123 status_t
1124 FUSEVolume::FSync(void* _node)
1125 {
1126 	FUSENode* node = (FUSENode*)_node;
1127 
1128 	// lock the directory
1129 	NodeReadLocker nodeLocker(this, node, true);
1130 	if (nodeLocker.Status() != B_OK)
1131 		RETURN_ERROR(nodeLocker.Status());
1132 
1133 	int fuseError;
1134 	bool dirty;
1135 	AutoLocker<Locker> locker(fLock);
1136 	if (fOps != NULL) {
1137 		// mark the node not dirty
1138 		dirty = node->dirty;
1139 		node->dirty = false;
1140 
1141 		locker.Unlock();
1142 
1143 		fuse_file_info cookie;
1144 		fuseError = fuse_ll_open(fOps, node->id, &cookie);
1145 		if (fuseError == 0) {
1146 			fuseError = fuse_ll_fsync(fOps, node->id, 0, &cookie);
1147 				// full sync, not only data
1148 			fuse_ll_flush(fOps, node->id, &cookie);
1149 			fuse_ll_release(fOps, node->id, &cookie);
1150 		}
1151 	} else {
1152 		// get a path for the node
1153 		char path[B_PATH_NAME_LENGTH];
1154 		size_t pathLen;
1155 		status_t error = _BuildPath(node, path, pathLen);
1156 		if (error != B_OK)
1157 			RETURN_ERROR(error);
1158 
1159 		// mark the node not dirty
1160 		dirty = node->dirty;
1161 		node->dirty = false;
1162 
1163 		locker.Unlock();
1164 
1165 		// open, sync, and close the node
1166 		FileCookie cookie(O_RDONLY);
1167 		fuseError = fuse_fs_open(fFS, path, &cookie);
1168 		if (fuseError == 0) {
1169 			fuseError = fuse_fs_fsync(fFS, path, 0, &cookie);
1170 				// full sync, not only data
1171 			fuse_fs_flush(fFS, path, &cookie);
1172 			fuse_fs_release(fFS, path, &cookie);
1173 		}
1174 	}
1175 
1176 	if (fuseError != 0) {
1177 		// sync'ing failed -- mark the node dirty again
1178 		locker.Lock();
1179 		node->dirty |= dirty;
1180 		RETURN_ERROR(fuseError);
1181 	}
1182 
1183 	return B_OK;
1184 }
1185 
1186 
1187 status_t
1188 FUSEVolume::ReadSymlink(void* _node, char* buffer, size_t bufferSize,
1189 	size_t* _bytesRead)
1190 {
1191 	FUSENode* node = (FUSENode*)_node;
1192 
1193 	// lock the directory
1194 	NodeReadLocker nodeLocker(this, node, true);
1195 	if (nodeLocker.Status() != B_OK)
1196 		RETURN_ERROR(nodeLocker.Status());
1197 
1198 	int fuseError;
1199 	if (fOps != NULL) {
1200 		fuseError = fuse_ll_readlink(fOps, node->id, buffer, bufferSize);
1201 		if (fuseError != 0) {
1202 			*_bytesRead = 0;
1203 			return fuseError;
1204 		}
1205 		// fuse_ll_readlink returns the actual size (even if the data didn't fit the buffer)
1206 		*_bytesRead = fuseError;
1207 	} else {
1208 		AutoLocker<Locker> locker(fLock);
1209 
1210 		// get a path for the node
1211 		char path[B_PATH_NAME_LENGTH];
1212 		size_t pathLen;
1213 		status_t error = _BuildPath(node, path, pathLen);
1214 		if (error != B_OK)
1215 			RETURN_ERROR(error);
1216 
1217 		locker.Unlock();
1218 
1219 		// read the symlink
1220 		int fuseError = fuse_fs_readlink(fFS, path, buffer, bufferSize);
1221 		if (fuseError != 0) {
1222 			*_bytesRead = 0;
1223 			return fuseError;
1224 		}
1225 
1226 		// fuse_fs_readlink() is supposed to return a NULL-terminated string, which
1227 		// the Haiku interface doesn't require. We have to return the string length,
1228 		// though.
1229 		*_bytesRead = strnlen(buffer, bufferSize);
1230 	}
1231 
1232 	return B_OK;
1233 }
1234 
1235 
1236 status_t
1237 FUSEVolume::CreateSymlink(void* _dir, const char* name, const char* target,
1238 	int mode)
1239 {
1240 	FUSENode* dir = (FUSENode*)_dir;
1241 	PRINT(("FUSEVolume::CreateSymlink(%p (%" B_PRId64 "), \"%s\" -> \"%s\", "
1242 		"%#x)\n", dir, dir->id, name, target, mode));
1243 
1244 	// lock the directory
1245 	NodeWriteLocker nodeLocker(this, dir, false);
1246 	if (nodeLocker.Status() != B_OK)
1247 		RETURN_ERROR(nodeLocker.Status());
1248 
1249 	int fuseError;
1250 	if (fOps != NULL) {
1251 		fuseError = fuse_ll_symlink(fOps, target, dir->id, name);
1252 	} else {
1253 		AutoLocker<Locker> locker(fLock);
1254 
1255 		// get a path for the entry
1256 		char path[B_PATH_NAME_LENGTH];
1257 		size_t pathLen;
1258 		status_t error = _BuildPath(dir, name, path, pathLen);
1259 		if (error != B_OK)
1260 			RETURN_ERROR(error);
1261 
1262 		locker.Unlock();
1263 
1264 		// create the symlink
1265 		fuseError = fuse_fs_symlink(fFS, target, path);
1266 	}
1267 
1268 	if (fuseError != 0)
1269 		RETURN_ERROR(fuseError);
1270 
1271 	// TODO: Set the mode?!
1272 
1273 	// mark the dir dirty
1274 	AutoLocker<Locker> locker(fLock);
1275 	dir->dirty = true;
1276 	locker.Unlock();
1277 
1278 	// send node monitoring message
1279 	ino_t nodeID;
1280 	if (_GetNodeID(dir, name, &nodeID)) {
1281 		UserlandFS::KernelEmu::notify_listener(B_ENTRY_CREATED, 0, fID, 0,
1282 			dir->id, nodeID, NULL, name);
1283 	}
1284 
1285 	return B_OK;
1286 }
1287 
1288 
1289 status_t
1290 FUSEVolume::Link(void* _dir, const char* name, void* _node)
1291 {
1292 	FUSENode* dir = (FUSENode*)_dir;
1293 	FUSENode* node = (FUSENode*)_node;
1294 	PRINT(("FUSEVolume::Link(%p (%" B_PRId64 "), \"%s\" -> %p (%" B_PRId64
1295 		"))\n", dir, dir->id, name, node, node->id));
1296 
1297 	// lock the directories -- the target directory for writing, the node's
1298 	// parent for reading
1299 	MultiNodeLocker nodeLocker(this, dir, false, true, node, true, false);
1300 	if (nodeLocker.Status() != B_OK)
1301 		RETURN_ERROR(nodeLocker.Status());
1302 
1303 	int fuseError;
1304 	if (fOps != NULL) {
1305 		fuseError = fuse_ll_link(fOps, node->id, dir->id, name);
1306 	} else {
1307 		AutoLocker<Locker> locker(fLock);
1308 
1309 		// get a path for the entries
1310 		char oldPath[B_PATH_NAME_LENGTH];
1311 		size_t oldPathLen;
1312 		status_t error = _BuildPath(node, oldPath, oldPathLen);
1313 		if (error != B_OK)
1314 			RETURN_ERROR(error);
1315 
1316 		char newPath[B_PATH_NAME_LENGTH];
1317 		size_t newPathLen;
1318 		error = _BuildPath(dir, name, newPath, newPathLen);
1319 		if (error != B_OK)
1320 			RETURN_ERROR(error);
1321 
1322 		locker.Unlock();
1323 
1324 		// link
1325 		fuseError = fuse_fs_link(fFS, oldPath, newPath);
1326 	}
1327 	if (fuseError != 0)
1328 		RETURN_ERROR(fuseError);
1329 
1330 	// mark the dir and the node dirty
1331 	AutoLocker<Locker> locker(fLock);
1332 	dir->dirty = true;
1333 	node->dirty = true;
1334 	locker.Unlock();
1335 
1336 	// send node monitoring message
1337 	UserlandFS::KernelEmu::notify_listener(B_ENTRY_CREATED, 0, fID, 0, dir->id,
1338 		node->id, NULL, name);
1339 
1340 	return B_OK;
1341 }
1342 
1343 
1344 status_t
1345 FUSEVolume::Unlink(void* _dir, const char* name)
1346 {
1347 	FUSENode* dir = (FUSENode*)_dir;
1348 	PRINT(("FUSEVolume::Unlink(%p (%" B_PRId64 "), \"%s\")\n", dir, dir->id,
1349 		name));
1350 
1351 	// lock the directory
1352 	NodeWriteLocker nodeLocker(this, dir, false);
1353 	if (nodeLocker.Status() != B_OK)
1354 		RETURN_ERROR(nodeLocker.Status());
1355 
1356 	// get the node ID (for the node monitoring message)
1357 	ino_t nodeID;
1358 	bool doNodeMonitoring = _GetNodeID(dir, name, &nodeID);
1359 
1360 	int fuseError;
1361 	if (fOps != NULL) {
1362 		fuseError = fuse_ll_unlink(fOps, dir->id, name);
1363 	} else {
1364 		AutoLocker<Locker> locker(fLock);
1365 
1366 		// get a path for the entry
1367 		char path[B_PATH_NAME_LENGTH];
1368 		size_t pathLen;
1369 		status_t error = _BuildPath(dir, name, path, pathLen);
1370 		if (error != B_OK)
1371 			RETURN_ERROR(error);
1372 
1373 		locker.Unlock();
1374 
1375 		// unlink
1376 		fuseError = fuse_fs_unlink(fFS, path);
1377 	}
1378 	if (fuseError != 0)
1379 		RETURN_ERROR(fuseError);
1380 
1381 	// remove the entry
1382 	AutoLocker<Locker> locker(fLock);
1383 	_RemoveEntry(dir, name);
1384 
1385 	// mark the dir dirty
1386 	dir->dirty = true;
1387 	locker.Unlock();
1388 
1389 	// send node monitoring message
1390 	if (doNodeMonitoring) {
1391 		UserlandFS::KernelEmu::notify_listener(B_ENTRY_REMOVED, 0, fID, 0,
1392 			dir->id, nodeID, NULL, name);
1393 	}
1394 
1395 	return B_OK;
1396 }
1397 
1398 
1399 status_t
1400 FUSEVolume::Rename(void* _oldDir, const char* oldName, void* _newDir,
1401 	const char* newName)
1402 {
1403 	FUSENode* oldDir = (FUSENode*)_oldDir;
1404 	FUSENode* newDir = (FUSENode*)_newDir;
1405 	PRINT(("FUSEVolume::Rename(%p (%" B_PRId64 "), \"%s\", %p (%" B_PRId64
1406 		"), \"%s\")\n", oldDir, oldDir->id, oldName, newDir, newDir->id,
1407 		newName));
1408 
1409 	// lock the directories
1410 	MultiNodeLocker nodeLocker(this, oldDir, false, true, newDir, false, true);
1411 	if (nodeLocker.Status() != B_OK)
1412 		RETURN_ERROR(nodeLocker.Status());
1413 
1414 	int fuseError;
1415 	if (fOps != NULL) {
1416 		fuseError = fuse_ll_rename(fOps, oldDir->id, oldName, newDir->id, newName);
1417 	} else {
1418 		AutoLocker<Locker> locker(fLock);
1419 
1420 		// get a path for the entries
1421 		char oldPath[B_PATH_NAME_LENGTH];
1422 		size_t oldPathLen;
1423 		status_t error = _BuildPath(oldDir, oldName, oldPath, oldPathLen);
1424 		if (error != B_OK)
1425 			RETURN_ERROR(error);
1426 
1427 		char newPath[B_PATH_NAME_LENGTH];
1428 		size_t newPathLen;
1429 		error = _BuildPath(newDir, newName, newPath, newPathLen);
1430 		if (error != B_OK)
1431 			RETURN_ERROR(error);
1432 
1433 		locker.Unlock();
1434 
1435 		// rename
1436 		fuseError = fuse_fs_rename(fFS, oldPath, newPath);
1437 	}
1438 	if (fuseError != 0)
1439 		RETURN_ERROR(fuseError);
1440 
1441 	// rename the entry
1442 	AutoLocker<Locker> locker(fLock);
1443 	_RenameEntry(oldDir, oldName, newDir, newName);
1444 
1445 	// mark the dirs dirty
1446 	oldDir->dirty = true;
1447 	newDir->dirty = true;
1448 
1449 	// send node monitoring message
1450 	ino_t nodeID;
1451 	if (_GetNodeID(newDir, newName, &nodeID)) {
1452 		UserlandFS::KernelEmu::notify_listener(B_ENTRY_MOVED, 0, fID,
1453 			oldDir->id, newDir->id, nodeID, oldName, newName);
1454 	}
1455 
1456 	return B_OK;
1457 }
1458 
1459 
1460 status_t
1461 FUSEVolume::Access(void* _node, int mode)
1462 {
1463 	FUSENode* node = (FUSENode*)_node;
1464 
1465 	// lock the directory
1466 	NodeReadLocker nodeLocker(this, node, true);
1467 	if (nodeLocker.Status() != B_OK)
1468 		RETURN_ERROR(nodeLocker.Status());
1469 
1470 	int fuseError;
1471 	if (fOps != NULL) {
1472 		fuseError = fuse_ll_access(fOps, node->id, mode);
1473 	} else {
1474 		AutoLocker<Locker> locker(fLock);
1475 
1476 		// get a path for the node
1477 		char path[B_PATH_NAME_LENGTH];
1478 		size_t pathLen;
1479 		status_t error = _BuildPath(node, path, pathLen);
1480 		if (error != B_OK)
1481 			RETURN_ERROR(error);
1482 
1483 		locker.Unlock();
1484 
1485 		// call the access hook on the path
1486 		fuseError = fuse_fs_access(fFS, path, mode);
1487 	}
1488 
1489 	if (fuseError != 0)
1490 		return fuseError;
1491 
1492 	return B_OK;
1493 }
1494 
1495 
1496 status_t
1497 FUSEVolume::ReadStat(void* _node, struct stat* st)
1498 {
1499 	FUSENode* node = (FUSENode*)_node;
1500 	PRINT(("FUSEVolume::ReadStat(%p (%" B_PRId64 "), %p)\n", node, node->id,
1501 		st));
1502 
1503 	// lock the directory
1504 	NodeReadLocker nodeLocker(this, node, true);
1505 	if (nodeLocker.Status() != B_OK)
1506 		RETURN_ERROR(nodeLocker.Status());
1507 
1508 	st->st_dev = GetID();
1509 	st->st_ino = node->id;
1510 	st->st_blksize = 2048;
1511 	st->st_type = 0;
1512 
1513 	int fuseError;
1514 	if (fOps != NULL) {
1515 		fuseError = fuse_ll_getattr(fOps, node->id, st);
1516 	} else {
1517 		AutoLocker<Locker> locker(fLock);
1518 
1519 		// get a path for the node
1520 		char path[B_PATH_NAME_LENGTH];
1521 		size_t pathLen;
1522 		status_t error = _BuildPath(node, path, pathLen);
1523 		if (error != B_OK)
1524 			RETURN_ERROR(error);
1525 
1526 		locker.Unlock();
1527 
1528 		// stat the path
1529 		fuseError = fuse_fs_getattr(fFS, path, st);
1530 	}
1531 	if (fuseError != 0)
1532 		return fuseError;
1533 
1534 	return B_OK;
1535 }
1536 
1537 
1538 status_t
1539 FUSEVolume::WriteStat(void* _node, const struct stat* st, uint32 mask)
1540 {
1541 	FUSENode* node = (FUSENode*)_node;
1542 	PRINT(("FUSEVolume::WriteStat(%p (%" B_PRId64 "), %p, %#" B_PRIx32 ")\n",
1543 		node, node->id, st, mask));
1544 
1545 	// lock the directory
1546 	NodeReadLocker nodeLocker(this, node, true);
1547 	if (nodeLocker.Status() != B_OK)
1548 		RETURN_ERROR(nodeLocker.Status());
1549 
1550 	if (fOps != NULL) {
1551 		int fuseError = fuse_ll_setattr(fOps, node->id, st, mask);
1552 		if (fuseError != 0)
1553 			RETURN_ERROR(fuseError);
1554 	} else {
1555 		AutoLocker<Locker> locker(fLock);
1556 
1557 		// get a path for the node
1558 		char path[B_PATH_NAME_LENGTH];
1559 		size_t pathLen;
1560 		status_t error = _BuildPath(node, path, pathLen);
1561 		if (error != B_OK)
1562 			RETURN_ERROR(error);
1563 
1564 		locker.Unlock();
1565 
1566 		// permissions
1567 		if ((mask & B_STAT_MODE) != 0) {
1568 			int fuseError = fuse_fs_chmod(fFS, path, st->st_mode);
1569 			if (fuseError != 0)
1570 				RETURN_ERROR(fuseError);
1571 		}
1572 
1573 		// owner
1574 		if ((mask & (B_STAT_UID | B_STAT_GID)) != 0) {
1575 			uid_t uid = (mask & B_STAT_UID) != 0 ? st->st_uid : (uid_t)-1;
1576 			gid_t gid = (mask & B_STAT_GID) != 0 ? st->st_gid : (gid_t)-1;
1577 			int fuseError = fuse_fs_chown(fFS, path, uid, gid);
1578 			if (fuseError != 0)
1579 				RETURN_ERROR(fuseError);
1580 		}
1581 
1582 		// size
1583 		if ((mask & B_STAT_SIZE) != 0) {
1584 			// truncate
1585 			int fuseError = fuse_fs_truncate(fFS, path, st->st_size);
1586 			if (fuseError != 0)
1587 				RETURN_ERROR(fuseError);
1588 		}
1589 
1590 		// access/modification time
1591 		if ((mask & (B_STAT_ACCESS_TIME | B_STAT_MODIFICATION_TIME)) != 0) {
1592 			timespec tv[2] = {
1593 				{st->st_atime, 0},
1594 				{st->st_mtime, 0}
1595 			};
1596 
1597 			// If either time is not specified, we need to stat the file to get the
1598 			// current value.
1599 			if ((mask & (B_STAT_ACCESS_TIME | B_STAT_MODIFICATION_TIME))
1600 					!= (B_STAT_ACCESS_TIME | B_STAT_MODIFICATION_TIME)) {
1601 				struct stat currentStat;
1602 				int fuseError = fuse_fs_getattr(fFS, path, &currentStat);
1603 				if (fuseError != 0)
1604 					RETURN_ERROR(fuseError);
1605 
1606 				if ((mask & B_STAT_ACCESS_TIME) == 0)
1607 					tv[0].tv_sec = currentStat.st_atime;
1608 				else
1609 				tv[1].tv_sec = currentStat.st_mtime;
1610 			}
1611 
1612 			int fuseError = fuse_fs_utimens(fFS, path, tv);
1613 			if (fuseError != 0)
1614 				RETURN_ERROR(fuseError);
1615 		}
1616 	}
1617 
1618 	// mark the node dirty
1619 	AutoLocker<Locker> locker(fLock);
1620 	node->dirty = true;
1621 
1622 	// send node monitoring message
1623 	uint32 changedFields = mask &
1624 		(B_STAT_MODE | B_STAT_UID | B_STAT_GID | B_STAT_SIZE
1625 		| B_STAT_ACCESS_TIME | B_STAT_MODIFICATION_TIME);
1626 
1627 	if (changedFields != 0) {
1628 		UserlandFS::KernelEmu::notify_listener(B_STAT_CHANGED, changedFields,
1629 			fID, 0, 0, node->id, NULL, NULL);
1630 	}
1631 
1632 	return B_OK;
1633 }
1634 
1635 
1636 // #pragma mark - files
1637 
1638 
1639 status_t
1640 FUSEVolume::Create(void* _dir, const char* name, int openMode, int mode,
1641 	void** _cookie, ino_t* _vnid)
1642 {
1643 	FUSENode* dir = (FUSENode*)_dir;
1644 	PRINT(("FUSEVolume::Create(%p (%" B_PRId64 "), \"%s\", %#x, %#x)\n", dir,
1645 		dir->id, name, openMode, mode));
1646 
1647 	// lock the directory
1648 	NodeWriteLocker nodeLocker(this, dir, false);
1649 	if (nodeLocker.Status() != B_OK)
1650 		RETURN_ERROR(nodeLocker.Status());
1651 
1652 	// allocate a file cookie
1653 	FileCookie* cookie = new(std::nothrow) FileCookie(openMode);
1654 	if (cookie == NULL)
1655 		RETURN_ERROR(B_NO_MEMORY);
1656 	ObjectDeleter<FileCookie> cookieDeleter(cookie);
1657 
1658 	FUSENode* node;
1659 	int fuseError;
1660 	if (fOps) {
1661 		fuse_file_info cookie;
1662 		fuse_ino_t ino;
1663 		fuseError = fuse_ll_create(fOps, dir->id, name, mode, &cookie, ino);
1664 		if (fuseError != 0)
1665 			RETURN_ERROR(fuseError);
1666 
1667 		// get the node
1668 		// TODO do we really need it?
1669 		status_t error = _GetNode(dir, name, &node);
1670 		if (error != B_OK) {
1671 			// This is bad. We've create the file successfully, but couldn't get
1672 			// the node. Delete the entry.
1673 			// We can't close the file because we don't know its inode.
1674 			fuse_ll_flush(fOps, ino, &cookie);
1675 			fuse_ll_release(fOps, ino, &cookie);
1676 			fuse_ll_unlink(fOps, dir->id, name);
1677 			RETURN_ERROR(error);
1678 		}
1679 	} else {
1680 		AutoLocker<Locker> locker(fLock);
1681 
1682 		// get a path for the node
1683 		char path[B_PATH_NAME_LENGTH];
1684 		size_t pathLen;
1685 		status_t error = _BuildPath(dir, name, path, pathLen);
1686 		if (error != B_OK)
1687 			RETURN_ERROR(error);
1688 
1689 		locker.Unlock();
1690 
1691 		// create the file
1692 		fuseError = fuse_fs_create(fFS, path, mode, cookie);
1693 		if (fuseError != 0)
1694 			RETURN_ERROR(fuseError);
1695 
1696 		// get the node
1697 		error = _GetNode(dir, name, &node);
1698 		if (error != B_OK) {
1699 			// This is bad. We've create the file successfully, but couldn't get
1700 			// the node. Close the file and delete the entry.
1701 			fuse_fs_flush(fFS, path, cookie);
1702 			fuse_fs_release(fFS, path, cookie);
1703 			fuse_fs_unlink(fFS, path);
1704 			RETURN_ERROR(error);
1705 		}
1706 	}
1707 
1708 	// mark the dir and the node dirty
1709 	AutoLocker<Locker> locker(fLock);
1710 	dir->dirty = true;
1711 	node->dirty = true;
1712 	locker.Unlock();
1713 
1714 	// send node monitoring message
1715 	UserlandFS::KernelEmu::notify_listener(B_ENTRY_CREATED, 0, fID, 0, dir->id,
1716 		node->id, NULL, name);
1717 
1718 	cookieDeleter.Detach();
1719 	*_cookie = cookie;
1720 	*_vnid = node->id;
1721 
1722 	return B_OK;
1723 }
1724 
1725 
1726 status_t
1727 FUSEVolume::Open(void* _node, int openMode, void** _cookie)
1728 {
1729 	FUSENode* node = (FUSENode*)_node;
1730 	PRINT(("FUSEVolume::Open(%p (%" B_PRId64 "), %#x)\n", node, node->id,
1731 		openMode));
1732 
1733 	// lock the directory
1734 	NodeReadLocker nodeLocker(this, node, true);
1735 	if (nodeLocker.Status() != B_OK)
1736 		RETURN_ERROR(nodeLocker.Status());
1737 
1738 	bool truncate = (openMode & O_TRUNC) != 0;
1739 	openMode &= ~O_TRUNC;
1740 
1741 	// allocate a file cookie
1742 	FileCookie* cookie = new(std::nothrow) FileCookie(openMode);
1743 	if (cookie == NULL)
1744 		RETURN_ERROR(B_NO_MEMORY);
1745 	ObjectDeleter<FileCookie> cookieDeleter(cookie);
1746 
1747 	char path[B_PATH_NAME_LENGTH];
1748 	size_t pathLen;
1749 
1750 	int fuseError;
1751 	struct fuse_file_info llCookie = { 0 };
1752 		// FIXME store this in the FileCookie for lowlevel streams, we'll need it in read, write...
1753 	if (fOps != NULL) {
1754 		llCookie.flags = openMode;
1755 		if (S_ISDIR(node->type))
1756 			fuseError = fuse_ll_opendir(fOps, node->id, &llCookie);
1757 		else
1758 			fuseError = fuse_ll_open(fOps, node->id, &llCookie);
1759 	} else {
1760 		AutoLocker<Locker> locker(fLock);
1761 
1762 		// get a path for the node
1763 		status_t error = _BuildPath(node, path, pathLen);
1764 		if (error != B_OK)
1765 			RETURN_ERROR(error);
1766 
1767 		locker.Unlock();
1768 
1769 		// open the file
1770 		fuseError = fuse_fs_open(fFS, path, cookie);
1771 	}
1772 
1773 	if (fuseError != 0)
1774 		RETURN_ERROR(fuseError);
1775 
1776 	// truncate the file, if requested
1777 	if (truncate) {
1778 		if (fOps != NULL) {
1779 			struct stat st;
1780 			st.st_size = 0;
1781 			fuseError = fuse_ll_setattr(fOps, node->id, &st, FUSE_SET_ATTR_SIZE);
1782 
1783 			if (fuseError != 0) {
1784 				fuse_ll_flush(fOps, node->id, &llCookie);
1785 				fuse_ll_release(fOps, node->id, &llCookie);
1786 				RETURN_ERROR(fuseError);
1787 			}
1788 		} else {
1789 			fuseError = fuse_fs_ftruncate(fFS, path, 0, cookie);
1790 			if (fuseError == ENOSYS) {
1791 				// Fallback to truncate if ftruncate is not implemented
1792 				fuseError = fuse_fs_truncate(fFS, path, 0);
1793 			}
1794 			if (fuseError != 0) {
1795 				fuse_fs_flush(fFS, path, cookie);
1796 				fuse_fs_release(fFS, path, cookie);
1797 				RETURN_ERROR(fuseError);
1798 			}
1799 		}
1800 
1801 		// mark the node dirty
1802 		AutoLocker<Locker> locker(fLock);
1803 		node->dirty = true;
1804 
1805 		// send node monitoring message
1806 		UserlandFS::KernelEmu::notify_listener(B_STAT_CHANGED,
1807 			B_STAT_SIZE | B_STAT_MODIFICATION_TIME, fID, 0, 0, node->id, NULL,
1808 			NULL);
1809 	}
1810 
1811 	if (S_ISREG(node->type)) {
1812 		if (cookie->direct_io || llCookie.direct_io) {
1813 			if (node->cacheCount > 0) {
1814 				// In some very rare cases, for the same node, the first `open`
1815 				// indicates that caching is allowed (by not setting `direct_io`),
1816 				// but a subsequent `open` on the same node indicates that it is
1817 				// NOT allowed (by setting `direct_io` to 1).
1818 				debugger("FUSEVolume::Open(): inconsistent direct_io flags!");
1819 				UserlandFS::KernelEmu::file_cache_delete(GetID(), node->id);
1820 				node->cacheCount = 0;
1821 			}
1822 		} else {
1823 			if (node->cacheCount == 0) {
1824 				struct stat st;
1825 				if (fOps != NULL) {
1826 					fuseError = fuse_ll_getattr(fOps, node->id, &st);
1827 				} else {
1828 					fuseError = fuse_fs_getattr(fFS, path, &st);
1829 				}
1830 				if (fuseError != 0) {
1831 					RETURN_ERROR(fuseError);
1832 				}
1833 				status_t error = UserlandFS::KernelEmu::file_cache_create(GetID(), node->id, st.st_size);
1834 				if (error != B_OK) {
1835 					RETURN_ERROR(error);
1836 				}
1837 			}
1838 			// Increment cacheCount by extra 1 if the cache is kept to prevent
1839 			// the cache from being deleted at close().
1840 			node->cacheCount += 1 + cookie->keep_cache + llCookie.keep_cache;
1841 		}
1842 	}
1843 
1844 	cookieDeleter.Detach();
1845 	*_cookie = cookie;
1846 
1847 	return B_OK;
1848 }
1849 
1850 
1851 status_t
1852 FUSEVolume::Close(void* _node, void* _cookie)
1853 {
1854 	FUSENode* node = (FUSENode*)_node;
1855 	FileCookie* cookie = (FileCookie*)_cookie;
1856 
1857 	RWLockableReadLocker cookieLocker(this, cookie);
1858 
1859 	// lock the directory
1860 	NodeReadLocker nodeLocker(this, node, true);
1861 	if (nodeLocker.Status() != B_OK)
1862 		RETURN_ERROR(nodeLocker.Status());
1863 
1864 	int fuseError;
1865 
1866 	if (fOps != NULL) {
1867 		fuseError = fuse_ll_flush(fOps, node->id, cookie);
1868 	} else {
1869 		AutoLocker<Locker> locker(fLock);
1870 
1871 		// get a path for the node
1872 		char path[B_PATH_NAME_LENGTH];
1873 		size_t pathLen;
1874 		status_t error = _BuildPath(node, path, pathLen);
1875 		if (error != B_OK)
1876 			RETURN_ERROR(error);
1877 
1878 		locker.Unlock();
1879 
1880 		// flush the file
1881 		fuseError = fuse_fs_flush(fFS, path, cookie);
1882 	}
1883 	if (fuseError != 0)
1884 		return fuseError;
1885 
1886 	if (S_ISREG(node->type) && node->cacheCount > 0) {
1887 		--node->cacheCount;
1888 		if (node->cacheCount == 0) {
1889 			UserlandFS::KernelEmu::file_cache_delete(GetID(), node->id);
1890 		}
1891 	}
1892 
1893 	return B_OK;
1894 }
1895 
1896 
1897 status_t
1898 FUSEVolume::FreeCookie(void* _node, void* _cookie)
1899 {
1900 	FUSENode* node = (FUSENode*)_node;
1901 	FileCookie* cookie = (FileCookie*)_cookie;
1902 
1903 	// no need to lock the cookie here, as no-one else uses it anymore
1904 
1905 	// lock the directory
1906 	NodeReadLocker nodeLocker(this, node, true);
1907 	if (nodeLocker.Status() != B_OK)
1908 		RETURN_ERROR(nodeLocker.Status());
1909 
1910 	ObjectDeleter<FileCookie> cookieDeleter(cookie);
1911 
1912 	int fuseError;
1913 	if (fOps) {
1914 		fuseError = fuse_ll_release(fOps, node->id, cookie);
1915 	} else {
1916 		AutoLocker<Locker> locker(fLock);
1917 
1918 		// get a path for the node
1919 		char path[B_PATH_NAME_LENGTH];
1920 		size_t pathLen;
1921 		status_t error = _BuildPath(node, path, pathLen);
1922 		if (error != B_OK)
1923 			RETURN_ERROR(error);
1924 
1925 		locker.Unlock();
1926 
1927 		// release the file
1928 		fuseError = fuse_fs_release(fFS, path, cookie);
1929 	}
1930 
1931 	if (fuseError != 0)
1932 		return fuseError;
1933 
1934 	return B_OK;
1935 }
1936 
1937 
1938 status_t
1939 FUSEVolume::Read(void* _node, void* _cookie, off_t pos, void* buffer,
1940 	size_t bufferSize, size_t* _bytesRead)
1941 {
1942 	FUSENode* node = (FUSENode*)_node;
1943 	FileCookie* cookie = (FileCookie*)_cookie;
1944 
1945 	RWLockableReadLocker cookieLocker(this, cookie);
1946 
1947 	// lock the directory
1948 	NodeReadLocker nodeLocker(this, node, true);
1949 	if (nodeLocker.Status() != B_OK)
1950 		RETURN_ERROR(nodeLocker.Status());
1951 
1952 	*_bytesRead = bufferSize;
1953 	status_t error = B_OK;
1954 
1955 	if (S_ISREG(node->type) && node->cacheCount > 0) {
1956 		error = UserlandFS::KernelEmu::file_cache_read(GetID(), node->id, cookie, pos,
1957 			buffer, _bytesRead);
1958 	} else
1959 		error = _InternalIO(node, cookie, NULL, pos, (char *)buffer, *_bytesRead, false);
1960 
1961 	if (error != B_OK)
1962 		RETURN_ERROR(error);
1963 
1964 	return B_OK;
1965 }
1966 
1967 
1968 status_t
1969 FUSEVolume::Write(void* _node, void* _cookie, off_t pos, const void* buffer,
1970 	size_t bufferSize, size_t* _bytesWritten)
1971 {
1972 	FUSENode* node = (FUSENode*)_node;
1973 	FileCookie* cookie = (FileCookie*)_cookie;
1974 
1975 	RWLockableReadLocker cookieLocker(this, cookie);
1976 
1977 	*_bytesWritten = 0;
1978 
1979 	// lock the directory
1980 	NodeReadLocker nodeLocker(this, node, true);
1981 	if (nodeLocker.Status() != B_OK)
1982 		RETURN_ERROR(nodeLocker.Status());
1983 
1984 	*_bytesWritten = bufferSize;
1985 	status_t error = B_OK;
1986 
1987 	if (S_ISREG(node->type) && node->cacheCount > 0) {
1988 		error = UserlandFS::KernelEmu::file_cache_write(GetID(), node->id, cookie, pos,
1989 			buffer, _bytesWritten);
1990 	} else
1991 		error = _InternalIO(node, cookie, NULL, pos, (char *)buffer, *_bytesWritten, true);
1992 
1993 	if (error != B_OK)
1994 		RETURN_ERROR(error);
1995 
1996 	// mark the node dirty
1997 	AutoLocker<Locker> locker(fLock);
1998 	node->dirty = true;
1999 
2000 	// send node monitoring message
2001 	UserlandFS::KernelEmu::notify_listener(B_STAT_CHANGED,
2002 		B_STAT_SIZE | B_STAT_MODIFICATION_TIME, fID, 0, 0, node->id, NULL,
2003 		NULL);
2004 		// TODO: The size possibly doesn't change.
2005 		// TODO: Avoid message flooding -- use a timeout and set the
2006 		// B_STAT_INTERIM_UPDATE flag.
2007 
2008 	return B_OK;
2009 }
2010 
2011 
2012 // #pragma mark - directories
2013 
2014 
2015 status_t
2016 FUSEVolume::CreateDir(void* _dir, const char* name, int mode)
2017 {
2018 	FUSENode* dir = (FUSENode*)_dir;
2019 	PRINT(("FUSEVolume::CreateDir(%p (%" B_PRId64 "), \"%s\", %#x)\n", dir,
2020 		dir->id, name, mode));
2021 
2022 	// lock the directory
2023 	NodeWriteLocker nodeLocker(this, dir, false);
2024 	if (nodeLocker.Status() != B_OK)
2025 		RETURN_ERROR(nodeLocker.Status());
2026 
2027 	int fuseError;
2028 	if (fOps != NULL) {
2029 		fuseError = fuse_ll_mkdir(fOps, dir->id, name, mode);
2030 	} else {
2031 		AutoLocker<Locker> locker(fLock);
2032 
2033 		// get a path for the entry
2034 		char path[B_PATH_NAME_LENGTH];
2035 		size_t pathLen;
2036 		status_t error = _BuildPath(dir, name, path, pathLen);
2037 		if (error != B_OK)
2038 			RETURN_ERROR(error);
2039 
2040 		locker.Unlock();
2041 
2042 		// create the dir
2043 		fuseError = fuse_fs_mkdir(fFS, path, mode);
2044 	}
2045 	if (fuseError != 0)
2046 		RETURN_ERROR(fuseError);
2047 
2048 	// mark the dir dirty
2049 	AutoLocker<Locker> locker(fLock);
2050 	dir->dirty = true;
2051 
2052 	// send node monitoring message
2053 	ino_t nodeID;
2054 	if (_GetNodeID(dir, name, &nodeID)) {
2055 		UserlandFS::KernelEmu::notify_listener(B_ENTRY_CREATED, 0, fID, 0,
2056 			dir->id, nodeID, NULL, name);
2057 	}
2058 
2059 	return B_OK;
2060 }
2061 
2062 
2063 status_t
2064 FUSEVolume::RemoveDir(void* _dir, const char* name)
2065 {
2066 	FUSENode* dir = (FUSENode*)_dir;
2067 	PRINT(("FUSEVolume::RemoveDir(%p (%" B_PRId64 "), \"%s\")\n", dir, dir->id,
2068 		name));
2069 
2070 	// lock the directory
2071 	NodeWriteLocker nodeLocker(this, dir, false);
2072 	if (nodeLocker.Status() != B_OK)
2073 		RETURN_ERROR(nodeLocker.Status());
2074 
2075 	// get the node ID (for the node monitoring message)
2076 	ino_t nodeID;
2077 	bool doNodeMonitoring = _GetNodeID(dir, name, &nodeID);
2078 
2079 	int fuseError;
2080 	if (fOps != NULL) {
2081 		fuseError = fuse_ll_rmdir(fOps, dir->id, name);
2082 	} else {
2083 		AutoLocker<Locker> locker(fLock);
2084 
2085 		// get a path for the entry
2086 		char path[B_PATH_NAME_LENGTH];
2087 		size_t pathLen;
2088 		status_t error = _BuildPath(dir, name, path, pathLen);
2089 		if (error != B_OK)
2090 			RETURN_ERROR(error);
2091 
2092 		locker.Unlock();
2093 
2094 		// remove the dir
2095 		fuseError = fuse_fs_rmdir(fFS, path);
2096 	}
2097 	if (fuseError != 0)
2098 		RETURN_ERROR(fuseError);
2099 
2100 	// remove the entry
2101 	AutoLocker<Locker> locker(fLock);
2102 	_RemoveEntry(dir, name);
2103 
2104 	// mark the parent dir dirty
2105 	dir->dirty = true;
2106 
2107 	// send node monitoring message
2108 	if (doNodeMonitoring) {
2109 		UserlandFS::KernelEmu::notify_listener(B_ENTRY_REMOVED, 0, fID, 0,
2110 			dir->id, nodeID, NULL, name);
2111 	}
2112 
2113 	return B_OK;
2114 }
2115 
2116 
2117 status_t
2118 FUSEVolume::OpenDir(void* _node, void** _cookie)
2119 {
2120 	FUSENode* node = (FUSENode*)_node;
2121 	PRINT(("FUSEVolume::OpenDir(%p (%" B_PRId64 "), %p)\n", node, node->id,
2122 		_cookie));
2123 
2124 	// lock the parent directory
2125 	NodeReadLocker nodeLocker(this, node, true);
2126 	if (nodeLocker.Status() != B_OK)
2127 		RETURN_ERROR(nodeLocker.Status());
2128 
2129 	// allocate a dir cookie
2130 	DirCookie* cookie = new(std::nothrow) DirCookie;
2131 	if (cookie == NULL)
2132 		RETURN_ERROR(B_NO_MEMORY);
2133 	ObjectDeleter<DirCookie> cookieDeleter(cookie);
2134 
2135 	if (fOps) {
2136 		int fuseError = fuse_ll_opendir(fOps, node->id, cookie);
2137 		if (fuseError != 0)
2138 			return fuseError;
2139 	} else {
2140 		AutoLocker<Locker> locker(fLock);
2141 
2142 		// get a path for the node
2143 		char path[B_PATH_NAME_LENGTH];
2144 		size_t pathLen;
2145 		status_t error = _BuildPath(node, path, pathLen);
2146 		if (error != B_OK)
2147 			RETURN_ERROR(error);
2148 
2149 		locker.Unlock();
2150 
2151 		if (fFS->ops.readdir == NULL && fFS->ops.getdir != NULL) {
2152 			// no open call -- the FS only supports the deprecated getdir()
2153 			// interface
2154 			cookie->getdirInterface = true;
2155 		} else {
2156 			// open the dir
2157 			int fuseError = fuse_fs_opendir(fFS, path, cookie);
2158 			if (fuseError != 0)
2159 				return fuseError;
2160 		}
2161 	}
2162 
2163 	cookieDeleter.Detach();
2164 	*_cookie = cookie;
2165 
2166 	return B_OK;
2167 }
2168 
2169 
2170 status_t
2171 FUSEVolume::CloseDir(void* node, void* _cookie)
2172 {
2173 	return B_OK;
2174 }
2175 
2176 
2177 status_t
2178 FUSEVolume::FreeDirCookie(void* _node, void* _cookie)
2179 {
2180 	FUSENode* node = (FUSENode*)_node;
2181 	DirCookie* cookie = (DirCookie*)_cookie;
2182 
2183 	// lock the parent directory
2184 	NodeReadLocker nodeLocker(this, node, true);
2185 	if (nodeLocker.Status() != B_OK)
2186 		RETURN_ERROR(nodeLocker.Status());
2187 
2188 	ObjectDeleter<DirCookie> cookieDeleter(cookie);
2189 
2190 	int fuseError;
2191 	if (fOps != NULL) {
2192 		fuseError = fuse_ll_releasedir(fOps, node->id, cookie);
2193 	} else {
2194 		if (cookie->getdirInterface)
2195 			return B_OK;
2196 
2197 		AutoLocker<Locker> locker(fLock);
2198 
2199 		// get a path for the node
2200 		char path[B_PATH_NAME_LENGTH];
2201 		size_t pathLen;
2202 		status_t error = _BuildPath(node, path, pathLen);
2203 		if (error != B_OK)
2204 			RETURN_ERROR(error);
2205 
2206 		locker.Unlock();
2207 
2208 		// release the dir
2209 		fuseError = fuse_fs_releasedir(fFS, path, cookie);
2210 	}
2211 
2212 	if (fuseError != 0)
2213 		return fuseError;
2214 
2215 	return B_OK;
2216 }
2217 
2218 
2219 status_t
2220 FUSEVolume::ReadDir(void* _node, void* _cookie, void* buffer, size_t bufferSize,
2221 	uint32 count, uint32* _countRead)
2222 {
2223 	PRINT(("FUSEVolume::ReadDir(%p, %p, %p, %" B_PRIuSIZE ", %" B_PRId32 ")\n",
2224 		_node, _cookie, buffer, bufferSize, count));
2225 	*_countRead = 0;
2226 
2227 	FUSENode* node = (FUSENode*)_node;
2228 	DirCookie* cookie = (DirCookie*)_cookie;
2229 
2230 	RWLockableWriteLocker cookieLocker(this, cookie);
2231 
2232 	uint32 countRead = 0;
2233 	status_t readDirError = B_OK;
2234 
2235 	AutoLocker<Locker> locker(fLock);
2236 
2237 	if (cookie->entryCache == NULL) {
2238 		// We don't have an entry cache (yet), so we need to ask the client
2239 		// file system to read the directory.
2240 
2241 		locker.Unlock();
2242 
2243 		// lock the directory
2244 		NodeReadLocker nodeLocker(this, node, false);
2245 		if (nodeLocker.Status() != B_OK)
2246 			RETURN_ERROR(nodeLocker.Status());
2247 
2248 		locker.Lock();
2249 
2250 		ReadDirBuffer readDirBuffer(this, node, cookie, buffer, bufferSize, count);
2251 		off_t offset = cookie->currentEntryOffset;
2252 
2253 		// read the dir
2254 		int fuseError;
2255 		if (fOps != NULL) {
2256 			locker.Unlock();
2257 
2258 			// TODO pass the cookie from opendir here instead of NULL
2259 			fuseError = fuse_ll_readdir(fOps, node->id, &readDirBuffer, (char*)buffer, bufferSize,
2260 				&_AddReadDirEntryLowLevel, offset, NULL);
2261 
2262 			// The request filler may or may not be used. If the filesystem decides that it has
2263 			// already cached the directory, it can reply with an already filled buffer from a
2264 			// previous run. So, we can't rely on any updates done to the cookie by that function.
2265 			// So we need to check the number of entries in the buffer, and advance the
2266 			// currentEntryOffset.
2267 			if (fuseError > 0) {
2268 				struct dirent* dirent = (struct dirent*)buffer;
2269 				while (countRead < count
2270 					&& (char*)dirent + dirent->d_reclen <= (char*)buffer + fuseError) {
2271 					countRead++;
2272 					dirent = (struct dirent*)(((char*)dirent) + dirent->d_reclen);
2273 					if (dirent->d_reclen == 0)
2274 						break;
2275 				}
2276 				cookie->currentEntryOffset += (char*)dirent - (char*)buffer;
2277 
2278 				fuseError = 0;
2279 			}
2280 			readDirError = 0;
2281 		} else {
2282 			// get a path for the node
2283 			char path[B_PATH_NAME_LENGTH];
2284 			size_t pathLen;
2285 			status_t error = _BuildPath(node, path, pathLen);
2286 			if (error != B_OK)
2287 				RETURN_ERROR(error);
2288 
2289 			locker.Unlock();
2290 
2291 			if (cookie->getdirInterface) {
2292 PRINT(("  using getdir() interface\n"));
2293 				fuseError = fFS->ops.getdir(path, (fuse_dirh_t)&readDirBuffer,
2294 					&_AddReadDirEntryGetDir);
2295 			} else {
2296 PRINT(("  using readdir() interface\n"));
2297 				fuseError = fuse_fs_readdir(fFS, path, &readDirBuffer,
2298 					&_AddReadDirEntry, offset, cookie);
2299 			}
2300 
2301 			countRead = readDirBuffer.entriesRead;
2302 			readDirError = readDirBuffer.error;
2303 		}
2304 
2305 		if (fuseError != 0)
2306 			return fuseError;
2307 
2308 		locker.Lock();
2309 
2310 	}
2311 
2312 	if (cookie->entryCache != NULL) {
2313 		// we're using an entry cache -- read into the buffer what we can
2314 		dirent* entryBuffer = (dirent*)buffer;
2315 		while (countRead < count
2316 			&& cookie->entryCache->ReadDirent(cookie->currentEntryIndex, fID,
2317 				countRead + 1 < count, entryBuffer, bufferSize)) {
2318 			countRead++;
2319 			cookie->currentEntryIndex++;
2320 			bufferSize -= entryBuffer->d_reclen;
2321 			entryBuffer
2322 				= (dirent*)((uint8*)entryBuffer + entryBuffer->d_reclen);
2323 		}
2324 	}
2325 
2326 	*_countRead = countRead;
2327 	return countRead > 0 ? B_OK : readDirError;
2328 }
2329 
2330 
2331 status_t
2332 FUSEVolume::RewindDir(void* _node, void* _cookie)
2333 {
2334 PRINT(("FUSEVolume::RewindDir(%p, %p)\n", _node, _cookie));
2335 	DirCookie* cookie = (DirCookie*)_cookie;
2336 
2337 	RWLockableWriteLocker cookieLocker(this, cookie);
2338 
2339 	if (cookie->getdirInterface) {
2340 		delete cookie->entryCache;
2341 		cookie->entryCache = NULL;
2342 		cookie->currentEntryIndex = 0;
2343 	} else {
2344 		cookie->currentEntryOffset = 0;
2345 	}
2346 
2347 	return B_OK;
2348 }
2349 
2350 
2351 // #pragma mark - attribute directories
2352 
2353 
2354 // OpenAttrDir
2355 status_t
2356 FUSEVolume::OpenAttrDir(void* _node, void** _cookie)
2357 {
2358 	// allocate an attribute directory cookie
2359 	AttrDirCookie* cookie = new(std::nothrow) AttrDirCookie;
2360 	if (cookie == NULL)
2361 		RETURN_ERROR(B_NO_MEMORY);
2362 
2363 	*_cookie = cookie;
2364 
2365 	return B_OK;
2366 }
2367 
2368 
2369 // CloseAttrDir
2370 status_t
2371 FUSEVolume::CloseAttrDir(void* node, void* cookie)
2372 {
2373 	return B_OK;
2374 }
2375 
2376 
2377 // FreeAttrDirCookie
2378 status_t
2379 FUSEVolume::FreeAttrDirCookie(void* _node, void* _cookie)
2380 {
2381 	delete (AttrDirCookie*)_cookie;
2382 	return B_OK;
2383 }
2384 
2385 
2386 // ReadAttrDir
2387 status_t
2388 FUSEVolume::ReadAttrDir(void* _node, void* _cookie, void* buffer,
2389 	size_t bufferSize, uint32 count, uint32* _countRead)
2390 {
2391 	FUSENode* node = (FUSENode*)_node;
2392 	AttrDirCookie* cookie = (AttrDirCookie*)_cookie;
2393 
2394 	RWLockableWriteLocker cookieLocker(this, cookie);
2395 
2396 	*_countRead = 0;
2397 
2398 	// lock the directory
2399 	NodeReadLocker nodeLocker(this, node, true);
2400 	if (nodeLocker.Status() != B_OK)
2401 		RETURN_ERROR(nodeLocker.Status());
2402 
2403 	char path[B_PATH_NAME_LENGTH];
2404 	size_t pathLen;
2405 	if (fOps == NULL) {
2406 		AutoLocker<Locker> locker(fLock);
2407 
2408 		// get a path for the node
2409 		status_t error = _BuildPath(node, path, pathLen);
2410 		if (error != B_OK)
2411 			RETURN_ERROR(error);
2412 
2413 		locker.Unlock();
2414 	}
2415 
2416 	if (!cookie->IsValid()) {
2417 		// cookie not yet valid -- get the length of the list
2418 		int listSize;
2419 		if (fOps != NULL)
2420 			listSize = fuse_ll_listxattr(fOps, node->id, NULL, 0);
2421 		else
2422 			listSize = fuse_fs_listxattr(fFS, path, NULL, 0);
2423 
2424 		if (listSize < 0)
2425 			RETURN_ERROR(listSize);
2426 
2427 		while (true) {
2428 			// allocate space for the listing
2429 			status_t error = cookie->Allocate(listSize);
2430 			if (error != B_OK)
2431 				RETURN_ERROR(error);
2432 
2433 			// read the listing
2434 			int bytesRead;
2435 			if (fOps != NULL) {
2436 				bytesRead = fuse_ll_listxattr(fOps, node->id, cookie->AttributesBuffer(),
2437 					listSize);
2438 			} else
2439 				bytesRead = fuse_fs_listxattr(fFS, path, cookie->AttributesBuffer(), listSize);
2440 			if (bytesRead < 0)
2441 				RETURN_ERROR(bytesRead);
2442 
2443 			if (bytesRead == listSize)
2444 				break;
2445 
2446 			// attributes listing changed -- reread it
2447 			listSize = bytesRead;
2448 		}
2449 
2450 		cookie->SetValid(true);
2451 	}
2452 
2453 	// we have a valid cookie now -- get the next entries from the cookie
2454 	uint32 countRead = 0;
2455 	dirent* entryBuffer = (dirent*)buffer;
2456 	while (countRead < count
2457 		&& cookie->ReadNextEntry(fID, node->id, countRead + 1 < count,
2458 			entryBuffer, bufferSize)) {
2459 		countRead++;
2460 		bufferSize -= entryBuffer->d_reclen;
2461 		entryBuffer = (dirent*)((uint8*)entryBuffer + entryBuffer->d_reclen);
2462 	}
2463 
2464 	*_countRead = countRead;
2465 	return B_OK;
2466 }
2467 
2468 
2469 // RewindAttrDir
2470 status_t
2471 FUSEVolume::RewindAttrDir(void* _node, void* _cookie)
2472 {
2473 	AttrDirCookie* cookie = (AttrDirCookie*)_cookie;
2474 
2475 	RWLockableWriteLocker cookieLocker(this, cookie);
2476 
2477 	cookie->Clear();
2478 
2479 	return B_OK;
2480 }
2481 
2482 
2483 // #pragma mark - attributes
2484 
2485 
2486 status_t
2487 FUSEVolume::OpenAttr(void* _node, const char* name, int openMode,
2488 	void** _cookie)
2489 {
2490 	FUSENode* node = (FUSENode*)_node;
2491 
2492 	// lock the node
2493 	NodeReadLocker nodeLocker(this, node, true);
2494 	if (nodeLocker.Status() != B_OK)
2495 		RETURN_ERROR(nodeLocker.Status());
2496 
2497 	if (openMode != O_RDONLY) {
2498 		// Write support currently not implemented
2499 		RETURN_ERROR(B_UNSUPPORTED);
2500 	}
2501 
2502 	char path[B_PATH_NAME_LENGTH];
2503 	size_t pathLen;
2504 	status_t error;
2505 	int attrSize;
2506 	if (fOps != NULL) {
2507 		attrSize = fuse_ll_getxattr(fOps, node->id, name, NULL, 0);
2508 	} else {
2509 		AutoLocker<Locker> locker(fLock);
2510 
2511 		// get a path for the node
2512 		error = _BuildPath(node, path, pathLen);
2513 		if (error != B_OK)
2514 			RETURN_ERROR(error);
2515 
2516 		locker.Unlock();
2517 
2518 		attrSize = fuse_fs_getxattr(fFS, path, name, NULL, 0);
2519 	}
2520 
2521 	if (attrSize < 0) {
2522 		if (strcmp(name, kAttrMimeTypeName) == 0) {
2523 			// Return a fake MIME type attribute based on the file extension
2524 			const char* mimeType = NULL;
2525 			error = set_mime(&mimeType, S_ISDIR(node->type) ? NULL : &path[0]);
2526 			if (error != B_OK)
2527 				return error;
2528 			*_cookie = new(std::nothrow)AttrCookie(name, mimeType);
2529 			return B_OK;
2530 		}
2531 
2532 		// Reading attribute failed
2533 		return attrSize;
2534 	}
2535 
2536 	AttrCookie* cookie = new(std::nothrow)AttrCookie(name);
2537 	if (cookie == NULL)
2538 		RETURN_ERROR(B_NO_MEMORY);
2539 	error = cookie->Allocate(attrSize);
2540 	if (error != B_OK) {
2541 		delete cookie;
2542 		RETURN_ERROR(error);
2543 	}
2544 
2545 	int bytesRead;
2546 	if (fOps != NULL)
2547 		bytesRead = fuse_ll_getxattr(fOps, node->id, name, cookie->Buffer(), attrSize);
2548 	else
2549 		bytesRead = fuse_fs_getxattr(fFS, path, name, cookie->Buffer(), attrSize);
2550 
2551 	if (bytesRead < 0) {
2552 		delete cookie;
2553 		return bytesRead;
2554 	}
2555 
2556 	*_cookie = cookie;
2557 
2558 	return B_OK;
2559 }
2560 
2561 
2562 status_t
2563 FUSEVolume::CloseAttr(void* _node, void* _cookie)
2564 {
2565 	return B_OK;
2566 }
2567 
2568 
2569 status_t
2570 FUSEVolume::FreeAttrCookie(void* _node, void* _cookie)
2571 {
2572 	delete (AttrCookie*)_cookie;
2573 	return B_OK;
2574 }
2575 
2576 
2577 status_t
2578 FUSEVolume::ReadAttr(void* _node, void* _cookie, off_t pos, void* buffer,
2579 	size_t bufferSize, size_t* bytesRead)
2580 {
2581 	AttrCookie* cookie = (AttrCookie*)_cookie;
2582 
2583 	RWLockableWriteLocker cookieLocker(this, cookie);
2584 
2585 	if (!cookie->IsValid())
2586 		RETURN_ERROR(B_BAD_VALUE);
2587 
2588 	cookie->Read(buffer, bufferSize, pos, bytesRead);
2589 
2590 	return B_OK;
2591 }
2592 
2593 
2594 status_t
2595 FUSEVolume::ReadAttrStat(void* _node, void* _cookie, struct stat* st)
2596 {
2597 	AttrCookie* cookie = (AttrCookie*)_cookie;
2598 
2599 	RWLockableWriteLocker cookieLocker(this, cookie);
2600 
2601 	if (!cookie->IsValid())
2602 		RETURN_ERROR(B_BAD_VALUE);
2603 
2604 	st->st_size = cookie->Size();
2605 	st->st_type = cookie->Type();
2606 
2607 	return B_OK;
2608 }
2609 
2610 
2611 // #pragma mark -
2612 
2613 
2614 ino_t
2615 FUSEVolume::_GenerateNodeID()
2616 {
2617 	ino_t id;
2618 	do {
2619 		id = fNextNodeID++;
2620 	} while (fNodes.Lookup(id) != NULL);
2621 
2622 	return id;
2623 }
2624 
2625 
2626 /*!	Gets the ID of the node the entry specified by \a dir and \a entryName
2627 	refers to. The ID is returned via \a _nodeID. The caller doesn't get a
2628 	reference to the node.
2629 */
2630 bool
2631 FUSEVolume::_GetNodeID(FUSENode* dir, const char* entryName, ino_t* _nodeID)
2632 {
2633 	while (true) {
2634 		AutoLocker<Locker> locker(fLock);
2635 
2636 		FUSENode* node;
2637 		status_t error = _InternalGetNode(dir, entryName, &node, locker);
2638 		if (error != B_OK)
2639 			return false;
2640 
2641 		if (node == NULL)
2642 			continue;
2643 
2644 		*_nodeID = node->id;
2645 		_PutNode(node);
2646 
2647 		return true;
2648 	}
2649 }
2650 
2651 
2652 /*!	Gets the node the entry specified by \a dir and \a entryName refers to. The
2653 	found node is returned via \a _node. The caller gets a reference to the node
2654 	as well as a vnode reference.
2655 */
2656 status_t
2657 FUSEVolume::_GetNode(FUSENode* dir, const char* entryName, FUSENode** _node)
2658 {
2659 	while (true) {
2660 		AutoLocker<Locker> locker(fLock);
2661 
2662 		FUSENode* node;
2663 		status_t error = _InternalGetNode(dir, entryName, &node, locker);
2664 		if (error != B_OK)
2665 			return error;
2666 
2667 		if (node == NULL)
2668 			continue;
2669 
2670 		ino_t nodeID = node->id;
2671 
2672 		locker.Unlock();
2673 
2674 		// get a reference for the caller
2675 		void* privateNode;
2676 		error = UserlandFS::KernelEmu::get_vnode(fID, nodeID, &privateNode);
2677 		if (error != B_OK)
2678 			RETURN_ERROR(error);
2679 
2680 		locker.Lock();
2681 
2682 		if (privateNode != node) {
2683 			// weird, the node changed!
2684 			ERROR(("FUSEVolume::_GetNode(): cookie for node %" B_PRId64
2685 				" changed: expected: %p, got: %p\n", nodeID, node,
2686 				privateNode));
2687 			UserlandFS::KernelEmu::put_vnode(fID, nodeID);
2688 			_PutNode(node);
2689 			continue;
2690 		}
2691 
2692 		// Put the node reference we got from _InternalGetNode. We've now got
2693 		// a reference from get_vnode().
2694 		_PutNode(node);
2695 
2696 		*_node = node;
2697 		return B_OK;
2698 	}
2699 }
2700 
2701 
2702 status_t
2703 FUSEVolume::_InternalGetNode(FUSENode* dir, const char* entryName,
2704 	FUSENode** _node, AutoLocker<Locker>& locker)
2705 {
2706 	// handle special cases
2707 	if (strcmp(entryName, ".") == 0) {
2708 		// same directory
2709 		if (!S_ISDIR(dir->type))
2710 			RETURN_ERROR(B_NOT_A_DIRECTORY);
2711 
2712 		dir->refCount++;
2713 		*_node = dir;
2714 		return B_OK;
2715 	}
2716 
2717 	if (strcmp(entryName, "..") == 0) {
2718 		// parent directory
2719 		if (!S_ISDIR(dir->type))
2720 			RETURN_ERROR(B_NOT_A_DIRECTORY);
2721 
2722 		FUSEEntry* entry = dir->entries.Head();
2723 		if (entry == NULL)
2724 			RETURN_ERROR(B_ENTRY_NOT_FOUND);
2725 
2726 		entry->parent->refCount++;
2727 		*_node = entry->parent;
2728 		return B_OK;
2729 	}
2730 
2731 	// lookup the entry in the table
2732 	FUSEEntry* entry = fEntries.Lookup(FUSEEntryRef(dir->id, entryName));
2733 	if (entry != NULL) {
2734 		entry->node->refCount++;
2735 		*_node = entry->node;
2736 		return B_OK;
2737 	}
2738 
2739 	int fuseError;
2740 	struct stat st;
2741 	if (fOps != NULL) {
2742 		fuseError = fuse_ll_lookup(fOps, dir->id, entryName, &st);
2743 	} else {
2744 		// construct a path for the entry
2745 		char path[B_PATH_NAME_LENGTH];
2746 		size_t pathLen = 0;
2747 		status_t error = _BuildPath(dir, entryName, path, pathLen);
2748 		if (error != B_OK)
2749 			return error;
2750 
2751 		locker.Unlock();
2752 
2753 		// stat the path
2754 		fuseError = fuse_fs_getattr(fFS, path, &st);
2755 	}
2756 	if (fuseError != 0)
2757 		return fuseError;
2758 
2759 	// lookup the entry in the table again
2760 	entry = fEntries.Lookup(FUSEEntryRef(dir->id, entryName));
2761 	if (entry != NULL) {
2762 		// check whether the node still matches
2763 		if (entry->node->id == st.st_ino) {
2764 			entry->node->refCount++;
2765 			*_node = entry->node;
2766 		} else {
2767 			// nope, something changed -- return a NULL node and let the caller
2768 			// call us again
2769 			*_node = NULL;
2770 		}
2771 
2772 		return B_OK;
2773 	}
2774 
2775 	// lookup the node in the table
2776 	FUSENode* node = NULL;
2777 	if (fUseNodeIDs)
2778 		node = fNodes.Lookup(st.st_ino);
2779 	else
2780 		st.st_ino = _GenerateNodeID();
2781 
2782 	if (node == NULL) {
2783 		// no node yet -- create one
2784 		node = new(std::nothrow) FUSENode(st.st_ino, st.st_mode & S_IFMT);
2785 		if (node == NULL)
2786 			RETURN_ERROR(B_NO_MEMORY);
2787 
2788 		fNodes.Insert(node);
2789 	} else {
2790 		// get a node reference for the entry
2791 		node->refCount++;
2792 	}
2793 
2794 	// create the entry
2795 	entry = FUSEEntry::Create(dir, entryName, node);
2796 	if (entry == NULL) {
2797 		_PutNode(node);
2798 		RETURN_ERROR(B_NO_MEMORY);
2799 	}
2800 
2801 	dir->refCount++;
2802 		// dir reference for the entry
2803 
2804 	fEntries.Insert(entry);
2805 	node->entries.Add(entry);
2806 
2807 	locker.Unlock();
2808 
2809 	// get a reference for the caller
2810 	node->refCount++;
2811 
2812 	*_node = node;
2813 	return B_OK;
2814 }
2815 
2816 
2817 void
2818 FUSEVolume::_PutNode(FUSENode* node)
2819 {
2820 	if (--node->refCount == 0) {
2821 		fNodes.Remove(node);
2822 		if (node->cacheCount != 0)
2823 			UserlandFS::KernelEmu::file_cache_delete(GetID(), node->id);
2824 		delete node;
2825 	}
2826 }
2827 
2828 
2829 void
2830 FUSEVolume::_PutNodes(FUSENode* const* nodes, int32 count)
2831 {
2832 	for (int32 i = 0; i < count; i++)
2833 		_PutNode(nodes[i]);
2834 }
2835 
2836 
2837 /*!	Volume must be locked. The entry's directory must be write locked.
2838  */
2839 void
2840 FUSEVolume::_RemoveEntry(FUSEEntry* entry)
2841 {
2842 	fEntries.Remove(entry);
2843 	entry->node->entries.Remove(entry);
2844 	_PutNode(entry->node);
2845 	_PutNode(entry->parent);
2846 	delete entry;
2847 }
2848 
2849 
2850 /*!	Volume must be locked. The directory must be write locked.
2851  */
2852 status_t
2853 FUSEVolume::_RemoveEntry(FUSENode* dir, const char* name)
2854 {
2855 	FUSEEntry* entry = fEntries.Lookup(FUSEEntryRef(dir->id, name));
2856 	if (entry == NULL)
2857 		return B_ENTRY_NOT_FOUND;
2858 
2859 	_RemoveEntry(entry);
2860 	return B_OK;
2861 }
2862 
2863 
2864 /*!	Volume must be locked. The directories must be write locked.
2865  */
2866 status_t
2867 FUSEVolume::_RenameEntry(FUSENode* oldDir, const char* oldName,
2868 	FUSENode* newDir, const char* newName)
2869 {
2870 	FUSEEntry* entry = fEntries.Lookup(FUSEEntryRef(oldDir->id, oldName));
2871 	if (entry == NULL)
2872 		return B_ENTRY_NOT_FOUND;
2873 
2874 	// get a node reference for the new entry
2875 	FUSENode* node = entry->node;
2876 	node->refCount++;
2877 
2878 	// remove the old entry
2879 	_RemoveEntry(entry);
2880 
2881 	// make sure there's no entry in our way
2882 	_RemoveEntry(newDir, newName);
2883 
2884 	// create a new entry
2885 	entry = FUSEEntry::Create(newDir, newName, node);
2886 	if (entry == NULL) {
2887 		_PutNode(node);
2888 		RETURN_ERROR(B_NO_MEMORY);
2889 	}
2890 
2891 	newDir->refCount++;
2892 		// dir reference for the entry
2893 
2894 	fEntries.Insert(entry);
2895 	node->entries.Add(entry);
2896 
2897 	return B_OK;
2898 }
2899 
2900 
2901 /*!	Locks the given node and all of its ancestors up to the root. The given
2902 	node is write-locked, if \a writeLock is \c true, read-locked otherwise. All
2903 	ancestors are always read-locked in either case.
2904 
2905 	If \a lockParent is \c true, the given node itself is ignored, but locking
2906 	starts with the parent node of the given node (\a writeLock applies to the
2907 	parent node then).
2908 
2909 	If the method fails, none of the nodes is locked.
2910 
2911 	The volume lock must not be held.
2912 */
2913 status_t
2914 FUSEVolume::_LockNodeChain(FUSENode* node, bool lockParent, bool writeLock)
2915 {
2916 	AutoLocker<Locker> locker(fLock);
2917 
2918 	FUSENode* originalNode = node;
2919 
2920 	if (lockParent && node != NULL)
2921 		node = node->Parent();
2922 
2923 	if (node == NULL)
2924 		RETURN_ERROR(B_ENTRY_NOT_FOUND);
2925 
2926 	LockIterator iterator(this, node, writeLock, NULL);
2927 
2928 	bool done;
2929 	do {
2930 		bool volumeUnlocked;
2931 		status_t error = iterator.LockNext(&done, &volumeUnlocked);
2932 		if (error != B_OK)
2933 			RETURN_ERROR(error);
2934 
2935 		if (volumeUnlocked) {
2936 			// check whether we're still locking the right node
2937 			if (lockParent && originalNode->Parent() != node) {
2938 				// We don't -- unlock everything and try again.
2939 				node = originalNode->Parent();
2940 				iterator.SetTo(this, node, writeLock, NULL);
2941 			}
2942 		}
2943 	} while (!done);
2944 
2945 	// Fail, if we couldn't lock all nodes up to the root.
2946 	if (iterator.lastLockedNode != fRootNode)
2947 		RETURN_ERROR(B_ENTRY_NOT_FOUND);
2948 
2949 	iterator.Detach();
2950 	return B_OK;
2951 }
2952 
2953 
2954 void
2955 FUSEVolume::_UnlockNodeChain(FUSENode* node, bool parent, bool writeLock)
2956 {
2957 	AutoLocker<Locker> locker(fLock);
2958 
2959 	if (parent && node != NULL)
2960 		node = node->Parent();
2961 
2962 	_UnlockNodeChainInternal(node, writeLock, NULL, NULL);
2963 }
2964 
2965 
2966 /*!	Unlocks all nodes from \a node up to (and including) \a stopNode (if
2967 	\c NULL, it is ignored). If \a stopBeforeNode is given, the method stops
2968 	before unlocking that node.
2969 	The volume lock must be held.
2970  */
2971 void
2972 FUSEVolume::_UnlockNodeChainInternal(FUSENode* node, bool writeLock,
2973 	FUSENode* stopNode, FUSENode* stopBeforeNode)
2974 {
2975 	FUSENode* originalNode = node;
2976 
2977 	while (node != NULL && node != stopBeforeNode) {
2978 		FUSENode* parent = node->Parent();
2979 
2980 		fLockManager.GenericUnlock(node == originalNode && writeLock, node);
2981 		_PutNode(node);
2982 
2983 		if (node == stopNode || parent == node)
2984 			break;
2985 
2986 		node = parent;
2987 	}
2988 }
2989 
2990 
2991 status_t
2992 FUSEVolume::_LockNodeChains(FUSENode* node1, bool lockParent1, bool writeLock1,
2993 	FUSENode* node2, bool lockParent2, bool writeLock2)
2994 {
2995 	// Since in this case locking is more complicated, we use a helper method.
2996 	// It does the main work, but simply returns telling us to retry when the
2997 	// node hierarchy changes.
2998 	bool retry;
2999 	do {
3000 		status_t error = _LockNodeChainsInternal(node1, lockParent1, writeLock1,
3001 			node2, lockParent2, writeLock2, &retry);
3002 		if (error != B_OK)
3003 			return error;
3004 	} while (retry);
3005 
3006 	return B_OK;
3007 }
3008 
3009 
3010 status_t
3011 FUSEVolume::_LockNodeChainsInternal(FUSENode* node1, bool lockParent1,
3012 	bool writeLock1, FUSENode* node2, bool lockParent2, bool writeLock2,
3013 	bool* _retry)
3014 {
3015 	// Locking order:
3016 	// * A child of a node has to be locked before its parent.
3017 	// * Sibling nodes have to be locked in ascending node ID order.
3018 	//
3019 	// This implies the following locking algorithm:
3020 	// * We find the closest common ancestor of the two given nodes (might even
3021 	//   be one of the given nodes).
3022 	// * We lock all ancestors on one branch (the one with the lower common
3023 	//   ancestor child node ID), but not including the common ancestor.
3024 	// * We lock all ancestors on the other branch, not including the common
3025 	//   ancestor.
3026 	// * We lock the common ancestor and all of its ancestors up to the root
3027 	//   node.
3028 	//
3029 	// When the hierarchy changes while we're waiting for a lock, we recheck the
3030 	// conditions and in doubt have to be restarted.
3031 
3032 	AutoLocker<Locker> locker(fLock);
3033 
3034 	FUSENode* originalNode1 = node1;
3035 	FUSENode* originalNode2 = node2;
3036 
3037 	if (lockParent1 && node1 != NULL)
3038 		node1 = node1->Parent();
3039 
3040 	if (lockParent2 && node2 != NULL)
3041 		node2 = node2->Parent();
3042 
3043 	if (node1 == NULL || node2 == NULL)
3044 		RETURN_ERROR(B_ENTRY_NOT_FOUND);
3045 
3046 	// find the first common ancestor
3047 	FUSENode* commonAncestor;
3048 	bool inverseLockingOrder;
3049 	if (!_FindCommonAncestor(node1, node2, &commonAncestor,
3050 			&inverseLockingOrder)) {
3051 		RETURN_ERROR(B_ENTRY_NOT_FOUND);
3052 	}
3053 
3054 	// lock the both node chains up to (but not including) the common ancestor
3055 	LockIterator iterator1(this, node1, writeLock1, commonAncestor);
3056 	LockIterator iterator2(this, node2, writeLock2, commonAncestor);
3057 
3058 	for (int i = 0; i < 2; i++) {
3059 		LockIterator& iterator = (i == 0) != inverseLockingOrder
3060 			? iterator1 : iterator2;
3061 
3062 		// If the node is the common ancestor, don't enter the "do" loop, since
3063 		// we don't have to lock anything here.
3064 		if (iterator.firstNode == commonAncestor)
3065 			continue;
3066 
3067 		bool done;
3068 		do {
3069 			bool volumeUnlocked;
3070 			status_t error = iterator.LockNext(&done, &volumeUnlocked);
3071 			if (error != B_OK)
3072 				RETURN_ERROR(error);
3073 
3074 			if (volumeUnlocked) {
3075 				// check whether we're still locking the right nodes
3076 				if ((lockParent1 && originalNode1->Parent() != node1)
3077 					|| (lockParent2 && originalNode2->Parent() != node2)) {
3078 					// We don't -- unlock everything and retry.
3079 					*_retry = true;
3080 					return B_OK;
3081 				}
3082 
3083 				// also recheck the common ancestor
3084 				FUSENode* newCommonParent;
3085 				bool newInverseLockingOrder;
3086 				if (!_FindCommonAncestor(node1, node2, &newCommonParent,
3087 						&newInverseLockingOrder)) {
3088 					RETURN_ERROR(B_ENTRY_NOT_FOUND);
3089 				}
3090 
3091 				if (newCommonParent != commonAncestor
3092 					|| inverseLockingOrder != newInverseLockingOrder) {
3093 					// Something changed -- unlock everything and retry.
3094 					*_retry = true;
3095 					return B_OK;
3096 				}
3097 			}
3098 		} while (!done);
3099 	}
3100 
3101 	// Continue locking from the common ancestor to the root. If one of the
3102 	// given nodes is the common ancestor and shall be write locked, we need to
3103 	// use the respective iterator.
3104 	LockIterator& iterator = node2 == commonAncestor && writeLock2
3105 		? iterator2 : iterator1;
3106 	iterator.SetStopBeforeNode(NULL);
3107 
3108 	bool done;
3109 	do {
3110 		bool volumeUnlocked;
3111 		status_t error = iterator.LockNext(&done, &volumeUnlocked);
3112 		if (error != B_OK)
3113 			RETURN_ERROR(error);
3114 
3115 		if (volumeUnlocked) {
3116 			// check whether we're still locking the right nodes
3117 			if ((lockParent1 && originalNode1->Parent() != node1)
3118 				|| (lockParent2 && originalNode2->Parent() != node2)) {
3119 				// We don't -- unlock everything and retry.
3120 				*_retry = true;
3121 				return B_OK;
3122 			}
3123 
3124 			// Also recheck the common ancestor, if we have just locked it.
3125 			// Otherwise we can just continue to lock, since nothing below the
3126 			// previously locked node can have changed.
3127 			if (iterator.lastLockedNode == commonAncestor) {
3128 				FUSENode* newCommonParent;
3129 				bool newInverseLockingOrder;
3130 				if (!_FindCommonAncestor(node1, node2, &newCommonParent,
3131 						&newInverseLockingOrder)) {
3132 					RETURN_ERROR(B_ENTRY_NOT_FOUND);
3133 				}
3134 
3135 				if (newCommonParent != commonAncestor
3136 					|| inverseLockingOrder != newInverseLockingOrder) {
3137 					// Something changed -- unlock everything and retry.
3138 					*_retry = true;
3139 					return B_OK;
3140 				}
3141 			}
3142 		}
3143 	} while (!done);
3144 
3145 	// Fail, if we couldn't lock all nodes up to the root.
3146 	if (iterator.lastLockedNode != fRootNode)
3147 		RETURN_ERROR(B_ENTRY_NOT_FOUND);
3148 
3149 	// everything went fine
3150 	iterator1.Detach();
3151 	iterator2.Detach();
3152 
3153 	*_retry = false;
3154 	return B_OK;
3155 }
3156 
3157 
3158 void
3159 FUSEVolume::_UnlockNodeChains(FUSENode* node1, bool lockParent1,
3160 	bool writeLock1, FUSENode* node2, bool lockParent2, bool writeLock2)
3161 {
3162 	AutoLocker<Locker> locker(fLock);
3163 
3164 	if (lockParent1 && node1 != NULL)
3165 		node1 = node1->Parent();
3166 
3167 	if (lockParent2 && node2 != NULL)
3168 		node2 = node2->Parent();
3169 
3170 	if (node1 == NULL || node2 == NULL)
3171 		return;
3172 
3173 	// find the common ancestor
3174 	FUSENode* commonAncestor;
3175 	bool inverseLockingOrder;
3176 	if (!_FindCommonAncestor(node1, node2, &commonAncestor,
3177 			&inverseLockingOrder)) {
3178 		return;
3179 	}
3180 
3181 	// Unlock one branch up to the common ancestor and then the complete other
3182 	// branch up to the root. If one of the given nodes is the common ancestor,
3183 	// we need to make sure, we write-unlock it, if requested.
3184 	if (node2 == commonAncestor && writeLock2) {
3185 		_UnlockNodeChainInternal(node1, writeLock1, NULL, commonAncestor);
3186 		_UnlockNodeChainInternal(node2, writeLock2, NULL, NULL);
3187 	} else {
3188 		_UnlockNodeChainInternal(node2, writeLock2, NULL, commonAncestor);
3189 		_UnlockNodeChainInternal(node1, writeLock1, NULL, NULL);
3190 	}
3191 }
3192 
3193 
3194 bool
3195 FUSEVolume::_FindCommonAncestor(FUSENode* node1, FUSENode* node2,
3196 	FUSENode** _commonAncestor, bool* _inverseLockingOrder)
3197 {
3198 	// handle trivial special case -- both nodes are the same
3199 	if (node1 == node2) {
3200 		*_commonAncestor = node1;
3201 		*_inverseLockingOrder = false;
3202 		return true;
3203 	}
3204 
3205 	// get the ancestors of both nodes
3206 	FUSENode* ancestors1[kMaxNodeTreeDepth];
3207 	FUSENode* ancestors2[kMaxNodeTreeDepth];
3208 	uint32 count1;
3209 	uint32 count2;
3210 
3211 	if (!_GetNodeAncestors(node1, ancestors1, &count1)
3212 		|| !_GetNodeAncestors(node2, ancestors2, &count2)) {
3213 		return false;
3214 	}
3215 
3216 	// find the first ancestor not common to both nodes
3217 	uint32 index = 0;
3218 	for (; index < count1 && index < count2; index++) {
3219 		FUSENode* ancestor1 = ancestors1[count1 - index - 1];
3220 		FUSENode* ancestor2 = ancestors2[count2 - index - 1];
3221 		if (ancestor1 != ancestor2) {
3222 			*_commonAncestor = ancestors1[count1 - index];
3223 			*_inverseLockingOrder = ancestor1->id > ancestor2->id;
3224 			return true;
3225 		}
3226 	}
3227 
3228 	// one node is an ancestor of the other
3229 	*_commonAncestor = ancestors1[count1 - index];
3230 	*_inverseLockingOrder = index == count1;
3231 	return true;
3232 }
3233 
3234 
3235 bool
3236 FUSEVolume::_GetNodeAncestors(FUSENode* node, FUSENode** ancestors,
3237 	uint32* _count)
3238 {
3239 	uint32 count = 0;
3240 	while (node != NULL && count < kMaxNodeTreeDepth) {
3241 		ancestors[count++] = node;
3242 
3243 		if (node == fRootNode) {
3244 			*_count = count;
3245 			return true;
3246 		}
3247 
3248 		node = node->Parent();
3249 	}
3250 
3251 	// Either the node is not in the tree or we hit the array limit.
3252 	return false;
3253 }
3254 
3255 
3256 status_t
3257 FUSEVolume::_BuildPath(FUSENode* dir, const char* entryName, char* path,
3258 	size_t& pathLen)
3259 {
3260 	// get the directory path
3261 	status_t error = _BuildPath(dir, path, pathLen);
3262 	if (error != B_OK)
3263 		return error;
3264 
3265 	if (path[pathLen - 1] != '/') {
3266 		path[pathLen++] = '/';
3267 		if (pathLen == B_PATH_NAME_LENGTH)
3268 			RETURN_ERROR(B_NAME_TOO_LONG);
3269 	}
3270 
3271 	// append the entry name
3272 	size_t len = strlen(entryName);
3273 	if (pathLen + len >= B_PATH_NAME_LENGTH)
3274 		RETURN_ERROR(B_NAME_TOO_LONG);
3275 
3276 	memcpy(path + pathLen, entryName, len + 1);
3277 	pathLen += len;
3278 
3279 	return B_OK;
3280 }
3281 
3282 
3283 status_t
3284 FUSEVolume::_BuildPath(FUSENode* node, char* path, size_t& pathLen)
3285 {
3286 	if (node == fRootNode) {
3287 		// we hit the root
3288 		strcpy(path, "/");
3289 		pathLen = 1;
3290 		return B_OK;
3291 	}
3292 
3293 	// get an entry for the node and get its path
3294 	FUSEEntry* entry = node->entries.Head();
3295 	if (entry == NULL)
3296 		RETURN_ERROR(B_ENTRY_NOT_FOUND);
3297 
3298 	return _BuildPath(entry->parent, entry->name, path, pathLen);
3299 }
3300 
3301 
3302 /*static*/ int
3303 FUSEVolume::_AddReadDirEntryLowLevel(void* _buffer, char* buf, size_t bufsize, const char* name,
3304 	const struct stat* st, off_t offset)
3305 {
3306 	ReadDirBuffer* buffer = (ReadDirBuffer*)_buffer;
3307 
3308 	ino_t nodeID = st != NULL ? st->st_ino : 0;
3309 	int type = st != NULL ? st->st_mode & S_IFMT : 0;
3310 	return buffer->volume->_AddReadDirEntryLowLevel(buffer, buf, bufsize, name, type, nodeID, offset);
3311 }
3312 
3313 
3314 /*static*/ int
3315 FUSEVolume::_AddReadDirEntry(void* _buffer, const char* name,
3316 	const struct stat* st, off_t offset)
3317 {
3318 	ReadDirBuffer* buffer = (ReadDirBuffer*)_buffer;
3319 
3320 	ino_t nodeID = st != NULL ? st->st_ino : 0;
3321 	int type = st != NULL ? st->st_mode & S_IFMT : 0;
3322 	return buffer->volume->_AddReadDirEntry(buffer, name, type, nodeID, offset);
3323 }
3324 
3325 
3326 /*static*/ int
3327 FUSEVolume::_AddReadDirEntryGetDir(fuse_dirh_t handle, const char* name,
3328 	int type, ino_t nodeID)
3329 {
3330 	ReadDirBuffer* buffer = (ReadDirBuffer*)handle;
3331 	return buffer->volume->_AddReadDirEntry(buffer, name, type << 12, nodeID, 0);
3332 }
3333 
3334 
3335 int
3336 FUSEVolume::_AddReadDirEntryLowLevel(ReadDirBuffer* buffer, char* buf, size_t bufsize, const char* name,
3337 	int type, ino_t nodeID, off_t offset)
3338 {
3339 	PRINT(("FUSEVolume::_AddReadDirEntryLowLevel(%p, \"%s\", %#x, %" B_PRId64 ", %"
3340 		B_PRId64 "\n", buffer, name, type, nodeID, offset));
3341 
3342 	AutoLocker<Locker> locker(fLock);
3343 
3344 	size_t entryLen = 0;
3345 
3346 	// create a node and an entry, if necessary
3347 	ino_t dirID = buffer->directory->id;
3348 	FUSEEntry* entry;
3349 	if (strcmp(name, ".") == 0) {
3350 		// current dir entry
3351 		nodeID = dirID;
3352 		type = S_IFDIR;
3353 	} else if (strcmp(name, "..") == 0) {
3354 		// parent dir entry
3355 		FUSEEntry* parentEntry = buffer->directory->entries.Head();
3356 		if (parentEntry == NULL) {
3357 			ERROR(("FUSEVolume::_AddReadDirEntry(): dir %" B_PRId64
3358 				" has no entry!\n", dirID));
3359 			return 0;
3360 		}
3361 		nodeID = parentEntry->parent->id;
3362 		type = S_IFDIR;
3363 	} else if ((entry = fEntries.Lookup(FUSEEntryRef(dirID, name))) == NULL) {
3364 		// get the node
3365 		FUSENode* node = NULL;
3366 		if (fUseNodeIDs)
3367 			node = fNodes.Lookup(nodeID);
3368 		else
3369 			nodeID = _GenerateNodeID();
3370 
3371 		if (node == NULL) {
3372 			// no node yet -- create one
3373 
3374 			// If we don't have a valid type, we need to stat the node first.
3375 			if (type == 0) {
3376 				struct stat st;
3377 				int fuseError;
3378 				if (fOps != NULL) {
3379 					fuseError = fuse_ll_getattr(fOps, node->id, &st);
3380 				} else {
3381 					char path[B_PATH_NAME_LENGTH];
3382 					size_t pathLen;
3383 					status_t error = _BuildPath(buffer->directory, name, path,
3384 							pathLen);
3385 					if (error != B_OK) {
3386 						buffer->error = error;
3387 						return 0;
3388 					}
3389 
3390 					locker.Unlock();
3391 
3392 					// stat the path
3393 					fuseError = fuse_fs_getattr(fFS, path, &st);
3394 				}
3395 
3396 				locker.Lock();
3397 
3398 				if (fuseError != 0) {
3399 					buffer->error = fuseError;
3400 					return 0;
3401 				}
3402 
3403 				type = st.st_mode & S_IFMT;
3404 			}
3405 
3406 			node = new(std::nothrow) FUSENode(nodeID, type);
3407 			if (node == NULL) {
3408 				buffer->error = B_NO_MEMORY;
3409 				return 1;
3410 			}
3411 			PRINT(("  -> create node: %p, id: %" B_PRId64 "\n", node, nodeID));
3412 
3413 			fNodes.Insert(node);
3414 		} else {
3415 			// get a node reference for the entry
3416 			node->refCount++;
3417 		}
3418 
3419 		// create the entry
3420 		entry = FUSEEntry::Create(buffer->directory, name, node);
3421 		if (entry == NULL) {
3422 			_PutNode(node);
3423 			buffer->error = B_NO_MEMORY;
3424 			return 1;
3425 		}
3426 
3427 		buffer->directory->refCount++;
3428 			// dir reference for the entry
3429 
3430 		fEntries.Insert(entry);
3431 		node->entries.Add(entry);
3432 	} else {
3433 		// TODO: Check whether the node's ID matches the one we got (if any)!
3434 		nodeID = entry->node->id;
3435 		type = entry->node->type;
3436 	}
3437 
3438 	// fill in the dirent
3439 	dirent* dirEntry = (dirent*)(buf);
3440 	dirEntry->d_dev = fID;
3441 	dirEntry->d_ino = nodeID;
3442 	strcpy(dirEntry->d_name, name);
3443 
3444 	// align the entry length, so the next dirent will be aligned
3445 	entryLen = offsetof(struct dirent, d_name) + strlen(name) + 1;
3446 	entryLen = ROUNDUP(entryLen, 8);
3447 
3448 	dirEntry->d_reclen = entryLen;
3449 
3450 	// update the buffer
3451 	buffer->usedSize += entryLen;
3452 
3453 	return 0;
3454 }
3455 
3456 
3457 int
3458 FUSEVolume::_AddReadDirEntry(ReadDirBuffer* buffer, const char* name,
3459 	int type, ino_t nodeID, off_t offset)
3460 {
3461 	PRINT(("FUSEVolume::_AddReadDirEntry(%p, \"%s\", %#x, %" B_PRId64 ", %"
3462 		B_PRId64 "\n", buffer, name, type, nodeID, offset));
3463 
3464 	AutoLocker<Locker> locker(fLock);
3465 
3466 	size_t entryLen = 0;
3467 	if (offset != 0) {
3468 		// does the caller want more entries?
3469 		if (buffer->entriesRead == buffer->maxEntries)
3470 			return 1;
3471 
3472 		// compute the entry length and check whether the entry still fits
3473 		entryLen = offsetof(struct dirent, d_name) + strlen(name) + 1;
3474 		if (buffer->usedSize + entryLen > buffer->bufferSize)
3475 			return 1;
3476 	}
3477 
3478 	// create a node and an entry, if necessary
3479 	ino_t dirID = buffer->directory->id;
3480 	FUSEEntry* entry;
3481 	if (strcmp(name, ".") == 0) {
3482 		// current dir entry
3483 		nodeID = dirID;
3484 		type = S_IFDIR;
3485 	} else if (strcmp(name, "..") == 0) {
3486 		// parent dir entry
3487 		FUSEEntry* parentEntry = buffer->directory->entries.Head();
3488 		if (parentEntry == NULL) {
3489 			ERROR(("FUSEVolume::_AddReadDirEntry(): dir %" B_PRId64
3490 				" has no entry!\n", dirID));
3491 			return 0;
3492 		}
3493 		nodeID = parentEntry->parent->id;
3494 		type = S_IFDIR;
3495 	} else if ((entry = fEntries.Lookup(FUSEEntryRef(dirID, name))) == NULL) {
3496 		// get the node
3497 		FUSENode* node = NULL;
3498 		if (fUseNodeIDs)
3499 			node = fNodes.Lookup(nodeID);
3500 		else
3501 			nodeID = _GenerateNodeID();
3502 
3503 		if (node == NULL) {
3504 			// no node yet -- create one
3505 
3506 			// If we don't have a valid type, we need to stat the node first.
3507 			if (type == 0) {
3508 				struct stat st;
3509 				int fuseError;
3510 				if (fOps != NULL) {
3511 					fuseError = fuse_ll_getattr(fOps, node->id, &st);
3512 				} else {
3513 					char path[B_PATH_NAME_LENGTH];
3514 					size_t pathLen;
3515 					status_t error = _BuildPath(buffer->directory, name, path,
3516 							pathLen);
3517 					if (error != B_OK) {
3518 						buffer->error = error;
3519 						return 0;
3520 					}
3521 
3522 					locker.Unlock();
3523 
3524 					// stat the path
3525 					fuseError = fuse_fs_getattr(fFS, path, &st);
3526 				}
3527 
3528 				locker.Lock();
3529 
3530 				if (fuseError != 0) {
3531 					buffer->error = fuseError;
3532 					return 0;
3533 				}
3534 
3535 				type = st.st_mode & S_IFMT;
3536 			}
3537 
3538 			node = new(std::nothrow) FUSENode(nodeID, type);
3539 			if (node == NULL) {
3540 				buffer->error = B_NO_MEMORY;
3541 				return 1;
3542 			}
3543 			PRINT(("  -> create node: %p, id: %" B_PRId64 "\n", node, nodeID));
3544 
3545 			fNodes.Insert(node);
3546 		} else {
3547 			// get a node reference for the entry
3548 			node->refCount++;
3549 		}
3550 
3551 		// create the entry
3552 		entry = FUSEEntry::Create(buffer->directory, name, node);
3553 		if (entry == NULL) {
3554 			_PutNode(node);
3555 			buffer->error = B_NO_MEMORY;
3556 			return 1;
3557 		}
3558 
3559 		buffer->directory->refCount++;
3560 			// dir reference for the entry
3561 
3562 		fEntries.Insert(entry);
3563 		node->entries.Add(entry);
3564 	} else {
3565 		// TODO: Check whether the node's ID matches the one we got (if any)!
3566 		nodeID = entry->node->id;
3567 		type = entry->node->type;
3568 	}
3569 
3570 	if (offset == 0) {
3571 		// cache the entry
3572 		if (buffer->cookie->entryCache == NULL) {
3573 			// no cache yet -- create it
3574 			buffer->cookie->entryCache = new(std::nothrow) DirEntryCache;
3575 			if (buffer->cookie->entryCache == NULL) {
3576 				buffer->error = B_NO_MEMORY;
3577 				return 1;
3578 			}
3579 		}
3580 
3581 		status_t error = buffer->cookie->entryCache->AddEntry(nodeID, name);
3582 		if (error != B_OK) {
3583 			buffer->error = error;
3584 			return 1;
3585 		}
3586 	} else {
3587 		// fill in the dirent
3588 		dirent* dirEntry = (dirent*)((uint8*)buffer->buffer + buffer->usedSize);
3589 		dirEntry->d_dev = fID;
3590 		dirEntry->d_ino = nodeID;
3591 		strcpy(dirEntry->d_name, name);
3592 
3593 		if (buffer->entriesRead + 1 < buffer->maxEntries) {
3594 			// align the entry length, so the next dirent will be aligned
3595 			entryLen = ROUNDUP(entryLen, 8);
3596 			entryLen = std::min(entryLen,
3597 				buffer->bufferSize - buffer->usedSize);
3598 		}
3599 
3600 		dirEntry->d_reclen = entryLen;
3601 
3602 		// update the buffer
3603 		buffer->usedSize += entryLen;
3604 		buffer->entriesRead++;
3605 		buffer->cookie->currentEntryOffset = offset;
3606 	}
3607 
3608 	return 0;
3609 }
3610 
3611 
3612 status_t
3613 FUSEVolume::_InternalIO(FUSENode* node, FileCookie* cookie, const char* path,
3614 	off_t pos, char* buffer, size_t& length, bool write)
3615 {
3616 	PRINT(("FUSEVolume::_InternalIO(%p, %p, %s, %" B_PRIdOFF ", %p, %" B_PRIuSIZE ", %d)\n",
3617 		node, cookie, path, pos, buffer, length, write));
3618 
3619 	int fuseError;
3620 	if (fOps != NULL) {
3621 		if (write)
3622 			fuseError = fuse_ll_write(fOps, node->id, buffer, length, pos, cookie);
3623 		else
3624 			fuseError = fuse_ll_read(fOps, node->id, buffer, length, pos, cookie);
3625 	} else {
3626 		char pathBuf[B_PATH_NAME_LENGTH];
3627 
3628 		if (path == NULL) {
3629 			AutoLocker<Locker> locker(fLock);
3630 
3631 			size_t pathLen;
3632 			status_t error = _BuildPath(node, pathBuf, pathLen);
3633 			if (error != B_OK)
3634 				RETURN_ERROR(error);
3635 
3636 			locker.Unlock();
3637 
3638 			path = pathBuf;
3639 		}
3640 
3641 		if (write)
3642 			fuseError = fuse_fs_write(fFS, path, buffer, length, pos, cookie);
3643 		else
3644 			fuseError = fuse_fs_read(fFS, path, buffer, length, pos, cookie);
3645 	}
3646 
3647 	if (fuseError < 0)
3648 		return fuseError;
3649 
3650 	length = fuseError;
3651 	return B_OK;
3652 }
3653