1/*
2 * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Distributed under the terms of the MIT License.
4 */
5
6#include "FUSEFileSystem.h"
7
8#include <stdlib.h>
9#include <string.h>
10
11#include <new>
12
13#include "fuse_fs.h"
14#include "FUSELowLevel.h"
15#include "FUSEVolume.h"
16
17#include "../RequestThread.h"
18
19
20class FUSEFileSystem::ArgumentVector {
21private:
22	enum { MAX_ARGUMENTS = 128 };
23
24public:
25	ArgumentVector()
26		:
27		fBuffer(NULL),
28		fCount(0)
29	{
30	}
31
32	~ArgumentVector()
33	{
34		free(fBuffer);
35	}
36
37	const char* const* Arguments() const
38	{
39		return fArguments;
40	}
41
42	int ArgumentCount() const
43	{
44		return fCount;
45	}
46
47	status_t Init(const char* firstElement, const char* arguments)
48	{
49		size_t firstElementSize = firstElement != NULL
50			? strlen(firstElement) + 1 : 0;
51
52		// allocate the buffer
53		fBuffer = (char*)malloc(firstElementSize + strlen(arguments) + 1);
54		if (fBuffer == NULL)
55			return B_NO_MEMORY;
56
57		fCount = 0;
58
59		bool inArgument = false;
60		int bufferIndex = 0;
61
62		// push the first element, if given
63		if (firstElement != NULL) {
64			memcpy(fBuffer, firstElement, firstElementSize);
65			fArguments[fCount++] = fBuffer;
66			bufferIndex = firstElementSize;
67		}
68
69		// parse the given string
70		for (; *arguments != '\0'; arguments++) {
71			char c = *arguments;
72			switch (c) {
73				case ' ':
74				case '\t':
75				case '\r':
76				case '\n':
77					// white-space marks argument boundaries
78					if (inArgument) {
79						// terminate the current argument
80						fBuffer[bufferIndex++] = '\0';
81						inArgument = false;
82					}
83					break;
84				case '\\':
85					c = *++arguments;
86					if (c == '\0')
87						break;
88					// fall through
89				default:
90					if (!inArgument) {
91						// push a new argument
92						if (fCount == MAX_ARGUMENTS)
93							break;
94
95						fArguments[fCount++] = fBuffer + bufferIndex;
96						inArgument = true;
97					}
98
99					fBuffer[bufferIndex++] = c;
100					break;
101			}
102		}
103
104		// terminate the last argument
105		if (inArgument)
106			fBuffer[bufferIndex++] = '\0';
107
108		// NULL terminate the argument array
109		fArguments[fCount] = NULL;
110
111		return B_OK;
112	}
113
114private:
115	char*		fBuffer;
116	const char*	fArguments[MAX_ARGUMENTS + 1];
117	int			fCount;
118};
119
120
121FUSEFileSystem::FUSEFileSystem(const char* fsName,
122	int (*mainFunction)(int, const char* const*))
123	:
124	FileSystem(fsName),
125	fMainFunction(mainFunction),
126	fInitThread(-1),
127	fInitStatus(B_NO_INIT),
128	fInitSemaphore(-1),
129	fExitSemaphore(-1),
130	fInitParameters(NULL),
131	fUserData(NULL),
132	fFS(NULL)
133{
134	fClientFSType = CLIENT_FS_FUSE;
135
136	// FS capabilities
137	fCapabilities.ClearAll();
138	fCapabilities.Set(FS_CAPABILITY_MOUNT, true);
139}
140
141
142FUSEFileSystem::~FUSEFileSystem()
143{
144	if (fInitSemaphore >= 0)
145		delete_sem(fInitSemaphore);
146
147	if (fExitSemaphore >= 0)
148		delete_sem(fExitSemaphore);
149
150	if (fInitThread >= 0)
151		wait_for_thread(fInitThread, NULL);
152}
153
154
155status_t
156FUSEFileSystem::CreateVolume(Volume** _volume, dev_t id)
157{
158printf("FUSEFileSystem::CreateVolume()\n");
159	// Only one volume is possible
160	if (!fVolumes.IsEmpty())
161		RETURN_ERROR(B_BUSY);
162
163	// create the volume
164	FUSEVolume* volume = new(std::nothrow) FUSEVolume(this, id);
165	if (volume == NULL)
166		return B_NO_MEMORY;
167
168	status_t error = volume->Init();
169	if (error != B_OK) {
170		delete volume;
171		return error;
172	}
173
174	*_volume = volume;
175	return B_OK;
176}
177
178
179status_t
180FUSEFileSystem::DeleteVolume(Volume* volume)
181{
182	delete volume;
183	return B_OK;
184}
185
186
187void
188FUSEFileSystem::InitRequestThreadContext(RequestThreadContext* context)
189{
190	// Statically assert that fuse_context fits in the RequestThreadContext
191	// FS data. We can't include <Debug.h> as it clashes with our "Debug.h".
192	do {
193		static const int staticAssertHolds
194			= sizeof(fuse_context) <= REQUEST_THREAD_CONTEXT_FS_DATA_SIZE;
195		struct __staticAssertStruct__ {
196			char __static_assert_failed__[2 * staticAssertHolds - 1];
197		};
198	} while (false);
199
200	// init a fuse_context
201	KernelRequest* request = context->GetRequest();
202	fuse_context* fuseContext = (fuse_context*)context->GetFSData();
203	fuseContext->fuse = (struct fuse*)this;
204	fuseContext->uid = request->user;
205	fuseContext->gid = request->group;
206	fuseContext->pid = request->team;
207	fuseContext->private_data = fFS != NULL ? fFS->userData : NULL;
208}
209
210
211status_t
212FUSEFileSystem::InitClientFS(const char* parameters)
213{
214PRINT(("FUSEFileSystem::InitClientFS()\n"));
215	// create the semaphores we need
216	fInitSemaphore = create_sem(0, "FUSE init sem");
217	if (fInitSemaphore < 0)
218		RETURN_ERROR(fInitSemaphore);
219
220	fExitSemaphore = create_sem(0, "FUSE exit sem");
221	if (fExitSemaphore < 0)
222		RETURN_ERROR(fExitSemaphore);
223
224	fInitStatus = 1;
225	fInitParameters = parameters;
226
227	// Start the initialization thread -- it will call main() and won't return
228	// until unmounting.
229	fInitThread = spawn_thread(&_InitializationThreadEntry,
230		"FUSE init", B_NORMAL_PRIORITY, this);
231	if (fInitThread < 0)
232		RETURN_ERROR(fInitThread);
233
234	resume_thread(fInitThread);
235
236	// wait for the initialization to finish
237PRINT(("  waiting for init thread...\n"));
238	while (acquire_sem(fInitSemaphore) == B_INTERRUPTED) {
239	}
240
241PRINT(("  waiting for init thread done\n"));
242	fInitSemaphore = -1;
243
244	if (fInitStatus > 0)
245		RETURN_ERROR(B_ERROR);
246	if (fInitStatus != B_OK)
247		RETURN_ERROR(fInitStatus);
248
249	// initialization went fine
250	return B_OK;
251}
252
253
254void
255FUSEFileSystem::ExitClientFS(status_t status)
256{
257	// set the exit status and notify the initialization thread
258	fExitStatus = status;
259	if (fExitSemaphore >= 0)
260		delete_sem(fExitSemaphore);
261
262	if (fInitThread >= 0)
263		wait_for_thread(fInitThread, NULL);
264}
265
266
267status_t
268FUSEFileSystem::FinishInitClientFS(fuse_config* config,
269	const fuse_operations* ops, size_t opSize, void* userData)
270{
271PRINT(("FUSEFileSystem::FinishInitClientFS()\n"));
272	fExitStatus = B_ERROR;
273
274	fFUSEConfig = *config;
275
276	// do the initialization
277	fInitStatus = _InitClientFS(ops, opSize, userData);
278	return fInitStatus;
279}
280
281
282status_t
283FUSEFileSystem::FinishInitClientFS(fuse_config* config,
284	const fuse_lowlevel_ops* ops, size_t opSize, void* userData)
285{
286PRINT(("FUSEFileSystem::FinishInitClientFS()\n"));
287	fExitStatus = B_ERROR;
288
289	fFUSEConfig = *config;
290
291	// do the initialization
292	fInitStatus = _InitClientFS(ops, opSize, userData);
293	return fInitStatus;
294}
295
296
297status_t
298FUSEFileSystem::MainLoop(bool multithreaded)
299{
300	// TODO: Respect the multithreaded flag!
301
302PRINT(("FUSEFileSystem::FinishMounting()\n"));
303	// notify the mount thread
304PRINT(("  notifying mount thread\n"));
305	delete_sem(fInitSemaphore);
306
307	// loop until unmounting
308PRINT(("  waiting for unmounting...\n"));
309	while (acquire_sem(fExitSemaphore) == B_INTERRUPTED) {
310	}
311PRINT(("  waiting for unmounting done\n"));
312
313	fExitSemaphore = -1;
314
315	if (fFS != NULL)
316		fuse_fs_destroy(fFS);
317	else
318		fuse_ll_destroy(&fLowLevelOps, fUserData);
319
320	return fExitStatus;
321}
322
323
324/*static*/ status_t
325FUSEFileSystem::_InitializationThreadEntry(void* data)
326{
327	return ((FUSEFileSystem*)data)->_InitializationThread();
328}
329
330
331status_t
332FUSEFileSystem::_InitializationThread()
333{
334	// parse the parameters
335	ArgumentVector args;
336	status_t error = args.Init(GetName(), fInitParameters);
337	if (error != B_OK) {
338		fInitStatus = error;
339		delete_sem(fInitSemaphore);
340		return B_OK;
341	}
342
343	// call main -- should not return until unmounting
344	fMainFunction(args.ArgumentCount(), args.Arguments());
345printf("FUSEFileSystem::_InitializationThread(): main() returned!\n");
346
347	if (fInitStatus > 0 && fInitSemaphore >= 0) {
348		// something went wrong early -- main() returned without calling
349		// fuse_main()
350		fInitStatus = B_ERROR;
351		delete_sem(fInitSemaphore);
352	}
353
354	return B_OK;
355}
356
357
358status_t
359FUSEFileSystem::_InitClientFS(const fuse_operations* ops, size_t opSize,
360	void* userData)
361{
362	// create a fuse_fs object
363	fFS = fuse_fs_new(ops, opSize, userData);
364	if (fFS == NULL)
365		return B_ERROR;
366
367	_InitCapabilities();
368PRINT(("volume capabilities:\n"));
369fVolumeCapabilities.Dump();
370PRINT(("node capabilities:\n"));
371fNodeCapabilities.Dump();
372
373	// init connection info
374	fConnectionInfo.proto_major = 0;
375	fConnectionInfo.proto_minor = 0;
376	fConnectionInfo.async_read = false;
377	fConnectionInfo.max_write = 64 * 1024;
378	fConnectionInfo.max_readahead = 64 * 1024;
379	fConnectionInfo.capable = FUSE_CAP_ATOMIC_O_TRUNC | FUSE_CAP_BIG_WRITES | FUSE_CAP_IOCTL_DIR
380		| FUSE_CAP_HAIKU_FUSE_EXTENSIONS;
381
382	fuse_fs_init(fFS, &fConnectionInfo);
383
384	return B_OK;
385}
386
387
388status_t
389FUSEFileSystem::_InitClientFS(const fuse_lowlevel_ops* lowLevelOps, size_t lowLevelOpSize,
390	void* userData)
391{
392	fLowLevelOps = *lowLevelOps;
393
394	_InitCapabilities();
395PRINT(("volume capabilities:\n"));
396fVolumeCapabilities.Dump();
397PRINT(("node capabilities:\n"));
398fNodeCapabilities.Dump();
399
400	// init connection info
401	fConnectionInfo.proto_major = 0;
402	fConnectionInfo.proto_minor = 0;
403	fConnectionInfo.async_read = false;
404	fConnectionInfo.max_write = 64 * 1024;
405	fConnectionInfo.max_readahead = 64 * 1024;
406	fUserData = userData;
407
408	fuse_ll_init(&fLowLevelOps, userData, &fConnectionInfo);
409
410	return B_OK;
411}
412
413
414void
415FUSEFileSystem::_InitCapabilities()
416{
417	fVolumeCapabilities.ClearAll();
418	fNodeCapabilities.ClearAll();
419
420	// Volume operations
421	fVolumeCapabilities.Set(FS_VOLUME_CAPABILITY_UNMOUNT, true);
422	fVolumeCapabilities.Set(FS_VOLUME_CAPABILITY_GET_VNODE, true);
423		// emulated
424
425	// vnode operations
426	fNodeCapabilities.Set(FS_VNODE_CAPABILITY_LOOKUP, true);
427		// emulated
428	fNodeCapabilities.Set(FS_VNODE_CAPABILITY_GET_VNODE_NAME, true);
429		// emulated
430	fNodeCapabilities.Set(FS_VNODE_CAPABILITY_PUT_VNODE, true);
431		// emulated
432	fNodeCapabilities.Set(FS_VNODE_CAPABILITY_REMOVE_VNODE, true);
433		// emulated
434
435	// asynchronous I/O
436	fNodeCapabilities.Set(FS_VNODE_CAPABILITY_IO, true);
437		// emulated
438
439	// index directory & index operations
440	// missing: FS_VOLUME_CAPABILITY_OPEN_INDEX_DIR
441	// missing: FS_VOLUME_CAPABILITY_CLOSE_INDEX_DIR
442	// missing: FS_VOLUME_CAPABILITY_FREE_INDEX_DIR_COOKIE
443	// missing: FS_VOLUME_CAPABILITY_READ_INDEX_DIR
444	// missing: FS_VOLUME_CAPABILITY_REWIND_INDEX_DIR
445
446	// missing: FS_VOLUME_CAPABILITY_CREATE_INDEX
447	// missing: FS_VOLUME_CAPABILITY_REMOVE_INDEX
448	// missing: FS_VOLUME_CAPABILITY_READ_INDEX_STAT
449
450	// query operations
451	// missing: FS_VOLUME_CAPABILITY_OPEN_QUERY
452	// missing: FS_VOLUME_CAPABILITY_CLOSE_QUERY
453	// missing: FS_VOLUME_CAPABILITY_FREE_QUERY_COOKIE
454	// missing: FS_VOLUME_CAPABILITY_READ_QUERY
455	// missing: FS_VOLUME_CAPABILITY_REWIND_QUERY
456
457	// VM file access
458	// missing: FS_VNODE_CAPABILITY_CAN_PAGE
459	// missing: FS_VNODE_CAPABILITY_READ_PAGES
460	// missing: FS_VNODE_CAPABILITY_WRITE_PAGES
461
462	// cache file access
463	// missing: FS_VNODE_CAPABILITY_GET_FILE_MAP
464
465	// common operations
466	// missing: FS_VNODE_CAPABILITY_IOCTL
467	fNodeCapabilities.Set(FS_VNODE_CAPABILITY_SET_FLAGS, true);
468		// emulated
469	// missing: FS_VNODE_CAPABILITY_SELECT
470	// missing: FS_VNODE_CAPABILITY_DESELECT
471
472	fNodeCapabilities.Set(FS_VNODE_CAPABILITY_CLOSE_ATTR, false);
473
474	if (fFS == NULL) {
475		fNodeCapabilities.Set(FS_VNODE_CAPABILITY_FSYNC, fLowLevelOps.fsync);
476
477		fNodeCapabilities.Set(FS_VNODE_CAPABILITY_READ_SYMLINK, fLowLevelOps.readlink);
478		fNodeCapabilities.Set(FS_VNODE_CAPABILITY_CREATE_SYMLINK, fLowLevelOps.symlink);
479
480		fNodeCapabilities.Set(FS_VNODE_CAPABILITY_LINK, fLowLevelOps.link);
481		fNodeCapabilities.Set(FS_VNODE_CAPABILITY_UNLINK, fLowLevelOps.unlink);
482		fNodeCapabilities.Set(FS_VNODE_CAPABILITY_RENAME, fLowLevelOps.rename);
483
484		fNodeCapabilities.Set(FS_VNODE_CAPABILITY_ACCESS, fLowLevelOps.access);
485		fNodeCapabilities.Set(FS_VNODE_CAPABILITY_READ_STAT, fLowLevelOps.getattr);
486		fNodeCapabilities.Set(FS_VNODE_CAPABILITY_WRITE_STAT, fLowLevelOps.setattr != NULL);
487
488		fVolumeCapabilities.Set(FS_VOLUME_CAPABILITY_READ_FS_INFO, fLowLevelOps.statfs);
489		// missing: FS_VOLUME_CAPABILITY_WRITE_FS_INFO
490		fVolumeCapabilities.Set(FS_VOLUME_CAPABILITY_SYNC, fLowLevelOps.fsync);
491		// emulated via fsync()
492
493		// file operations
494		fNodeCapabilities.Set(FS_VNODE_CAPABILITY_CREATE, fLowLevelOps.create);
495		fNodeCapabilities.Set(FS_VNODE_CAPABILITY_OPEN, fLowLevelOps.open);
496		fNodeCapabilities.Set(FS_VNODE_CAPABILITY_CLOSE, fLowLevelOps.flush);
497		fNodeCapabilities.Set(FS_VNODE_CAPABILITY_FREE_COOKIE, fLowLevelOps.release);
498		fNodeCapabilities.Set(FS_VNODE_CAPABILITY_READ, fLowLevelOps.read);
499		fNodeCapabilities.Set(FS_VNODE_CAPABILITY_WRITE, fLowLevelOps.write);
500
501		// directory operations
502		fNodeCapabilities.Set(FS_VNODE_CAPABILITY_CREATE_DIR, fLowLevelOps.mkdir);
503		fNodeCapabilities.Set(FS_VNODE_CAPABILITY_REMOVE_DIR, fLowLevelOps.rmdir);
504		bool readDirSupport = fLowLevelOps.opendir != NULL || fLowLevelOps.readdir != NULL;
505		fNodeCapabilities.Set(FS_VNODE_CAPABILITY_OPEN_DIR, readDirSupport);
506		// not needed: FS_VNODE_CAPABILITY_CLOSE_DIR
507		fNodeCapabilities.Set(FS_VNODE_CAPABILITY_FREE_DIR_COOKIE, readDirSupport);
508		fNodeCapabilities.Set(FS_VNODE_CAPABILITY_READ_DIR, readDirSupport);
509		fNodeCapabilities.Set(FS_VNODE_CAPABILITY_REWIND_DIR, readDirSupport);
510
511		// attribute directory operations
512		bool hasAttributes = fLowLevelOps.listxattr != NULL;
513		fNodeCapabilities.Set(FS_VNODE_CAPABILITY_OPEN_ATTR_DIR, hasAttributes);
514		// not needed: FS_VNODE_CAPABILITY_CLOSE_ATTR_DIR
515		fNodeCapabilities.Set(FS_VNODE_CAPABILITY_FREE_ATTR_DIR_COOKIE, hasAttributes);
516		fNodeCapabilities.Set(FS_VNODE_CAPABILITY_READ_ATTR_DIR, hasAttributes);
517		fNodeCapabilities.Set(FS_VNODE_CAPABILITY_REWIND_ATTR_DIR, hasAttributes);
518
519		// attribute operations
520		// 	// we emulate open_attr() and free_attr_dir_cookie() if either read_attr()
521		// 	// or write_attr() is present
522		// 	bool hasAttributes = (fLowLevelOps.read_attr || fLowLevelOps.write_attr);
523		// 	fNodeCapabilities.Set(FS_VNODE_CAPABILITY_CREATE_ATTR, hasAttributes);
524		fNodeCapabilities.Set(FS_VNODE_CAPABILITY_OPEN_ATTR, hasAttributes);
525		fNodeCapabilities.Set(FS_VNODE_CAPABILITY_FREE_ATTR_COOKIE, hasAttributes);
526		fNodeCapabilities.Set(FS_VNODE_CAPABILITY_READ_ATTR, fLowLevelOps.getxattr);
527		// 	fNodeCapabilities.Set(FS_VNODE_CAPABILITY_WRITE_ATTR, fLowLevelOps.write_attr);
528
529		fNodeCapabilities.Set(FS_VNODE_CAPABILITY_READ_ATTR_STAT, fLowLevelOps.getxattr);
530// 		// missing: FS_VNODE_CAPABILITY_WRITE_ATTR_STAT
531// 		fNodeCapabilities.Set(FS_VNODE_CAPABILITY_RENAME_ATTR, fLowLevelOps.rename_attr);
532	} else {
533		fNodeCapabilities.Set(FS_VNODE_CAPABILITY_FSYNC, fFS->ops.fsync);
534
535		fNodeCapabilities.Set(FS_VNODE_CAPABILITY_READ_SYMLINK, fFS->ops.readlink);
536		fNodeCapabilities.Set(FS_VNODE_CAPABILITY_CREATE_SYMLINK, fFS->ops.symlink);
537
538		fNodeCapabilities.Set(FS_VNODE_CAPABILITY_LINK, fFS->ops.link);
539		fNodeCapabilities.Set(FS_VNODE_CAPABILITY_UNLINK, fFS->ops.unlink);
540		fNodeCapabilities.Set(FS_VNODE_CAPABILITY_RENAME, fFS->ops.rename);
541
542		fNodeCapabilities.Set(FS_VNODE_CAPABILITY_ACCESS, fFS->ops.access);
543		fNodeCapabilities.Set(FS_VNODE_CAPABILITY_READ_STAT, fFS->ops.getattr);
544		fNodeCapabilities.Set(FS_VNODE_CAPABILITY_WRITE_STAT,
545				fFS->ops.chmod != NULL || fFS->ops.chown != NULL
546				|| fFS->ops.truncate != NULL || fFS->ops.utimens != NULL
547				|| fFS->ops.utime != NULL);
548
549		fVolumeCapabilities.Set(FS_VOLUME_CAPABILITY_READ_FS_INFO, fFS->ops.statfs);
550		// missing: FS_VOLUME_CAPABILITY_WRITE_FS_INFO
551		fVolumeCapabilities.Set(FS_VOLUME_CAPABILITY_SYNC, fFS->ops.fsync);
552		// emulated via fsync()
553
554		// file operations
555		fNodeCapabilities.Set(FS_VNODE_CAPABILITY_CREATE, fFS->ops.create);
556		fNodeCapabilities.Set(FS_VNODE_CAPABILITY_OPEN, fFS->ops.open);
557		fNodeCapabilities.Set(FS_VNODE_CAPABILITY_CLOSE, fFS->ops.flush);
558		fNodeCapabilities.Set(FS_VNODE_CAPABILITY_FREE_COOKIE, fFS->ops.release);
559		fNodeCapabilities.Set(FS_VNODE_CAPABILITY_READ, fFS->ops.read);
560		fNodeCapabilities.Set(FS_VNODE_CAPABILITY_WRITE, fFS->ops.write);
561
562		// directory operations
563		fNodeCapabilities.Set(FS_VNODE_CAPABILITY_CREATE_DIR, fFS->ops.mkdir);
564		fNodeCapabilities.Set(FS_VNODE_CAPABILITY_REMOVE_DIR, fFS->ops.rmdir);
565		bool readDirSupport = fFS->ops.opendir != NULL || fFS->ops.readdir != NULL
566			|| fFS->ops.getdir;
567		fNodeCapabilities.Set(FS_VNODE_CAPABILITY_OPEN_DIR, readDirSupport);
568		// not needed: FS_VNODE_CAPABILITY_CLOSE_DIR
569		fNodeCapabilities.Set(FS_VNODE_CAPABILITY_FREE_DIR_COOKIE, readDirSupport);
570		fNodeCapabilities.Set(FS_VNODE_CAPABILITY_READ_DIR, readDirSupport);
571		fNodeCapabilities.Set(FS_VNODE_CAPABILITY_REWIND_DIR, readDirSupport);
572
573		// attribute directory operations
574		bool hasAttributes = fFS->ops.listxattr != NULL;
575		fNodeCapabilities.Set(FS_VNODE_CAPABILITY_OPEN_ATTR_DIR, hasAttributes);
576		// not needed: FS_VNODE_CAPABILITY_CLOSE_ATTR_DIR
577		fNodeCapabilities.Set(FS_VNODE_CAPABILITY_FREE_ATTR_DIR_COOKIE,
578				hasAttributes);
579		fNodeCapabilities.Set(FS_VNODE_CAPABILITY_READ_ATTR_DIR, hasAttributes);
580		fNodeCapabilities.Set(FS_VNODE_CAPABILITY_REWIND_ATTR_DIR, hasAttributes);
581
582		// attribute operations
583		// 	// we emulate open_attr() and free_attr_dir_cookie() if either read_attr()
584		// 	// or write_attr() is present
585		// 	bool hasAttributes = (fFS->ops.read_attr || fFS->ops.write_attr);
586		// 	fNodeCapabilities.Set(FS_VNODE_CAPABILITY_CREATE_ATTR, hasAttributes);
587		fNodeCapabilities.Set(FS_VNODE_CAPABILITY_OPEN_ATTR, hasAttributes);
588		fNodeCapabilities.Set(FS_VNODE_CAPABILITY_FREE_ATTR_COOKIE, hasAttributes);
589		fNodeCapabilities.Set(FS_VNODE_CAPABILITY_READ_ATTR, fFS->ops.getxattr);
590		// 	fNodeCapabilities.Set(FS_VNODE_CAPABILITY_WRITE_ATTR, fFS->ops.write_attr);
591
592		fNodeCapabilities.Set(FS_VNODE_CAPABILITY_READ_ATTR_STAT,
593		fFS->ops.getxattr);
594// 		// missing: FS_VNODE_CAPABILITY_WRITE_ATTR_STAT
595// 		fNodeCapabilities.Set(FS_VNODE_CAPABILITY_RENAME_ATTR, fFS->ops.rename_attr);
596	}
597}
598
599
600// #pragma mark - bootstrapping
601
602
603status_t
604userlandfs_create_file_system(const char* fsName, image_id image,
605	FileSystem** _fileSystem)
606{
607printf("userlandfs_create_file_system()\n");
608	// look up the main() function of the add-on
609	int (*mainFunction)(int argc, const char* const* argv);
610	status_t error = get_image_symbol(image, "main", B_SYMBOL_TYPE_TEXT,
611		(void**)&mainFunction);
612	if (error != B_OK)
613		return error;
614printf("userlandfs_create_file_system(): found main: %p\n", mainFunction);
615
616	// create the file system
617	FUSEFileSystem* fileSystem = new(std::nothrow) FUSEFileSystem(fsName,
618		mainFunction);
619	if (fileSystem == NULL)
620		return B_NO_MEMORY;
621
622	*_fileSystem = fileSystem;
623	return B_OK;
624}
625