2 FUSE: Filesystem in Userspace
3 Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
5 This program can be distributed under the terms of the GNU GPL.
8 /* This program does the mounting and unmounting of FUSE filesystems */
12 #include "mount_util.h"
24 #include <sys/mnttab.h>
25 #else /* __SOLARIS__ */
28 #include <sys/fsuid.h>
29 #endif /* __SOLARIS__ */
33 #include <sys/mount.h>
34 #include <sys/socket.h>
35 #include <sys/utsname.h>
37 #define FUSE_DEV_NEW "/dev/fuse"
40 #define MS_DIRSYNC 128
43 static const char *progname
= "ntfs-3g-mount";
45 static int mount_max
= 1000;
48 int restore_privs(void);
53 * fusermount is not implemented in fuse-lite for Solaris,
54 * only the minimal functions are provided.
58 * Solaris doesn't have setfsuid/setfsgid.
59 * This doesn't really matter anyway as this program shouldn't be made
60 * suid on Solaris. It should instead be used via a profile with the
61 * sys_mount privilege.
69 int restore_privs(void)
74 #else /* __SOLARIS__ */
76 static const char *get_user_name(void)
78 struct passwd
*pw
= getpwuid(getuid());
79 if (pw
!= NULL
&& pw
->pw_name
!= NULL
)
82 fprintf(stderr
, "%s: could not determine username\n", progname
);
91 gid_t new_gid
= getgid();
93 if (setresgid(-1, new_gid
, getegid()) < 0) {
94 perror("priv drop: setresgid failed");
97 if (getegid() != new_gid
){
98 perror("dropping group privilege failed");
105 uid_t new_uid
= getuid();
107 if (setresuid(-1, new_uid
, geteuid()) < 0) {
108 perror("priv drop: setresuid failed");
111 if (geteuid() != new_uid
){
112 perror("dropping user privilege failed");
120 int restore_privs(void)
124 uid_t ruid
, euid
, suid
;
126 if (getresuid(&ruid
, &euid
, &suid
) < 0) {
127 perror("priv restore: getresuid failed");
130 if (setresuid(-1, suid
, -1) < 0) {
131 perror("priv restore: setresuid failed");
134 if (geteuid() != suid
) {
135 perror("restoring privilege failed");
142 gid_t rgid
, egid
, sgid
;
144 if (getresgid(&rgid
, &egid
, &sgid
) < 0) {
145 perror("priv restore: getresgid failed");
148 if (setresgid(-1, sgid
, -1) < 0) {
149 perror("priv restore: setresgid failed");
152 if (getegid() != sgid
){
153 perror("restoring group privilege failed");
162 static int add_mount(const char *source
, const char *mnt
, const char *type
,
165 return fuse_mnt_add_mount(progname
, source
, mnt
, type
, opts
);
168 static int count_fuse_fs(void)
172 const char *mtab
= _PATH_MOUNTED
;
173 FILE *fp
= setmntent(mtab
, "r");
175 fprintf(stderr
, "%s: failed to open %s: %s\n", progname
, mtab
,
179 while ((entp
= getmntent(fp
)) != NULL
) {
180 if (strcmp(entp
->mnt_type
, "fuse") == 0 ||
181 strncmp(entp
->mnt_type
, "fuse.", 5) == 0)
189 #else /* IGNORE_MTAB */
190 static int count_fuse_fs()
195 static int add_mount(const char *source
, const char *mnt
, const char *type
,
204 #endif /* IGNORE_MTAB */
206 static int begins_with(const char *s
, const char *beg
)
208 if (strncmp(s
, beg
, strlen(beg
)) == 0)
221 static struct mount_flags mount_flags
[] = {
222 {"rw", MS_RDONLY
, 0, 1},
223 {"ro", MS_RDONLY
, 1, 1},
224 {"suid", MS_NOSUID
, 0, 0},
225 {"nosuid", MS_NOSUID
, 1, 1},
226 {"dev", MS_NODEV
, 0, 0},
227 {"nodev", MS_NODEV
, 1, 1},
228 {"exec", MS_NOEXEC
, 0, 1},
229 {"noexec", MS_NOEXEC
, 1, 1},
230 {"async", MS_SYNCHRONOUS
, 0, 1},
231 {"sync", MS_SYNCHRONOUS
, 1, 1},
232 {"atime", MS_NOATIME
, 0, 1},
233 {"noatime", MS_NOATIME
, 1, 1},
234 {"dirsync", MS_DIRSYNC
, 1, 1},
238 static int find_mount_flag(const char *s
, unsigned len
, int *on
, int *flag
)
242 for (i
= 0; mount_flags
[i
].opt
!= NULL
; i
++) {
243 const char *opt
= mount_flags
[i
].opt
;
244 if (strlen(opt
) == len
&& strncmp(opt
, s
, len
) == 0) {
245 *on
= mount_flags
[i
].on
;
246 *flag
= mount_flags
[i
].flag
;
247 if (!mount_flags
[i
].safe
&& getuid() != 0) {
249 fprintf(stderr
, "%s: unsafe option '%s' ignored\n",
258 static int add_option(char **optsp
, const char *opt
, unsigned expand
)
262 newopts
= strdup(opt
);
264 unsigned oldsize
= strlen(*optsp
);
265 unsigned newsize
= oldsize
+ 1 + strlen(opt
) + expand
+ 1;
266 newopts
= (char *) realloc(*optsp
, newsize
);
268 sprintf(newopts
+ oldsize
, ",%s", opt
);
270 if (newopts
== NULL
) {
271 fprintf(stderr
, "%s: failed to allocate memory\n", progname
);
278 static int get_mnt_opts(int flags
, char *opts
, char **mnt_optsp
)
283 if (!(flags
& MS_RDONLY
) && add_option(mnt_optsp
, "rw", 0) == -1)
286 for (i
= 0; mount_flags
[i
].opt
!= NULL
; i
++) {
287 if (mount_flags
[i
].on
&& (flags
& mount_flags
[i
].flag
) &&
288 add_option(mnt_optsp
, mount_flags
[i
].opt
, 0) == -1)
292 if (add_option(mnt_optsp
, opts
, 0) == -1)
294 /* remove comma from end of opts*/
295 l
= strlen(*mnt_optsp
);
296 if ((*mnt_optsp
)[l
-1] == ',')
297 (*mnt_optsp
)[l
-1] = '\0';
299 const char *user
= get_user_name();
303 if (add_option(mnt_optsp
, "user=", strlen(user
)) == -1)
305 strcat(*mnt_optsp
, user
);
310 static int opt_eq(const char *s
, unsigned len
, const char *opt
)
312 if(strlen(opt
) == len
&& strncmp(s
, opt
, len
) == 0)
318 static int get_string_opt(const char *s
, unsigned len
, const char *opt
,
321 unsigned opt_len
= strlen(opt
);
325 *val
= (char *) malloc(len
- opt_len
+ 1);
327 fprintf(stderr
, "%s: failed to allocate memory\n", progname
);
331 memcpy(*val
, s
+ opt_len
, len
- opt_len
);
332 (*val
)[len
- opt_len
] = '\0';
336 static int do_mount(const char *mnt
, char **typep
, mode_t rootmode
,
337 int fd
, const char *opts
, const char *dev
, char **sourcep
,
341 int flags
= MS_NOSUID
| MS_NODEV
;
343 char *mnt_opts
= NULL
;
351 optbuf
= (char *) malloc(strlen(opts
) + 128);
353 fprintf(stderr
, "%s: failed to allocate memory\n", progname
);
357 for (s
= opts
, d
= optbuf
; *s
;) {
359 const char *fsname_str
= "fsname=";
360 for (len
= 0; s
[len
] && s
[len
] != ','; len
++);
361 if (begins_with(s
, fsname_str
)) {
362 if (!get_string_opt(s
, len
, fsname_str
, &fsname
))
364 } else if (opt_eq(s
, len
, "blkdev")) {
366 } else if (!begins_with(s
, "fd=") &&
367 !begins_with(s
, "rootmode=") &&
368 !begins_with(s
, "user_id=") &&
369 !begins_with(s
, "group_id=")) {
373 if (opt_eq(s
, len
, "large_read")) {
374 struct utsname utsname
;
376 res
= uname(&utsname
);
378 sscanf(utsname
.release
, "%u.%u", &kmaj
, &kmin
) == 2 &&
379 (kmaj
> 2 || (kmaj
== 2 && kmin
> 4))) {
380 fprintf(stderr
, "%s: note: 'large_read' mount option is "
381 "deprecated for %i.%i kernels\n", progname
, kmaj
, kmin
);
386 if (find_mount_flag(s
, len
, &on
, &flag
)) {
403 res
= get_mnt_opts(flags
, optbuf
, &mnt_opts
);
407 sprintf(d
, "fd=%i,rootmode=%o,user_id=%i,group_id=%i",
408 fd
, rootmode
, getuid(), getgid());
410 source
= malloc((fsname
? strlen(fsname
) : 0) + strlen(dev
) + 32);
413 if (!type
|| !source
) {
414 fprintf(stderr
, "%s: failed to allocate memory\n", progname
);
418 strcpy(type
, blkdev
? "fuseblk" : "fuse");
421 strcpy(source
, fsname
);
428 res
= mount(source
, mnt
, type
, flags
, optbuf
);
429 if (res
== -1 && errno
== EINVAL
) {
430 /* It could be an old version not supporting group_id */
431 sprintf(d
, "fd=%i,rootmode=%o,user_id=%i", fd
, rootmode
, getuid());
432 res
= mount(source
, mnt
, type
, flags
, optbuf
);
439 int errno_save
= errno
;
440 if (blkdev
&& errno
== ENODEV
&& !fuse_mnt_check_fuseblk())
441 fprintf(stderr
, "%s: 'fuseblk' support missing\n", progname
);
443 fprintf(stderr
, "%s: mount failed: %s\n", progname
, strerror(errno_save
));
444 if (errno_save
== EPERM
)
445 fprintf(stderr
, "User doesn't have privilege to mount. "
446 "For more information\nplease see: "
447 "http://tuxera.com/community/ntfs-3g-faq/#unprivileged\n");
453 *mnt_optsp
= mnt_opts
;
467 static int check_perm(const char **mntp
, struct stat
*stbuf
, int *currdir_fd
,
471 const char *mnt
= *mntp
;
472 const char *origmnt
= mnt
;
474 res
= stat(mnt
, stbuf
);
476 fprintf(stderr
, "%s: failed to access mountpoint %s: %s\n",
477 progname
, mnt
, strerror(errno
));
481 /* No permission checking is done for root */
485 if (S_ISDIR(stbuf
->st_mode
)) {
486 *currdir_fd
= open(".", O_RDONLY
);
487 if (*currdir_fd
== -1) {
488 fprintf(stderr
, "%s: failed to open current directory: %s\n",
489 progname
, strerror(errno
));
494 fprintf(stderr
, "%s: failed to chdir to mountpoint: %s\n",
495 progname
, strerror(errno
));
499 res
= lstat(mnt
, stbuf
);
501 fprintf(stderr
, "%s: failed to access mountpoint %s: %s\n",
502 progname
, origmnt
, strerror(errno
));
506 if ((stbuf
->st_mode
& S_ISVTX
) && stbuf
->st_uid
!= getuid()) {
507 fprintf(stderr
, "%s: mountpoint %s not owned by user\n",
512 res
= access(mnt
, W_OK
);
514 fprintf(stderr
, "%s: user has no write access to mountpoint %s\n",
518 } else if (S_ISREG(stbuf
->st_mode
)) {
519 static char procfile
[256];
520 *mountpoint_fd
= open(mnt
, O_WRONLY
);
521 if (*mountpoint_fd
== -1) {
522 fprintf(stderr
, "%s: failed to open %s: %s\n", progname
, mnt
,
526 res
= fstat(*mountpoint_fd
, stbuf
);
528 fprintf(stderr
, "%s: failed to access mountpoint %s: %s\n",
529 progname
, mnt
, strerror(errno
));
532 if (!S_ISREG(stbuf
->st_mode
)) {
533 fprintf(stderr
, "%s: mountpoint %s is no longer a regular file\n",
538 sprintf(procfile
, "/proc/self/fd/%i", *mountpoint_fd
);
542 "%s: mountpoint %s is not a directory or a regular file\n",
551 static int try_open(const char *dev
, char **devp
)
557 fd
= open(dev
, O_RDWR
);
563 fprintf(stderr
, "%s: failed to allocate memory\n", progname
);
567 } else if (errno
== ENODEV
||
568 errno
== ENOENT
) /* check for ENOENT too, for the udev case */
571 fprintf(stderr
, "%s: failed to open %s: %s\n", progname
, dev
,
577 static int open_fuse_device(char **devp
)
581 fd
= try_open(FUSE_DEV_NEW
, devp
);
585 fprintf(stderr
, "%s: fuse device is missing, try 'modprobe fuse' as root\n",
592 static int mount_fuse(const char *mnt
, const char *opts
)
600 char *mnt_opts
= NULL
;
601 const char *real_mnt
= mnt
;
603 int mountpoint_fd
= -1;
605 fd
= open_fuse_device(&dev
);
609 if (getuid() != 0 && mount_max
!= -1) {
610 if (count_fuse_fs() >= mount_max
) {
611 fprintf(stderr
, "%s: too many mounted FUSE filesystems (%d+)\n",
612 progname
, mount_max
);
617 res
= check_perm(&real_mnt
, &stbuf
, &currdir_fd
, &mountpoint_fd
);
619 res
= do_mount(real_mnt
, &type
, stbuf
.st_mode
& S_IFMT
, fd
, opts
, dev
,
622 if (currdir_fd
!= -1) {
623 __attribute__((unused
))int ignored_fchdir_status
=
627 if (mountpoint_fd
!= -1)
628 close(mountpoint_fd
);
636 if (geteuid() == 0) {
638 if (setgroups(0, NULL
) == -1) {
639 perror("priv drop: setgroups failed");
643 res
= add_mount(source
, mnt
, type
, mnt_opts
);
645 umount2(mnt
, 2); /* lazy umount */
666 int fusermount(int unmount
, int quiet
, int lazy
, const char *opts
,
673 mnt
= fuse_mnt_resolve_path(progname
, origmnt
);
677 old_umask
= umask(033);
685 res
= fuse_mnt_umount(progname
, mnt
, lazy
);
687 res
= umount2(mnt
, lazy
? 2 : 0);
688 if (res
== -1 && !quiet
)
689 fprintf(stderr
, "%s: failed to unmount %s: %s\n", progname
,
690 mnt
, strerror(errno
));
697 res
= mount_fuse(mnt
, opts
);
704 #endif /* __SOLARIS__ */