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