Tizen 2.1 base
[framework/base/fuse.git] / lib / mount_bsd.c
1 /*
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
30 enum {
31         KEY_ALLOW_ROOT,
32         KEY_RO,
33         KEY_HELP,
34         KEY_VERSION,
35         KEY_KERN
36 };
37
38 struct 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
48 static 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
102 static 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
111 static void mount_version(void)
112 {
113         system(FUSERMOUNT_PROG " --version");
114 }
115
116 static 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
148 void 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
195 static 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
223 void 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)
231                 return;
232
233         devname_r(sbuf.st_rdev, S_IFCHR, dev, 128);
234
235         if (strncmp(dev, "fuse", 4))
236                 return;
237
238         strtol(dev + 4, &ep, 10);
239         if (*ep != '\0')
240                 return;
241
242         do_unmount(dev, fd);
243 }
244
245 /* Check if kernel is doing init in background */
246 static int init_backgrounded(void)
247 {
248         unsigned ibg, len;
249
250         len = sizeof(ibg);
251
252         if (sysctlbyname("vfs.fuse.init_backgrounded", &ibg, &len, NULL, 0))
253                 return 0;
254
255         return ibg;
256 }
257
258
259 static int fuse_mount_core(const char *mountpoint, const char *opts)
260 {
261         const char *mountprog = FUSERMOUNT_PROG;
262         int fd;
263         char *fdnam, *dev;
264         pid_t pid, cpid;
265         int status;
266
267         fdnam = getenv("FUSE_DEV_FD");
268
269         if (fdnam) {
270                 char *ep;
271
272                 fd = strtol(fdnam, &ep, 10);
273
274                 if (*ep != '\0') {
275                         fprintf(stderr, "invalid value given in FUSE_DEV_FD\n");
276                         return -1;
277                 }
278
279                 if (fd < 0)
280                         return -1;
281
282                 goto mount;
283         }
284
285         dev = getenv("FUSE_DEV_NAME");
286
287         if (! dev)
288                 dev = (char *)FUSE_DEV_TRUNK;
289
290         if ((fd = open(dev, O_RDWR)) < 0) {
291                 perror("fuse: failed to open fuse device");
292                 return -1;
293         }
294
295 mount:
296         if (getenv("FUSE_NO_MOUNT") || ! mountpoint)
297                 goto out;
298
299         pid = fork();
300         cpid = pid;
301
302         if (pid == -1) {
303                 perror("fuse: fork() failed");
304                 close(fd);
305                 return -1;
306         }
307
308         if (pid == 0) {
309                 if (! init_backgrounded()) {
310                         /*
311                          * If init is not backgrounded, we have to
312                          * call the mount util backgrounded, to avoid
313                          * deadlock.
314                          */
315
316                         pid = fork();
317
318                         if (pid == -1) {
319                                 perror("fuse: fork() failed");
320                                 close(fd);
321                                 exit(1);
322                         }
323                 }
324
325                 if (pid == 0) {
326                         const char *argv[32];
327                         int a = 0;
328
329                         if (! fdnam && asprintf(&fdnam, "%d", fd) == -1) {
330                                 perror("fuse: failed to assemble mount arguments");
331                                 exit(1);
332                         }
333
334                         argv[a++] = mountprog;
335                         if (opts) {
336                                 argv[a++] = "-o";
337                                 argv[a++] = opts;
338                         }
339                         argv[a++] = fdnam;
340                         argv[a++] = mountpoint;
341                         argv[a++] = NULL;
342                         execvp(mountprog, (char **) argv);
343                         perror("fuse: failed to exec mount program");
344                         exit(1);
345                 }
346
347                 exit(0);
348         }
349
350         if (waitpid(cpid, &status, 0) == -1 || WEXITSTATUS(status) != 0) {
351                 perror("fuse: failed to mount file system");
352                 close(fd);
353                 return -1;
354         }
355
356 out:
357         return fd;
358 }
359
360 int fuse_kern_mount(const char *mountpoint, struct fuse_args *args)
361 {
362         struct mount_opts mo;
363         int res = -1;
364
365         memset(&mo, 0, sizeof(mo));
366         /* mount util should not try to spawn the daemon */
367         setenv("MOUNT_FUSEFS_SAFE", "1", 1);
368         /* to notify the mount util it's called from lib */
369         setenv("MOUNT_FUSEFS_CALL_BY_LIB", "1", 1);
370
371         if (args &&
372             fuse_opt_parse(args, &mo, fuse_mount_opts, fuse_mount_opt_proc) == -1)
373                 return -1;
374
375         if (mo.allow_other && mo.allow_root) {
376                 fprintf(stderr, "fuse: 'allow_other' and 'allow_root' options are mutually exclusive\n");
377                 goto out;
378         }
379         if (mo.ishelp)
380                 return 0;
381
382         res = fuse_mount_core(mountpoint, mo.kernel_opts);
383 out:
384         free(mo.kernel_opts);
385         return res;
386 }
387
388 FUSE_SYMVER(".symver fuse_unmount_compat22,fuse_unmount@FUSE_2.2");