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