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