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