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