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