• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
32 static 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 
57 struct 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 }
91 static 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 
fuse_cmdline_help(void)130 void 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 
fuse_helper_opt_proc(void * data,const char * arg,int key,struct fuse_args * outargs)145 static 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 */
add_default_subtype(const char * progname,struct fuse_args * args)179 static 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 
205 int fuse_parse_cmdline_312(struct fuse_args *args,
206 			   struct fuse_cmdline_opts *opts);
207 FUSE_SYMVER("fuse_parse_cmdline_312", "fuse_parse_cmdline@@FUSE_3.12")
fuse_parse_cmdline_312(struct fuse_args * args,struct fuse_cmdline_opts * opts)208 int 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  */
234 int fuse_parse_cmdline_30(struct fuse_args *args,
235 		       struct fuse_cmdline_opts *opts);
236 FUSE_SYMVER("fuse_parse_cmdline_30", "fuse_parse_cmdline@FUSE_3.0")
fuse_parse_cmdline_30(struct fuse_args * args,struct fuse_cmdline_opts * out_opts)237 int 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 
fuse_daemonize(int foreground)253 int 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 
fuse_main_real(int argc,char * argv[],const struct fuse_operations * op,size_t op_size,void * user_data)307 int 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);
386 out3:
387 	fuse_unmount(fuse);
388 out2:
389 	fuse_destroy(fuse);
390 out1:
391 	fuse_loop_cfg_destroy(loop_config);
392 	free(opts.mountpoint);
393 	fuse_opt_free_args(&args);
394 	return res;
395 }
396 
397 
fuse_apply_conn_info_opts(struct fuse_conn_info_opts * opts,struct fuse_conn_info * conn)398 void 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 
fuse_parse_conn_info_opts(struct fuse_args * args)445 struct 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 
fuse_open_channel(const char * mountpoint,const char * options)461 int 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