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