xref: /haiku/src/add-ons/kernel/file_systems/userlandfs/server/fuse/FUSEVolume.cpp (revision a5061ecec55353a5f394759473f1fd6df04890da)
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 		delete cookie;
2216 		return bytesRead;
2217 	}
2218 
2219 	*_cookie = cookie;
2220 
2221 	return B_OK;
2222 }
2223 
2224 
2225 status_t
2226 FUSEVolume::CloseAttr(void* _node, void* _cookie)
2227 {
2228 	return B_OK;
2229 }
2230 
2231 
2232 status_t
2233 FUSEVolume::FreeAttrCookie(void* _node, void* _cookie)
2234 {
2235 	delete (AttrCookie*)_cookie;
2236 	return B_OK;
2237 }
2238 
2239 
2240 status_t
2241 FUSEVolume::ReadAttr(void* _node, void* _cookie, off_t pos, void* buffer,
2242 	size_t bufferSize, size_t* bytesRead)
2243 {
2244 	AttrCookie* cookie = (AttrCookie*)_cookie;
2245 
2246 	RWLockableWriteLocker cookieLocker(this, cookie);
2247 
2248 	if (!cookie->IsValid())
2249 		RETURN_ERROR(B_BAD_VALUE);
2250 
2251 	cookie->Read(buffer, bufferSize, pos, bytesRead);
2252 
2253 	return B_OK;
2254 }
2255 
2256 
2257 status_t
2258 FUSEVolume::ReadAttrStat(void* _node, void* _cookie, struct stat* st)
2259 {
2260 	AttrCookie* cookie = (AttrCookie*)_cookie;
2261 
2262 	RWLockableWriteLocker cookieLocker(this, cookie);
2263 
2264 	if (!cookie->IsValid())
2265 		RETURN_ERROR(B_BAD_VALUE);
2266 
2267 	st->st_size = cookie->Size();
2268 	st->st_type = cookie->Type();
2269 
2270 	return B_OK;
2271 }
2272 
2273 
2274 // #pragma mark -
2275 
2276 
2277 ino_t
2278 FUSEVolume::_GenerateNodeID()
2279 {
2280 	ino_t id;
2281 	do {
2282 		id = fNextNodeID++;
2283 	} while (fNodes.Lookup(id) != NULL);
2284 
2285 	return id;
2286 }
2287 
2288 
2289 /*!	Gets the ID of the node the entry specified by \a dir and \a entryName
2290 	refers to. The ID is returned via \a _nodeID. The caller doesn't get a
2291 	reference to the node.
2292 */
2293 bool
2294 FUSEVolume::_GetNodeID(FUSENode* dir, const char* entryName, ino_t* _nodeID)
2295 {
2296 	while (true) {
2297 		AutoLocker<Locker> locker(fLock);
2298 
2299 		FUSENode* node;
2300 		status_t error = _InternalGetNode(dir, entryName, &node, locker);
2301 		if (error != B_OK)
2302 			return false;
2303 
2304 		if (node == NULL)
2305 			continue;
2306 
2307 		*_nodeID = node->id;
2308 		_PutNode(node);
2309 
2310 		return true;
2311 	}
2312 }
2313 
2314 
2315 /*!	Gets the node the entry specified by \a dir and \a entryName refers to. The
2316 	found node is returned via \a _node. The caller gets a reference to the node
2317 	as well as a vnode reference.
2318 */
2319 status_t
2320 FUSEVolume::_GetNode(FUSENode* dir, const char* entryName, FUSENode** _node)
2321 {
2322 	while (true) {
2323 		AutoLocker<Locker> locker(fLock);
2324 
2325 		FUSENode* node;
2326 		status_t error = _InternalGetNode(dir, entryName, &node, locker);
2327 		if (error != B_OK)
2328 			return error;
2329 
2330 		if (node == NULL)
2331 			continue;
2332 
2333 		ino_t nodeID = node->id;
2334 
2335 		locker.Unlock();
2336 
2337 		// get a reference for the caller
2338 		void* privateNode;
2339 		error = UserlandFS::KernelEmu::get_vnode(fID, nodeID, &privateNode);
2340 		if (error != B_OK)
2341 			RETURN_ERROR(error);
2342 
2343 		locker.Lock();
2344 
2345 		if (privateNode != node) {
2346 			// weird, the node changed!
2347 			ERROR(("FUSEVolume::_GetNode(): cookie for node %" B_PRId64
2348 				" changed: expected: %p, got: %p\n", nodeID, node,
2349 				privateNode));
2350 			UserlandFS::KernelEmu::put_vnode(fID, nodeID);
2351 			_PutNode(node);
2352 			continue;
2353 		}
2354 
2355 		// Put the node reference we got from _InternalGetNode. We've now got
2356 		// a reference from get_vnode().
2357 		_PutNode(node);
2358 
2359 		*_node = node;
2360 		return B_OK;
2361 	}
2362 }
2363 
2364 
2365 status_t
2366 FUSEVolume::_InternalGetNode(FUSENode* dir, const char* entryName,
2367 	FUSENode** _node, AutoLocker<Locker>& locker)
2368 {
2369 	// handle special cases
2370 	if (strcmp(entryName, ".") == 0) {
2371 		// same directory
2372 		if (!S_ISDIR(dir->type))
2373 			RETURN_ERROR(B_NOT_A_DIRECTORY);
2374 
2375 		dir->refCount++;
2376 		*_node = dir;
2377 		return B_OK;
2378 	}
2379 
2380 	if (strcmp(entryName, "..") == 0) {
2381 		// parent directory
2382 		if (!S_ISDIR(dir->type))
2383 			RETURN_ERROR(B_NOT_A_DIRECTORY);
2384 
2385 		FUSEEntry* entry = dir->entries.Head();
2386 		if (entry == NULL)
2387 			RETURN_ERROR(B_ENTRY_NOT_FOUND);
2388 
2389 		entry->parent->refCount++;
2390 		*_node = entry->parent;
2391 		return B_OK;
2392 	}
2393 
2394 	// lookup the entry in the table
2395 	FUSEEntry* entry = fEntries.Lookup(FUSEEntryRef(dir->id, entryName));
2396 	if (entry != NULL) {
2397 		entry->node->refCount++;
2398 		*_node = entry->node;
2399 		return B_OK;
2400 	}
2401 
2402 	// construct a path for the entry
2403 	char path[B_PATH_NAME_LENGTH];
2404 	size_t pathLen = 0;
2405 	status_t error = _BuildPath(dir, entryName, path, pathLen);
2406 	if (error != B_OK)
2407 		return error;
2408 
2409 	locker.Unlock();
2410 
2411 	// stat the path
2412 	struct stat st;
2413 	int fuseError = fuse_fs_getattr(fFS, path, &st);
2414 	if (fuseError != 0)
2415 		return fuseError;
2416 
2417 	locker.Lock();
2418 
2419 	// lookup the entry in the table again
2420 	entry = fEntries.Lookup(FUSEEntryRef(dir->id, entryName));
2421 	if (entry != NULL) {
2422 		// check whether the node still matches
2423 		if (entry->node->id == st.st_ino) {
2424 			entry->node->refCount++;
2425 			*_node = entry->node;
2426 		} else {
2427 			// nope, something changed -- return a NULL node and let the caller
2428 			// call us again
2429 			*_node = NULL;
2430 		}
2431 
2432 		return B_OK;
2433 	}
2434 
2435 	// lookup the node in the table
2436 	FUSENode* node = NULL;
2437 	if (fUseNodeIDs)
2438 		node = fNodes.Lookup(st.st_ino);
2439 	else
2440 		st.st_ino = _GenerateNodeID();
2441 
2442 	if (node == NULL) {
2443 		// no node yet -- create one
2444 		node = new(std::nothrow) FUSENode(st.st_ino, st.st_mode & S_IFMT);
2445 		if (node == NULL)
2446 			RETURN_ERROR(B_NO_MEMORY);
2447 
2448 		fNodes.Insert(node);
2449 	} else {
2450 		// get a node reference for the entry
2451 		node->refCount++;
2452 	}
2453 
2454 	// create the entry
2455 	entry = FUSEEntry::Create(dir, entryName, node);
2456 	if (entry == NULL) {
2457 		_PutNode(node);
2458 		RETURN_ERROR(B_NO_MEMORY);
2459 	}
2460 
2461 	dir->refCount++;
2462 		// dir reference for the entry
2463 
2464 	fEntries.Insert(entry);
2465 	node->entries.Add(entry);
2466 
2467 	locker.Unlock();
2468 
2469 	// get a reference for the caller
2470 	node->refCount++;
2471 
2472 	*_node = node;
2473 	return B_OK;
2474 }
2475 
2476 
2477 void
2478 FUSEVolume::_PutNode(FUSENode* node)
2479 {
2480 	if (--node->refCount == 0) {
2481 		fNodes.Remove(node);
2482 		delete node;
2483 	}
2484 }
2485 
2486 
2487 void
2488 FUSEVolume::_PutNodes(FUSENode* const* nodes, int32 count)
2489 {
2490 	for (int32 i = 0; i < count; i++)
2491 		_PutNode(nodes[i]);
2492 }
2493 
2494 
2495 /*!	Volume must be locked. The entry's directory must be write locked.
2496  */
2497 void
2498 FUSEVolume::_RemoveEntry(FUSEEntry* entry)
2499 {
2500 	fEntries.Remove(entry);
2501 	entry->node->entries.Remove(entry);
2502 	_PutNode(entry->node);
2503 	_PutNode(entry->parent);
2504 	delete entry;
2505 }
2506 
2507 
2508 /*!	Volume must be locked. The directory must be write locked.
2509  */
2510 status_t
2511 FUSEVolume::_RemoveEntry(FUSENode* dir, const char* name)
2512 {
2513 	FUSEEntry* entry = fEntries.Lookup(FUSEEntryRef(dir->id, name));
2514 	if (entry == NULL)
2515 		return B_ENTRY_NOT_FOUND;
2516 
2517 	_RemoveEntry(entry);
2518 	return B_OK;
2519 }
2520 
2521 
2522 /*!	Volume must be locked. The directories must be write locked.
2523  */
2524 status_t
2525 FUSEVolume::_RenameEntry(FUSENode* oldDir, const char* oldName,
2526 	FUSENode* newDir, const char* newName)
2527 {
2528 	FUSEEntry* entry = fEntries.Lookup(FUSEEntryRef(oldDir->id, oldName));
2529 	if (entry == NULL)
2530 		return B_ENTRY_NOT_FOUND;
2531 
2532 	// get a node reference for the new entry
2533 	FUSENode* node = entry->node;
2534 	node->refCount++;
2535 
2536 	// remove the old entry
2537 	_RemoveEntry(entry);
2538 
2539 	// make sure there's no entry in our way
2540 	_RemoveEntry(newDir, newName);
2541 
2542 	// create a new entry
2543 	entry = FUSEEntry::Create(newDir, newName, node);
2544 	if (entry == NULL) {
2545 		_PutNode(node);
2546 		RETURN_ERROR(B_NO_MEMORY);
2547 	}
2548 
2549 	newDir->refCount++;
2550 		// dir reference for the entry
2551 
2552 	fEntries.Insert(entry);
2553 	node->entries.Add(entry);
2554 
2555 	return B_OK;
2556 }
2557 
2558 
2559 /*!	Locks the given node and all of its ancestors up to the root. The given
2560 	node is write-locked, if \a writeLock is \c true, read-locked otherwise. All
2561 	ancestors are always read-locked in either case.
2562 
2563 	If \a lockParent is \c true, the given node itself is ignored, but locking
2564 	starts with the parent node of the given node (\a writeLock applies to the
2565 	parent node then).
2566 
2567 	If the method fails, none of the nodes is locked.
2568 
2569 	The volume lock must not be held.
2570 */
2571 status_t
2572 FUSEVolume::_LockNodeChain(FUSENode* node, bool lockParent, bool writeLock)
2573 {
2574 	AutoLocker<Locker> locker(fLock);
2575 
2576 	FUSENode* originalNode = node;
2577 
2578 	if (lockParent && node != NULL)
2579 		node = node->Parent();
2580 
2581 	if (node == NULL)
2582 		RETURN_ERROR(B_ENTRY_NOT_FOUND);
2583 
2584 	LockIterator iterator(this, node, writeLock, NULL);
2585 
2586 	bool done;
2587 	do {
2588 		bool volumeUnlocked;
2589 		status_t error = iterator.LockNext(&done, &volumeUnlocked);
2590 		if (error != B_OK)
2591 			RETURN_ERROR(error);
2592 
2593 		if (volumeUnlocked) {
2594 			// check whether we're still locking the right node
2595 			if (lockParent && originalNode->Parent() != node) {
2596 				// We don't -- unlock everything and try again.
2597 				node = originalNode->Parent();
2598 				iterator.SetTo(this, node, writeLock, NULL);
2599 			}
2600 		}
2601 	} while (!done);
2602 
2603 	// Fail, if we couldn't lock all nodes up to the root.
2604 	if (iterator.lastLockedNode != fRootNode)
2605 		RETURN_ERROR(B_ENTRY_NOT_FOUND);
2606 
2607 	iterator.Detach();
2608 	return B_OK;
2609 }
2610 
2611 
2612 void
2613 FUSEVolume::_UnlockNodeChain(FUSENode* node, bool parent, bool writeLock)
2614 {
2615 	AutoLocker<Locker> locker(fLock);
2616 
2617 	if (parent && node != NULL)
2618 		node = node->Parent();
2619 
2620 	_UnlockNodeChainInternal(node, writeLock, NULL, NULL);
2621 }
2622 
2623 
2624 /*!	Unlocks all nodes from \a node up to (and including) \a stopNode (if
2625 	\c NULL, it is ignored). If \a stopBeforeNode is given, the method stops
2626 	before unlocking that node.
2627 	The volume lock must be held.
2628  */
2629 void
2630 FUSEVolume::_UnlockNodeChainInternal(FUSENode* node, bool writeLock,
2631 	FUSENode* stopNode, FUSENode* stopBeforeNode)
2632 {
2633 	FUSENode* originalNode = node;
2634 
2635 	while (node != NULL && node != stopBeforeNode) {
2636 		FUSENode* parent = node->Parent();
2637 
2638 		fLockManager.GenericUnlock(node == originalNode && writeLock, node);
2639 		_PutNode(node);
2640 
2641 		if (node == stopNode || parent == node)
2642 			break;
2643 
2644 		node = parent;
2645 	}
2646 }
2647 
2648 
2649 status_t
2650 FUSEVolume::_LockNodeChains(FUSENode* node1, bool lockParent1, bool writeLock1,
2651 	FUSENode* node2, bool lockParent2, bool writeLock2)
2652 {
2653 	// Since in this case locking is more complicated, we use a helper method.
2654 	// It does the main work, but simply returns telling us to retry when the
2655 	// node hierarchy changes.
2656 	bool retry;
2657 	do {
2658 		status_t error = _LockNodeChainsInternal(node1, lockParent1, writeLock1,
2659 			node2, lockParent2, writeLock2, &retry);
2660 		if (error != B_OK)
2661 			return error;
2662 	} while (retry);
2663 
2664 	return B_OK;
2665 }
2666 
2667 
2668 status_t
2669 FUSEVolume::_LockNodeChainsInternal(FUSENode* node1, bool lockParent1,
2670 	bool writeLock1, FUSENode* node2, bool lockParent2, bool writeLock2,
2671 	bool* _retry)
2672 {
2673 	// Locking order:
2674 	// * A child of a node has to be locked before its parent.
2675 	// * Sibling nodes have to be locked in ascending node ID order.
2676 	//
2677 	// This implies the following locking algorithm:
2678 	// * We find the closest common ancestor of the two given nodes (might even
2679 	//   be one of the given nodes).
2680 	// * We lock all ancestors on one branch (the one with the lower common
2681 	//   ancestor child node ID), but not including the common ancestor.
2682 	// * We lock all ancestors on the other branch, not including the common
2683 	//   ancestor.
2684 	// * We lock the common ancestor and all of its ancestors up to the root
2685 	//   node.
2686 	//
2687 	// When the hierarchy changes while we're waiting for a lock, we recheck the
2688 	// conditions and in doubt have to be restarted.
2689 
2690 	AutoLocker<Locker> locker(fLock);
2691 
2692 	FUSENode* originalNode1 = node1;
2693 	FUSENode* originalNode2 = node2;
2694 
2695 	if (lockParent1 && node1 != NULL)
2696 		node1 = node1->Parent();
2697 
2698 	if (lockParent2 && node2 != NULL)
2699 		node2 = node2->Parent();
2700 
2701 	if (node1 == NULL || node2 == NULL)
2702 		RETURN_ERROR(B_ENTRY_NOT_FOUND);
2703 
2704 	// find the first common ancestor
2705 	FUSENode* commonAncestor;
2706 	bool inverseLockingOrder;
2707 	if (!_FindCommonAncestor(node1, node2, &commonAncestor,
2708 			&inverseLockingOrder)) {
2709 		RETURN_ERROR(B_ENTRY_NOT_FOUND);
2710 	}
2711 
2712 	// lock the both node chains up to (but not including) the common ancestor
2713 	LockIterator iterator1(this, node1, writeLock1, commonAncestor);
2714 	LockIterator iterator2(this, node2, writeLock2, commonAncestor);
2715 
2716 	for (int i = 0; i < 2; i++) {
2717 		LockIterator& iterator = (i == 0) != inverseLockingOrder
2718 			? iterator1 : iterator2;
2719 
2720 		// If the node is the common ancestor, don't enter the "do" loop, since
2721 		// we don't have to lock anything here.
2722 		if (iterator.firstNode == commonAncestor)
2723 			continue;
2724 
2725 		bool done;
2726 		do {
2727 			bool volumeUnlocked;
2728 			status_t error = iterator.LockNext(&done, &volumeUnlocked);
2729 			if (error != B_OK)
2730 				RETURN_ERROR(error);
2731 
2732 			if (volumeUnlocked) {
2733 				// check whether we're still locking the right nodes
2734 				if ((lockParent1 && originalNode1->Parent() != node1)
2735 					|| (lockParent2 && originalNode2->Parent() != node2)) {
2736 					// We don't -- unlock everything and retry.
2737 					*_retry = true;
2738 					return B_OK;
2739 				}
2740 
2741 				// also recheck the common ancestor
2742 				FUSENode* newCommonParent;
2743 				bool newInverseLockingOrder;
2744 				if (!_FindCommonAncestor(node1, node2, &newCommonParent,
2745 						&newInverseLockingOrder)) {
2746 					RETURN_ERROR(B_ENTRY_NOT_FOUND);
2747 				}
2748 
2749 				if (newCommonParent != commonAncestor
2750 					|| inverseLockingOrder != newInverseLockingOrder) {
2751 					// Something changed -- unlock everything and retry.
2752 					*_retry = true;
2753 					return B_OK;
2754 				}
2755 			}
2756 		} while (!done);
2757 	}
2758 
2759 	// Continue locking from the common ancestor to the root. If one of the
2760 	// given nodes is the common ancestor and shall be write locked, we need to
2761 	// use the respective iterator.
2762 	LockIterator& iterator = node2 == commonAncestor && writeLock2
2763 		? iterator2 : iterator1;
2764 	iterator.SetStopBeforeNode(NULL);
2765 
2766 	bool done;
2767 	do {
2768 		bool volumeUnlocked;
2769 		status_t error = iterator.LockNext(&done, &volumeUnlocked);
2770 		if (error != B_OK)
2771 			RETURN_ERROR(error);
2772 
2773 		if (volumeUnlocked) {
2774 			// check whether we're still locking the right nodes
2775 			if ((lockParent1 && originalNode1->Parent() != node1)
2776 				|| (lockParent2 && originalNode2->Parent() != node2)) {
2777 				// We don't -- unlock everything and retry.
2778 				*_retry = true;
2779 				return B_OK;
2780 			}
2781 
2782 			// Also recheck the common ancestor, if we have just locked it.
2783 			// Otherwise we can just continue to lock, since nothing below the
2784 			// previously locked node can have changed.
2785 			if (iterator.lastLockedNode == commonAncestor) {
2786 				FUSENode* newCommonParent;
2787 				bool newInverseLockingOrder;
2788 				if (!_FindCommonAncestor(node1, node2, &newCommonParent,
2789 						&newInverseLockingOrder)) {
2790 					RETURN_ERROR(B_ENTRY_NOT_FOUND);
2791 				}
2792 
2793 				if (newCommonParent != commonAncestor
2794 					|| inverseLockingOrder != newInverseLockingOrder) {
2795 					// Something changed -- unlock everything and retry.
2796 					*_retry = true;
2797 					return B_OK;
2798 				}
2799 			}
2800 		}
2801 	} while (!done);
2802 
2803 	// Fail, if we couldn't lock all nodes up to the root.
2804 	if (iterator.lastLockedNode != fRootNode)
2805 		RETURN_ERROR(B_ENTRY_NOT_FOUND);
2806 
2807 	// everything went fine
2808 	iterator1.Detach();
2809 	iterator2.Detach();
2810 
2811 	*_retry = false;
2812 	return B_OK;
2813 }
2814 
2815 
2816 void
2817 FUSEVolume::_UnlockNodeChains(FUSENode* node1, bool lockParent1,
2818 	bool writeLock1, FUSENode* node2, bool lockParent2, bool writeLock2)
2819 {
2820 	AutoLocker<Locker> locker(fLock);
2821 
2822 	if (lockParent1 && node1 != NULL)
2823 		node1 = node1->Parent();
2824 
2825 	if (lockParent2 && node2 != NULL)
2826 		node2 = node2->Parent();
2827 
2828 	if (node1 == NULL || node2 == NULL)
2829 		return;
2830 
2831 	// find the common ancestor
2832 	FUSENode* commonAncestor;
2833 	bool inverseLockingOrder;
2834 	if (!_FindCommonAncestor(node1, node2, &commonAncestor,
2835 			&inverseLockingOrder)) {
2836 		return;
2837 	}
2838 
2839 	// Unlock one branch up to the common ancestor and then the complete other
2840 	// branch up to the root. If one of the given nodes is the common ancestor,
2841 	// we need to make sure, we write-unlock it, if requested.
2842 	if (node2 == commonAncestor && writeLock2) {
2843 		_UnlockNodeChainInternal(node1, writeLock1, NULL, commonAncestor);
2844 		_UnlockNodeChainInternal(node2, writeLock2, NULL, NULL);
2845 	} else {
2846 		_UnlockNodeChainInternal(node2, writeLock2, NULL, commonAncestor);
2847 		_UnlockNodeChainInternal(node1, writeLock1, NULL, NULL);
2848 	}
2849 }
2850 
2851 
2852 bool
2853 FUSEVolume::_FindCommonAncestor(FUSENode* node1, FUSENode* node2,
2854 	FUSENode** _commonAncestor, bool* _inverseLockingOrder)
2855 {
2856 	// handle trivial special case -- both nodes are the same
2857 	if (node1 == node2) {
2858 		*_commonAncestor = node1;
2859 		*_inverseLockingOrder = false;
2860 		return true;
2861 	}
2862 
2863 	// get the ancestors of both nodes
2864 	FUSENode* ancestors1[kMaxNodeTreeDepth];
2865 	FUSENode* ancestors2[kMaxNodeTreeDepth];
2866 	uint32 count1;
2867 	uint32 count2;
2868 
2869 	if (!_GetNodeAncestors(node1, ancestors1, &count1)
2870 		|| !_GetNodeAncestors(node2, ancestors2, &count2)) {
2871 		return false;
2872 	}
2873 
2874 	// find the first ancestor not common to both nodes
2875 	uint32 index = 0;
2876 	for (; index < count1 && index < count2; index++) {
2877 		FUSENode* ancestor1 = ancestors1[count1 - index - 1];
2878 		FUSENode* ancestor2 = ancestors2[count2 - index - 1];
2879 		if (ancestor1 != ancestor2) {
2880 			*_commonAncestor = ancestors1[count1 - index];
2881 			*_inverseLockingOrder = ancestor1->id > ancestor2->id;
2882 			return true;
2883 		}
2884 	}
2885 
2886 	// one node is an ancestor of the other
2887 	*_commonAncestor = ancestors1[count1 - index];
2888 	*_inverseLockingOrder = index == count1;
2889 	return true;
2890 }
2891 
2892 
2893 bool
2894 FUSEVolume::_GetNodeAncestors(FUSENode* node, FUSENode** ancestors,
2895 	uint32* _count)
2896 {
2897 	uint32 count = 0;
2898 	while (node != NULL && count < kMaxNodeTreeDepth) {
2899 		ancestors[count++] = node;
2900 
2901 		if (node == fRootNode) {
2902 			*_count = count;
2903 			return true;
2904 		}
2905 
2906 		node = node->Parent();
2907 	}
2908 
2909 	// Either the node is not in the tree or we hit the array limit.
2910 	return false;
2911 }
2912 
2913 
2914 status_t
2915 FUSEVolume::_BuildPath(FUSENode* dir, const char* entryName, char* path,
2916 	size_t& pathLen)
2917 {
2918 	// get the directory path
2919 	status_t error = _BuildPath(dir, path, pathLen);
2920 	if (error != B_OK)
2921 		return error;
2922 
2923 	if (path[pathLen - 1] != '/') {
2924 		path[pathLen++] = '/';
2925 		if (pathLen == B_PATH_NAME_LENGTH)
2926 			RETURN_ERROR(B_NAME_TOO_LONG);
2927 	}
2928 
2929 	// append the entry name
2930 	size_t len = strlen(entryName);
2931 	if (pathLen + len >= B_PATH_NAME_LENGTH)
2932 		RETURN_ERROR(B_NAME_TOO_LONG);
2933 
2934 	memcpy(path + pathLen, entryName, len + 1);
2935 	pathLen += len;
2936 
2937 	return B_OK;
2938 }
2939 
2940 
2941 status_t
2942 FUSEVolume::_BuildPath(FUSENode* node, char* path, size_t& pathLen)
2943 {
2944 	if (node == fRootNode) {
2945 		// we hit the root
2946 		strcpy(path, "/");
2947 		pathLen = 1;
2948 		return B_OK;
2949 	}
2950 
2951 	// get an entry for the node and get its path
2952 	FUSEEntry* entry = node->entries.Head();
2953 	if (entry == NULL)
2954 		RETURN_ERROR(B_ENTRY_NOT_FOUND);
2955 
2956 	return _BuildPath(entry->parent, entry->name, path, pathLen);
2957 }
2958 
2959 
2960 /*static*/ int
2961 FUSEVolume::_AddReadDirEntry(void* _buffer, const char* name,
2962 	const struct stat* st, off_t offset)
2963 {
2964 	ReadDirBuffer* buffer = (ReadDirBuffer*)_buffer;
2965 
2966 	ino_t nodeID = st != NULL ? st->st_ino : 0;
2967 	int type = st != NULL ? st->st_mode & S_IFMT : 0;
2968 	return buffer->volume->_AddReadDirEntry(buffer, name, type, nodeID, offset);
2969 }
2970 
2971 
2972 /*static*/ int
2973 FUSEVolume::_AddReadDirEntryGetDir(fuse_dirh_t handle, const char* name,
2974 	int type, ino_t nodeID)
2975 {
2976 	ReadDirBuffer* buffer = (ReadDirBuffer*)handle;
2977 	return buffer->volume->_AddReadDirEntry(buffer, name, type << 12, nodeID,
2978 		0);
2979 }
2980 
2981 
2982 int
2983 FUSEVolume::_AddReadDirEntry(ReadDirBuffer* buffer, const char* name, int type,
2984 	ino_t nodeID, off_t offset)
2985 {
2986 	PRINT(("FUSEVolume::_AddReadDirEntry(%p, \"%s\", %#x, %" B_PRId64 ", %"
2987 		B_PRId64 "\n", buffer, name, type, nodeID, offset));
2988 
2989 	AutoLocker<Locker> locker(fLock);
2990 
2991 	size_t entryLen = 0;
2992 	if (offset != 0) {
2993 		// does the caller want more entries?
2994 		if (buffer->entriesRead == buffer->maxEntries)
2995 			return 1;
2996 
2997 		// compute the entry length and check whether the entry still fits
2998 		entryLen = sizeof(dirent) + strlen(name);
2999 		if (buffer->usedSize + entryLen > buffer->bufferSize)
3000 			return 1;
3001 	}
3002 
3003 	// create a node and an entry, if necessary
3004 	ino_t dirID = buffer->directory->id;
3005 	FUSEEntry* entry;
3006 	if (strcmp(name, ".") == 0) {
3007 		// current dir entry
3008 		nodeID = dirID;
3009 		type = S_IFDIR;
3010 	} else if (strcmp(name, "..") == 0) {
3011 		// parent dir entry
3012 		FUSEEntry* parentEntry = buffer->directory->entries.Head();
3013 		if (parentEntry == NULL) {
3014 			ERROR(("FUSEVolume::_AddReadDirEntry(): dir %" B_PRId64
3015 				" has no entry!\n", dirID));
3016 			return 0;
3017 		}
3018 		nodeID = parentEntry->parent->id;
3019 		type = S_IFDIR;
3020 	} else if ((entry = fEntries.Lookup(FUSEEntryRef(dirID, name))) == NULL) {
3021 		// get the node
3022 		FUSENode* node = NULL;
3023 		if (fUseNodeIDs)
3024 			node = fNodes.Lookup(nodeID);
3025 		else
3026 			nodeID = _GenerateNodeID();
3027 
3028 		if (node == NULL) {
3029 			// no node yet -- create one
3030 
3031 			// If we don't have a valid type, we need to stat the node first.
3032 			if (type == 0) {
3033 				char path[B_PATH_NAME_LENGTH];
3034 				size_t pathLen;
3035 				status_t error = _BuildPath(buffer->directory, name, path,
3036 					pathLen);
3037 				if (error != B_OK) {
3038 					buffer->error = error;
3039 					return 0;
3040 				}
3041 
3042 				locker.Unlock();
3043 
3044 				// stat the path
3045 				struct stat st;
3046 				int fuseError = fuse_fs_getattr(fFS, path, &st);
3047 
3048 				locker.Lock();
3049 
3050 				if (fuseError != 0) {
3051 					buffer->error = fuseError;
3052 					return 0;
3053 				}
3054 
3055 				type = st.st_mode & S_IFMT;
3056 			}
3057 
3058 			node = new(std::nothrow) FUSENode(nodeID, type);
3059 			if (node == NULL) {
3060 				buffer->error = B_NO_MEMORY;
3061 				return 1;
3062 			}
3063 			PRINT(("  -> create node: %p, id: %" B_PRId64 "\n", node, nodeID));
3064 
3065 			fNodes.Insert(node);
3066 		} else {
3067 			// get a node reference for the entry
3068 			node->refCount++;
3069 		}
3070 
3071 		// create the entry
3072 		entry = FUSEEntry::Create(buffer->directory, name, node);
3073 		if (entry == NULL) {
3074 			_PutNode(node);
3075 			buffer->error = B_NO_MEMORY;
3076 			return 1;
3077 		}
3078 
3079 		buffer->directory->refCount++;
3080 			// dir reference for the entry
3081 
3082 		fEntries.Insert(entry);
3083 		node->entries.Add(entry);
3084 	} else {
3085 		// TODO: Check whether the node's ID matches the one we got (if any)!
3086 		nodeID = entry->node->id;
3087 		type = entry->node->type;
3088 	}
3089 
3090 	if (offset == 0) {
3091 		// cache the entry
3092 		if (buffer->cookie->entryCache == NULL) {
3093 			// no cache yet -- create it
3094 			buffer->cookie->entryCache = new(std::nothrow) DirEntryCache;
3095 			if (buffer->cookie->entryCache == NULL) {
3096 				buffer->error = B_NO_MEMORY;
3097 				return 1;
3098 			}
3099 		}
3100 
3101 		status_t error = buffer->cookie->entryCache->AddEntry(nodeID, name);
3102 		if (error != B_OK) {
3103 			buffer->error = error;
3104 			return 1;
3105 		}
3106 	} else {
3107 		// fill in the dirent
3108 		dirent* dirEntry = (dirent*)((uint8*)buffer->buffer + buffer->usedSize);
3109 		dirEntry->d_dev = fID;
3110 		dirEntry->d_ino = nodeID;
3111 		strcpy(dirEntry->d_name, name);
3112 
3113 		if (buffer->entriesRead + 1 < buffer->maxEntries) {
3114 			// align the entry length, so the next dirent will be aligned
3115 			entryLen = (entryLen + 7) / 8 * 8;
3116 			entryLen = std::min(entryLen,
3117 				buffer->bufferSize - buffer->usedSize);
3118 		}
3119 
3120 		dirEntry->d_reclen = entryLen;
3121 
3122 		// update the buffer
3123 		buffer->usedSize += entryLen;
3124 		buffer->entriesRead++;
3125 		buffer->cookie->currentEntryOffset = offset;
3126 	}
3127 
3128 	return 0;
3129 }
3130