blob: 3aec3e3eb2b1a59e40b3eb246c07854848611155 [file] [log] [blame]
bigbiff bigbiff9c754052013-01-09 09:09:08 -05001/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2005-2008 Csaba Henk <csaba.henk@creo.hu>
4
5 This program can be distributed under the terms of the GNU LGPLv2.
6 See the file COPYING.LIB.
7*/
8
9#include "fuse_i.h"
10#include "fuse_misc.h"
11#include "fuse_opt.h"
12
13#include <sys/stat.h>
14#include <sys/wait.h>
15#include <sys/sysctl.h>
16#include <sys/user.h>
17#include <stdio.h>
18#include <stdlib.h>
19#include <unistd.h>
20#include <stddef.h>
21#include <fcntl.h>
22#include <errno.h>
23#include <string.h>
24#include <paths.h>
25#include <limits.h>
26
27#define FUSERMOUNT_PROG "mount_fusefs"
28#define FUSE_DEV_TRUNK "/dev/fuse"
29
30enum {
31 KEY_ALLOW_ROOT,
32 KEY_RO,
33 KEY_HELP,
34 KEY_VERSION,
35 KEY_KERN
36};
37
38struct mount_opts {
39 int allow_other;
40 int allow_root;
41 int ishelp;
42 char *kernel_opts;
43};
44
45#define FUSE_DUAL_OPT_KEY(templ, key) \
46 FUSE_OPT_KEY(templ, key), FUSE_OPT_KEY("no" templ, key)
47
48static const struct fuse_opt fuse_mount_opts[] = {
49 { "allow_other", offsetof(struct mount_opts, allow_other), 1 },
50 { "allow_root", offsetof(struct mount_opts, allow_root), 1 },
51 FUSE_OPT_KEY("allow_root", KEY_ALLOW_ROOT),
52 FUSE_OPT_KEY("-r", KEY_RO),
53 FUSE_OPT_KEY("-h", KEY_HELP),
54 FUSE_OPT_KEY("--help", KEY_HELP),
55 FUSE_OPT_KEY("-V", KEY_VERSION),
56 FUSE_OPT_KEY("--version", KEY_VERSION),
57 /* standard FreeBSD mount options */
58 FUSE_DUAL_OPT_KEY("dev", KEY_KERN),
59 FUSE_DUAL_OPT_KEY("async", KEY_KERN),
60 FUSE_DUAL_OPT_KEY("atime", KEY_KERN),
61 FUSE_DUAL_OPT_KEY("dev", KEY_KERN),
62 FUSE_DUAL_OPT_KEY("exec", KEY_KERN),
63 FUSE_DUAL_OPT_KEY("suid", KEY_KERN),
64 FUSE_DUAL_OPT_KEY("symfollow", KEY_KERN),
65 FUSE_DUAL_OPT_KEY("rdonly", KEY_KERN),
66 FUSE_DUAL_OPT_KEY("sync", KEY_KERN),
67 FUSE_DUAL_OPT_KEY("union", KEY_KERN),
68 FUSE_DUAL_OPT_KEY("userquota", KEY_KERN),
69 FUSE_DUAL_OPT_KEY("groupquota", KEY_KERN),
70 FUSE_DUAL_OPT_KEY("clusterr", KEY_KERN),
71 FUSE_DUAL_OPT_KEY("clusterw", KEY_KERN),
72 FUSE_DUAL_OPT_KEY("suiddir", KEY_KERN),
73 FUSE_DUAL_OPT_KEY("snapshot", KEY_KERN),
74 FUSE_DUAL_OPT_KEY("multilabel", KEY_KERN),
75 FUSE_DUAL_OPT_KEY("acls", KEY_KERN),
76 FUSE_DUAL_OPT_KEY("force", KEY_KERN),
77 FUSE_DUAL_OPT_KEY("update", KEY_KERN),
78 FUSE_DUAL_OPT_KEY("ro", KEY_KERN),
79 FUSE_DUAL_OPT_KEY("rw", KEY_KERN),
80 FUSE_DUAL_OPT_KEY("auto", KEY_KERN),
81 /* options supported under both Linux and FBSD */
82 FUSE_DUAL_OPT_KEY("allow_other", KEY_KERN),
83 FUSE_DUAL_OPT_KEY("default_permissions",KEY_KERN),
84 FUSE_OPT_KEY("max_read=", KEY_KERN),
85 FUSE_OPT_KEY("subtype=", KEY_KERN),
86 /* FBSD FUSE specific mount options */
87 FUSE_DUAL_OPT_KEY("private", KEY_KERN),
88 FUSE_DUAL_OPT_KEY("neglect_shares", KEY_KERN),
89 FUSE_DUAL_OPT_KEY("push_symlinks_in", KEY_KERN),
90 FUSE_OPT_KEY("nosync_unmount", KEY_KERN),
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_KEY("nonempty", KEY_KERN),
98 FUSE_OPT_KEY("large_read", KEY_KERN),
99 FUSE_OPT_END
100};
101
102static void mount_help(void)
103{
104 fprintf(stderr,
105 " -o allow_root allow access to root\n"
106 );
107 system(FUSERMOUNT_PROG " --help");
108 fputc('\n', stderr);
109}
110
111static void mount_version(void)
112{
113 system(FUSERMOUNT_PROG " --version");
114}
115
116static int fuse_mount_opt_proc(void *data, const char *arg, int key,
117 struct fuse_args *outargs)
118{
119 struct mount_opts *mo = data;
120
121 switch (key) {
122 case KEY_ALLOW_ROOT:
123 if (fuse_opt_add_opt(&mo->kernel_opts, "allow_other") == -1 ||
124 fuse_opt_add_arg(outargs, "-oallow_root") == -1)
125 return -1;
126 return 0;
127
128 case KEY_RO:
129 arg = "ro";
130 /* fall through */
131
132 case KEY_KERN:
133 return fuse_opt_add_opt(&mo->kernel_opts, arg);
134
135 case KEY_HELP:
136 mount_help();
137 mo->ishelp = 1;
138 break;
139
140 case KEY_VERSION:
141 mount_version();
142 mo->ishelp = 1;
143 break;
144 }
145 return 1;
146}
147
148void fuse_unmount_compat22(const char *mountpoint)
149{
150 char dev[128];
151 char *ssc, *umount_cmd;
152 FILE *sf;
153 int rv;
154 char seekscript[] =
155 /* error message is annoying in help output */
156 "exec 2>/dev/null; "
157 "/usr/bin/fstat " FUSE_DEV_TRUNK "* | "
158 "/usr/bin/awk 'BEGIN{ getline; if (! ($3 == \"PID\" && $10 == \"NAME\")) exit 1; }; "
159 " { if ($3 == %d) print $10; }' | "
160 "/usr/bin/sort | "
161 "/usr/bin/uniq | "
162 "/usr/bin/awk '{ i += 1; if (i > 1){ exit 1; }; printf; }; END{ if (i == 0) exit 1; }'";
163
164 (void) mountpoint;
165
166 /*
167 * If we don't know the fd, we have to resort to the scripted
168 * solution -- iterating over the fd-s is unpractical, as we
169 * don't know how many of open files we have. (This could be
170 * looked up in procfs -- however, that's optional on FBSD; or
171 * read out from the kmem -- however, that's bound to
172 * privileges (in fact, that's what happens when we call the
173 * setgid kmem fstat(1) utility).
174 */
175 if (asprintf(&ssc, seekscript, getpid()) == -1)
176 return;
177
178 errno = 0;
179 sf = popen(ssc, "r");
180 free(ssc);
181 if (! sf)
182 return;
183
184 fgets(dev, sizeof(dev), sf);
185 rv = pclose(sf);
186 if (rv)
187 return;
188
189 if (asprintf(&umount_cmd, "/sbin/umount %s", dev) == -1)
190 return;
191 system(umount_cmd);
192 free(umount_cmd);
193}
194
195static void do_unmount(char *dev, int fd)
196{
197 char device_path[SPECNAMELEN + 12];
198 const char *argv[4];
199 const char umount_cmd[] = "/sbin/umount";
200 pid_t pid;
201
202 snprintf(device_path, SPECNAMELEN + 12, _PATH_DEV "%s", dev);
203
204 argv[0] = umount_cmd;
205 argv[1] = "-f";
206 argv[2] = device_path;
207 argv[3] = NULL;
208
209 pid = fork();
210
211 if (pid == -1)
212 return;
213
214 if (pid == 0) {
215 close(fd);
216 execvp(umount_cmd, (char **)argv);
217 exit(1);
218 }
219
220 waitpid(pid, NULL, 0);
221}
222
223void fuse_kern_unmount(const char *mountpoint, int fd)
224{
225 char *ep, dev[128];
226 struct stat sbuf;
227
228 (void)mountpoint;
229
230 if (fstat(fd, &sbuf) == -1)
Matt Mower523a0592015-12-13 11:31:00 -0600231 goto out;
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500232
233 devname_r(sbuf.st_rdev, S_IFCHR, dev, 128);
234
235 if (strncmp(dev, "fuse", 4))
Matt Mower523a0592015-12-13 11:31:00 -0600236 goto out;
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500237
238 strtol(dev + 4, &ep, 10);
239 if (*ep != '\0')
Matt Mower523a0592015-12-13 11:31:00 -0600240 goto out;
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500241
242 do_unmount(dev, fd);
Matt Mower523a0592015-12-13 11:31:00 -0600243
244out:
245 close(fd);
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500246}
247
248/* Check if kernel is doing init in background */
249static int init_backgrounded(void)
250{
251 unsigned ibg, len;
252
253 len = sizeof(ibg);
254
255 if (sysctlbyname("vfs.fuse.init_backgrounded", &ibg, &len, NULL, 0))
256 return 0;
257
258 return ibg;
259}
260
261
262static int fuse_mount_core(const char *mountpoint, const char *opts)
263{
264 const char *mountprog = FUSERMOUNT_PROG;
265 int fd;
266 char *fdnam, *dev;
267 pid_t pid, cpid;
268 int status;
269
270 fdnam = getenv("FUSE_DEV_FD");
271
272 if (fdnam) {
273 char *ep;
274
275 fd = strtol(fdnam, &ep, 10);
276
277 if (*ep != '\0') {
278 fprintf(stderr, "invalid value given in FUSE_DEV_FD\n");
279 return -1;
280 }
281
282 if (fd < 0)
283 return -1;
284
285 goto mount;
286 }
287
288 dev = getenv("FUSE_DEV_NAME");
289
290 if (! dev)
291 dev = (char *)FUSE_DEV_TRUNK;
292
293 if ((fd = open(dev, O_RDWR)) < 0) {
294 perror("fuse: failed to open fuse device");
295 return -1;
296 }
297
298mount:
299 if (getenv("FUSE_NO_MOUNT") || ! mountpoint)
300 goto out;
301
302 pid = fork();
303 cpid = pid;
304
305 if (pid == -1) {
306 perror("fuse: fork() failed");
307 close(fd);
308 return -1;
309 }
310
311 if (pid == 0) {
312 if (! init_backgrounded()) {
313 /*
314 * If init is not backgrounded, we have to
315 * call the mount util backgrounded, to avoid
316 * deadlock.
317 */
318
319 pid = fork();
320
321 if (pid == -1) {
322 perror("fuse: fork() failed");
323 close(fd);
324 exit(1);
325 }
326 }
327
328 if (pid == 0) {
329 const char *argv[32];
330 int a = 0;
331
332 if (! fdnam && asprintf(&fdnam, "%d", fd) == -1) {
333 perror("fuse: failed to assemble mount arguments");
334 exit(1);
335 }
336
337 argv[a++] = mountprog;
338 if (opts) {
339 argv[a++] = "-o";
340 argv[a++] = opts;
341 }
342 argv[a++] = fdnam;
343 argv[a++] = mountpoint;
344 argv[a++] = NULL;
345 execvp(mountprog, (char **) argv);
346 perror("fuse: failed to exec mount program");
347 exit(1);
348 }
349
350 exit(0);
351 }
352
353 if (waitpid(cpid, &status, 0) == -1 || WEXITSTATUS(status) != 0) {
354 perror("fuse: failed to mount file system");
355 close(fd);
356 return -1;
357 }
358
359out:
360 return fd;
361}
362
363int fuse_kern_mount(const char *mountpoint, struct fuse_args *args)
364{
365 struct mount_opts mo;
366 int res = -1;
367
368 memset(&mo, 0, sizeof(mo));
369 /* mount util should not try to spawn the daemon */
370 setenv("MOUNT_FUSEFS_SAFE", "1", 1);
371 /* to notify the mount util it's called from lib */
372 setenv("MOUNT_FUSEFS_CALL_BY_LIB", "1", 1);
373
374 if (args &&
375 fuse_opt_parse(args, &mo, fuse_mount_opts, fuse_mount_opt_proc) == -1)
376 return -1;
377
378 if (mo.allow_other && mo.allow_root) {
379 fprintf(stderr, "fuse: 'allow_other' and 'allow_root' options are mutually exclusive\n");
380 goto out;
381 }
382 if (mo.ishelp)
383 return 0;
384
385 res = fuse_mount_core(mountpoint, mo.kernel_opts);
386out:
387 free(mo.kernel_opts);
388 return res;
389}
390
391FUSE_SYMVER(".symver fuse_unmount_compat22,fuse_unmount@FUSE_2.2");