1/* 2 FUSE: Filesystem in Userspace 3 Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu> 4 5 Architecture-independent mounting code. 6 7 This program can be distributed under the terms of the GNU LGPLv2. 8 See the file COPYING.LIB. 9*/ 10 11#include "fuse_config.h" 12#include "mount_util.h" 13 14#include <stdio.h> 15#include <unistd.h> 16#include <stdlib.h> 17#include <string.h> 18#include <signal.h> 19#include <dirent.h> 20#include <errno.h> 21#include <fcntl.h> 22#include <limits.h> 23#include <paths.h> 24#if !defined( __NetBSD__) && !defined(__FreeBSD__) && !defined(__DragonFly__) 25#include <mntent.h> 26#else 27#define IGNORE_MTAB 28#endif 29#include <sys/stat.h> 30#include <sys/wait.h> 31 32#include "fuse_mount_compat.h" 33 34#include <sys/param.h> 35 36#if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__) 37#define umount2(mnt, flags) unmount(mnt, ((flags) == 2) ? MNT_FORCE : 0) 38#endif 39 40#ifdef IGNORE_MTAB 41#define mtab_needs_update(mnt) 0 42#else 43static int mtab_needs_update(const char *mnt) 44{ 45 int res; 46 struct stat stbuf; 47 48 /* If mtab is within new mount, don't touch it */ 49 if (strncmp(mnt, _PATH_MOUNTED, strlen(mnt)) == 0 && 50 _PATH_MOUNTED[strlen(mnt)] == '/') 51 return 0; 52 53 /* 54 * Skip mtab update if /etc/mtab: 55 * 56 * - doesn't exist, 57 * - is a symlink, 58 * - is on a read-only filesystem. 59 */ 60 res = lstat(_PATH_MOUNTED, &stbuf); 61 if (res == -1) { 62 if (errno == ENOENT) 63 return 0; 64 } else { 65 uid_t ruid; 66 int err; 67 68 if (S_ISLNK(stbuf.st_mode)) 69 return 0; 70 71 ruid = getuid(); 72 if (ruid != 0) 73 setreuid(0, -1); 74 75 res = access(_PATH_MOUNTED, W_OK); 76 err = (res == -1) ? errno : 0; 77 if (ruid != 0) 78 setreuid(ruid, -1); 79 80 if (err == EROFS) 81 return 0; 82 } 83 84 return 1; 85} 86#endif /* IGNORE_MTAB */ 87 88static int add_mount(const char *progname, const char *fsname, 89 const char *mnt, const char *type, const char *opts) 90{ 91 int res; 92 int status; 93 sigset_t blockmask; 94 sigset_t oldmask; 95 96 sigemptyset(&blockmask); 97 sigaddset(&blockmask, SIGCHLD); 98 res = sigprocmask(SIG_BLOCK, &blockmask, &oldmask); 99 if (res == -1) { 100 fprintf(stderr, "%s: sigprocmask: %s\n", progname, strerror(errno)); 101 return -1; 102 } 103 104 res = fork(); 105 if (res == -1) { 106 fprintf(stderr, "%s: fork: %s\n", progname, strerror(errno)); 107 goto out_restore; 108 } 109 if (res == 0) { 110 char *env = NULL; 111 112 sigprocmask(SIG_SETMASK, &oldmask, NULL); 113 114 if(setuid(geteuid()) == -1) { 115 fprintf(stderr, "%s: setuid: %s\n", progname, strerror(errno)); 116 res = -1; 117 goto out_restore; 118 } 119 120 execle("/bin/mount", "/bin/mount", "--no-canonicalize", "-i", 121 "-f", "-t", type, "-o", opts, fsname, mnt, NULL, &env); 122 fprintf(stderr, "%s: failed to execute /bin/mount: %s\n", 123 progname, strerror(errno)); 124 exit(1); 125 } 126 res = waitpid(res, &status, 0); 127 if (res == -1) 128 fprintf(stderr, "%s: waitpid: %s\n", progname, strerror(errno)); 129 130 if (status != 0) 131 res = -1; 132 133 out_restore: 134 sigprocmask(SIG_SETMASK, &oldmask, NULL); 135 136 return res; 137} 138 139int fuse_mnt_add_mount(const char *progname, const char *fsname, 140 const char *mnt, const char *type, const char *opts) 141{ 142 if (!mtab_needs_update(mnt)) 143 return 0; 144 145 return add_mount(progname, fsname, mnt, type, opts); 146} 147 148static int exec_umount(const char *progname, const char *rel_mnt, int lazy) 149{ 150 int res; 151 int status; 152 sigset_t blockmask; 153 sigset_t oldmask; 154 155 sigemptyset(&blockmask); 156 sigaddset(&blockmask, SIGCHLD); 157 res = sigprocmask(SIG_BLOCK, &blockmask, &oldmask); 158 if (res == -1) { 159 fprintf(stderr, "%s: sigprocmask: %s\n", progname, strerror(errno)); 160 return -1; 161 } 162 163 res = fork(); 164 if (res == -1) { 165 fprintf(stderr, "%s: fork: %s\n", progname, strerror(errno)); 166 goto out_restore; 167 } 168 if (res == 0) { 169 char *env = NULL; 170 171 sigprocmask(SIG_SETMASK, &oldmask, NULL); 172 173 if(setuid(geteuid()) == -1) { 174 fprintf(stderr, "%s: setuid: %s\n", progname, strerror(errno)); 175 res = -1; 176 goto out_restore; 177 } 178 179 if (lazy) { 180 execle("/bin/umount", "/bin/umount", "-i", rel_mnt, 181 "-l", NULL, &env); 182 } else { 183 execle("/bin/umount", "/bin/umount", "-i", rel_mnt, 184 NULL, &env); 185 } 186 fprintf(stderr, "%s: failed to execute /bin/umount: %s\n", 187 progname, strerror(errno)); 188 exit(1); 189 } 190 res = waitpid(res, &status, 0); 191 if (res == -1) 192 fprintf(stderr, "%s: waitpid: %s\n", progname, strerror(errno)); 193 194 if (status != 0) { 195 res = -1; 196 } 197 198 out_restore: 199 sigprocmask(SIG_SETMASK, &oldmask, NULL); 200 return res; 201 202} 203 204int fuse_mnt_umount(const char *progname, const char *abs_mnt, 205 const char *rel_mnt, int lazy) 206{ 207 int res; 208 209 if (!mtab_needs_update(abs_mnt)) { 210 res = umount2(rel_mnt, lazy ? 2 : 0); 211 if (res == -1) 212 fprintf(stderr, "%s: failed to unmount %s: %s\n", 213 progname, abs_mnt, strerror(errno)); 214 return res; 215 } 216 217 return exec_umount(progname, rel_mnt, lazy); 218} 219 220static int remove_mount(const char *progname, const char *mnt) 221{ 222 int res; 223 int status; 224 sigset_t blockmask; 225 sigset_t oldmask; 226 227 sigemptyset(&blockmask); 228 sigaddset(&blockmask, SIGCHLD); 229 res = sigprocmask(SIG_BLOCK, &blockmask, &oldmask); 230 if (res == -1) { 231 fprintf(stderr, "%s: sigprocmask: %s\n", progname, strerror(errno)); 232 return -1; 233 } 234 235 res = fork(); 236 if (res == -1) { 237 fprintf(stderr, "%s: fork: %s\n", progname, strerror(errno)); 238 goto out_restore; 239 } 240 if (res == 0) { 241 char *env = NULL; 242 243 sigprocmask(SIG_SETMASK, &oldmask, NULL); 244 245 if(setuid(geteuid()) == -1) { 246 fprintf(stderr, "%s: setuid: %s\n", progname, strerror(errno)); 247 res = -1; 248 goto out_restore; 249 } 250 251 execle("/bin/umount", "/bin/umount", "--no-canonicalize", "-i", 252 "--fake", mnt, NULL, &env); 253 fprintf(stderr, "%s: failed to execute /bin/umount: %s\n", 254 progname, strerror(errno)); 255 exit(1); 256 } 257 res = waitpid(res, &status, 0); 258 if (res == -1) 259 fprintf(stderr, "%s: waitpid: %s\n", progname, strerror(errno)); 260 261 if (status != 0) 262 res = -1; 263 264 out_restore: 265 sigprocmask(SIG_SETMASK, &oldmask, NULL); 266 return res; 267} 268 269int fuse_mnt_remove_mount(const char *progname, const char *mnt) 270{ 271 if (!mtab_needs_update(mnt)) 272 return 0; 273 274 return remove_mount(progname, mnt); 275} 276 277char *fuse_mnt_resolve_path(const char *progname, const char *orig) 278{ 279 char buf[PATH_MAX]; 280 char *copy; 281 char *dst; 282 char *end; 283 char *lastcomp; 284 const char *toresolv; 285 286 if (!orig[0]) { 287 fprintf(stderr, "%s: invalid mountpoint '%s'\n", progname, 288 orig); 289 return NULL; 290 } 291 292 copy = strdup(orig); 293 if (copy == NULL) { 294 fprintf(stderr, "%s: failed to allocate memory\n", progname); 295 return NULL; 296 } 297 298 toresolv = copy; 299 lastcomp = NULL; 300 for (end = copy + strlen(copy) - 1; end > copy && *end == '/'; end --); 301 if (end[0] != '/') { 302 char *tmp; 303 end[1] = '\0'; 304 tmp = strrchr(copy, '/'); 305 if (tmp == NULL) { 306 lastcomp = copy; 307 toresolv = "."; 308 } else { 309 lastcomp = tmp + 1; 310 if (tmp == copy) 311 toresolv = "/"; 312 } 313 if (strcmp(lastcomp, ".") == 0 || strcmp(lastcomp, "..") == 0) { 314 lastcomp = NULL; 315 toresolv = copy; 316 } 317 else if (tmp) 318 tmp[0] = '\0'; 319 } 320 if (realpath(toresolv, buf) == NULL) { 321 fprintf(stderr, "%s: bad mount point %s: %s\n", progname, orig, 322 strerror(errno)); 323 free(copy); 324 return NULL; 325 } 326 if (lastcomp == NULL) 327 dst = strdup(buf); 328 else { 329 dst = (char *) malloc(strlen(buf) + 1 + strlen(lastcomp) + 1); 330 if (dst) { 331 unsigned buflen = strlen(buf); 332 if (buflen && buf[buflen-1] == '/') 333 sprintf(dst, "%s%s", buf, lastcomp); 334 else 335 sprintf(dst, "%s/%s", buf, lastcomp); 336 } 337 } 338 free(copy); 339 if (dst == NULL) 340 fprintf(stderr, "%s: failed to allocate memory\n", progname); 341 return dst; 342} 343 344int fuse_mnt_check_fuseblk(void) 345{ 346 char buf[256]; 347 FILE *f = fopen("/proc/filesystems", "r"); 348 if (!f) 349 return 1; 350 351 while (fgets(buf, sizeof(buf), f)) 352 if (strstr(buf, "fuseblk\n")) { 353 fclose(f); 354 return 1; 355 } 356 357 fclose(f); 358 return 0; 359} 360 361int fuse_mnt_parse_fuse_fd(const char *mountpoint) 362{ 363 int fd = -1; 364 int len = 0; 365 366 if (sscanf(mountpoint, "/dev/fd/%u%n", &fd, &len) == 1 && 367 len == strlen(mountpoint)) { 368 return fd; 369 } 370 371 return -1; 372} 373