blob: 0bb3aee54545578ca7efdccb00d199b636fef73b
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 | |
30 | enum { |
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 | |
42 | struct 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 | |
56 | static 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 | |
92 | struct mount_flags { |
93 | const char *opt; |
94 | unsigned long flag; |
95 | int on; |
96 | }; |
97 | |
98 | static 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 | |
115 | static 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 | |
133 | static 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 | |
172 | void 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 | |
195 | static 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 | |
210 | int 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 | |
248 | out: |
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 |