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