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