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