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