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