xref: /haiku/src/add-ons/kernel/file_systems/userlandfs/server/fuse/FUSEVolume.cpp (revision a5a3b2d9a3d95cbae71eaf371708c73a1780ac0d)
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 (%" B_PRId64 "), \"%s\" -> \"%s\", "
1003 		"%#x)\n", dir, 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 (%" B_PRId64 "), \"%s\" -> %p (%" B_PRId64
1050 		"))\n", dir, dir->id, name, 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 (%" B_PRId64 "), \"%s\")\n", dir, dir->id,
1099 		name));
1100 
1101 	// lock the directory
1102 	NodeWriteLocker nodeLocker(this, dir, false);
1103 	if (nodeLocker.Status() != B_OK)
1104 		RETURN_ERROR(nodeLocker.Status());
1105 
1106 	// get the node ID (for the node monitoring message)
1107 	ino_t nodeID;
1108 	bool doNodeMonitoring = _GetNodeID(dir, name, &nodeID);
1109 
1110 	AutoLocker<Locker> locker(fLock);
1111 
1112 	// get a path for the entry
1113 	char path[B_PATH_NAME_LENGTH];
1114 	size_t pathLen;
1115 	status_t error = _BuildPath(dir, name, path, pathLen);
1116 	if (error != B_OK)
1117 		RETURN_ERROR(error);
1118 
1119 	locker.Unlock();
1120 
1121 	// unlink
1122 	int fuseError = fuse_fs_unlink(fFS, path);
1123 	if (fuseError != 0)
1124 		RETURN_ERROR(fuseError);
1125 
1126 	// remove the entry
1127 	locker.Lock();
1128 	_RemoveEntry(dir, name);
1129 
1130 	// mark the dir dirty
1131 	dir->dirty = true;
1132 	locker.Unlock();
1133 
1134 	// send node monitoring message
1135 	if (doNodeMonitoring) {
1136 		UserlandFS::KernelEmu::notify_listener(B_ENTRY_REMOVED, 0, fID, 0,
1137 			dir->id, nodeID, NULL, name);
1138 	}
1139 
1140 	return B_OK;
1141 }
1142 
1143 
1144 status_t
1145 FUSEVolume::Rename(void* _oldDir, const char* oldName, void* _newDir,
1146 	const char* newName)
1147 {
1148 	FUSENode* oldDir = (FUSENode*)_oldDir;
1149 	FUSENode* newDir = (FUSENode*)_newDir;
1150 	PRINT(("FUSEVolume::Rename(%p (%" B_PRId64 "), \"%s\", %p (%" B_PRId64
1151 		"), \"%s\")\n", oldDir, oldDir->id, oldName, newDir, newDir->id,
1152 		newName));
1153 
1154 	// lock the directories
1155 	MultiNodeLocker nodeLocker(this, oldDir, false, true, newDir, false, true);
1156 	if (nodeLocker.Status() != B_OK)
1157 		RETURN_ERROR(nodeLocker.Status());
1158 
1159 	AutoLocker<Locker> locker(fLock);
1160 
1161 	// get a path for the entries
1162 	char oldPath[B_PATH_NAME_LENGTH];
1163 	size_t oldPathLen;
1164 	status_t error = _BuildPath(oldDir, oldName, oldPath, oldPathLen);
1165 	if (error != B_OK)
1166 		RETURN_ERROR(error);
1167 
1168 	char newPath[B_PATH_NAME_LENGTH];
1169 	size_t newPathLen;
1170 	error = _BuildPath(newDir, newName, newPath, newPathLen);
1171 	if (error != B_OK)
1172 		RETURN_ERROR(error);
1173 
1174 	locker.Unlock();
1175 
1176 	// rename
1177 	int fuseError = fuse_fs_rename(fFS, oldPath, newPath);
1178 	if (fuseError != 0)
1179 		RETURN_ERROR(fuseError);
1180 
1181 	// rename the entry
1182 	locker.Lock();
1183 	_RenameEntry(oldDir, oldName, newDir, newName);
1184 
1185 	// mark the dirs dirty
1186 	oldDir->dirty = true;
1187 	newDir->dirty = true;
1188 
1189 	// send node monitoring message
1190 	ino_t nodeID;
1191 	if (_GetNodeID(newDir, newName, &nodeID)) {
1192 		UserlandFS::KernelEmu::notify_listener(B_ENTRY_MOVED, 0, fID,
1193 			oldDir->id, newDir->id, nodeID, oldName, newName);
1194 	}
1195 
1196 	return B_OK;
1197 }
1198 
1199 
1200 status_t
1201 FUSEVolume::Access(void* _node, int mode)
1202 {
1203 	FUSENode* node = (FUSENode*)_node;
1204 
1205 	// lock the directory
1206 	NodeReadLocker nodeLocker(this, node, true);
1207 	if (nodeLocker.Status() != B_OK)
1208 		RETURN_ERROR(nodeLocker.Status());
1209 
1210 	AutoLocker<Locker> locker(fLock);
1211 
1212 	// get a path for the node
1213 	char path[B_PATH_NAME_LENGTH];
1214 	size_t pathLen;
1215 	status_t error = _BuildPath(node, path, pathLen);
1216 	if (error != B_OK)
1217 		RETURN_ERROR(error);
1218 
1219 	locker.Unlock();
1220 
1221 	// call the access hook on the path
1222 	int fuseError = fuse_fs_access(fFS, path, mode);
1223 	if (fuseError != 0)
1224 		return fuseError;
1225 
1226 	return B_OK;
1227 }
1228 
1229 
1230 status_t
1231 FUSEVolume::ReadStat(void* _node, struct stat* st)
1232 {
1233 	FUSENode* node = (FUSENode*)_node;
1234 	PRINT(("FUSEVolume::ReadStat(%p (%" B_PRId64 "), %p)\n", node, node->id,
1235 		st));
1236 
1237 	// lock the directory
1238 	NodeReadLocker nodeLocker(this, node, true);
1239 	if (nodeLocker.Status() != B_OK)
1240 		RETURN_ERROR(nodeLocker.Status());
1241 
1242 	AutoLocker<Locker> locker(fLock);
1243 
1244 	// get a path for the node
1245 	char path[B_PATH_NAME_LENGTH];
1246 	size_t pathLen;
1247 	status_t error = _BuildPath(node, path, pathLen);
1248 	if (error != B_OK)
1249 		RETURN_ERROR(error);
1250 
1251 	locker.Unlock();
1252 
1253 	// stat the path
1254 	int fuseError = fuse_fs_getattr(fFS, path, st);
1255 	if (fuseError != 0)
1256 		return fuseError;
1257 
1258 	return B_OK;
1259 }
1260 
1261 
1262 status_t
1263 FUSEVolume::WriteStat(void* _node, const struct stat* st, uint32 mask)
1264 {
1265 	FUSENode* node = (FUSENode*)_node;
1266 	PRINT(("FUSEVolume::WriteStat(%p (%" B_PRId64 "), %p, %#" B_PRIx32 ")\n",
1267 		node, node->id, st, mask));
1268 
1269 	// lock the directory
1270 	NodeReadLocker nodeLocker(this, node, true);
1271 	if (nodeLocker.Status() != B_OK)
1272 		RETURN_ERROR(nodeLocker.Status());
1273 
1274 	AutoLocker<Locker> locker(fLock);
1275 
1276 	// get a path for the node
1277 	char path[B_PATH_NAME_LENGTH];
1278 	size_t pathLen;
1279 	status_t error = _BuildPath(node, path, pathLen);
1280 	if (error != B_OK)
1281 		RETURN_ERROR(error);
1282 
1283 	locker.Unlock();
1284 
1285 	// permissions
1286 	if ((mask & B_STAT_MODE) != 0) {
1287 		int fuseError = fuse_fs_chmod(fFS, path, st->st_mode);
1288 		if (fuseError != 0)
1289 			RETURN_ERROR(fuseError);
1290 	}
1291 
1292 	// owner
1293 	if ((mask & (B_STAT_UID | B_STAT_GID)) != 0) {
1294 		uid_t uid = (mask & B_STAT_UID) != 0 ? st->st_uid : (uid_t)-1;
1295 		gid_t gid = (mask & B_STAT_GID) != 0 ? st->st_gid : (gid_t)-1;
1296 		int fuseError = fuse_fs_chown(fFS, path, uid, gid);
1297 		if (fuseError != 0)
1298 			RETURN_ERROR(fuseError);
1299 	}
1300 
1301 	// size
1302 	if ((mask & B_STAT_SIZE) != 0) {
1303 		// truncate
1304 		int fuseError = fuse_fs_truncate(fFS, path, st->st_size);
1305 		if (fuseError != 0)
1306 			RETURN_ERROR(fuseError);
1307 	}
1308 
1309 	// access/modification time
1310 	if ((mask & (B_STAT_ACCESS_TIME | B_STAT_MODIFICATION_TIME)) != 0) {
1311 		timespec tv[2] = {
1312 			{st->st_atime, 0},
1313 			{st->st_mtime, 0}
1314 		};
1315 
1316 		// If either time is not specified, we need to stat the file to get the
1317 		// current value.
1318 		if ((mask & (B_STAT_ACCESS_TIME | B_STAT_MODIFICATION_TIME))
1319 				!= (B_STAT_ACCESS_TIME | B_STAT_MODIFICATION_TIME)) {
1320 			struct stat currentStat;
1321 			int fuseError = fuse_fs_getattr(fFS, path, &currentStat);
1322 			if (fuseError != 0)
1323 				RETURN_ERROR(fuseError);
1324 
1325 			if ((mask & B_STAT_ACCESS_TIME) == 0)
1326 				tv[0].tv_sec = currentStat.st_atime;
1327 			else
1328 				tv[1].tv_sec = currentStat.st_mtime;
1329 		}
1330 
1331 		int fuseError = fuse_fs_utimens(fFS, path, tv);
1332 		if (fuseError != 0)
1333 			RETURN_ERROR(fuseError);
1334 	}
1335 
1336 	// mark the node dirty
1337 	locker.Lock();
1338 	node->dirty = true;
1339 
1340 	// send node monitoring message
1341 	uint32 changedFields = mask &
1342 		(B_STAT_MODE | B_STAT_UID | B_STAT_GID | B_STAT_SIZE
1343 		| B_STAT_ACCESS_TIME | B_STAT_MODIFICATION_TIME);
1344 
1345 	if (changedFields != 0) {
1346 		UserlandFS::KernelEmu::notify_listener(B_STAT_CHANGED, changedFields,
1347 			fID, 0, 0, node->id, NULL, NULL);
1348 	}
1349 
1350 	return B_OK;
1351 }
1352 
1353 
1354 // #pragma mark - files
1355 
1356 
1357 status_t
1358 FUSEVolume::Create(void* _dir, const char* name, int openMode, int mode,
1359 	void** _cookie, ino_t* _vnid)
1360 {
1361 	FUSENode* dir = (FUSENode*)_dir;
1362 	PRINT(("FUSEVolume::Create(%p (%" B_PRId64 "), \"%s\", %#x, %#x)\n", dir,
1363 		dir->id, name, openMode, mode));
1364 
1365 	// lock the directory
1366 	NodeWriteLocker nodeLocker(this, dir, false);
1367 	if (nodeLocker.Status() != B_OK)
1368 		RETURN_ERROR(nodeLocker.Status());
1369 
1370 	// allocate a file cookie
1371 	FileCookie* cookie = new(std::nothrow) FileCookie(openMode);
1372 	if (cookie == NULL)
1373 		RETURN_ERROR(B_NO_MEMORY);
1374 	ObjectDeleter<FileCookie> cookieDeleter(cookie);
1375 
1376 	AutoLocker<Locker> locker(fLock);
1377 
1378 	// get a path for the node
1379 	char path[B_PATH_NAME_LENGTH];
1380 	size_t pathLen;
1381 	status_t error = _BuildPath(dir, name, path, pathLen);
1382 	if (error != B_OK)
1383 		RETURN_ERROR(error);
1384 
1385 	locker.Unlock();
1386 
1387 	// create the file
1388 	int fuseError = fuse_fs_create(fFS, path, mode, cookie);
1389 	if (fuseError != 0)
1390 		RETURN_ERROR(fuseError);
1391 
1392 	// get the node
1393 	FUSENode* node;
1394 	error = _GetNode(dir, name, &node);
1395 	if (error != B_OK) {
1396 		// This is bad. We've create the file successfully, but couldn't get
1397 		// the node. Close the file and delete the entry.
1398 		fuse_fs_flush(fFS, path, cookie);
1399 		fuse_fs_release(fFS, path, cookie);
1400 		fuse_fs_unlink(fFS, path);
1401 		RETURN_ERROR(error);
1402 	}
1403 
1404 	// mark the dir and the node dirty
1405 	locker.Lock();
1406 	dir->dirty = true;
1407 	node->dirty = true;
1408 	locker.Unlock();
1409 
1410 	// send node monitoring message
1411 	UserlandFS::KernelEmu::notify_listener(B_ENTRY_CREATED, 0, fID, 0, dir->id,
1412 		node->id, NULL, name);
1413 
1414 	cookieDeleter.Detach();
1415 	*_cookie = cookie;
1416 	*_vnid = node->id;
1417 
1418 	return B_OK;
1419 }
1420 
1421 
1422 status_t
1423 FUSEVolume::Open(void* _node, int openMode, void** _cookie)
1424 {
1425 	FUSENode* node = (FUSENode*)_node;
1426 	PRINT(("FUSEVolume::Open(%p (%" B_PRId64 "), %#x)\n", node, node->id,
1427 		openMode));
1428 
1429 	// lock the directory
1430 	NodeReadLocker nodeLocker(this, node, true);
1431 	if (nodeLocker.Status() != B_OK)
1432 		RETURN_ERROR(nodeLocker.Status());
1433 
1434 	bool truncate = (openMode & O_TRUNC) != 0;
1435 	openMode &= ~O_TRUNC;
1436 
1437 	// allocate a file cookie
1438 	FileCookie* cookie = new(std::nothrow) FileCookie(openMode);
1439 	if (cookie == NULL)
1440 		RETURN_ERROR(B_NO_MEMORY);
1441 	ObjectDeleter<FileCookie> cookieDeleter(cookie);
1442 
1443 	AutoLocker<Locker> locker(fLock);
1444 
1445 	// get a path for the node
1446 	char path[B_PATH_NAME_LENGTH];
1447 	size_t pathLen;
1448 	status_t error = _BuildPath(node, path, pathLen);
1449 	if (error != B_OK)
1450 		RETURN_ERROR(error);
1451 
1452 	locker.Unlock();
1453 
1454 	// open the file
1455 	int fuseError = fuse_fs_open(fFS, path, cookie);
1456 	if (fuseError != 0)
1457 		RETURN_ERROR(fuseError);
1458 
1459 	// truncate the file, if requested
1460 	if (truncate) {
1461 		fuseError = fuse_fs_ftruncate(fFS, path, 0, cookie);
1462 		if (fuseError != 0) {
1463 			fuse_fs_flush(fFS, path, cookie);
1464 			fuse_fs_release(fFS, path, cookie);
1465 			RETURN_ERROR(fuseError);
1466 		}
1467 
1468 		// mark the node dirty
1469 		locker.Lock();
1470 		node->dirty = true;
1471 
1472 		// send node monitoring message
1473 		UserlandFS::KernelEmu::notify_listener(B_STAT_CHANGED,
1474 			B_STAT_SIZE | B_STAT_MODIFICATION_TIME, fID, 0, 0, node->id, NULL,
1475 			NULL);
1476 	}
1477 
1478 	cookieDeleter.Detach();
1479 	*_cookie = cookie;
1480 
1481 	return B_OK;
1482 }
1483 
1484 
1485 status_t
1486 FUSEVolume::Close(void* _node, void* _cookie)
1487 {
1488 	FUSENode* node = (FUSENode*)_node;
1489 	FileCookie* cookie = (FileCookie*)_cookie;
1490 
1491 	RWLockableReadLocker cookieLocker(this, cookie);
1492 
1493 	// lock the directory
1494 	NodeReadLocker nodeLocker(this, node, true);
1495 	if (nodeLocker.Status() != B_OK)
1496 		RETURN_ERROR(nodeLocker.Status());
1497 
1498 	AutoLocker<Locker> locker(fLock);
1499 
1500 	// get a path for the node
1501 	char path[B_PATH_NAME_LENGTH];
1502 	size_t pathLen;
1503 	status_t error = _BuildPath(node, path, pathLen);
1504 	if (error != B_OK)
1505 		RETURN_ERROR(error);
1506 
1507 	locker.Unlock();
1508 
1509 	// flush the file
1510 	int fuseError = fuse_fs_flush(fFS, path, cookie);
1511 	if (fuseError != 0)
1512 		return fuseError;
1513 
1514 	return B_OK;
1515 }
1516 
1517 
1518 status_t
1519 FUSEVolume::FreeCookie(void* _node, void* _cookie)
1520 {
1521 	FUSENode* node = (FUSENode*)_node;
1522 	FileCookie* cookie = (FileCookie*)_cookie;
1523 
1524 	// no need to lock the cookie here, as no-one else uses it anymore
1525 
1526 	// lock the directory
1527 	NodeReadLocker nodeLocker(this, node, true);
1528 	if (nodeLocker.Status() != B_OK)
1529 		RETURN_ERROR(nodeLocker.Status());
1530 
1531 	ObjectDeleter<FileCookie> cookieDeleter(cookie);
1532 
1533 	AutoLocker<Locker> locker(fLock);
1534 
1535 	// get a path for the node
1536 	char path[B_PATH_NAME_LENGTH];
1537 	size_t pathLen;
1538 	status_t error = _BuildPath(node, path, pathLen);
1539 	if (error != B_OK)
1540 		RETURN_ERROR(error);
1541 
1542 	locker.Unlock();
1543 
1544 	// release the file
1545 	int fuseError = fuse_fs_release(fFS, path, cookie);
1546 	if (fuseError != 0)
1547 		return fuseError;
1548 
1549 	return B_OK;
1550 }
1551 
1552 
1553 status_t
1554 FUSEVolume::Read(void* _node, void* _cookie, off_t pos, void* buffer,
1555 	size_t bufferSize, size_t* _bytesRead)
1556 {
1557 	FUSENode* node = (FUSENode*)_node;
1558 	FileCookie* cookie = (FileCookie*)_cookie;
1559 
1560 	RWLockableReadLocker cookieLocker(this, cookie);
1561 
1562 	*_bytesRead = 0;
1563 
1564 	// lock the directory
1565 	NodeReadLocker nodeLocker(this, node, true);
1566 	if (nodeLocker.Status() != B_OK)
1567 		RETURN_ERROR(nodeLocker.Status());
1568 
1569 	AutoLocker<Locker> locker(fLock);
1570 
1571 	// get a path for the node
1572 	char path[B_PATH_NAME_LENGTH];
1573 	size_t pathLen;
1574 	status_t error = _BuildPath(node, path, pathLen);
1575 	if (error != B_OK)
1576 		RETURN_ERROR(error);
1577 
1578 	locker.Unlock();
1579 
1580 	// read the file
1581 	int bytesRead = fuse_fs_read(fFS, path, (char*)buffer, bufferSize, pos,
1582 		cookie);
1583 	if (bytesRead < 0)
1584 		return bytesRead;
1585 
1586 	*_bytesRead = bytesRead;
1587 	return B_OK;
1588 }
1589 
1590 
1591 status_t
1592 FUSEVolume::Write(void* _node, void* _cookie, off_t pos, const void* buffer,
1593 	size_t bufferSize, size_t* _bytesWritten)
1594 {
1595 	FUSENode* node = (FUSENode*)_node;
1596 	FileCookie* cookie = (FileCookie*)_cookie;
1597 
1598 	RWLockableReadLocker cookieLocker(this, cookie);
1599 
1600 	*_bytesWritten = 0;
1601 
1602 	// lock the directory
1603 	NodeReadLocker nodeLocker(this, node, true);
1604 	if (nodeLocker.Status() != B_OK)
1605 		RETURN_ERROR(nodeLocker.Status());
1606 
1607 	AutoLocker<Locker> locker(fLock);
1608 
1609 	// get a path for the node
1610 	char path[B_PATH_NAME_LENGTH];
1611 	size_t pathLen;
1612 	status_t error = _BuildPath(node, path, pathLen);
1613 	if (error != B_OK)
1614 		RETURN_ERROR(error);
1615 
1616 	locker.Unlock();
1617 
1618 	// write the file
1619 	int bytesWritten = fuse_fs_write(fFS, path, (const char*)buffer, bufferSize,
1620 		pos, cookie);
1621 	if (bytesWritten < 0)
1622 		return bytesWritten;
1623 
1624 	// mark the node dirty
1625 	locker.Lock();
1626 	node->dirty = true;
1627 
1628 	// send node monitoring message
1629 	UserlandFS::KernelEmu::notify_listener(B_STAT_CHANGED,
1630 		B_STAT_SIZE | B_STAT_MODIFICATION_TIME, fID, 0, 0, node->id, NULL,
1631 		NULL);
1632 		// TODO: The size possibly doesn't change.
1633 		// TODO: Avoid message flooding -- use a timeout and set the
1634 		// B_STAT_INTERIM_UPDATE flag.
1635 
1636 	*_bytesWritten = bytesWritten;
1637 	return B_OK;
1638 }
1639 
1640 
1641 // #pragma mark - directories
1642 
1643 
1644 status_t
1645 FUSEVolume::CreateDir(void* _dir, const char* name, int mode)
1646 {
1647 	FUSENode* dir = (FUSENode*)_dir;
1648 	PRINT(("FUSEVolume::CreateDir(%p (%" B_PRId64 "), \"%s\", %#x)\n", dir,
1649 		dir->id, name, mode));
1650 
1651 	// lock the directory
1652 	NodeWriteLocker nodeLocker(this, dir, false);
1653 	if (nodeLocker.Status() != B_OK)
1654 		RETURN_ERROR(nodeLocker.Status());
1655 
1656 	AutoLocker<Locker> locker(fLock);
1657 
1658 	// get a path for the entry
1659 	char path[B_PATH_NAME_LENGTH];
1660 	size_t pathLen;
1661 	status_t error = _BuildPath(dir, name, path, pathLen);
1662 	if (error != B_OK)
1663 		RETURN_ERROR(error);
1664 
1665 	locker.Unlock();
1666 
1667 	// create the dir
1668 	int fuseError = fuse_fs_mkdir(fFS, path, mode);
1669 	if (fuseError != 0)
1670 		RETURN_ERROR(fuseError);
1671 
1672 	// mark the dir dirty
1673 	locker.Lock();
1674 	dir->dirty = true;
1675 
1676 	// send node monitoring message
1677 	ino_t nodeID;
1678 	if (_GetNodeID(dir, name, &nodeID)) {
1679 		UserlandFS::KernelEmu::notify_listener(B_ENTRY_CREATED, 0, fID, 0,
1680 			dir->id, nodeID, NULL, name);
1681 	}
1682 
1683 	return B_OK;
1684 }
1685 
1686 
1687 status_t
1688 FUSEVolume::RemoveDir(void* _dir, const char* name)
1689 {
1690 	FUSENode* dir = (FUSENode*)_dir;
1691 	PRINT(("FUSEVolume::RemoveDir(%p (%" B_PRId64 "), \"%s\")\n", dir, dir->id,
1692 		name));
1693 
1694 	// lock the directory
1695 	NodeWriteLocker nodeLocker(this, dir, false);
1696 	if (nodeLocker.Status() != B_OK)
1697 		RETURN_ERROR(nodeLocker.Status());
1698 
1699 	// get the node ID (for the node monitoring message)
1700 	ino_t nodeID;
1701 	bool doNodeMonitoring = _GetNodeID(dir, name, &nodeID);
1702 
1703 	AutoLocker<Locker> locker(fLock);
1704 
1705 	// get a path for the entry
1706 	char path[B_PATH_NAME_LENGTH];
1707 	size_t pathLen;
1708 	status_t error = _BuildPath(dir, name, path, pathLen);
1709 	if (error != B_OK)
1710 		RETURN_ERROR(error);
1711 
1712 	locker.Unlock();
1713 
1714 	// remove the dir
1715 	int fuseError = fuse_fs_rmdir(fFS, path);
1716 	if (fuseError != 0)
1717 		RETURN_ERROR(fuseError);
1718 
1719 	// remove the entry
1720 	locker.Lock();
1721 	_RemoveEntry(dir, name);
1722 
1723 	// mark the parent dir dirty
1724 	dir->dirty = true;
1725 
1726 	// send node monitoring message
1727 	if (doNodeMonitoring) {
1728 		UserlandFS::KernelEmu::notify_listener(B_ENTRY_REMOVED, 0, fID, 0,
1729 			dir->id, nodeID, NULL, name);
1730 	}
1731 
1732 	return B_OK;
1733 }
1734 
1735 
1736 status_t
1737 FUSEVolume::OpenDir(void* _node, void** _cookie)
1738 {
1739 	FUSENode* node = (FUSENode*)_node;
1740 	PRINT(("FUSEVolume::OpenDir(%p (%" B_PRId64 "), %p)\n", node, node->id,
1741 		_cookie));
1742 
1743 	// lock the parent directory
1744 	NodeReadLocker nodeLocker(this, node, true);
1745 	if (nodeLocker.Status() != B_OK)
1746 		RETURN_ERROR(nodeLocker.Status());
1747 
1748 	// allocate a dir cookie
1749 	DirCookie* cookie = new(std::nothrow) DirCookie;
1750 	if (cookie == NULL)
1751 		RETURN_ERROR(B_NO_MEMORY);
1752 	ObjectDeleter<DirCookie> cookieDeleter(cookie);
1753 
1754 	AutoLocker<Locker> locker(fLock);
1755 
1756 	// get a path for the node
1757 	char path[B_PATH_NAME_LENGTH];
1758 	size_t pathLen;
1759 	status_t error = _BuildPath(node, path, pathLen);
1760 	if (error != B_OK)
1761 		RETURN_ERROR(error);
1762 
1763 	locker.Unlock();
1764 
1765 	if (fFS->ops.readdir == NULL && fFS->ops.getdir != NULL) {
1766 		// no open call -- the FS only supports the deprecated getdir()
1767 		// interface
1768 		cookie->getdirInterface = true;
1769 	} else {
1770 		// open the dir
1771 		int fuseError = fuse_fs_opendir(fFS, path, cookie);
1772 		if (fuseError != 0)
1773 			return fuseError;
1774 	}
1775 
1776 	cookieDeleter.Detach();
1777 	*_cookie = cookie;
1778 
1779 	return B_OK;
1780 }
1781 
1782 
1783 status_t
1784 FUSEVolume::CloseDir(void* node, void* _cookie)
1785 {
1786 	return B_OK;
1787 }
1788 
1789 
1790 status_t
1791 FUSEVolume::FreeDirCookie(void* _node, void* _cookie)
1792 {
1793 	FUSENode* node = (FUSENode*)_node;
1794 	DirCookie* cookie = (DirCookie*)_cookie;
1795 
1796 	// lock the parent directory
1797 	NodeReadLocker nodeLocker(this, node, true);
1798 	if (nodeLocker.Status() != B_OK)
1799 		RETURN_ERROR(nodeLocker.Status());
1800 
1801 	ObjectDeleter<DirCookie> cookieDeleter(cookie);
1802 
1803 	if (cookie->getdirInterface)
1804 		return B_OK;
1805 
1806 	AutoLocker<Locker> locker(fLock);
1807 
1808 	// get a path for the node
1809 	char path[B_PATH_NAME_LENGTH];
1810 	size_t pathLen;
1811 	status_t error = _BuildPath(node, path, pathLen);
1812 	if (error != B_OK)
1813 		RETURN_ERROR(error);
1814 
1815 	locker.Unlock();
1816 
1817 	// release the dir
1818 	int fuseError = fuse_fs_releasedir(fFS, path, cookie);
1819 	if (fuseError != 0)
1820 		return fuseError;
1821 
1822 	return B_OK;
1823 }
1824 
1825 
1826 status_t
1827 FUSEVolume::ReadDir(void* _node, void* _cookie, void* buffer, size_t bufferSize,
1828 	uint32 count, uint32* _countRead)
1829 {
1830 	PRINT(("FUSEVolume::ReadDir(%p, %p, %p, %" B_PRIuSIZE ", %" B_PRId32 ")\n",
1831 		_node, _cookie, buffer, bufferSize, count));
1832 	*_countRead = 0;
1833 
1834 	FUSENode* node = (FUSENode*)_node;
1835 	DirCookie* cookie = (DirCookie*)_cookie;
1836 
1837 	RWLockableWriteLocker cookieLocker(this, cookie);
1838 
1839 	uint32 countRead = 0;
1840 	status_t readDirError = B_OK;
1841 
1842 	AutoLocker<Locker> locker(fLock);
1843 
1844 	if (cookie->entryCache == NULL) {
1845 		// We don't have an entry cache (yet), so we need to ask the client
1846 		// file system to read the directory.
1847 
1848 		locker.Unlock();
1849 
1850 		// lock the directory
1851 		NodeReadLocker nodeLocker(this, node, false);
1852 		if (nodeLocker.Status() != B_OK)
1853 			RETURN_ERROR(nodeLocker.Status());
1854 
1855 		locker.Lock();
1856 
1857 		ReadDirBuffer readDirBuffer(this, node, cookie, buffer, bufferSize,
1858 			count);
1859 
1860 		// get a path for the node
1861 		char path[B_PATH_NAME_LENGTH];
1862 		size_t pathLen;
1863 		status_t error = _BuildPath(node, path, pathLen);
1864 		if (error != B_OK)
1865 			RETURN_ERROR(error);
1866 
1867 		off_t offset = cookie->currentEntryOffset;
1868 
1869 		locker.Unlock();
1870 
1871 		// read the dir
1872 		int fuseError;
1873 		if (cookie->getdirInterface) {
1874 PRINT(("  using getdir() interface\n"));
1875 			fuseError = fFS->ops.getdir(path, (fuse_dirh_t)&readDirBuffer,
1876 				&_AddReadDirEntryGetDir);
1877 		} else {
1878 PRINT(("  using readdir() interface\n"));
1879 			fuseError = fuse_fs_readdir(fFS, path, &readDirBuffer,
1880 				&_AddReadDirEntry, offset, cookie);
1881 		}
1882 		if (fuseError != 0)
1883 			return fuseError;
1884 
1885 		locker.Lock();
1886 
1887 		countRead = readDirBuffer.entriesRead;
1888 		readDirError = readDirBuffer.error;
1889 	}
1890 
1891 	if (cookie->entryCache != NULL) {
1892 		// we're using an entry cache -- read into the buffer what we can
1893 		dirent* entryBuffer = (dirent*)buffer;
1894 		while (countRead < count
1895 			&& cookie->entryCache->ReadDirent(cookie->currentEntryIndex, fID,
1896 				countRead + 1 < count, entryBuffer, bufferSize)) {
1897 			countRead++;
1898 			cookie->currentEntryIndex++;
1899 			bufferSize -= entryBuffer->d_reclen;
1900 			entryBuffer
1901 				= (dirent*)((uint8*)entryBuffer + entryBuffer->d_reclen);
1902 		}
1903 	}
1904 
1905 	*_countRead = countRead;
1906 	return countRead > 0 ? B_OK : readDirError;
1907 }
1908 
1909 
1910 status_t
1911 FUSEVolume::RewindDir(void* _node, void* _cookie)
1912 {
1913 PRINT(("FUSEVolume::RewindDir(%p, %p)\n", _node, _cookie));
1914 	DirCookie* cookie = (DirCookie*)_cookie;
1915 
1916 	RWLockableWriteLocker cookieLocker(this, cookie);
1917 
1918 	if (cookie->getdirInterface) {
1919 		delete cookie->entryCache;
1920 		cookie->entryCache = NULL;
1921 		cookie->currentEntryIndex = 0;
1922 	} else {
1923 		cookie->currentEntryOffset = 0;
1924 	}
1925 
1926 	return B_OK;
1927 }
1928 
1929 
1930 // #pragma mark - attribute directories
1931 
1932 
1933 // OpenAttrDir
1934 status_t
1935 FUSEVolume::OpenAttrDir(void* _node, void** _cookie)
1936 {
1937 	// allocate an attribute directory cookie
1938 	AttrDirCookie* cookie = new(std::nothrow) AttrDirCookie;
1939 	if (cookie == NULL)
1940 		RETURN_ERROR(B_NO_MEMORY);
1941 
1942 	*_cookie = cookie;
1943 
1944 	return B_OK;
1945 }
1946 
1947 
1948 // CloseAttrDir
1949 status_t
1950 FUSEVolume::CloseAttrDir(void* node, void* cookie)
1951 {
1952 	return B_OK;
1953 }
1954 
1955 
1956 // FreeAttrDirCookie
1957 status_t
1958 FUSEVolume::FreeAttrDirCookie(void* _node, void* _cookie)
1959 {
1960 	delete (AttrDirCookie*)_cookie;
1961 	return B_OK;
1962 }
1963 
1964 
1965 // ReadAttrDir
1966 status_t
1967 FUSEVolume::ReadAttrDir(void* _node, void* _cookie, void* buffer,
1968 	size_t bufferSize, uint32 count, uint32* _countRead)
1969 {
1970 	FUSENode* node = (FUSENode*)_node;
1971 	AttrDirCookie* cookie = (AttrDirCookie*)_cookie;
1972 
1973 	RWLockableWriteLocker cookieLocker(this, cookie);
1974 
1975 	*_countRead = 0;
1976 
1977 	// lock the directory
1978 	NodeReadLocker nodeLocker(this, node, true);
1979 	if (nodeLocker.Status() != B_OK)
1980 		RETURN_ERROR(nodeLocker.Status());
1981 
1982 	AutoLocker<Locker> locker(fLock);
1983 
1984 	// get a path for the node
1985 	char path[B_PATH_NAME_LENGTH];
1986 	size_t pathLen;
1987 	status_t error = _BuildPath(node, path, pathLen);
1988 	if (error != B_OK)
1989 		RETURN_ERROR(error);
1990 
1991 	locker.Unlock();
1992 
1993 	if (!cookie->IsValid()) {
1994 		// cookie not yet valid -- get the length of the list
1995 		int listSize = fuse_fs_listxattr(fFS, path, NULL, 0);
1996 		if (listSize < 0)
1997 			RETURN_ERROR(listSize);
1998 
1999 		while (true) {
2000 			// allocate space for the listing
2001 			error = cookie->Allocate(listSize);
2002 			if (error != B_OK)
2003 				RETURN_ERROR(error);
2004 
2005 			// read the listing
2006 			int bytesRead = fuse_fs_listxattr(fFS, path,
2007 				cookie->AttributesBuffer(), listSize);
2008 			if (bytesRead < 0)
2009 				RETURN_ERROR(bytesRead);
2010 
2011 			if (bytesRead == listSize)
2012 				break;
2013 
2014 			// attributes listing changed -- reread it
2015 			listSize = bytesRead;
2016 		}
2017 
2018 		cookie->SetValid(true);
2019 	}
2020 
2021 	// we have a valid cookie now -- get the next entries from the cookie
2022 	uint32 countRead = 0;
2023 	dirent* entryBuffer = (dirent*)buffer;
2024 	while (countRead < count
2025 		&& cookie->ReadNextEntry(fID, node->id, countRead + 1 < count,
2026 			entryBuffer, bufferSize)) {
2027 		countRead++;
2028 		bufferSize -= entryBuffer->d_reclen;
2029 		entryBuffer = (dirent*)((uint8*)entryBuffer + entryBuffer->d_reclen);
2030 	}
2031 
2032 	*_countRead = countRead;
2033 	return B_OK;
2034 }
2035 
2036 
2037 // RewindAttrDir
2038 status_t
2039 FUSEVolume::RewindAttrDir(void* _node, void* _cookie)
2040 {
2041 	AttrDirCookie* cookie = (AttrDirCookie*)_cookie;
2042 
2043 	RWLockableWriteLocker cookieLocker(this, cookie);
2044 
2045 	cookie->Clear();
2046 
2047 	return B_OK;
2048 }
2049 
2050 
2051 // #pragma mark -
2052 
2053 
2054 ino_t
2055 FUSEVolume::_GenerateNodeID()
2056 {
2057 	ino_t id;
2058 	do {
2059 		id = fNextNodeID++;
2060 	} while (fNodes.Lookup(id) != NULL);
2061 
2062 	return id;
2063 }
2064 
2065 
2066 /*!	Gets the ID of the node the entry specified by \a dir and \a entryName
2067 	refers to. The ID is returned via \a _nodeID. The caller doesn't get a
2068 	reference to the node.
2069 */
2070 bool
2071 FUSEVolume::_GetNodeID(FUSENode* dir, const char* entryName, ino_t* _nodeID)
2072 {
2073 	while (true) {
2074 		AutoLocker<Locker> locker(fLock);
2075 
2076 		FUSENode* node;
2077 		status_t error = _InternalGetNode(dir, entryName, &node, locker);
2078 		if (error != B_OK)
2079 			return false;
2080 
2081 		if (node == NULL)
2082 			continue;
2083 
2084 		*_nodeID = node->id;
2085 		_PutNode(node);
2086 
2087 		return true;
2088 	}
2089 }
2090 
2091 
2092 /*!	Gets the node the entry specified by \a dir and \a entryName refers to. The
2093 	found node is returned via \a _node. The caller gets a reference to the node
2094 	as well as a vnode reference.
2095 */
2096 status_t
2097 FUSEVolume::_GetNode(FUSENode* dir, const char* entryName, FUSENode** _node)
2098 {
2099 	while (true) {
2100 		AutoLocker<Locker> locker(fLock);
2101 
2102 		FUSENode* node;
2103 		status_t error = _InternalGetNode(dir, entryName, &node, locker);
2104 		if (error != B_OK)
2105 			return error;
2106 
2107 		if (node == NULL)
2108 			continue;
2109 
2110 		ino_t nodeID = node->id;
2111 
2112 		locker.Unlock();
2113 
2114 		// get a reference for the caller
2115 		void* privateNode;
2116 		error = UserlandFS::KernelEmu::get_vnode(fID, nodeID, &privateNode);
2117 		if (error != B_OK)
2118 			RETURN_ERROR(error);
2119 
2120 		locker.Lock();
2121 
2122 		if (privateNode != node) {
2123 			// weird, the node changed!
2124 			ERROR(("FUSEVolume::_GetNode(): cookie for node %" B_PRId64
2125 				" changed: expected: %p, got: %p\n", nodeID, node,
2126 				privateNode));
2127 			UserlandFS::KernelEmu::put_vnode(fID, nodeID);
2128 			_PutNode(node);
2129 			continue;
2130 		}
2131 
2132 		// Put the node reference we got from _InternalGetNode. We've now got
2133 		// a reference from get_vnode().
2134 		_PutNode(node);
2135 
2136 		*_node = node;
2137 		return B_OK;
2138 	}
2139 }
2140 
2141 
2142 status_t
2143 FUSEVolume::_InternalGetNode(FUSENode* dir, const char* entryName,
2144 	FUSENode** _node, AutoLocker<Locker>& locker)
2145 {
2146 	// handle special cases
2147 	if (strcmp(entryName, ".") == 0) {
2148 		// same directory
2149 		if (!S_ISDIR(dir->type))
2150 			RETURN_ERROR(B_NOT_A_DIRECTORY);
2151 
2152 		dir->refCount++;
2153 		*_node = dir;
2154 		return B_OK;
2155 	}
2156 
2157 	if (strcmp(entryName, "..") == 0) {
2158 		// parent directory
2159 		if (!S_ISDIR(dir->type))
2160 			RETURN_ERROR(B_NOT_A_DIRECTORY);
2161 
2162 		FUSEEntry* entry = dir->entries.Head();
2163 		if (entry == NULL)
2164 			RETURN_ERROR(B_ENTRY_NOT_FOUND);
2165 
2166 		entry->parent->refCount++;
2167 		*_node = entry->parent;
2168 		return B_OK;
2169 	}
2170 
2171 	// lookup the entry in the table
2172 	FUSEEntry* entry = fEntries.Lookup(FUSEEntryRef(dir->id, entryName));
2173 	if (entry != NULL) {
2174 		entry->node->refCount++;
2175 		*_node = entry->node;
2176 		return B_OK;
2177 	}
2178 
2179 	// construct a path for the entry
2180 	char path[B_PATH_NAME_LENGTH];
2181 	size_t pathLen = 0;
2182 	status_t error = _BuildPath(dir, entryName, path, pathLen);
2183 	if (error != B_OK)
2184 		return error;
2185 
2186 	locker.Unlock();
2187 
2188 	// stat the path
2189 	struct stat st;
2190 	int fuseError = fuse_fs_getattr(fFS, path, &st);
2191 	if (fuseError != 0)
2192 		return fuseError;
2193 
2194 	locker.Lock();
2195 
2196 	// lookup the entry in the table again
2197 	entry = fEntries.Lookup(FUSEEntryRef(dir->id, entryName));
2198 	if (entry != NULL) {
2199 		// check whether the node still matches
2200 		if (entry->node->id == st.st_ino) {
2201 			entry->node->refCount++;
2202 			*_node = entry->node;
2203 		} else {
2204 			// nope, something changed -- return a NULL node and let the caller
2205 			// call us again
2206 			*_node = NULL;
2207 		}
2208 
2209 		return B_OK;
2210 	}
2211 
2212 	// lookup the node in the table
2213 	FUSENode* node = NULL;
2214 	if (fUseNodeIDs)
2215 		node = fNodes.Lookup(st.st_ino);
2216 	else
2217 		st.st_ino = _GenerateNodeID();
2218 
2219 	if (node == NULL) {
2220 		// no node yet -- create one
2221 		node = new(std::nothrow) FUSENode(st.st_ino, st.st_mode & S_IFMT);
2222 		if (node == NULL)
2223 			RETURN_ERROR(B_NO_MEMORY);
2224 
2225 		fNodes.Insert(node);
2226 	} else {
2227 		// get a node reference for the entry
2228 		node->refCount++;
2229 	}
2230 
2231 	// create the entry
2232 	entry = FUSEEntry::Create(dir, entryName, node);
2233 	if (entry == NULL) {
2234 		_PutNode(node);
2235 		RETURN_ERROR(B_NO_MEMORY);
2236 	}
2237 
2238 	dir->refCount++;
2239 		// dir reference for the entry
2240 
2241 	fEntries.Insert(entry);
2242 	node->entries.Add(entry);
2243 
2244 	locker.Unlock();
2245 
2246 	// get a reference for the caller
2247 	node->refCount++;
2248 
2249 	*_node = node;
2250 	return B_OK;
2251 }
2252 
2253 
2254 void
2255 FUSEVolume::_PutNode(FUSENode* node)
2256 {
2257 	if (--node->refCount == 0) {
2258 		fNodes.Remove(node);
2259 		delete node;
2260 	}
2261 }
2262 
2263 
2264 void
2265 FUSEVolume::_PutNodes(FUSENode* const* nodes, int32 count)
2266 {
2267 	for (int32 i = 0; i < count; i++)
2268 		_PutNode(nodes[i]);
2269 }
2270 
2271 
2272 /*!	Volume must be locked. The entry's directory must be write locked.
2273  */
2274 void
2275 FUSEVolume::_RemoveEntry(FUSEEntry* entry)
2276 {
2277 	fEntries.Remove(entry);
2278 	entry->node->entries.Remove(entry);
2279 	_PutNode(entry->node);
2280 	_PutNode(entry->parent);
2281 	delete entry;
2282 }
2283 
2284 
2285 /*!	Volume must be locked. The directory must be write locked.
2286  */
2287 status_t
2288 FUSEVolume::_RemoveEntry(FUSENode* dir, const char* name)
2289 {
2290 	FUSEEntry* entry = fEntries.Lookup(FUSEEntryRef(dir->id, name));
2291 	if (entry == NULL)
2292 		return B_ENTRY_NOT_FOUND;
2293 
2294 	_RemoveEntry(entry);
2295 	return B_OK;
2296 }
2297 
2298 
2299 /*!	Volume must be locked. The directories must be write locked.
2300  */
2301 status_t
2302 FUSEVolume::_RenameEntry(FUSENode* oldDir, const char* oldName,
2303 	FUSENode* newDir, const char* newName)
2304 {
2305 	FUSEEntry* entry = fEntries.Lookup(FUSEEntryRef(oldDir->id, oldName));
2306 	if (entry == NULL)
2307 		return B_ENTRY_NOT_FOUND;
2308 
2309 	// get a node reference for the new entry
2310 	FUSENode* node = entry->node;
2311 	node->refCount++;
2312 
2313 	// remove the old entry
2314 	_RemoveEntry(entry);
2315 
2316 	// make sure there's no entry in our way
2317 	_RemoveEntry(newDir, newName);
2318 
2319 	// create a new entry
2320 	entry = FUSEEntry::Create(newDir, newName, node);
2321 	if (entry == NULL) {
2322 		_PutNode(node);
2323 		RETURN_ERROR(B_NO_MEMORY);
2324 	}
2325 
2326 	newDir->refCount++;
2327 		// dir reference for the entry
2328 
2329 	fEntries.Insert(entry);
2330 	node->entries.Add(entry);
2331 
2332 	return B_OK;
2333 }
2334 
2335 
2336 /*!	Locks the given node and all of its ancestors up to the root. The given
2337 	node is write-locked, if \a writeLock is \c true, read-locked otherwise. All
2338 	ancestors are always read-locked in either case.
2339 
2340 	If \a lockParent is \c true, the given node itself is ignored, but locking
2341 	starts with the parent node of the given node (\a writeLock applies to the
2342 	parent node then).
2343 
2344 	If the method fails, none of the nodes is locked.
2345 
2346 	The volume lock must not be held.
2347 */
2348 status_t
2349 FUSEVolume::_LockNodeChain(FUSENode* node, bool lockParent, bool writeLock)
2350 {
2351 	AutoLocker<Locker> locker(fLock);
2352 
2353 	FUSENode* originalNode = node;
2354 
2355 	if (lockParent && node != NULL)
2356 		node = node->Parent();
2357 
2358 	if (node == NULL)
2359 		RETURN_ERROR(B_ENTRY_NOT_FOUND);
2360 
2361 	LockIterator iterator(this, node, writeLock, NULL);
2362 
2363 	bool done;
2364 	do {
2365 		bool volumeUnlocked;
2366 		status_t error = iterator.LockNext(&done, &volumeUnlocked);
2367 		if (error != B_OK)
2368 			RETURN_ERROR(error);
2369 
2370 		if (volumeUnlocked) {
2371 			// check whether we're still locking the right node
2372 			if (lockParent && originalNode->Parent() != node) {
2373 				// We don't -- unlock everything and try again.
2374 				node = originalNode->Parent();
2375 				iterator.SetTo(this, node, writeLock, NULL);
2376 			}
2377 		}
2378 	} while (!done);
2379 
2380 	// Fail, if we couldn't lock all nodes up to the root.
2381 	if (iterator.lastLockedNode != fRootNode)
2382 		RETURN_ERROR(B_ENTRY_NOT_FOUND);
2383 
2384 	iterator.Detach();
2385 	return B_OK;
2386 }
2387 
2388 
2389 void
2390 FUSEVolume::_UnlockNodeChain(FUSENode* node, bool parent, bool writeLock)
2391 {
2392 	AutoLocker<Locker> locker(fLock);
2393 
2394 	if (parent && node != NULL)
2395 		node = node->Parent();
2396 
2397 	_UnlockNodeChainInternal(node, writeLock, NULL, NULL);
2398 }
2399 
2400 
2401 /*!	Unlocks all nodes from \a node up to (and including) \a stopNode (if
2402 	\c NULL, it is ignored). If \a stopBeforeNode is given, the method stops
2403 	before unlocking that node.
2404 	The volume lock must be held.
2405  */
2406 void
2407 FUSEVolume::_UnlockNodeChainInternal(FUSENode* node, bool writeLock,
2408 	FUSENode* stopNode, FUSENode* stopBeforeNode)
2409 {
2410 	FUSENode* originalNode = node;
2411 
2412 	while (node != NULL && node != stopBeforeNode) {
2413 		FUSENode* parent = node->Parent();
2414 
2415 		fLockManager.GenericUnlock(node == originalNode && writeLock, node);
2416 		_PutNode(node);
2417 
2418 		if (node == stopNode || parent == node)
2419 			break;
2420 
2421 		node = parent;
2422 	}
2423 }
2424 
2425 
2426 status_t
2427 FUSEVolume::_LockNodeChains(FUSENode* node1, bool lockParent1, bool writeLock1,
2428 	FUSENode* node2, bool lockParent2, bool writeLock2)
2429 {
2430 	// Since in this case locking is more complicated, we use a helper method.
2431 	// It does the main work, but simply returns telling us to retry when the
2432 	// node hierarchy changes.
2433 	bool retry;
2434 	do {
2435 		status_t error = _LockNodeChainsInternal(node1, lockParent1, writeLock1,
2436 			node2, lockParent2, writeLock2, &retry);
2437 		if (error != B_OK)
2438 			return error;
2439 	} while (retry);
2440 
2441 	return B_OK;
2442 }
2443 
2444 
2445 status_t
2446 FUSEVolume::_LockNodeChainsInternal(FUSENode* node1, bool lockParent1,
2447 	bool writeLock1, FUSENode* node2, bool lockParent2, bool writeLock2,
2448 	bool* _retry)
2449 {
2450 	// Locking order:
2451 	// * A child of a node has to be locked before its parent.
2452 	// * Sibling nodes have to be locked in ascending node ID order.
2453 	//
2454 	// This implies the following locking algorithm:
2455 	// * We find the closest common ancestor of the two given nodes (might even
2456 	//   be one of the given nodes).
2457 	// * We lock all ancestors on one branch (the one with the lower common
2458 	//   ancestor child node ID), but not including the common ancestor.
2459 	// * We lock all ancestors on the other branch, not including the common
2460 	//   ancestor.
2461 	// * We lock the common ancestor and all of its ancestors up to the root
2462 	//   node.
2463 	//
2464 	// When the hierarchy changes while we're waiting for a lock, we recheck the
2465 	// conditions and in doubt have to be restarted.
2466 
2467 	AutoLocker<Locker> locker(fLock);
2468 
2469 	FUSENode* originalNode1 = node1;
2470 	FUSENode* originalNode2 = node2;
2471 
2472 	if (lockParent1 && node1 != NULL)
2473 		node1 = node1->Parent();
2474 
2475 	if (lockParent2 && node2 != NULL)
2476 		node2 = node2->Parent();
2477 
2478 	if (node1 == NULL || node2 == NULL)
2479 		RETURN_ERROR(B_ENTRY_NOT_FOUND);
2480 
2481 	// find the first common ancestor
2482 	FUSENode* commonAncestor;
2483 	bool inverseLockingOrder;
2484 	if (!_FindCommonAncestor(node1, node2, &commonAncestor,
2485 			&inverseLockingOrder)) {
2486 		RETURN_ERROR(B_ENTRY_NOT_FOUND);
2487 	}
2488 
2489 	// lock the both node chains up to (but not including) the common ancestor
2490 	LockIterator iterator1(this, node1, writeLock1, commonAncestor);
2491 	LockIterator iterator2(this, node2, writeLock2, commonAncestor);
2492 
2493 	for (int i = 0; i < 2; i++) {
2494 		LockIterator& iterator = (i == 0) != inverseLockingOrder
2495 			? iterator1 : iterator2;
2496 
2497 		// If the node is the common ancestor, don't enter the "do" loop, since
2498 		// we don't have to lock anything here.
2499 		if (iterator.firstNode == commonAncestor)
2500 			continue;
2501 
2502 		bool done;
2503 		do {
2504 			bool volumeUnlocked;
2505 			status_t error = iterator.LockNext(&done, &volumeUnlocked);
2506 			if (error != B_OK)
2507 				RETURN_ERROR(error);
2508 
2509 			if (volumeUnlocked) {
2510 				// check whether we're still locking the right nodes
2511 				if ((lockParent1 && originalNode1->Parent() != node1)
2512 					|| (lockParent2 && originalNode2->Parent() != node2)) {
2513 					// We don't -- unlock everything and retry.
2514 					*_retry = true;
2515 					return B_OK;
2516 				}
2517 
2518 				// also recheck the common ancestor
2519 				FUSENode* newCommonParent;
2520 				bool newInverseLockingOrder;
2521 				if (!_FindCommonAncestor(node1, node2, &newCommonParent,
2522 						&newInverseLockingOrder)) {
2523 					RETURN_ERROR(B_ENTRY_NOT_FOUND);
2524 				}
2525 
2526 				if (newCommonParent != commonAncestor
2527 					|| inverseLockingOrder != newInverseLockingOrder) {
2528 					// Something changed -- unlock everything and retry.
2529 					*_retry = true;
2530 					return B_OK;
2531 				}
2532 			}
2533 		} while (!done);
2534 	}
2535 
2536 	// Continue locking from the common ancestor to the root. If one of the
2537 	// given nodes is the common ancestor and shall be write locked, we need to
2538 	// use the respective iterator.
2539 	LockIterator& iterator = node2 == commonAncestor && writeLock2
2540 		? iterator2 : iterator1;
2541 	iterator.SetStopBeforeNode(NULL);
2542 
2543 	bool done;
2544 	do {
2545 		bool volumeUnlocked;
2546 		status_t error = iterator.LockNext(&done, &volumeUnlocked);
2547 		if (error != B_OK)
2548 			RETURN_ERROR(error);
2549 
2550 		if (volumeUnlocked) {
2551 			// check whether we're still locking the right nodes
2552 			if ((lockParent1 && originalNode1->Parent() != node1)
2553 				|| (lockParent2 && originalNode2->Parent() != node2)) {
2554 				// We don't -- unlock everything and retry.
2555 				*_retry = true;
2556 				return B_OK;
2557 			}
2558 
2559 			// Also recheck the common ancestor, if we have just locked it.
2560 			// Otherwise we can just continue to lock, since nothing below the
2561 			// previously locked node can have changed.
2562 			if (iterator.lastLockedNode == commonAncestor) {
2563 				FUSENode* newCommonParent;
2564 				bool newInverseLockingOrder;
2565 				if (!_FindCommonAncestor(node1, node2, &newCommonParent,
2566 						&newInverseLockingOrder)) {
2567 					RETURN_ERROR(B_ENTRY_NOT_FOUND);
2568 				}
2569 
2570 				if (newCommonParent != commonAncestor
2571 					|| inverseLockingOrder != newInverseLockingOrder) {
2572 					// Something changed -- unlock everything and retry.
2573 					*_retry = true;
2574 					return B_OK;
2575 				}
2576 			}
2577 		}
2578 	} while (!done);
2579 
2580 	// Fail, if we couldn't lock all nodes up to the root.
2581 	if (iterator.lastLockedNode != fRootNode)
2582 		RETURN_ERROR(B_ENTRY_NOT_FOUND);
2583 
2584 	// everything went fine
2585 	iterator1.Detach();
2586 	iterator2.Detach();
2587 
2588 	*_retry = false;
2589 	return B_OK;
2590 }
2591 
2592 
2593 void
2594 FUSEVolume::_UnlockNodeChains(FUSENode* node1, bool lockParent1,
2595 	bool writeLock1, FUSENode* node2, bool lockParent2, bool writeLock2)
2596 {
2597 	AutoLocker<Locker> locker(fLock);
2598 
2599 	if (lockParent1 && node1 != NULL)
2600 		node1 = node1->Parent();
2601 
2602 	if (lockParent2 && node2 != NULL)
2603 		node2 = node2->Parent();
2604 
2605 	if (node1 == NULL || node2 == NULL)
2606 		return;
2607 
2608 	// find the common ancestor
2609 	FUSENode* commonAncestor;
2610 	bool inverseLockingOrder;
2611 	if (!_FindCommonAncestor(node1, node2, &commonAncestor,
2612 			&inverseLockingOrder)) {
2613 		return;
2614 	}
2615 
2616 	// Unlock one branch up to the common ancestor and then the complete other
2617 	// branch up to the root. If one of the given nodes is the common ancestor,
2618 	// we need to make sure, we write-unlock it, if requested.
2619 	if (node2 == commonAncestor && writeLock2) {
2620 		_UnlockNodeChainInternal(node1, writeLock1, NULL, commonAncestor);
2621 		_UnlockNodeChainInternal(node2, writeLock2, NULL, NULL);
2622 	} else {
2623 		_UnlockNodeChainInternal(node2, writeLock2, NULL, commonAncestor);
2624 		_UnlockNodeChainInternal(node1, writeLock1, NULL, NULL);
2625 	}
2626 }
2627 
2628 
2629 bool
2630 FUSEVolume::_FindCommonAncestor(FUSENode* node1, FUSENode* node2,
2631 	FUSENode** _commonAncestor, bool* _inverseLockingOrder)
2632 {
2633 	// handle trivial special case -- both nodes are the same
2634 	if (node1 == node2) {
2635 		*_commonAncestor = node1;
2636 		*_inverseLockingOrder = false;
2637 		return true;
2638 	}
2639 
2640 	// get the ancestors of both nodes
2641 	FUSENode* ancestors1[kMaxNodeTreeDepth];
2642 	FUSENode* ancestors2[kMaxNodeTreeDepth];
2643 	uint32 count1;
2644 	uint32 count2;
2645 
2646 	if (!_GetNodeAncestors(node1, ancestors1, &count1)
2647 		|| !_GetNodeAncestors(node2, ancestors2, &count2)) {
2648 		return false;
2649 	}
2650 
2651 	// find the first ancestor not common to both nodes
2652 	uint32 index = 0;
2653 	for (; index < count1 && index < count2; index++) {
2654 		FUSENode* ancestor1 = ancestors1[count1 - index - 1];
2655 		FUSENode* ancestor2 = ancestors2[count2 - index - 1];
2656 		if (ancestor1 != ancestor2) {
2657 			*_commonAncestor = ancestors1[count1 - index];
2658 			*_inverseLockingOrder = ancestor1->id > ancestor2->id;
2659 			return true;
2660 		}
2661 	}
2662 
2663 	// one node is an ancestor of the other
2664 	*_commonAncestor = ancestors1[count1 - index];
2665 	*_inverseLockingOrder = index == count1;
2666 	return true;
2667 }
2668 
2669 
2670 bool
2671 FUSEVolume::_GetNodeAncestors(FUSENode* node, FUSENode** ancestors,
2672 	uint32* _count)
2673 {
2674 	uint32 count = 0;
2675 	while (node != NULL && count < kMaxNodeTreeDepth) {
2676 		ancestors[count++] = node;
2677 
2678 		if (node == fRootNode) {
2679 			*_count = count;
2680 			return true;
2681 		}
2682 
2683 		node = node->Parent();
2684 	}
2685 
2686 	// Either the node is not in the tree or we hit the array limit.
2687 	return false;
2688 }
2689 
2690 
2691 status_t
2692 FUSEVolume::_BuildPath(FUSENode* dir, const char* entryName, char* path,
2693 	size_t& pathLen)
2694 {
2695 	// get the directory path
2696 	status_t error = _BuildPath(dir, path, pathLen);
2697 	if (error != B_OK)
2698 		return error;
2699 
2700 	if (path[pathLen - 1] != '/') {
2701 		path[pathLen++] = '/';
2702 		if (pathLen == B_PATH_NAME_LENGTH)
2703 			RETURN_ERROR(B_NAME_TOO_LONG);
2704 	}
2705 
2706 	// append the entry name
2707 	size_t len = strlen(entryName);
2708 	if (pathLen + len >= B_PATH_NAME_LENGTH)
2709 		RETURN_ERROR(B_NAME_TOO_LONG);
2710 
2711 	memcpy(path + pathLen, entryName, len + 1);
2712 	pathLen += len;
2713 
2714 	return B_OK;
2715 }
2716 
2717 
2718 status_t
2719 FUSEVolume::_BuildPath(FUSENode* node, char* path, size_t& pathLen)
2720 {
2721 	if (node == fRootNode) {
2722 		// we hit the root
2723 		strcpy(path, "/");
2724 		pathLen = 1;
2725 		return B_OK;
2726 	}
2727 
2728 	// get an entry for the node and get its path
2729 	FUSEEntry* entry = node->entries.Head();
2730 	if (entry == NULL)
2731 		RETURN_ERROR(B_ENTRY_NOT_FOUND);
2732 
2733 	return _BuildPath(entry->parent, entry->name, path, pathLen);
2734 }
2735 
2736 
2737 /*static*/ int
2738 FUSEVolume::_AddReadDirEntry(void* _buffer, const char* name,
2739 	const struct stat* st, off_t offset)
2740 {
2741 	ReadDirBuffer* buffer = (ReadDirBuffer*)_buffer;
2742 
2743 	ino_t nodeID = st != NULL ? st->st_ino : 0;
2744 	int type = st != NULL ? st->st_mode & S_IFMT : 0;
2745 	return buffer->volume->_AddReadDirEntry(buffer, name, type, nodeID, offset);
2746 }
2747 
2748 
2749 /*static*/ int
2750 FUSEVolume::_AddReadDirEntryGetDir(fuse_dirh_t handle, const char* name,
2751 	int type, ino_t nodeID)
2752 {
2753 	ReadDirBuffer* buffer = (ReadDirBuffer*)handle;
2754 	return buffer->volume->_AddReadDirEntry(buffer, name, type << 12, nodeID,
2755 		0);
2756 }
2757 
2758 
2759 int
2760 FUSEVolume::_AddReadDirEntry(ReadDirBuffer* buffer, const char* name, int type,
2761 	ino_t nodeID, off_t offset)
2762 {
2763 	PRINT(("FUSEVolume::_AddReadDirEntry(%p, \"%s\", %#x, %" B_PRId64 ", %"
2764 		B_PRId64 "\n", buffer, name, type, nodeID, offset));
2765 
2766 	AutoLocker<Locker> locker(fLock);
2767 
2768 	size_t entryLen = 0;
2769 	if (offset != 0) {
2770 		// does the caller want more entries?
2771 		if (buffer->entriesRead == buffer->maxEntries)
2772 			return 1;
2773 
2774 		// compute the entry length and check whether the entry still fits
2775 		entryLen = sizeof(dirent) + strlen(name);
2776 		if (buffer->usedSize + entryLen > buffer->bufferSize)
2777 			return 1;
2778 	}
2779 
2780 	// create a node and an entry, if necessary
2781 	ino_t dirID = buffer->directory->id;
2782 	FUSEEntry* entry;
2783 	if (strcmp(name, ".") == 0) {
2784 		// current dir entry
2785 		nodeID = dirID;
2786 		type = S_IFDIR;
2787 	} else if (strcmp(name, "..") == 0) {
2788 		// parent dir entry
2789 		FUSEEntry* parentEntry = buffer->directory->entries.Head();
2790 		if (parentEntry == NULL) {
2791 			ERROR(("FUSEVolume::_AddReadDirEntry(): dir %" B_PRId64
2792 				" has no entry!\n", dirID));
2793 			return 0;
2794 		}
2795 		nodeID = parentEntry->parent->id;
2796 		type = S_IFDIR;
2797 	} else if ((entry = fEntries.Lookup(FUSEEntryRef(dirID, name))) == NULL) {
2798 		// get the node
2799 		FUSENode* node = NULL;
2800 		if (fUseNodeIDs)
2801 			node = fNodes.Lookup(nodeID);
2802 		else
2803 			nodeID = _GenerateNodeID();
2804 
2805 		if (node == NULL) {
2806 			// no node yet -- create one
2807 
2808 			// If we don't have a valid type, we need to stat the node first.
2809 			if (type == 0) {
2810 				char path[B_PATH_NAME_LENGTH];
2811 				size_t pathLen;
2812 				status_t error = _BuildPath(buffer->directory, name, path,
2813 					pathLen);
2814 				if (error != B_OK) {
2815 					buffer->error = error;
2816 					return 0;
2817 				}
2818 
2819 				locker.Unlock();
2820 
2821 				// stat the path
2822 				struct stat st;
2823 				int fuseError = fuse_fs_getattr(fFS, path, &st);
2824 
2825 				locker.Lock();
2826 
2827 				if (fuseError != 0) {
2828 					buffer->error = fuseError;
2829 					return 0;
2830 				}
2831 
2832 				type = st.st_mode & S_IFMT;
2833 			}
2834 
2835 			node = new(std::nothrow) FUSENode(nodeID, type);
2836 			if (node == NULL) {
2837 				buffer->error = B_NO_MEMORY;
2838 				return 1;
2839 			}
2840 			PRINT(("  -> create node: %p, id: %" B_PRId64 "\n", node, nodeID));
2841 
2842 			fNodes.Insert(node);
2843 		} else {
2844 			// get a node reference for the entry
2845 			node->refCount++;
2846 		}
2847 
2848 		// create the entry
2849 		entry = FUSEEntry::Create(buffer->directory, name, node);
2850 		if (entry == NULL) {
2851 			_PutNode(node);
2852 			buffer->error = B_NO_MEMORY;
2853 			return 1;
2854 		}
2855 
2856 		buffer->directory->refCount++;
2857 			// dir reference for the entry
2858 
2859 		fEntries.Insert(entry);
2860 		node->entries.Add(entry);
2861 	} else {
2862 		// TODO: Check whether the node's ID matches the one we got (if any)!
2863 		nodeID = entry->node->id;
2864 		type = entry->node->type;
2865 	}
2866 
2867 	if (offset == 0) {
2868 		// cache the entry
2869 		if (buffer->cookie->entryCache == NULL) {
2870 			// no cache yet -- create it
2871 			buffer->cookie->entryCache = new(std::nothrow) DirEntryCache;
2872 			if (buffer->cookie->entryCache == NULL) {
2873 				buffer->error = B_NO_MEMORY;
2874 				return 1;
2875 			}
2876 		}
2877 
2878 		status_t error = buffer->cookie->entryCache->AddEntry(nodeID, name);
2879 		if (error != B_OK) {
2880 			buffer->error = error;
2881 			return 1;
2882 		}
2883 	} else {
2884 		// fill in the dirent
2885 		dirent* dirEntry = (dirent*)((uint8*)buffer->buffer + buffer->usedSize);
2886 		dirEntry->d_dev = fID;
2887 		dirEntry->d_ino = nodeID;
2888 		strcpy(dirEntry->d_name, name);
2889 
2890 		if (buffer->entriesRead + 1 < buffer->maxEntries) {
2891 			// align the entry length, so the next dirent will be aligned
2892 			entryLen = (entryLen + 7) / 8 * 8;
2893 			entryLen = std::min(entryLen,
2894 				buffer->bufferSize - buffer->usedSize);
2895 		}
2896 
2897 		dirEntry->d_reclen = entryLen;
2898 
2899 		// update the buffer
2900 		buffer->usedSize += entryLen;
2901 		buffer->entriesRead++;
2902 		buffer->cookie->currentEntryOffset = offset;
2903 	}
2904 
2905 	return 0;
2906 }
2907