1/*
2    FUSE: Filesystem in Userspace
3    Copyright (C) 2001-2007  Miklos Szeredi <miklos@szeredi.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 "config.h"
10#include "fuse_i.h"
11#include "fuse_opt.h"
12#include "mount_util.h"
13
14#include <stdio.h>
15#include <stdlib.h>
16#include <unistd.h>
17#include <stddef.h>
18#include <fcntl.h>
19#include <errno.h>
20#include <sys/poll.h>
21#include <sys/socket.h>
22#include <sys/un.h>
23#include <sys/wait.h>
24#include <sys/mount.h>
25
26#ifndef MS_DIRSYNC
27#define MS_DIRSYNC 128
28#endif
29
30enum {
31    KEY_KERN_FLAG,
32    KEY_KERN_OPT,
33    KEY_FUSERMOUNT_OPT,
34    KEY_SUBTYPE_OPT,
35    KEY_MTAB_OPT,
36    KEY_ALLOW_ROOT,
37    KEY_RO,
38    KEY_HELP,
39    KEY_VERSION,
40};
41
42struct mount_opts {
43    int allow_other;
44    int allow_root;
45    int ishelp;
46    int flags;
47    int blkdev;
48    char *fsname;
49    char *mtab_opts;
50    char *fusermount_opts;
51    char *kernel_opts;
52};
53
54#define FUSE_MOUNT_OPT(t, p) { t, offsetof(struct mount_opts, p), 1 }
55
56static const struct fuse_opt fuse_mount_opts[] = {
57    FUSE_MOUNT_OPT("allow_other",       allow_other),
58    FUSE_MOUNT_OPT("allow_root",        allow_root),
59    FUSE_MOUNT_OPT("blkdev",            blkdev),
60    FUSE_MOUNT_OPT("fsname=%s",         fsname),
61    FUSE_OPT_KEY("allow_other",         KEY_KERN_OPT),
62    FUSE_OPT_KEY("allow_root",          KEY_ALLOW_ROOT),
63    FUSE_OPT_KEY("blkdev",              KEY_FUSERMOUNT_OPT),
64    FUSE_OPT_KEY("fsname=",             KEY_FUSERMOUNT_OPT),
65    FUSE_OPT_KEY("large_read",          KEY_KERN_OPT),
66    FUSE_OPT_KEY("blksize=",            KEY_KERN_OPT),
67    FUSE_OPT_KEY("default_permissions", KEY_KERN_OPT),
68    FUSE_OPT_KEY("max_read=",           KEY_KERN_OPT),
69    FUSE_OPT_KEY("max_read=",           FUSE_OPT_KEY_KEEP),
70    FUSE_OPT_KEY("user=",               KEY_MTAB_OPT),
71    FUSE_OPT_KEY("-r",                  KEY_RO),
72    FUSE_OPT_KEY("ro",                  KEY_KERN_FLAG),
73    FUSE_OPT_KEY("rw",                  KEY_KERN_FLAG),
74    FUSE_OPT_KEY("suid",                KEY_KERN_FLAG),
75    FUSE_OPT_KEY("nosuid",              KEY_KERN_FLAG),
76    FUSE_OPT_KEY("dev",                 KEY_KERN_FLAG),
77    FUSE_OPT_KEY("nodev",               KEY_KERN_FLAG),
78    FUSE_OPT_KEY("exec",                KEY_KERN_FLAG),
79    FUSE_OPT_KEY("noexec",              KEY_KERN_FLAG),
80    FUSE_OPT_KEY("async",               KEY_KERN_FLAG),
81    FUSE_OPT_KEY("sync",                KEY_KERN_FLAG),
82    FUSE_OPT_KEY("dirsync",             KEY_KERN_FLAG),
83    FUSE_OPT_KEY("atime",               KEY_KERN_FLAG),
84    FUSE_OPT_KEY("noatime",             KEY_KERN_FLAG),
85    FUSE_OPT_KEY("-h",                  KEY_HELP),
86    FUSE_OPT_KEY("--help",              KEY_HELP),
87    FUSE_OPT_KEY("-V",                  KEY_VERSION),
88    FUSE_OPT_KEY("--version",           KEY_VERSION),
89    FUSE_OPT_END
90};
91
92struct mount_flags {
93    const char *opt;
94    unsigned long flag;
95    int on;
96};
97
98static struct mount_flags mount_flags[] = {
99    {"rw",      MS_RDONLY,      0},
100    {"ro",      MS_RDONLY,      1},
101    {"suid",    MS_NOSUID,      0},
102    {"nosuid",  MS_NOSUID,      1},
103    {"dev",     MS_NODEV,       0},
104    {"nodev",   MS_NODEV,       1},
105    {"exec",    MS_NOEXEC,      0},
106    {"noexec",  MS_NOEXEC,      1},
107    {"async",   MS_SYNCHRONOUS, 0},
108    {"sync",    MS_SYNCHRONOUS, 1},
109    {"atime",   MS_NOATIME,     0},
110    {"noatime", MS_NOATIME,     1},
111    {"dirsync", MS_DIRSYNC,     1},
112    {NULL,      0,              0}
113};
114
115static void set_mount_flag(const char *s, int *flags)
116{
117    int i;
118
119    for (i = 0; mount_flags[i].opt != NULL; i++) {
120        const char *opt = mount_flags[i].opt;
121        if (strcmp(opt, s) == 0) {
122            if (mount_flags[i].on)
123                *flags |= mount_flags[i].flag;
124            else
125                *flags &= ~mount_flags[i].flag;
126            return;
127        }
128    }
129    fprintf(stderr, "fuse: internal error, can't find mount flag\n");
130    abort();
131}
132
133static int fuse_mount_opt_proc(void *data, const char *arg, int key,
134                               struct fuse_args *outargs)
135{
136    struct mount_opts *mo = data;
137
138    switch (key) {
139    case KEY_ALLOW_ROOT:
140        if (fuse_opt_add_opt(&mo->kernel_opts, "allow_other") == -1 ||
141            fuse_opt_add_arg(outargs, "-oallow_root") == -1)
142            return -1;
143        return 0;
144
145    case KEY_RO:
146        arg = "ro";
147        /* fall through */
148    case KEY_KERN_FLAG:
149        set_mount_flag(arg, &mo->flags);
150        return 0;
151
152    case KEY_KERN_OPT:
153        return fuse_opt_add_opt(&mo->kernel_opts, arg);
154
155    case KEY_FUSERMOUNT_OPT:
156        return fuse_opt_add_opt(&mo->fusermount_opts, arg);
157
158    case KEY_MTAB_OPT:
159        return fuse_opt_add_opt(&mo->mtab_opts, arg);
160
161    case KEY_HELP:
162        mo->ishelp = 1;
163        break;
164
165    case KEY_VERSION:
166        mo->ishelp = 1;
167        break;
168    }
169    return 1;
170}
171
172void fuse_kern_unmount(const char *mountpoint, int fd)
173{
174    int res;
175
176    if (!mountpoint)
177        return;
178
179    if (fd != -1) {
180        struct pollfd pfd;
181
182        pfd.fd = fd;
183        pfd.events = 0;
184        res = poll(&pfd, 1, 0);
185        /* If file poll returns POLLERR on the device file descriptor,
186           then the filesystem is already unmounted */
187        if (res == 1 && (pfd.revents & POLLERR))
188            return;
189    }
190    close(fd);
191
192    fusermount(1, 0, 1, "", mountpoint);
193}
194
195static int get_mnt_flag_opts(char **mnt_optsp, int flags)
196{
197    int i;
198
199    if (!(flags & MS_RDONLY) && fuse_opt_add_opt(mnt_optsp, "rw") == -1)
200        return -1;
201
202    for (i = 0; mount_flags[i].opt != NULL; i++) {
203        if (mount_flags[i].on && (flags & mount_flags[i].flag) &&
204            fuse_opt_add_opt(mnt_optsp, mount_flags[i].opt) == -1)
205            return -1;
206    }
207    return 0;
208}
209
210int fuse_kern_mount(const char *mountpoint, struct fuse_args *args)
211{
212    struct mount_opts mo;
213    int res = -1;
214    char *mnt_opts = NULL;
215
216    memset(&mo, 0, sizeof(mo));
217    if (getuid())
218	    mo.flags = MS_NOSUID | MS_NODEV;
219
220    if (args &&
221        fuse_opt_parse(args, &mo, fuse_mount_opts, fuse_mount_opt_proc) == -1)
222        return -1;
223
224    if (mo.allow_other && mo.allow_root) {
225        fprintf(stderr, "fuse: 'allow_other' and 'allow_root' options are mutually exclusive\n");
226        goto out;
227    }
228    res = 0;
229    if (mo.ishelp)
230        goto out;
231
232    res = -1;
233    if (get_mnt_flag_opts(&mnt_opts, mo.flags) == -1)
234        goto out;
235    if (!(mo.flags & MS_NODEV) && fuse_opt_add_opt(&mnt_opts, "dev") == -1)
236        goto out;
237    if (!(mo.flags & MS_NOSUID) && fuse_opt_add_opt(&mnt_opts, "suid") == -1)
238        goto out;
239    if (mo.kernel_opts && fuse_opt_add_opt(&mnt_opts, mo.kernel_opts) == -1)
240        goto out;
241    if (mo.mtab_opts &&  fuse_opt_add_opt(&mnt_opts, mo.mtab_opts) == -1)
242        goto out;
243    if (mo.fusermount_opts && fuse_opt_add_opt(&mnt_opts, mo.fusermount_opts) < 0)
244        goto out;
245
246    res = fusermount(0, 0, 0, mnt_opts ? mnt_opts : "", mountpoint);
247
248out:
249    free(mnt_opts);
250    free(mo.fsname);
251    free(mo.fusermount_opts);
252    free(mo.kernel_opts);
253    free(mo.mtab_opts);
254    return res;
255}
256
257