1 /*
2   FUSE: Filesystem in Userspace
3   Copyright (C) 2005-2008 Csaba Henk <csaba.henk@creo.hu>
4 
5   Architecture specific file system mounting (FreeBSD).
6 
7   This program can be distributed under the terms of the GNU LGPLv2.
8   See the file COPYING.LIB.
9 */
10 
11 #include "config.h"
12 #include "fuse_i.h"
13 #include "fuse_misc.h"
14 #include "fuse_opt.h"
15 
16 #include <sys/param.h>
17 #include <sys/mount.h>
18 
19 #include <sys/stat.h>
20 #include <sys/wait.h>
21 #include <sys/sysctl.h>
22 #include <sys/user.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <unistd.h>
26 #include <stddef.h>
27 #include <fcntl.h>
28 #include <errno.h>
29 #include <string.h>
30 #include <paths.h>
31 #include <limits.h>
32 
33 #define FUSERMOUNT_PROG		"mount_fusefs"
34 #define FUSE_DEV_TRUNK		"/dev/fuse"
35 
36 enum {
37 	KEY_RO,
38 	KEY_KERN
39 };
40 
41 struct mount_opts {
42 	int allow_other;
43 	char *kernel_opts;
44 	unsigned max_read;
45 };
46 
47 #define FUSE_DUAL_OPT_KEY(templ, key)				\
48 	FUSE_OPT_KEY(templ, key), FUSE_OPT_KEY("no" templ, key)
49 
50 static const struct fuse_opt fuse_mount_opts[] = {
51 	{ "allow_other", offsetof(struct mount_opts, allow_other), 1 },
52 	{ "max_read=%u", offsetof(struct mount_opts, max_read), 1 },
53 	FUSE_OPT_KEY("-r",			KEY_RO),
54 	/* standard FreeBSD mount options */
55 	FUSE_DUAL_OPT_KEY("dev",		KEY_KERN),
56 	FUSE_DUAL_OPT_KEY("async",		KEY_KERN),
57 	FUSE_DUAL_OPT_KEY("atime",		KEY_KERN),
58 	FUSE_DUAL_OPT_KEY("dev",		KEY_KERN),
59 	FUSE_DUAL_OPT_KEY("exec",		KEY_KERN),
60 	FUSE_DUAL_OPT_KEY("suid",		KEY_KERN),
61 	FUSE_DUAL_OPT_KEY("symfollow",		KEY_KERN),
62 	FUSE_DUAL_OPT_KEY("rdonly",		KEY_KERN),
63 	FUSE_DUAL_OPT_KEY("sync",		KEY_KERN),
64 	FUSE_DUAL_OPT_KEY("union",		KEY_KERN),
65 	FUSE_DUAL_OPT_KEY("userquota",		KEY_KERN),
66 	FUSE_DUAL_OPT_KEY("groupquota",		KEY_KERN),
67 	FUSE_DUAL_OPT_KEY("clusterr",		KEY_KERN),
68 	FUSE_DUAL_OPT_KEY("clusterw",		KEY_KERN),
69 	FUSE_DUAL_OPT_KEY("suiddir",		KEY_KERN),
70 	FUSE_DUAL_OPT_KEY("snapshot",		KEY_KERN),
71 	FUSE_DUAL_OPT_KEY("multilabel",		KEY_KERN),
72 	FUSE_DUAL_OPT_KEY("acls",		KEY_KERN),
73 	FUSE_DUAL_OPT_KEY("force",		KEY_KERN),
74 	FUSE_DUAL_OPT_KEY("update",		KEY_KERN),
75 	FUSE_DUAL_OPT_KEY("ro",			KEY_KERN),
76 	FUSE_DUAL_OPT_KEY("rw",			KEY_KERN),
77 	FUSE_DUAL_OPT_KEY("auto",		KEY_KERN),
78 	FUSE_DUAL_OPT_KEY("automounted",	KEY_KERN),
79 	/* options supported under both Linux and FBSD */
80 	FUSE_DUAL_OPT_KEY("allow_other",	KEY_KERN),
81 	FUSE_DUAL_OPT_KEY("default_permissions",KEY_KERN),
82 	FUSE_OPT_KEY("max_read=",		KEY_KERN),
83 	FUSE_OPT_KEY("subtype=",		KEY_KERN),
84 	/* FBSD FUSE specific mount options */
85 	FUSE_DUAL_OPT_KEY("private",		KEY_KERN),
86 	FUSE_DUAL_OPT_KEY("neglect_shares",	KEY_KERN),
87 	FUSE_DUAL_OPT_KEY("push_symlinks_in",	KEY_KERN),
88 	FUSE_OPT_KEY("nosync_unmount",		KEY_KERN),
89 #if __FreeBSD_version >= 1200519
90 	FUSE_DUAL_OPT_KEY("intr",		KEY_KERN),
91 #endif
92 	/* stock FBSD mountopt parsing routine lets anything be negated... */
93 	/*
94 	 * Linux specific mount options, but let just the mount util
95 	 * handle them
96 	 */
97 	FUSE_OPT_KEY("fsname=",			KEY_KERN),
98 	FUSE_OPT_END
99 };
100 
fuse_mount_version(void)101 void fuse_mount_version(void)
102 {
103 	system(FUSERMOUNT_PROG " --version");
104 }
105 
get_max_read(struct mount_opts * o)106 unsigned get_max_read(struct mount_opts *o)
107 {
108 	return o->max_read;
109 }
110 
fuse_mount_opt_proc(void * data,const char * arg,int key,struct fuse_args * outargs)111 static int fuse_mount_opt_proc(void *data, const char *arg, int key,
112 			       struct fuse_args *outargs)
113 {
114 	(void) outargs;
115 	struct mount_opts *mo = data;
116 
117 	switch (key) {
118 	case KEY_RO:
119 		arg = "ro";
120 		/* fall through */
121 
122 	case KEY_KERN:
123 		return fuse_opt_add_opt(&mo->kernel_opts, arg);
124 	}
125 
126 	/* Pass through unknown options */
127 	return 1;
128 }
129 
fuse_kern_unmount(const char * mountpoint,int fd)130 void fuse_kern_unmount(const char *mountpoint, int fd)
131 {
132 	close(fd);
133 	unmount(mountpoint, MNT_FORCE);
134 }
135 
136 /* Check if kernel is doing init in background */
init_backgrounded(void)137 static int init_backgrounded(void)
138 {
139 	unsigned ibg;
140 	size_t len;
141 
142 	len = sizeof(ibg);
143 
144 	if (sysctlbyname("vfs.fuse.init_backgrounded", &ibg, &len, NULL, 0))
145 		return 0;
146 
147 	return ibg;
148 }
149 
150 
fuse_mount_core(const char * mountpoint,const char * opts)151 static int fuse_mount_core(const char *mountpoint, const char *opts)
152 {
153 	const char *mountprog = FUSERMOUNT_PROG;
154 	int fd;
155 	char *fdnam, *dev;
156 	pid_t pid, cpid;
157 	int status;
158 
159 	fdnam = getenv("FUSE_DEV_FD");
160 
161 	if (fdnam) {
162 		char *ep;
163 
164 		fd = strtol(fdnam, &ep, 10);
165 
166 		if (*ep != '\0') {
167 			fuse_log(FUSE_LOG_ERR, "invalid value given in FUSE_DEV_FD\n");
168 			return -1;
169 		}
170 
171 		if (fd < 0)
172 			return -1;
173 
174 		goto mount;
175 	}
176 
177 	dev = getenv("FUSE_DEV_NAME");
178 
179 	if (! dev)
180 		dev = (char *)FUSE_DEV_TRUNK;
181 
182 	if ((fd = open(dev, O_RDWR)) < 0) {
183 		perror("fuse: failed to open fuse device");
184 		return -1;
185 	}
186 
187 mount:
188 	if (getenv("FUSE_NO_MOUNT") || ! mountpoint)
189 		goto out;
190 
191 	pid = fork();
192 	cpid = pid;
193 
194 	if (pid == -1) {
195 		perror("fuse: fork() failed");
196 		close(fd);
197 		return -1;
198 	}
199 
200 	if (pid == 0) {
201 		if (! init_backgrounded()) {
202 			/*
203 			 * If init is not backgrounded, we have to
204 			 * call the mount util backgrounded, to avoid
205 			 * deadlock.
206 			 */
207 
208 			pid = fork();
209 
210 			if (pid == -1) {
211 				perror("fuse: fork() failed");
212 				close(fd);
213 				exit(1);
214 			}
215 		}
216 
217 		if (pid == 0) {
218 			const char *argv[32];
219 			int a = 0;
220 			int ret = -1;
221 
222 			if (! fdnam)
223 			{
224 				ret = asprintf(&fdnam, "%d", fd);
225 				if(ret == -1)
226 				{
227 					perror("fuse: failed to assemble mount arguments");
228 					close(fd);
229 					exit(1);
230 				}
231 			}
232 
233 			argv[a++] = mountprog;
234 			if (opts) {
235 				argv[a++] = "-o";
236 				argv[a++] = opts;
237 			}
238 			argv[a++] = fdnam;
239 			argv[a++] = mountpoint;
240 			argv[a++] = NULL;
241 			execvp(mountprog, (char **) argv);
242 			perror("fuse: failed to exec mount program");
243 			free(fdnam);
244 			exit(1);
245 		}
246 
247 		exit(0);
248 	}
249 
250 	if (waitpid(cpid, &status, 0) == -1 || WEXITSTATUS(status) != 0) {
251 		perror("fuse: failed to mount file system");
252 		close(fd);
253 		return -1;
254 	}
255 
256 out:
257 	return fd;
258 }
259 
parse_mount_opts(struct fuse_args * args)260 struct mount_opts *parse_mount_opts(struct fuse_args *args)
261 {
262 	struct mount_opts *mo;
263 
264 	mo = (struct mount_opts*) malloc(sizeof(struct mount_opts));
265 	if (mo == NULL)
266 		return NULL;
267 
268 	memset(mo, 0, sizeof(struct mount_opts));
269 
270 	if (args &&
271 	    fuse_opt_parse(args, mo, fuse_mount_opts, fuse_mount_opt_proc) == -1)
272 		goto err_out;
273 
274 	return mo;
275 
276 err_out:
277 	destroy_mount_opts(mo);
278 	return NULL;
279 }
280 
destroy_mount_opts(struct mount_opts * mo)281 void destroy_mount_opts(struct mount_opts *mo)
282 {
283 	free(mo->kernel_opts);
284 	free(mo);
285 }
286 
fuse_kern_mount(const char * mountpoint,struct mount_opts * mo)287 int fuse_kern_mount(const char *mountpoint, struct mount_opts *mo)
288 {
289 	/* mount util should not try to spawn the daemon */
290 	setenv("MOUNT_FUSEFS_SAFE", "1", 1);
291 	/* to notify the mount util it's called from lib */
292 	setenv("MOUNT_FUSEFS_CALL_BY_LIB", "1", 1);
293 
294 	return fuse_mount_core(mountpoint, mo->kernel_opts);
295 }
296