xref: /haiku/src/add-ons/kernel/file_systems/userlandfs/server/fuse/FUSEFileSystem.cpp (revision 4a55cc230cf7566cadcbb23b1928eefff8aea9a2)
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 
20 class FUSEFileSystem::ArgumentVector {
21 private:
22 	enum { MAX_ARGUMENTS = 128 };
23 
24 public:
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 
114 private:
115 	char*		fBuffer;
116 	const char*	fArguments[MAX_ARGUMENTS + 1];
117 	int			fCount;
118 };
119 
120 
121 FUSEFileSystem::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 
142 FUSEFileSystem::~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 
155 status_t
156 FUSEFileSystem::CreateVolume(Volume** _volume, dev_t id)
157 {
158 printf("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 
179 status_t
180 FUSEFileSystem::DeleteVolume(Volume* volume)
181 {
182 	delete volume;
183 	return B_OK;
184 }
185 
186 
187 void
188 FUSEFileSystem::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 
211 status_t
212 FUSEFileSystem::InitClientFS(const char* parameters)
213 {
214 PRINT(("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
237 PRINT(("  waiting for init thread...\n"));
238 	while (acquire_sem(fInitSemaphore) == B_INTERRUPTED) {
239 	}
240 
241 PRINT(("  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 
254 void
255 FUSEFileSystem::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 
267 status_t
268 FUSEFileSystem::FinishInitClientFS(fuse_config* config,
269 	const fuse_operations* ops, size_t opSize, void* userData)
270 {
271 PRINT(("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 
282 status_t
283 FUSEFileSystem::FinishInitClientFS(fuse_config* config,
284 	const fuse_lowlevel_ops* ops, size_t opSize, void* userData)
285 {
286 PRINT(("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 
297 status_t
298 FUSEFileSystem::MainLoop(bool multithreaded)
299 {
300 	// TODO: Respect the multithreaded flag!
301 
302 PRINT(("FUSEFileSystem::FinishMounting()\n"));
303 	// notify the mount thread
304 PRINT(("  notifying mount thread\n"));
305 	delete_sem(fInitSemaphore);
306 
307 	// loop until unmounting
308 PRINT(("  waiting for unmounting...\n"));
309 	while (acquire_sem(fExitSemaphore) == B_INTERRUPTED) {
310 	}
311 PRINT(("  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
325 FUSEFileSystem::_InitializationThreadEntry(void* data)
326 {
327 	return ((FUSEFileSystem*)data)->_InitializationThread();
328 }
329 
330 
331 status_t
332 FUSEFileSystem::_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());
345 printf("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 
358 status_t
359 FUSEFileSystem::_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();
368 PRINT(("volume capabilities:\n"));
369 fVolumeCapabilities.Dump();
370 PRINT(("node capabilities:\n"));
371 fNodeCapabilities.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 
388 status_t
389 FUSEFileSystem::_InitClientFS(const fuse_lowlevel_ops* lowLevelOps, size_t lowLevelOpSize,
390 	void* userData)
391 {
392 	fLowLevelOps = *lowLevelOps;
393 
394 	_InitCapabilities();
395 PRINT(("volume capabilities:\n"));
396 fVolumeCapabilities.Dump();
397 PRINT(("node capabilities:\n"));
398 fNodeCapabilities.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 
414 void
415 FUSEFileSystem::_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 	// index directory & index operations
436 	// missing: FS_VOLUME_CAPABILITY_OPEN_INDEX_DIR
437 	// missing: FS_VOLUME_CAPABILITY_CLOSE_INDEX_DIR
438 	// missing: FS_VOLUME_CAPABILITY_FREE_INDEX_DIR_COOKIE
439 	// missing: FS_VOLUME_CAPABILITY_READ_INDEX_DIR
440 	// missing: FS_VOLUME_CAPABILITY_REWIND_INDEX_DIR
441 
442 	// missing: FS_VOLUME_CAPABILITY_CREATE_INDEX
443 	// missing: FS_VOLUME_CAPABILITY_REMOVE_INDEX
444 	// missing: FS_VOLUME_CAPABILITY_READ_INDEX_STAT
445 
446 	// query operations
447 	// missing: FS_VOLUME_CAPABILITY_OPEN_QUERY
448 	// missing: FS_VOLUME_CAPABILITY_CLOSE_QUERY
449 	// missing: FS_VOLUME_CAPABILITY_FREE_QUERY_COOKIE
450 	// missing: FS_VOLUME_CAPABILITY_READ_QUERY
451 	// missing: FS_VOLUME_CAPABILITY_REWIND_QUERY
452 
453 	// VM file access
454 	// missing: FS_VNODE_CAPABILITY_CAN_PAGE
455 	// missing: FS_VNODE_CAPABILITY_READ_PAGES
456 	// missing: FS_VNODE_CAPABILITY_WRITE_PAGES
457 
458 	// cache file access
459 	// missing: FS_VNODE_CAPABILITY_GET_FILE_MAP
460 
461 	// common operations
462 	// missing: FS_VNODE_CAPABILITY_IOCTL
463 	fNodeCapabilities.Set(FS_VNODE_CAPABILITY_SET_FLAGS, true);
464 		// emulated
465 	// missing: FS_VNODE_CAPABILITY_SELECT
466 	// missing: FS_VNODE_CAPABILITY_DESELECT
467 
468 	fNodeCapabilities.Set(FS_VNODE_CAPABILITY_CLOSE_ATTR, false);
469 
470 	if (fFS == NULL) {
471 		fNodeCapabilities.Set(FS_VNODE_CAPABILITY_FSYNC, fLowLevelOps.fsync);
472 
473 		fNodeCapabilities.Set(FS_VNODE_CAPABILITY_READ_SYMLINK, fLowLevelOps.readlink);
474 		fNodeCapabilities.Set(FS_VNODE_CAPABILITY_CREATE_SYMLINK, fLowLevelOps.symlink);
475 
476 		fNodeCapabilities.Set(FS_VNODE_CAPABILITY_LINK, fLowLevelOps.link);
477 		fNodeCapabilities.Set(FS_VNODE_CAPABILITY_UNLINK, fLowLevelOps.unlink);
478 		fNodeCapabilities.Set(FS_VNODE_CAPABILITY_RENAME, fLowLevelOps.rename);
479 
480 		fNodeCapabilities.Set(FS_VNODE_CAPABILITY_ACCESS, fLowLevelOps.access);
481 		fNodeCapabilities.Set(FS_VNODE_CAPABILITY_READ_STAT, fLowLevelOps.getattr);
482 		fNodeCapabilities.Set(FS_VNODE_CAPABILITY_WRITE_STAT, fLowLevelOps.setattr != NULL);
483 
484 		fVolumeCapabilities.Set(FS_VOLUME_CAPABILITY_READ_FS_INFO, fLowLevelOps.statfs);
485 		// missing: FS_VOLUME_CAPABILITY_WRITE_FS_INFO
486 		fVolumeCapabilities.Set(FS_VOLUME_CAPABILITY_SYNC, fLowLevelOps.fsync);
487 		// emulated via fsync()
488 
489 		// file operations
490 		fNodeCapabilities.Set(FS_VNODE_CAPABILITY_CREATE, fLowLevelOps.create);
491 		fNodeCapabilities.Set(FS_VNODE_CAPABILITY_OPEN, fLowLevelOps.open);
492 		fNodeCapabilities.Set(FS_VNODE_CAPABILITY_CLOSE, fLowLevelOps.flush);
493 		fNodeCapabilities.Set(FS_VNODE_CAPABILITY_FREE_COOKIE, fLowLevelOps.release);
494 		fNodeCapabilities.Set(FS_VNODE_CAPABILITY_READ, fLowLevelOps.read);
495 		fNodeCapabilities.Set(FS_VNODE_CAPABILITY_WRITE, fLowLevelOps.write);
496 
497 		// directory operations
498 		fNodeCapabilities.Set(FS_VNODE_CAPABILITY_CREATE_DIR, fLowLevelOps.mkdir);
499 		fNodeCapabilities.Set(FS_VNODE_CAPABILITY_REMOVE_DIR, fLowLevelOps.rmdir);
500 		bool readDirSupport = fLowLevelOps.opendir != NULL || fLowLevelOps.readdir != NULL;
501 		fNodeCapabilities.Set(FS_VNODE_CAPABILITY_OPEN_DIR, readDirSupport);
502 		// not needed: FS_VNODE_CAPABILITY_CLOSE_DIR
503 		fNodeCapabilities.Set(FS_VNODE_CAPABILITY_FREE_DIR_COOKIE, readDirSupport);
504 		fNodeCapabilities.Set(FS_VNODE_CAPABILITY_READ_DIR, readDirSupport);
505 		fNodeCapabilities.Set(FS_VNODE_CAPABILITY_REWIND_DIR, readDirSupport);
506 
507 		// attribute directory operations
508 		bool hasAttributes = fLowLevelOps.listxattr != NULL;
509 		fNodeCapabilities.Set(FS_VNODE_CAPABILITY_OPEN_ATTR_DIR, hasAttributes);
510 		// not needed: FS_VNODE_CAPABILITY_CLOSE_ATTR_DIR
511 		fNodeCapabilities.Set(FS_VNODE_CAPABILITY_FREE_ATTR_DIR_COOKIE, hasAttributes);
512 		fNodeCapabilities.Set(FS_VNODE_CAPABILITY_READ_ATTR_DIR, hasAttributes);
513 		fNodeCapabilities.Set(FS_VNODE_CAPABILITY_REWIND_ATTR_DIR, hasAttributes);
514 
515 		// attribute operations
516 		// 	// we emulate open_attr() and free_attr_dir_cookie() if either read_attr()
517 		// 	// or write_attr() is present
518 		// 	bool hasAttributes = (fLowLevelOps.read_attr || fLowLevelOps.write_attr);
519 		// 	fNodeCapabilities.Set(FS_VNODE_CAPABILITY_CREATE_ATTR, hasAttributes);
520 		fNodeCapabilities.Set(FS_VNODE_CAPABILITY_OPEN_ATTR, hasAttributes);
521 		fNodeCapabilities.Set(FS_VNODE_CAPABILITY_FREE_ATTR_COOKIE, hasAttributes);
522 		fNodeCapabilities.Set(FS_VNODE_CAPABILITY_READ_ATTR, fLowLevelOps.getxattr);
523 		// 	fNodeCapabilities.Set(FS_VNODE_CAPABILITY_WRITE_ATTR, fLowLevelOps.write_attr);
524 
525 		fNodeCapabilities.Set(FS_VNODE_CAPABILITY_READ_ATTR_STAT, fLowLevelOps.getxattr);
526 // 		// missing: FS_VNODE_CAPABILITY_WRITE_ATTR_STAT
527 // 		fNodeCapabilities.Set(FS_VNODE_CAPABILITY_RENAME_ATTR, fLowLevelOps.rename_attr);
528 	} else {
529 		fNodeCapabilities.Set(FS_VNODE_CAPABILITY_FSYNC, fFS->ops.fsync);
530 
531 		fNodeCapabilities.Set(FS_VNODE_CAPABILITY_READ_SYMLINK, fFS->ops.readlink);
532 		fNodeCapabilities.Set(FS_VNODE_CAPABILITY_CREATE_SYMLINK, fFS->ops.symlink);
533 
534 		fNodeCapabilities.Set(FS_VNODE_CAPABILITY_LINK, fFS->ops.link);
535 		fNodeCapabilities.Set(FS_VNODE_CAPABILITY_UNLINK, fFS->ops.unlink);
536 		fNodeCapabilities.Set(FS_VNODE_CAPABILITY_RENAME, fFS->ops.rename);
537 
538 		fNodeCapabilities.Set(FS_VNODE_CAPABILITY_ACCESS, fFS->ops.access);
539 		fNodeCapabilities.Set(FS_VNODE_CAPABILITY_READ_STAT, fFS->ops.getattr);
540 		fNodeCapabilities.Set(FS_VNODE_CAPABILITY_WRITE_STAT,
541 				fFS->ops.chmod != NULL || fFS->ops.chown != NULL
542 				|| fFS->ops.truncate != NULL || fFS->ops.utimens != NULL
543 				|| fFS->ops.utime != NULL);
544 
545 		fVolumeCapabilities.Set(FS_VOLUME_CAPABILITY_READ_FS_INFO, fFS->ops.statfs);
546 		// missing: FS_VOLUME_CAPABILITY_WRITE_FS_INFO
547 		fVolumeCapabilities.Set(FS_VOLUME_CAPABILITY_SYNC, fFS->ops.fsync);
548 		// emulated via fsync()
549 
550 		// file operations
551 		fNodeCapabilities.Set(FS_VNODE_CAPABILITY_CREATE, fFS->ops.create);
552 		fNodeCapabilities.Set(FS_VNODE_CAPABILITY_OPEN, fFS->ops.open);
553 		fNodeCapabilities.Set(FS_VNODE_CAPABILITY_CLOSE, fFS->ops.flush);
554 		fNodeCapabilities.Set(FS_VNODE_CAPABILITY_FREE_COOKIE, fFS->ops.release);
555 		fNodeCapabilities.Set(FS_VNODE_CAPABILITY_READ, fFS->ops.read);
556 		fNodeCapabilities.Set(FS_VNODE_CAPABILITY_WRITE, fFS->ops.write);
557 
558 		// directory operations
559 		fNodeCapabilities.Set(FS_VNODE_CAPABILITY_CREATE_DIR, fFS->ops.mkdir);
560 		fNodeCapabilities.Set(FS_VNODE_CAPABILITY_REMOVE_DIR, fFS->ops.rmdir);
561 		bool readDirSupport = fFS->ops.opendir != NULL || fFS->ops.readdir != NULL
562 			|| fFS->ops.getdir;
563 		fNodeCapabilities.Set(FS_VNODE_CAPABILITY_OPEN_DIR, readDirSupport);
564 		// not needed: FS_VNODE_CAPABILITY_CLOSE_DIR
565 		fNodeCapabilities.Set(FS_VNODE_CAPABILITY_FREE_DIR_COOKIE, readDirSupport);
566 		fNodeCapabilities.Set(FS_VNODE_CAPABILITY_READ_DIR, readDirSupport);
567 		fNodeCapabilities.Set(FS_VNODE_CAPABILITY_REWIND_DIR, readDirSupport);
568 
569 		// attribute directory operations
570 		bool hasAttributes = fFS->ops.listxattr != NULL;
571 		fNodeCapabilities.Set(FS_VNODE_CAPABILITY_OPEN_ATTR_DIR, hasAttributes);
572 		// not needed: FS_VNODE_CAPABILITY_CLOSE_ATTR_DIR
573 		fNodeCapabilities.Set(FS_VNODE_CAPABILITY_FREE_ATTR_DIR_COOKIE,
574 				hasAttributes);
575 		fNodeCapabilities.Set(FS_VNODE_CAPABILITY_READ_ATTR_DIR, hasAttributes);
576 		fNodeCapabilities.Set(FS_VNODE_CAPABILITY_REWIND_ATTR_DIR, hasAttributes);
577 
578 		// attribute operations
579 		// 	// we emulate open_attr() and free_attr_dir_cookie() if either read_attr()
580 		// 	// or write_attr() is present
581 		// 	bool hasAttributes = (fFS->ops.read_attr || fFS->ops.write_attr);
582 		// 	fNodeCapabilities.Set(FS_VNODE_CAPABILITY_CREATE_ATTR, hasAttributes);
583 		fNodeCapabilities.Set(FS_VNODE_CAPABILITY_OPEN_ATTR, hasAttributes);
584 		fNodeCapabilities.Set(FS_VNODE_CAPABILITY_FREE_ATTR_COOKIE, hasAttributes);
585 		fNodeCapabilities.Set(FS_VNODE_CAPABILITY_READ_ATTR, fFS->ops.getxattr);
586 		// 	fNodeCapabilities.Set(FS_VNODE_CAPABILITY_WRITE_ATTR, fFS->ops.write_attr);
587 
588 		fNodeCapabilities.Set(FS_VNODE_CAPABILITY_READ_ATTR_STAT,
589 		fFS->ops.getxattr);
590 // 		// missing: FS_VNODE_CAPABILITY_WRITE_ATTR_STAT
591 // 		fNodeCapabilities.Set(FS_VNODE_CAPABILITY_RENAME_ATTR, fFS->ops.rename_attr);
592 	}
593 }
594 
595 
596 // #pragma mark - bootstrapping
597 
598 
599 status_t
600 userlandfs_create_file_system(const char* fsName, image_id image,
601 	FileSystem** _fileSystem)
602 {
603 printf("userlandfs_create_file_system()\n");
604 	// look up the main() function of the add-on
605 	int (*mainFunction)(int argc, const char* const* argv);
606 	status_t error = get_image_symbol(image, "main", B_SYMBOL_TYPE_TEXT,
607 		(void**)&mainFunction);
608 	if (error != B_OK)
609 		return error;
610 printf("userlandfs_create_file_system(): found main: %p\n", mainFunction);
611 
612 	// create the file system
613 	FUSEFileSystem* fileSystem = new(std::nothrow) FUSEFileSystem(fsName,
614 		mainFunction);
615 	if (fileSystem == NULL)
616 		return B_NO_MEMORY;
617 
618 	*_fileSystem = fileSystem;
619 	return B_OK;
620 }
621