xref: /haiku/src/add-ons/kernel/file_systems/userlandfs/server/fuse/FUSEVolume.cpp (revision 91054f1d38dd7827c0f0ba9490c213775ec7b471)
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.
29 static const uint32 kMaxNodeTreeDepth = 1024;
30 
31 
32 struct 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 
129 private:
130 	struct Entry {
131 		ino_t	nodeID;
132 		uint32	nameOffset;
133 		uint32	nameSize;
134 	};
135 
136 private:
137 	Entry*	fEntries;
138 	char*	fNames;
139 	uint32	fEntryCount;
140 	uint32	fEntryCapacity;
141 	size_t	fNamesSize;
142 	size_t	fNamesCapacity;
143 };
144 
145 
146 struct 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 
177 struct 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 
192 struct 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 
275 private:
276 	char*	fAttributes;
277 	size_t	fAttributesSize;
278 	size_t	fCurrentOffset;
279 	bool	fValid;
280 };
281 
282 
283 struct 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 
311 struct 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 
415 struct 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 
445 private:
446 	RWLockManager*	fLockManager;
447 };
448 
449 
450 struct 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 
480 private:
481 	RWLockManager*	fLockManager;
482 };
483 
484 
485 struct 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 
498 struct 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 
511 struct 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 
535 private:
536 	FUSEVolume*	fVolume;
537 	FUSENode*	fNode;
538 	status_t	fStatus;
539 	bool		fParent;
540 	bool		fWriteLock;
541 };
542 
543 
544 struct FUSEVolume::NodeReadLocker : NodeLocker {
545 	NodeReadLocker(FUSEVolume* volume, FUSENode* node, bool parent)
546 		:
547 		NodeLocker(volume, node, parent, false)
548 	{
549 	}
550 };
551 
552 
553 struct FUSEVolume::NodeWriteLocker : NodeLocker {
554 	NodeWriteLocker(FUSEVolume* volume, FUSENode* node, bool parent)
555 		:
556 		NodeLocker(volume, node, parent, true)
557 	{
558 	}
559 };
560 
561 
562 struct 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 
595 private:
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 
610 inline FUSEFileSystem*
611 FUSEVolume::_FileSystem() const
612 {
613 	return static_cast<FUSEFileSystem*>(fFileSystem);
614 }
615 
616 
617 FUSEVolume::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 
628 FUSEVolume::~FUSEVolume()
629 {
630 }
631 
632 
633 status_t
634 FUSEVolume::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 
662 status_t
663 FUSEVolume::Mount(const char* device, uint32 flags, const char* parameters,
664 	ino_t* rootID)
665 {
666 printf("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 
727 status_t
728 FUSEVolume::Unmount()
729 {
730 printf("FUSEVolume::Unmount()\n");
731 	_FileSystem()->ExitClientFS(B_OK);
732 	return B_OK;
733 }
734 
735 
736 status_t
737 FUSEVolume::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 
783 status_t
784 FUSEVolume::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 
811 status_t
812 FUSEVolume::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 
833 status_t
834 FUSEVolume::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 
852 status_t
853 FUSEVolume::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 
873 status_t
874 FUSEVolume::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 
886 status_t
887 FUSEVolume::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 
897 status_t
898 FUSEVolume::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 
913 status_t
914 FUSEVolume::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 
959 status_t
960 FUSEVolume::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 
997 status_t
998 FUSEVolume::CreateSymlink(void* _dir, const char* name, const char* target,
999 	int mode)
1000 {
1001 	FUSENode* dir = (FUSENode*)_dir;
1002 PRINT(("FUSEVolume::CreateSymlink(%p (%lld), \"%s\" -> \"%s\", %#x)\n", dir,
1003 dir->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 
1044 status_t
1045 FUSEVolume::Link(void* _dir, const char* name, void* _node)
1046 {
1047 	FUSENode* dir = (FUSENode*)_dir;
1048 	FUSENode* node = (FUSENode*)_node;
1049 PRINT(("FUSEVolume::Link(%p (%lld), \"%s\" -> %p (%lld))\n", dir, dir->id, name,
1050 node, 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 
1094 status_t
1095 FUSEVolume::Unlink(void* _dir, const char* name)
1096 {
1097 	FUSENode* dir = (FUSENode*)_dir;
1098 PRINT(("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 
1143 status_t
1144 FUSEVolume::Rename(void* _oldDir, const char* oldName, void* _newDir,
1145 	const char* newName)
1146 {
1147 	FUSENode* oldDir = (FUSENode*)_oldDir;
1148 	FUSENode* newDir = (FUSENode*)_newDir;
1149 PRINT(("FUSEVolume::Rename(%p (%lld), \"%s\", %p (%lld), \"%s\")\n", oldDir,
1150 oldDir->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 
1198 status_t
1199 FUSEVolume::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 
1228 status_t
1229 FUSEVolume::ReadStat(void* _node, struct stat* st)
1230 {
1231 	FUSENode* node = (FUSENode*)_node;
1232 PRINT(("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 
1259 status_t
1260 FUSEVolume::WriteStat(void* _node, const struct stat* st, uint32 mask)
1261 {
1262 	FUSENode* node = (FUSENode*)_node;
1263 PRINT(("FUSEVolume::WriteStat(%p (%lld), %p, %#lx)\n", node, node->id, st,
1264 mask));
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 
1354 status_t
1355 FUSEVolume::Create(void* _dir, const char* name, int openMode, int mode,
1356 	void** _cookie, ino_t* _vnid)
1357 {
1358 	FUSENode* dir = (FUSENode*)_dir;
1359 PRINT(("FUSEVolume::Create(%p (%lld), \"%s\", %#x, %#x)\n", dir, dir->id, name,
1360 openMode, 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 
1419 status_t
1420 FUSEVolume::Open(void* _node, int openMode, void** _cookie)
1421 {
1422 	FUSENode* node = (FUSENode*)_node;
1423 PRINT(("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 
1481 status_t
1482 FUSEVolume::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 
1514 status_t
1515 FUSEVolume::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 
1549 status_t
1550 FUSEVolume::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 
1587 status_t
1588 FUSEVolume::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 
1640 status_t
1641 FUSEVolume::CreateDir(void* _dir, const char* name, int mode)
1642 {
1643 	FUSENode* dir = (FUSENode*)_dir;
1644 PRINT(("FUSEVolume::CreateDir(%p (%lld), \"%s\", %#x)\n", dir, dir->id, name,
1645 mode));
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 
1683 status_t
1684 FUSEVolume::RemoveDir(void* _dir, const char* name)
1685 {
1686 	FUSENode* dir = (FUSENode*)_dir;
1687 PRINT(("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 
1731 status_t
1732 FUSEVolume::OpenDir(void* _node, void** _cookie)
1733 {
1734 	FUSENode* node = (FUSENode*)_node;
1735 PRINT(("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 
1777 status_t
1778 FUSEVolume::CloseDir(void* node, void* _cookie)
1779 {
1780 	return B_OK;
1781 }
1782 
1783 
1784 status_t
1785 FUSEVolume::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 
1820 status_t
1821 FUSEVolume::ReadDir(void* _node, void* _cookie, void* buffer, size_t bufferSize,
1822 	uint32 count, uint32* _countRead)
1823 {
1824 PRINT(("FUSEVolume::ReadDir(%p, %p, %p, %lu, %ld)\n", _node, _cookie, buffer,
1825 bufferSize, 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) {
1868 PRINT(("  using getdir() interface\n"));
1869 			fuseError = fFS->ops.getdir(path, (fuse_dirh_t)&readDirBuffer,
1870 				&_AddReadDirEntryGetDir);
1871 		} else {
1872 PRINT(("  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 
1904 status_t
1905 FUSEVolume::RewindDir(void* _node, void* _cookie)
1906 {
1907 PRINT(("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
1928 status_t
1929 FUSEVolume::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
1943 status_t
1944 FUSEVolume::CloseAttrDir(void* node, void* cookie)
1945 {
1946 	return B_OK;
1947 }
1948 
1949 
1950 // FreeAttrDirCookie
1951 status_t
1952 FUSEVolume::FreeAttrDirCookie(void* _node, void* _cookie)
1953 {
1954 	delete (AttrDirCookie*)_cookie;
1955 	return B_OK;
1956 }
1957 
1958 
1959 // ReadAttrDir
1960 status_t
1961 FUSEVolume::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
2032 status_t
2033 FUSEVolume::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 
2048 ino_t
2049 FUSEVolume::_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 */
2064 bool
2065 FUSEVolume::_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 */
2090 status_t
2091 FUSEVolume::_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 
2135 status_t
2136 FUSEVolume::_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 
2247 void
2248 FUSEVolume::_PutNode(FUSENode* node)
2249 {
2250 	if (--node->refCount == 0) {
2251 		fNodes.Remove(node);
2252 		delete node;
2253 	}
2254 }
2255 
2256 
2257 void
2258 FUSEVolume::_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  */
2267 void
2268 FUSEVolume::_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  */
2280 status_t
2281 FUSEVolume::_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  */
2294 status_t
2295 FUSEVolume::_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 */
2341 status_t
2342 FUSEVolume::_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 
2382 void
2383 FUSEVolume::_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  */
2399 void
2400 FUSEVolume::_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 
2419 status_t
2420 FUSEVolume::_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 
2438 status_t
2439 FUSEVolume::_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 
2586 void
2587 FUSEVolume::_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 
2622 bool
2623 FUSEVolume::_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 
2663 bool
2664 FUSEVolume::_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 
2684 status_t
2685 FUSEVolume::_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 
2711 status_t
2712 FUSEVolume::_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
2731 FUSEVolume::_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
2743 FUSEVolume::_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 
2752 int
2753 FUSEVolume::_AddReadDirEntry(ReadDirBuffer* buffer, const char* name, int type,
2754 	ino_t nodeID, off_t offset)
2755 {
2756 PRINT(("FUSEVolume::_AddReadDirEntry(%p, \"%s\", %#x, %lld, %lld\n", buffer,
2757 name, type, nodeID, offset));
2758 
2759 	AutoLocker<Locker> locker(fLock);
2760 
2761 	size_t entryLen;
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 			}
2833 PRINT(("  -> 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