xref: /third_party/libfuse/lib/helper.c (revision 6881f68f)
1/*
2  FUSE: Filesystem in Userspace
3  Copyright (C) 2001-2007  Miklos Szeredi <miklos@szeredi.hu>
4
5  Helper functions to create (simple) standalone programs. With the
6  aid of these functions it should be possible to create full FUSE
7  file system by implementing nothing but the request handlers.
8
9  This program can be distributed under the terms of the GNU LGPLv2.
10  See the file COPYING.LIB.
11*/
12
13#include "fuse_config.h"
14#include "fuse_i.h"
15#include "fuse_misc.h"
16#include "fuse_opt.h"
17#include "fuse_lowlevel.h"
18#include "mount_util.h"
19
20#include <stdio.h>
21#include <stdlib.h>
22#include <stddef.h>
23#include <unistd.h>
24#include <string.h>
25#include <limits.h>
26#include <errno.h>
27#include <sys/param.h>
28
29#define FUSE_HELPER_OPT(t, p) \
30	{ t, offsetof(struct fuse_cmdline_opts, p), 1 }
31
32static const struct fuse_opt fuse_helper_opts[] = {
33	FUSE_HELPER_OPT("-h",		show_help),
34	FUSE_HELPER_OPT("--help",	show_help),
35	FUSE_HELPER_OPT("-V",		show_version),
36	FUSE_HELPER_OPT("--version",	show_version),
37	FUSE_HELPER_OPT("-d",		debug),
38	FUSE_HELPER_OPT("debug",	debug),
39	FUSE_HELPER_OPT("-d",		foreground),
40	FUSE_HELPER_OPT("debug",	foreground),
41	FUSE_OPT_KEY("-d",		FUSE_OPT_KEY_KEEP),
42	FUSE_OPT_KEY("debug",		FUSE_OPT_KEY_KEEP),
43	FUSE_HELPER_OPT("-f",		foreground),
44	FUSE_HELPER_OPT("-s",		singlethread),
45	FUSE_HELPER_OPT("fsname=",	nodefault_subtype),
46	FUSE_OPT_KEY("fsname=",		FUSE_OPT_KEY_KEEP),
47#ifndef __FreeBSD__
48	FUSE_HELPER_OPT("subtype=",	nodefault_subtype),
49	FUSE_OPT_KEY("subtype=",	FUSE_OPT_KEY_KEEP),
50#endif
51	FUSE_HELPER_OPT("clone_fd",	clone_fd),
52	FUSE_HELPER_OPT("max_idle_threads=%u", max_idle_threads),
53	FUSE_HELPER_OPT("max_threads=%u", max_threads),
54	FUSE_OPT_END
55};
56
57struct fuse_conn_info_opts {
58	int atomic_o_trunc;
59	int no_remote_posix_lock;
60	int no_remote_flock;
61	int splice_write;
62	int splice_move;
63	int splice_read;
64	int no_splice_write;
65	int no_splice_move;
66	int no_splice_read;
67	int auto_inval_data;
68	int no_auto_inval_data;
69	int no_readdirplus;
70	int no_readdirplus_auto;
71	int async_dio;
72	int no_async_dio;
73	int writeback_cache;
74	int no_writeback_cache;
75	int async_read;
76	int sync_read;
77	unsigned max_write;
78	unsigned max_readahead;
79	unsigned max_background;
80	unsigned congestion_threshold;
81	unsigned time_gran;
82	int set_max_write;
83	int set_max_readahead;
84	int set_max_background;
85	int set_congestion_threshold;
86	int set_time_gran;
87};
88
89#define CONN_OPTION(t, p, v)					\
90	{ t, offsetof(struct fuse_conn_info_opts, p), v }
91static const struct fuse_opt conn_info_opt_spec[] = {
92	CONN_OPTION("max_write=%u", max_write, 0),
93	CONN_OPTION("max_write=", set_max_write, 1),
94	CONN_OPTION("max_readahead=%u", max_readahead, 0),
95	CONN_OPTION("max_readahead=", set_max_readahead, 1),
96	CONN_OPTION("max_background=%u", max_background, 0),
97	CONN_OPTION("max_background=", set_max_background, 1),
98	CONN_OPTION("congestion_threshold=%u", congestion_threshold, 0),
99	CONN_OPTION("congestion_threshold=", set_congestion_threshold, 1),
100	CONN_OPTION("sync_read", sync_read, 1),
101	CONN_OPTION("async_read", async_read, 1),
102	CONN_OPTION("atomic_o_trunc", atomic_o_trunc, 1),
103	CONN_OPTION("no_remote_lock", no_remote_posix_lock, 1),
104	CONN_OPTION("no_remote_lock", no_remote_flock, 1),
105	CONN_OPTION("no_remote_flock", no_remote_flock, 1),
106	CONN_OPTION("no_remote_posix_lock", no_remote_posix_lock, 1),
107	CONN_OPTION("splice_write", splice_write, 1),
108	CONN_OPTION("no_splice_write", no_splice_write, 1),
109	CONN_OPTION("splice_move", splice_move, 1),
110	CONN_OPTION("no_splice_move", no_splice_move, 1),
111	CONN_OPTION("splice_read", splice_read, 1),
112	CONN_OPTION("no_splice_read", no_splice_read, 1),
113	CONN_OPTION("auto_inval_data", auto_inval_data, 1),
114	CONN_OPTION("no_auto_inval_data", no_auto_inval_data, 1),
115	CONN_OPTION("readdirplus=no", no_readdirplus, 1),
116	CONN_OPTION("readdirplus=yes", no_readdirplus, 0),
117	CONN_OPTION("readdirplus=yes", no_readdirplus_auto, 1),
118	CONN_OPTION("readdirplus=auto", no_readdirplus, 0),
119	CONN_OPTION("readdirplus=auto", no_readdirplus_auto, 0),
120	CONN_OPTION("async_dio", async_dio, 1),
121	CONN_OPTION("no_async_dio", no_async_dio, 1),
122	CONN_OPTION("writeback_cache", writeback_cache, 1),
123	CONN_OPTION("no_writeback_cache", no_writeback_cache, 1),
124	CONN_OPTION("time_gran=%u", time_gran, 0),
125	CONN_OPTION("time_gran=", set_time_gran, 1),
126	FUSE_OPT_END
127};
128
129
130void fuse_cmdline_help(void)
131{
132	printf("    -h   --help            print help\n"
133	       "    -V   --version         print version\n"
134	       "    -d   -o debug          enable debug output (implies -f)\n"
135	       "    -f                     foreground operation\n"
136	       "    -s                     disable multi-threaded operation\n"
137	       "    -o clone_fd            use separate fuse device fd for each thread\n"
138	       "                           (may improve performance)\n"
139	       "    -o max_idle_threads    the maximum number of idle worker threads\n"
140	       "                           allowed (default: -1)\n"
141	       "    -o max_threads         the maximum number of worker threads\n"
142	       "                           allowed (default: 10)\n");
143}
144
145static int fuse_helper_opt_proc(void *data, const char *arg, int key,
146				struct fuse_args *outargs)
147{
148	(void) outargs;
149	struct fuse_cmdline_opts *opts = data;
150
151	switch (key) {
152	case FUSE_OPT_KEY_NONOPT:
153		if (!opts->mountpoint) {
154			if (fuse_mnt_parse_fuse_fd(arg) != -1) {
155				return fuse_opt_add_opt(&opts->mountpoint, arg);
156			}
157
158			char mountpoint[PATH_MAX] = "";
159			if (realpath(arg, mountpoint) == NULL) {
160				fuse_log(FUSE_LOG_ERR,
161					"fuse: bad mount point `%s': %s\n",
162					arg, strerror(errno));
163				return -1;
164			}
165			return fuse_opt_add_opt(&opts->mountpoint, mountpoint);
166		} else {
167			fuse_log(FUSE_LOG_ERR, "fuse: invalid argument `%s'\n", arg);
168			return -1;
169		}
170
171	default:
172		/* Pass through unknown options */
173		return 1;
174	}
175}
176
177/* Under FreeBSD, there is no subtype option so this
178   function actually sets the fsname */
179static int add_default_subtype(const char *progname, struct fuse_args *args)
180{
181	int res;
182	char *subtype_opt;
183
184	const char *basename = strrchr(progname, '/');
185	if (basename == NULL)
186		basename = progname;
187	else if (basename[1] != '\0')
188		basename++;
189
190	subtype_opt = (char *) malloc(strlen(basename) + 64);
191	if (subtype_opt == NULL) {
192		fuse_log(FUSE_LOG_ERR, "fuse: memory allocation failed\n");
193		return -1;
194	}
195#ifdef __FreeBSD__
196	sprintf(subtype_opt, "-ofsname=%s", basename);
197#else
198	sprintf(subtype_opt, "-osubtype=%s", basename);
199#endif
200	res = fuse_opt_add_arg(args, subtype_opt);
201	free(subtype_opt);
202	return res;
203}
204
205int fuse_parse_cmdline_312(struct fuse_args *args,
206			   struct fuse_cmdline_opts *opts);
207FUSE_SYMVER("fuse_parse_cmdline_312", "fuse_parse_cmdline@@FUSE_3.12")
208int fuse_parse_cmdline_312(struct fuse_args *args,
209			   struct fuse_cmdline_opts *opts)
210{
211	memset(opts, 0, sizeof(struct fuse_cmdline_opts));
212
213	opts->max_idle_threads = UINT_MAX; /* new default in fuse version 3.12 */
214	opts->max_threads = 10;
215
216	if (fuse_opt_parse(args, opts, fuse_helper_opts,
217			   fuse_helper_opt_proc) == -1)
218		return -1;
219
220	/* *Linux*: if neither -o subtype nor -o fsname are specified,
221	   set subtype to program's basename.
222	   *FreeBSD*: if fsname is not specified, set to program's
223	   basename. */
224	if (!opts->nodefault_subtype)
225		if (add_default_subtype(args->argv[0], args) == -1)
226			return -1;
227
228	return 0;
229}
230
231/**
232 * struct fuse_cmdline_opts got extended in libfuse-3.12
233 */
234int fuse_parse_cmdline_30(struct fuse_args *args,
235		       struct fuse_cmdline_opts *opts);
236FUSE_SYMVER("fuse_parse_cmdline_30", "fuse_parse_cmdline@FUSE_3.0")
237int fuse_parse_cmdline_30(struct fuse_args *args,
238			  struct fuse_cmdline_opts *out_opts)
239{
240	struct fuse_cmdline_opts opts;
241
242	int rc = fuse_parse_cmdline_312(args, &opts);
243	if (rc == 0) {
244		/* copy up to the size of the old pre 3.12 struct */
245		memcpy(out_opts, &opts,
246		       offsetof(struct fuse_cmdline_opts, max_idle_threads) +
247		       sizeof(opts.max_idle_threads));
248	}
249
250	return rc;
251}
252
253int fuse_daemonize(int foreground)
254{
255	if (!foreground) {
256		int nullfd;
257		int waiter[2];
258		char completed;
259
260		if (pipe(waiter)) {
261			perror("fuse_daemonize: pipe");
262			return -1;
263		}
264
265		/*
266		 * demonize current process by forking it and killing the
267		 * parent.  This makes current process as a child of 'init'.
268		 */
269		switch(fork()) {
270		case -1:
271			perror("fuse_daemonize: fork");
272			return -1;
273		case 0:
274			break;
275		default:
276			(void) read(waiter[0], &completed, sizeof(completed));
277			_exit(0);
278		}
279
280		if (setsid() == -1) {
281			perror("fuse_daemonize: setsid");
282			return -1;
283		}
284
285		(void) chdir("/");
286
287		nullfd = open("/dev/null", O_RDWR, 0);
288		if (nullfd != -1) {
289			(void) dup2(nullfd, 0);
290			(void) dup2(nullfd, 1);
291			(void) dup2(nullfd, 2);
292			if (nullfd > 2)
293				close(nullfd);
294		}
295
296		/* Propagate completion of daemon initialization */
297		completed = 1;
298		(void) write(waiter[1], &completed, sizeof(completed));
299		close(waiter[0]);
300		close(waiter[1]);
301	} else {
302		(void) chdir("/");
303	}
304	return 0;
305}
306
307int fuse_main_real(int argc, char *argv[], const struct fuse_operations *op,
308		   size_t op_size, void *user_data)
309{
310	struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
311	struct fuse *fuse;
312	struct fuse_cmdline_opts opts;
313	int res;
314	struct fuse_loop_config *loop_config = NULL;
315
316	if (fuse_parse_cmdline(&args, &opts) != 0)
317		return 1;
318
319	if (opts.show_version) {
320		printf("FUSE library version %s\n", PACKAGE_VERSION);
321		fuse_lowlevel_version();
322		res = 0;
323		goto out1;
324	}
325
326	if (opts.show_help) {
327		if(args.argv[0][0] != '\0')
328			printf("usage: %s [options] <mountpoint>\n\n",
329			       args.argv[0]);
330		printf("FUSE options:\n");
331		fuse_cmdline_help();
332		fuse_lib_help(&args);
333		res = 0;
334		goto out1;
335	}
336
337	if (!opts.show_help &&
338	    !opts.mountpoint) {
339		fuse_log(FUSE_LOG_ERR, "error: no mountpoint specified\n");
340		res = 2;
341		goto out1;
342	}
343
344
345	fuse = fuse_new_31(&args, op, op_size, user_data);
346	if (fuse == NULL) {
347		res = 3;
348		goto out1;
349	}
350
351	if (fuse_mount(fuse,opts.mountpoint) != 0) {
352		res = 4;
353		goto out2;
354	}
355
356	if (fuse_daemonize(opts.foreground) != 0) {
357		res = 5;
358		goto out3;
359	}
360
361	struct fuse_session *se = fuse_get_session(fuse);
362	if (fuse_set_signal_handlers(se) != 0) {
363		res = 6;
364		goto out3;
365	}
366
367	if (opts.singlethread)
368		res = fuse_loop(fuse);
369	else {
370		loop_config = fuse_loop_cfg_create();
371		if (loop_config == NULL) {
372			res = 7;
373			goto out3;
374		}
375
376		fuse_loop_cfg_set_clone_fd(loop_config, opts.clone_fd);
377
378		fuse_loop_cfg_set_idle_threads(loop_config, opts.max_idle_threads);
379		fuse_loop_cfg_set_max_threads(loop_config, opts.max_threads);
380		res = fuse_loop_mt(fuse, loop_config);
381	}
382	if (res)
383		res = 8;
384
385	fuse_remove_signal_handlers(se);
386out3:
387	fuse_unmount(fuse);
388out2:
389	fuse_destroy(fuse);
390out1:
391	fuse_loop_cfg_destroy(loop_config);
392	free(opts.mountpoint);
393	fuse_opt_free_args(&args);
394	return res;
395}
396
397
398void fuse_apply_conn_info_opts(struct fuse_conn_info_opts *opts,
399			       struct fuse_conn_info *conn)
400{
401	if(opts->set_max_write)
402		conn->max_write = opts->max_write;
403	if(opts->set_max_background)
404		conn->max_background = opts->max_background;
405	if(opts->set_congestion_threshold)
406		conn->congestion_threshold = opts->congestion_threshold;
407	if(opts->set_time_gran)
408		conn->time_gran = opts->time_gran;
409	if(opts->set_max_readahead)
410		conn->max_readahead = opts->max_readahead;
411
412#define LL_ENABLE(cond,cap) \
413	if (cond) conn->want |= (cap)
414#define LL_DISABLE(cond,cap) \
415	if (cond) conn->want &= ~(cap)
416
417	LL_ENABLE(opts->splice_read, FUSE_CAP_SPLICE_READ);
418	LL_DISABLE(opts->no_splice_read, FUSE_CAP_SPLICE_READ);
419
420	LL_ENABLE(opts->splice_write, FUSE_CAP_SPLICE_WRITE);
421	LL_DISABLE(opts->no_splice_write, FUSE_CAP_SPLICE_WRITE);
422
423	LL_ENABLE(opts->splice_move, FUSE_CAP_SPLICE_MOVE);
424	LL_DISABLE(opts->no_splice_move, FUSE_CAP_SPLICE_MOVE);
425
426	LL_ENABLE(opts->auto_inval_data, FUSE_CAP_AUTO_INVAL_DATA);
427	LL_DISABLE(opts->no_auto_inval_data, FUSE_CAP_AUTO_INVAL_DATA);
428
429	LL_DISABLE(opts->no_readdirplus, FUSE_CAP_READDIRPLUS);
430	LL_DISABLE(opts->no_readdirplus_auto, FUSE_CAP_READDIRPLUS_AUTO);
431
432	LL_ENABLE(opts->async_dio, FUSE_CAP_ASYNC_DIO);
433	LL_DISABLE(opts->no_async_dio, FUSE_CAP_ASYNC_DIO);
434
435	LL_ENABLE(opts->writeback_cache, FUSE_CAP_WRITEBACK_CACHE);
436	LL_DISABLE(opts->no_writeback_cache, FUSE_CAP_WRITEBACK_CACHE);
437
438	LL_ENABLE(opts->async_read, FUSE_CAP_ASYNC_READ);
439	LL_DISABLE(opts->sync_read, FUSE_CAP_ASYNC_READ);
440
441	LL_DISABLE(opts->no_remote_posix_lock, FUSE_CAP_POSIX_LOCKS);
442	LL_DISABLE(opts->no_remote_flock, FUSE_CAP_FLOCK_LOCKS);
443}
444
445struct fuse_conn_info_opts* fuse_parse_conn_info_opts(struct fuse_args *args)
446{
447	struct fuse_conn_info_opts *opts;
448
449	opts = calloc(1, sizeof(struct fuse_conn_info_opts));
450	if(opts == NULL) {
451		fuse_log(FUSE_LOG_ERR, "calloc failed\n");
452		return NULL;
453	}
454	if(fuse_opt_parse(args, opts, conn_info_opt_spec, NULL) == -1) {
455		free(opts);
456		return NULL;
457	}
458	return opts;
459}
460
461int fuse_open_channel(const char *mountpoint, const char* options)
462{
463	struct mount_opts *opts = NULL;
464	int fd = -1;
465	const char *argv[] = { "", "-o", options };
466	int argc = sizeof(argv) / sizeof(argv[0]);
467	struct fuse_args args = FUSE_ARGS_INIT(argc, (char**) argv);
468
469	opts = parse_mount_opts(&args);
470	if (opts == NULL)
471		return -1;
472
473	fd = fuse_kern_mount(mountpoint, opts);
474	destroy_mount_opts(opts);
475
476	return fd;
477}
478