new: support user mount via setuid root ntfs-3g (fusermount isn't required)
parent
f101412c23
commit
9c0f8eef72
|
@ -26,6 +26,7 @@ libfuse_lite_la_SOURCES = \
|
|||
fuse_opt.c \
|
||||
fuse_session.c \
|
||||
fuse_signals.c \
|
||||
fusermount.c \
|
||||
helper.c \
|
||||
mount.c \
|
||||
mount_util.c \
|
||||
|
|
|
@ -0,0 +1,793 @@
|
|||
/*
|
||||
FUSE: Filesystem in Userspace
|
||||
Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
This program can be distributed under the terms of the GNU GPL.
|
||||
See the file COPYING.
|
||||
*/
|
||||
/* This program does the mounting and unmounting of FUSE filesystems */
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include "mount_util.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <unistd.h>
|
||||
#include <getopt.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <pwd.h>
|
||||
#include <mntent.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/fsuid.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/utsname.h>
|
||||
|
||||
#define FUSE_DEV_OLD "/proc/fs/fuse/dev"
|
||||
#define FUSE_DEV_NEW "/dev/fuse"
|
||||
#define FUSE_VERSION_FILE_OLD "/proc/fs/fuse/version"
|
||||
#define FUSE_CONF "/etc/fuse.conf"
|
||||
|
||||
#ifndef MS_DIRSYNC
|
||||
#define MS_DIRSYNC 128
|
||||
#endif
|
||||
|
||||
static const char *progname = "ntfs-3g-mount";
|
||||
|
||||
static int user_allow_other = 0;
|
||||
static int mount_max = 1000;
|
||||
|
||||
static const char *get_user_name(void)
|
||||
{
|
||||
struct passwd *pw = getpwuid(getuid());
|
||||
if (pw != NULL && pw->pw_name != NULL)
|
||||
return pw->pw_name;
|
||||
else {
|
||||
fprintf(stderr, "%s: could not determine username\n", progname);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static uid_t oldfsuid;
|
||||
static gid_t oldfsgid;
|
||||
|
||||
static void drop_privs(void)
|
||||
{
|
||||
if (getuid() != 0) {
|
||||
oldfsuid = setfsuid(getuid());
|
||||
oldfsgid = setfsgid(getgid());
|
||||
}
|
||||
}
|
||||
|
||||
static void restore_privs(void)
|
||||
{
|
||||
if (getuid() != 0) {
|
||||
setfsuid(oldfsuid);
|
||||
setfsgid(oldfsgid);
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef IGNORE_MTAB
|
||||
static int add_mount(const char *source, const char *mnt, const char *type,
|
||||
const char *opts)
|
||||
{
|
||||
return fuse_mnt_add_mount(progname, source, mnt, type, opts);
|
||||
}
|
||||
|
||||
static int unmount_fuse(const char *mnt, int quiet, int lazy)
|
||||
{
|
||||
if (getuid() != 0) {
|
||||
struct mntent *entp;
|
||||
FILE *fp;
|
||||
const char *user = NULL;
|
||||
char uidstr[32];
|
||||
unsigned uidlen = 0;
|
||||
int found;
|
||||
const char *mtab = _PATH_MOUNTED;
|
||||
|
||||
user = get_user_name();
|
||||
if (user == NULL)
|
||||
return -1;
|
||||
|
||||
fp = setmntent(mtab, "r");
|
||||
if (fp == NULL) {
|
||||
fprintf(stderr, "%s: failed to open %s: %s\n", progname, mtab,
|
||||
strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
uidlen = sprintf(uidstr, "%u", getuid());
|
||||
|
||||
found = 0;
|
||||
while ((entp = getmntent(fp)) != NULL) {
|
||||
if (!found && strcmp(entp->mnt_dir, mnt) == 0 &&
|
||||
(strcmp(entp->mnt_type, "fuse") == 0 ||
|
||||
strcmp(entp->mnt_type, "fuseblk") == 0 ||
|
||||
strncmp(entp->mnt_type, "fuse.", 5) == 0 ||
|
||||
strncmp(entp->mnt_type, "fuseblk.", 8) == 0)) {
|
||||
char *p = strstr(entp->mnt_opts, "user=");
|
||||
if (p && (p == entp->mnt_opts || *(p-1) == ',') &&
|
||||
strcmp(p + 5, user) == 0) {
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
/* /etc/mtab is a link pointing to /proc/mounts: */
|
||||
else if ((p = strstr(entp->mnt_opts, "user_id=")) &&
|
||||
(p == entp->mnt_opts || *(p-1) == ',') &&
|
||||
strncmp(p + 8, uidstr, uidlen) == 0 &&
|
||||
(*(p+8+uidlen) == ',' || *(p+8+uidlen) == '\0')) {
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
endmntent(fp);
|
||||
|
||||
if (!found) {
|
||||
if (!quiet)
|
||||
fprintf(stderr, "%s: entry for %s not found in %s\n", progname,
|
||||
mnt, mtab);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return fuse_mnt_umount(progname, mnt, lazy);
|
||||
}
|
||||
|
||||
static int count_fuse_fs(void)
|
||||
{
|
||||
struct mntent *entp;
|
||||
int count = 0;
|
||||
const char *mtab = _PATH_MOUNTED;
|
||||
FILE *fp = setmntent(mtab, "r");
|
||||
if (fp == NULL) {
|
||||
fprintf(stderr, "%s: failed to open %s: %s\n", progname, mtab,
|
||||
strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
while ((entp = getmntent(fp)) != NULL) {
|
||||
if (strcmp(entp->mnt_type, "fuse") == 0 ||
|
||||
strncmp(entp->mnt_type, "fuse.", 5) == 0)
|
||||
count ++;
|
||||
}
|
||||
endmntent(fp);
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
#else /* IGNORE_MTAB */
|
||||
static int count_fuse_fs()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int add_mount(const char *source, const char *mnt, const char *type,
|
||||
const char *opts)
|
||||
{
|
||||
(void) source;
|
||||
(void) mnt;
|
||||
(void) type;
|
||||
(void) opts;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int unmount_fuse(const char *mnt, int quiet, int lazy)
|
||||
{
|
||||
return fuse_mnt_umount(progname, mnt, lazy);
|
||||
}
|
||||
#endif /* IGNORE_MTAB */
|
||||
|
||||
static void strip_line(char *line)
|
||||
{
|
||||
char *s = strchr(line, '#');
|
||||
if (s != NULL)
|
||||
s[0] = '\0';
|
||||
for (s = line + strlen(line) - 1; s >= line && isspace((unsigned char) *s); s--);
|
||||
s[1] = '\0';
|
||||
for (s = line; isspace((unsigned char) *s); s++);
|
||||
if (s != line)
|
||||
memmove(line, s, strlen(s)+1);
|
||||
}
|
||||
|
||||
static void parse_line(char *line, int linenum)
|
||||
{
|
||||
int tmp;
|
||||
if (strcmp(line, "user_allow_other") == 0)
|
||||
user_allow_other = 1;
|
||||
else if (sscanf(line, "mount_max = %i", &tmp) == 1)
|
||||
mount_max = tmp;
|
||||
else if(line[0])
|
||||
fprintf(stderr, "%s: unknown parameter in %s at line %i: '%s'\n",
|
||||
progname, FUSE_CONF, linenum, line);
|
||||
}
|
||||
|
||||
static void read_conf(void)
|
||||
{
|
||||
FILE *fp = fopen(FUSE_CONF, "r");
|
||||
if (fp != NULL) {
|
||||
int linenum = 1;
|
||||
char line[256];
|
||||
int isnewline = 1;
|
||||
while (fgets(line, sizeof(line), fp) != NULL) {
|
||||
if (isnewline) {
|
||||
if (line[strlen(line)-1] == '\n') {
|
||||
strip_line(line);
|
||||
parse_line(line, linenum);
|
||||
} else {
|
||||
fprintf(stderr, "%s: reading %s: line %i too long\n",
|
||||
progname, FUSE_CONF, linenum);
|
||||
isnewline = 0;
|
||||
}
|
||||
} else if(line[strlen(line)-1] == '\n')
|
||||
isnewline = 1;
|
||||
if (isnewline)
|
||||
linenum ++;
|
||||
}
|
||||
fclose(fp);
|
||||
} else if (errno != ENOENT) {
|
||||
fprintf(stderr, "%s: failed to open %s: %s\n", progname, FUSE_CONF,
|
||||
strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
static int begins_with(const char *s, const char *beg)
|
||||
{
|
||||
if (strncmp(s, beg, strlen(beg)) == 0)
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct mount_flags {
|
||||
const char *opt;
|
||||
unsigned long flag;
|
||||
int on;
|
||||
int safe;
|
||||
};
|
||||
|
||||
static struct mount_flags mount_flags[] = {
|
||||
{"rw", MS_RDONLY, 0, 1},
|
||||
{"ro", MS_RDONLY, 1, 1},
|
||||
{"suid", MS_NOSUID, 0, 0},
|
||||
{"nosuid", MS_NOSUID, 1, 1},
|
||||
{"dev", MS_NODEV, 0, 0},
|
||||
{"nodev", MS_NODEV, 1, 1},
|
||||
{"exec", MS_NOEXEC, 0, 1},
|
||||
{"noexec", MS_NOEXEC, 1, 1},
|
||||
{"async", MS_SYNCHRONOUS, 0, 1},
|
||||
{"sync", MS_SYNCHRONOUS, 1, 1},
|
||||
{"atime", MS_NOATIME, 0, 1},
|
||||
{"noatime", MS_NOATIME, 1, 1},
|
||||
{"dirsync", MS_DIRSYNC, 1, 1},
|
||||
{NULL, 0, 0, 0}
|
||||
};
|
||||
|
||||
static int find_mount_flag(const char *s, unsigned len, int *on, int *flag)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; mount_flags[i].opt != NULL; i++) {
|
||||
const char *opt = mount_flags[i].opt;
|
||||
if (strlen(opt) == len && strncmp(opt, s, len) == 0) {
|
||||
*on = mount_flags[i].on;
|
||||
*flag = mount_flags[i].flag;
|
||||
if (!mount_flags[i].safe && getuid() != 0) {
|
||||
*flag = 0;
|
||||
fprintf(stderr, "%s: unsafe option %s ignored\n",
|
||||
progname, opt);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int add_option(char **optsp, const char *opt, unsigned expand)
|
||||
{
|
||||
char *newopts;
|
||||
if (*optsp == NULL)
|
||||
newopts = strdup(opt);
|
||||
else {
|
||||
unsigned oldsize = strlen(*optsp);
|
||||
unsigned newsize = oldsize + 1 + strlen(opt) + expand + 1;
|
||||
newopts = (char *) realloc(*optsp, newsize);
|
||||
if (newopts)
|
||||
sprintf(newopts + oldsize, ",%s", opt);
|
||||
}
|
||||
if (newopts == NULL) {
|
||||
fprintf(stderr, "%s: failed to allocate memory\n", progname);
|
||||
return -1;
|
||||
}
|
||||
*optsp = newopts;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_mnt_opts(int flags, char *opts, char **mnt_optsp)
|
||||
{
|
||||
int i;
|
||||
int l;
|
||||
|
||||
if (!(flags & MS_RDONLY) && add_option(mnt_optsp, "rw", 0) == -1)
|
||||
return -1;
|
||||
|
||||
for (i = 0; mount_flags[i].opt != NULL; i++) {
|
||||
if (mount_flags[i].on && (flags & mount_flags[i].flag) &&
|
||||
add_option(mnt_optsp, mount_flags[i].opt, 0) == -1)
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (add_option(mnt_optsp, opts, 0) == -1)
|
||||
return -1;
|
||||
/* remove comma from end of opts*/
|
||||
l = strlen(*mnt_optsp);
|
||||
if ((*mnt_optsp)[l-1] == ',')
|
||||
(*mnt_optsp)[l-1] = '\0';
|
||||
if (getuid() != 0) {
|
||||
const char *user = get_user_name();
|
||||
if (user == NULL)
|
||||
return -1;
|
||||
|
||||
if (add_option(mnt_optsp, "user=", strlen(user)) == -1)
|
||||
return -1;
|
||||
strcat(*mnt_optsp, user);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int opt_eq(const char *s, unsigned len, const char *opt)
|
||||
{
|
||||
if(strlen(opt) == len && strncmp(s, opt, len) == 0)
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_string_opt(const char *s, unsigned len, const char *opt,
|
||||
char **val)
|
||||
{
|
||||
unsigned opt_len = strlen(opt);
|
||||
|
||||
if (*val)
|
||||
free(*val);
|
||||
*val = (char *) malloc(len - opt_len + 1);
|
||||
if (!*val) {
|
||||
fprintf(stderr, "%s: failed to allocate memory\n", progname);
|
||||
return 0;
|
||||
}
|
||||
|
||||
memcpy(*val, s + opt_len, len - opt_len);
|
||||
(*val)[len - opt_len] = '\0';
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int do_mount(const char *mnt, char **typep, mode_t rootmode,
|
||||
int fd, const char *opts, const char *dev, char **sourcep,
|
||||
char **mnt_optsp, off_t rootsize)
|
||||
{
|
||||
int res;
|
||||
int flags = MS_NOSUID | MS_NODEV;
|
||||
char *optbuf;
|
||||
char *mnt_opts = NULL;
|
||||
const char *s;
|
||||
char *d;
|
||||
char *fsname = NULL;
|
||||
char *subtype = NULL;
|
||||
char *source = NULL;
|
||||
char *type = NULL;
|
||||
int check_empty = 1;
|
||||
int blkdev = 0;
|
||||
|
||||
optbuf = (char *) malloc(strlen(opts) + 128);
|
||||
if (!optbuf) {
|
||||
fprintf(stderr, "%s: failed to allocate memory\n", progname);
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (s = opts, d = optbuf; *s;) {
|
||||
unsigned len;
|
||||
const char *fsname_str = "fsname=";
|
||||
const char *subtype_str = "subtype=";
|
||||
for (len = 0; s[len] && s[len] != ','; len++);
|
||||
if (begins_with(s, fsname_str)) {
|
||||
if (!get_string_opt(s, len, fsname_str, &fsname))
|
||||
goto err;
|
||||
} else if (begins_with(s, subtype_str)) {
|
||||
if (!get_string_opt(s, len, subtype_str, &subtype))
|
||||
goto err;
|
||||
} else if (opt_eq(s, len, "blkdev")) {
|
||||
if (getuid() != 0) {
|
||||
fprintf(stderr, "%s: option blkdev is privileged\n", progname);
|
||||
goto err;
|
||||
}
|
||||
blkdev = 1;
|
||||
} else if (opt_eq(s, len, "nonempty")) {
|
||||
check_empty = 0;
|
||||
} else if (!begins_with(s, "fd=") &&
|
||||
!begins_with(s, "rootmode=") &&
|
||||
!begins_with(s, "user_id=") &&
|
||||
!begins_with(s, "group_id=")) {
|
||||
int on;
|
||||
int flag;
|
||||
int skip_option = 0;
|
||||
if (opt_eq(s, len, "large_read")) {
|
||||
struct utsname utsname;
|
||||
unsigned kmaj, kmin;
|
||||
res = uname(&utsname);
|
||||
if (res == 0 &&
|
||||
sscanf(utsname.release, "%u.%u", &kmaj, &kmin) == 2 &&
|
||||
(kmaj > 2 || (kmaj == 2 && kmin > 4))) {
|
||||
fprintf(stderr, "%s: note: 'large_read' mount option is deprecated for %i.%i kernels\n", progname, kmaj, kmin);
|
||||
skip_option = 1;
|
||||
}
|
||||
}
|
||||
if (getuid() != 0 && !user_allow_other &&
|
||||
(opt_eq(s, len, "allow_other") ||
|
||||
opt_eq(s, len, "allow_root"))) {
|
||||
fprintf(stderr, "%s: option %.*s only allowed if 'user_allow_other' is set in /etc/fuse.conf\n", progname, len, s);
|
||||
goto err;
|
||||
}
|
||||
if (!skip_option) {
|
||||
if (find_mount_flag(s, len, &on, &flag)) {
|
||||
if (on)
|
||||
flags |= flag;
|
||||
else
|
||||
flags &= ~flag;
|
||||
} else {
|
||||
memcpy(d, s, len);
|
||||
d += len;
|
||||
*d++ = ',';
|
||||
}
|
||||
}
|
||||
}
|
||||
s += len;
|
||||
if (*s)
|
||||
s++;
|
||||
}
|
||||
*d = '\0';
|
||||
res = get_mnt_opts(flags, optbuf, &mnt_opts);
|
||||
if (res == -1)
|
||||
goto err;
|
||||
|
||||
sprintf(d, "fd=%i,rootmode=%o,user_id=%i,group_id=%i",
|
||||
fd, rootmode, getuid(), getgid());
|
||||
|
||||
if (check_empty &&
|
||||
fuse_mnt_check_empty(progname, mnt, rootmode, rootsize) == -1)
|
||||
goto err;
|
||||
|
||||
source = malloc((fsname ? strlen(fsname) : 0) +
|
||||
(subtype ? strlen(subtype) : 0) + strlen(dev) + 32);
|
||||
|
||||
type = malloc((subtype ? strlen(subtype) : 0) + 32);
|
||||
if (!type || !source) {
|
||||
fprintf(stderr, "%s: failed to allocate memory\n", progname);
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (subtype)
|
||||
sprintf(type, "%s.%s", blkdev ? "fuseblk" : "fuse", subtype);
|
||||
else
|
||||
strcpy(type, blkdev ? "fuseblk" : "fuse");
|
||||
|
||||
if (fsname)
|
||||
strcpy(source, fsname);
|
||||
else
|
||||
strcpy(source, subtype ? subtype : dev);
|
||||
|
||||
res = mount(source, mnt, type, flags, optbuf);
|
||||
if (res == -1 && errno == ENODEV && subtype) {
|
||||
/* Probably missing subtype support */
|
||||
strcpy(type, blkdev ? "fuseblk" : "fuse");
|
||||
if (fsname) {
|
||||
if (!blkdev)
|
||||
sprintf(source, "%s#%s", subtype, fsname);
|
||||
} else {
|
||||
strcpy(source, type);
|
||||
}
|
||||
|
||||
res = mount(source, mnt, type, flags, optbuf);
|
||||
}
|
||||
if (res == -1 && errno == EINVAL) {
|
||||
/* It could be an old version not supporting group_id */
|
||||
sprintf(d, "fd=%i,rootmode=%o,user_id=%i", fd, rootmode, getuid());
|
||||
res = mount(source, mnt, type, flags, optbuf);
|
||||
}
|
||||
if (res == -1) {
|
||||
int errno_save = errno;
|
||||
if (blkdev && errno == ENODEV && !fuse_mnt_check_fuseblk())
|
||||
fprintf(stderr, "%s: 'fuseblk' support missing\n", progname);
|
||||
else
|
||||
fprintf(stderr, "%s: mount failed: %s\n", progname, strerror(errno_save));
|
||||
goto err;
|
||||
} else {
|
||||
*sourcep = source;
|
||||
*typep = type;
|
||||
*mnt_optsp = mnt_opts;
|
||||
}
|
||||
free(optbuf);
|
||||
|
||||
return res;
|
||||
|
||||
err:
|
||||
free(fsname);
|
||||
free(subtype);
|
||||
free(source);
|
||||
free(type);
|
||||
free(mnt_opts);
|
||||
free(optbuf);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int check_version(const char *dev)
|
||||
{
|
||||
int res;
|
||||
int majorver;
|
||||
int minorver;
|
||||
const char *version_file;
|
||||
FILE *vf;
|
||||
|
||||
if (strcmp(dev, FUSE_DEV_OLD) != 0)
|
||||
return 0;
|
||||
|
||||
version_file = FUSE_VERSION_FILE_OLD;
|
||||
vf = fopen(version_file, "r");
|
||||
if (vf == NULL) {
|
||||
fprintf(stderr, "%s: kernel interface too old\n", progname);
|
||||
return -1;
|
||||
}
|
||||
res = fscanf(vf, "%i.%i", &majorver, &minorver);
|
||||
fclose(vf);
|
||||
if (res != 2) {
|
||||
fprintf(stderr, "%s: error reading %s\n", progname, version_file);
|
||||
return -1;
|
||||
}
|
||||
if (majorver < 3) {
|
||||
fprintf(stderr, "%s: kernel interface too old\n", progname);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int check_perm(const char **mntp, struct stat *stbuf, int *currdir_fd,
|
||||
int *mountpoint_fd)
|
||||
{
|
||||
int res;
|
||||
const char *mnt = *mntp;
|
||||
const char *origmnt = mnt;
|
||||
|
||||
res = lstat(mnt, stbuf);
|
||||
if (res == -1) {
|
||||
fprintf(stderr, "%s: failed to access mountpoint %s: %s\n",
|
||||
progname, mnt, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* No permission checking is done for root */
|
||||
if (getuid() == 0)
|
||||
return 0;
|
||||
|
||||
if (S_ISDIR(stbuf->st_mode)) {
|
||||
*currdir_fd = open(".", O_RDONLY);
|
||||
if (*currdir_fd == -1) {
|
||||
fprintf(stderr, "%s: failed to open current directory: %s\n",
|
||||
progname, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
res = chdir(mnt);
|
||||
if (res == -1) {
|
||||
fprintf(stderr, "%s: failed to chdir to mountpoint: %s\n",
|
||||
progname, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
mnt = *mntp = ".";
|
||||
res = lstat(mnt, stbuf);
|
||||
if (res == -1) {
|
||||
fprintf(stderr, "%s: failed to access mountpoint %s: %s\n",
|
||||
progname, origmnt, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((stbuf->st_mode & S_ISVTX) && stbuf->st_uid != getuid()) {
|
||||
fprintf(stderr, "%s: mountpoint %s not owned by user\n",
|
||||
progname, origmnt);
|
||||
return -1;
|
||||
}
|
||||
|
||||
res = access(mnt, W_OK);
|
||||
if (res == -1) {
|
||||
fprintf(stderr, "%s: user has no write access to mountpoint %s\n",
|
||||
progname, origmnt);
|
||||
return -1;
|
||||
}
|
||||
} else if (S_ISREG(stbuf->st_mode)) {
|
||||
static char procfile[256];
|
||||
*mountpoint_fd = open(mnt, O_WRONLY);
|
||||
if (*mountpoint_fd == -1) {
|
||||
fprintf(stderr, "%s: failed to open %s: %s\n", progname, mnt,
|
||||
strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
res = fstat(*mountpoint_fd, stbuf);
|
||||
if (res == -1) {
|
||||
fprintf(stderr, "%s: failed to access mountpoint %s: %s\n",
|
||||
progname, mnt, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
if (!S_ISREG(stbuf->st_mode)) {
|
||||
fprintf(stderr, "%s: mountpoint %s is no longer a regular file\n",
|
||||
progname, mnt);
|
||||
return -1;
|
||||
}
|
||||
|
||||
sprintf(procfile, "/proc/self/fd/%i", *mountpoint_fd);
|
||||
*mntp = procfile;
|
||||
} else {
|
||||
fprintf(stderr,
|
||||
"%s: mountpoint %s is not a directory or a regular file\n",
|
||||
progname, mnt);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int try_open(const char *dev, char **devp, int silent)
|
||||
{
|
||||
int fd = open(dev, O_RDWR);
|
||||
if (fd != -1) {
|
||||
*devp = strdup(dev);
|
||||
if (*devp == NULL) {
|
||||
fprintf(stderr, "%s: failed to allocate memory\n", progname);
|
||||
close(fd);
|
||||
fd = -1;
|
||||
}
|
||||
} else if (errno == ENODEV ||
|
||||
errno == ENOENT) /* check for ENOENT too, for the udev case */
|
||||
return -2;
|
||||
else if (!silent) {
|
||||
fprintf(stderr, "%s: failed to open %s: %s\n", progname, dev,
|
||||
strerror(errno));
|
||||
}
|
||||
return fd;
|
||||
}
|
||||
|
||||
static int try_open_fuse_device(char **devp)
|
||||
{
|
||||
int fd;
|
||||
int err;
|
||||
|
||||
drop_privs();
|
||||
fd = try_open(FUSE_DEV_NEW, devp, 0);
|
||||
restore_privs();
|
||||
if (fd >= 0)
|
||||
return fd;
|
||||
|
||||
err = fd;
|
||||
fd = try_open(FUSE_DEV_OLD, devp, 1);
|
||||
if (fd >= 0)
|
||||
return fd;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int open_fuse_device(char **devp)
|
||||
{
|
||||
int fd = try_open_fuse_device(devp);
|
||||
if (fd >= -1)
|
||||
return fd;
|
||||
|
||||
fprintf(stderr, "%s: fuse device not found, try 'modprobe fuse' first\n",
|
||||
progname);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
static int mount_fuse(const char *mnt, const char *opts)
|
||||
{
|
||||
int res;
|
||||
int fd;
|
||||
char *dev;
|
||||
struct stat stbuf;
|
||||
char *type = NULL;
|
||||
char *source = NULL;
|
||||
char *mnt_opts = NULL;
|
||||
const char *real_mnt = mnt;
|
||||
int currdir_fd = -1;
|
||||
int mountpoint_fd = -1;
|
||||
|
||||
fd = open_fuse_device(&dev);
|
||||
if (fd == -1)
|
||||
return -1;
|
||||
|
||||
drop_privs();
|
||||
read_conf();
|
||||
|
||||
if (getuid() != 0 && mount_max != -1) {
|
||||
int mount_count = count_fuse_fs();
|
||||
if (mount_count >= mount_max) {
|
||||
fprintf(stderr, "%s: too many FUSE filesystems mounted; "
|
||||
"mount_max=N can be set in /etc/fuse.conf\n", progname);
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
res = check_version(dev);
|
||||
if (res != -1) {
|
||||
res = check_perm(&real_mnt, &stbuf, &currdir_fd, &mountpoint_fd);
|
||||
restore_privs();
|
||||
if (res != -1)
|
||||
res = do_mount(real_mnt, &type, stbuf.st_mode & S_IFMT, fd, opts,
|
||||
dev, &source, &mnt_opts, stbuf.st_size);
|
||||
} else
|
||||
restore_privs();
|
||||
|
||||
if (currdir_fd != -1) {
|
||||
fchdir(currdir_fd);
|
||||
close(currdir_fd);
|
||||
}
|
||||
if (mountpoint_fd != -1)
|
||||
close(mountpoint_fd);
|
||||
|
||||
if (res == -1) {
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (geteuid() == 0) {
|
||||
res = add_mount(source, mnt, type, mnt_opts);
|
||||
if (res == -1) {
|
||||
umount2(mnt, 2); /* lazy umount */
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
free(source);
|
||||
free(type);
|
||||
free(mnt_opts);
|
||||
free(dev);
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
int fusermount(int unmount, int quiet, int lazy, const char *opts,
|
||||
const char *origmnt)
|
||||
{
|
||||
int res;
|
||||
char *mnt;
|
||||
mode_t old_umask;
|
||||
|
||||
if (lazy && !unmount) {
|
||||
fprintf(stderr, "%s: -z can only be used with -u\n", progname);
|
||||
return -1;
|
||||
}
|
||||
|
||||
drop_privs();
|
||||
mnt = fuse_mnt_resolve_path(progname, origmnt);
|
||||
restore_privs();
|
||||
if (mnt == NULL)
|
||||
return -1;
|
||||
|
||||
old_umask = umask(033);
|
||||
if (unmount) {
|
||||
if (geteuid() == 0)
|
||||
res = unmount_fuse(mnt, quiet, lazy);
|
||||
else {
|
||||
res = umount2(mnt, lazy ? 2 : 0);
|
||||
if (res == -1 && !quiet)
|
||||
fprintf(stderr, "%s: failed to unmount %s: %s\n", progname,
|
||||
mnt, strerror(errno));
|
||||
}
|
||||
} else
|
||||
res = mount_fuse(mnt, opts);
|
||||
|
||||
umask(old_umask);
|
||||
return res;
|
||||
}
|
|
@ -23,13 +23,6 @@
|
|||
#include <sys/wait.h>
|
||||
#include <sys/mount.h>
|
||||
|
||||
#define FUSERMOUNT_PROG "fusermount"
|
||||
#define FUSE_COMMFD_ENV "_FUSE_COMMFD"
|
||||
|
||||
#ifndef HAVE_FORK
|
||||
#define fork() vfork()
|
||||
#endif
|
||||
|
||||
#ifndef MS_DIRSYNC
|
||||
#define MS_DIRSYNC 128
|
||||
#endif
|
||||
|
@ -103,38 +96,6 @@ static const struct fuse_opt fuse_mount_opts[] = {
|
|||
FUSE_OPT_END
|
||||
};
|
||||
|
||||
static void mount_help(void)
|
||||
{
|
||||
fprintf(stderr,
|
||||
" -o allow_other allow access to other users\n"
|
||||
" -o allow_root allow access to root\n"
|
||||
" -o nonempty allow mounts over non-empty file/dir\n"
|
||||
" -o default_permissions enable permission checking by kernel\n"
|
||||
" -o fsname=NAME set filesystem name\n"
|
||||
" -o subtype=NAME set filesystem type\n"
|
||||
" -o large_read issue large read requests (2.4 only)\n"
|
||||
" -o max_read=N set maximum size of read requests\n"
|
||||
"\n"
|
||||
);
|
||||
}
|
||||
|
||||
static void exec_fusermount(const char *argv[])
|
||||
{
|
||||
execv(FUSERMOUNT_DIR "/" FUSERMOUNT_PROG, (char **) argv);
|
||||
execvp(FUSERMOUNT_PROG, (char **) argv);
|
||||
}
|
||||
|
||||
static void mount_version(void)
|
||||
{
|
||||
int pid = fork();
|
||||
if (!pid) {
|
||||
const char *argv[] = { FUSERMOUNT_PROG, "--version", NULL };
|
||||
exec_fusermount(argv);
|
||||
_exit(1);
|
||||
} else if (pid != -1)
|
||||
waitpid(pid, NULL, 0);
|
||||
}
|
||||
|
||||
struct mount_flags {
|
||||
const char *opt;
|
||||
unsigned long flag;
|
||||
|
@ -208,66 +169,19 @@ static int fuse_mount_opt_proc(void *data, const char *arg, int key,
|
|||
return fuse_opt_add_opt(&mo->mtab_opts, arg);
|
||||
|
||||
case KEY_HELP:
|
||||
mount_help();
|
||||
mo->ishelp = 1;
|
||||
break;
|
||||
|
||||
case KEY_VERSION:
|
||||
mount_version();
|
||||
mo->ishelp = 1;
|
||||
break;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* return value:
|
||||
* >= 0 => fd
|
||||
* -1 => error
|
||||
*/
|
||||
static int receive_fd(int fd)
|
||||
{
|
||||
struct msghdr msg;
|
||||
struct iovec iov;
|
||||
char buf[1];
|
||||
int rv;
|
||||
size_t ccmsg[CMSG_SPACE(sizeof(int)) / sizeof(size_t)];
|
||||
struct cmsghdr *cmsg;
|
||||
|
||||
iov.iov_base = buf;
|
||||
iov.iov_len = 1;
|
||||
|
||||
msg.msg_name = 0;
|
||||
msg.msg_namelen = 0;
|
||||
msg.msg_iov = &iov;
|
||||
msg.msg_iovlen = 1;
|
||||
/* old BSD implementations should use msg_accrights instead of
|
||||
* msg_control; the interface is different. */
|
||||
msg.msg_control = ccmsg;
|
||||
msg.msg_controllen = sizeof(ccmsg);
|
||||
|
||||
while(((rv = recvmsg(fd, &msg, 0)) == -1) && errno == EINTR);
|
||||
if (rv == -1) {
|
||||
perror("recvmsg");
|
||||
return -1;
|
||||
}
|
||||
if(!rv) {
|
||||
/* EOF */
|
||||
return -1;
|
||||
}
|
||||
|
||||
cmsg = CMSG_FIRSTHDR(&msg);
|
||||
if (!cmsg->cmsg_type == SCM_RIGHTS) {
|
||||
fprintf(stderr, "got control message of unknown type %d\n",
|
||||
cmsg->cmsg_type);
|
||||
return -1;
|
||||
}
|
||||
return *(int*)CMSG_DATA(cmsg);
|
||||
}
|
||||
|
||||
void fuse_kern_unmount(const char *mountpoint, int fd)
|
||||
{
|
||||
int res;
|
||||
int pid;
|
||||
|
||||
if (!mountpoint)
|
||||
return;
|
||||
|
@ -294,81 +208,17 @@ void fuse_kern_unmount(const char *mountpoint, int fd)
|
|||
if (res == 0)
|
||||
return;
|
||||
|
||||
pid = fork();
|
||||
if(pid == -1)
|
||||
return;
|
||||
|
||||
if(pid == 0) {
|
||||
const char *argv[] =
|
||||
{ FUSERMOUNT_PROG, "-u", "-q", "-z", "--", mountpoint, NULL };
|
||||
|
||||
exec_fusermount(argv);
|
||||
_exit(1);
|
||||
}
|
||||
waitpid(pid, NULL, 0);
|
||||
fusermount(1, 0, 1, "", mountpoint);
|
||||
}
|
||||
|
||||
static int fuse_mount_fusermount(const char *mountpoint, const char *opts,
|
||||
int quiet)
|
||||
static int fuse_mount_fusermount(const char *mountpoint, const char *opts)
|
||||
{
|
||||
int fds[2], pid;
|
||||
int res;
|
||||
int rv;
|
||||
if (!mountpoint) {
|
||||
fprintf(stderr, "fuse: missing mountpoint\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!mountpoint) {
|
||||
fprintf(stderr, "fuse: missing mountpoint\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
res = socketpair(PF_UNIX, SOCK_STREAM, 0, fds);
|
||||
if(res == -1) {
|
||||
perror("fuse: socketpair() failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
pid = fork();
|
||||
if(pid == -1) {
|
||||
perror("fuse: fork() failed");
|
||||
close(fds[0]);
|
||||
close(fds[1]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(pid == 0) {
|
||||
char env[10];
|
||||
const char *argv[32];
|
||||
int a = 0;
|
||||
|
||||
if (quiet) {
|
||||
int fd = open("/dev/null", O_RDONLY);
|
||||
dup2(fd, 1);
|
||||
dup2(fd, 2);
|
||||
}
|
||||
|
||||
argv[a++] = FUSERMOUNT_PROG;
|
||||
if (opts) {
|
||||
argv[a++] = "-o";
|
||||
argv[a++] = opts;
|
||||
}
|
||||
argv[a++] = "--";
|
||||
argv[a++] = mountpoint;
|
||||
argv[a++] = NULL;
|
||||
|
||||
close(fds[1]);
|
||||
fcntl(fds[0], F_SETFD, 0);
|
||||
snprintf(env, sizeof(env), "%i", fds[0]);
|
||||
setenv(FUSE_COMMFD_ENV, env, 1);
|
||||
exec_fusermount(argv);
|
||||
perror("fuse: failed to exec fusermount");
|
||||
_exit(1);
|
||||
}
|
||||
|
||||
close(fds[0]);
|
||||
rv = receive_fd(fds[1]);
|
||||
close(fds[1]);
|
||||
waitpid(pid, NULL, 0); /* bury zombie */
|
||||
|
||||
return rv;
|
||||
return fusermount(0, 0, 0, opts ? opts : "", mountpoint);
|
||||
}
|
||||
|
||||
static int fuse_mount_sys(const char *mnt, struct mount_opts *mo,
|
||||
|
@ -550,12 +400,12 @@ int fuse_kern_mount(const char *mountpoint, struct fuse_args *args)
|
|||
goto out;
|
||||
}
|
||||
|
||||
res = fuse_mount_fusermount(mountpoint, tmp_opts, 1);
|
||||
res = fuse_mount_fusermount(mountpoint, tmp_opts);
|
||||
free(tmp_opts);
|
||||
if (res == -1)
|
||||
res = fuse_mount_fusermount(mountpoint, mnt_opts, 0);
|
||||
res = fuse_mount_fusermount(mountpoint, mnt_opts);
|
||||
} else {
|
||||
res = fuse_mount_fusermount(mountpoint, mnt_opts, 0);
|
||||
res = fuse_mount_fusermount(mountpoint, mnt_opts);
|
||||
}
|
||||
}
|
||||
out:
|
||||
|
|
|
@ -15,3 +15,7 @@ char *fuse_mnt_resolve_path(const char *progname, const char *orig);
|
|||
int fuse_mnt_check_empty(const char *progname, const char *mnt,
|
||||
mode_t rootmode, off_t rootsize);
|
||||
int fuse_mnt_check_fuseblk(void);
|
||||
|
||||
int fusermount(int unmount, int quiet, int lazy, const char *opts,
|
||||
const char *origmnt);
|
||||
|
||||
|
|
|
@ -2100,7 +2100,7 @@ static fuse_fstype load_fuse_module(void)
|
|||
struct timespec req = { 0, 100000000 }; /* 100 msec */
|
||||
fuse_fstype fstype;
|
||||
|
||||
if (!stat(cmd, &st) && !getuid()) {
|
||||
if (!stat(cmd, &st) && !geteuid()) {
|
||||
pid = fork();
|
||||
if (!pid) {
|
||||
execl(cmd, cmd, "fuse", NULL);
|
||||
|
@ -2185,14 +2185,34 @@ static void set_user_mount_option(char *parsed_options, uid_t uid)
|
|||
strcat(parsed_options, option);
|
||||
}
|
||||
|
||||
static int set_uid(uid_t uid)
|
||||
{
|
||||
if (setuid(uid)) {
|
||||
ntfs_log_perror("Failed to set uid to %d", uid);
|
||||
return NTFS_VOLUME_NO_PRIVILEGE;
|
||||
}
|
||||
return NTFS_VOLUME_OK;
|
||||
}
|
||||
|
||||
static struct fuse *mount_fuse(char *parsed_options)
|
||||
{
|
||||
uid_t uid, euid;
|
||||
struct fuse *fh = NULL;
|
||||
struct fuse_args args = FUSE_ARGS_INIT(0, NULL);
|
||||
|
||||
/* Libfuse can't always find fusermount, so let's help it. */
|
||||
if (setenv("PATH", ":/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin", 0))
|
||||
ntfs_log_perror("WARNING: Failed to set $PATH\n");
|
||||
/*
|
||||
* We must raise privilege if possible, otherwise the user[s] fstab
|
||||
* option doesn't work because mount(8) always drops privilege what
|
||||
* the blkdev option requires.
|
||||
*/
|
||||
uid = getuid();
|
||||
euid = geteuid();
|
||||
|
||||
if (set_uid(euid))
|
||||
return NULL;
|
||||
|
||||
ctx->fc = try_fuse_mount(parsed_options);
|
||||
if (!ctx->fc)
|
||||
|
@ -2210,14 +2230,23 @@ static struct fuse *mount_fuse(char *parsed_options)
|
|||
if (!fh)
|
||||
goto err;
|
||||
|
||||
if (fuse_set_signal_handlers(fuse_get_session(fh))) {
|
||||
fuse_destroy(fh);
|
||||
fh = NULL;
|
||||
goto err;
|
||||
}
|
||||
if (fuse_set_signal_handlers(fuse_get_session(fh)))
|
||||
goto err_destory;
|
||||
/*
|
||||
* We can't drop privilege if internal FUSE is used because internal
|
||||
* unmount needs it. Kernel 2.6.25 may include unprivileged full
|
||||
* mount/unmount support.
|
||||
*/
|
||||
#ifndef FUSE_INTERNAL
|
||||
if (set_uid(uid))
|
||||
goto err_destory;
|
||||
#endif
|
||||
out:
|
||||
fuse_opt_free_args(&args);
|
||||
return fh;
|
||||
err_destory:
|
||||
fuse_destroy(fh);
|
||||
fh = NULL;
|
||||
err:
|
||||
fuse_unmount(opts.mnt_point, ctx->fc);
|
||||
goto out;
|
||||
|
@ -2229,7 +2258,6 @@ int main(int argc, char *argv[])
|
|||
struct fuse *fh;
|
||||
fuse_fstype fstype = FSTYPE_UNKNOWN;
|
||||
struct stat sbuf;
|
||||
uid_t uid, euid;
|
||||
int err;
|
||||
|
||||
utils_set_locale();
|
||||
|
@ -2248,16 +2276,7 @@ int main(int argc, char *argv[])
|
|||
err = NTFS_VOLUME_SYNTAX_ERROR;
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
uid = getuid();
|
||||
euid = geteuid();
|
||||
|
||||
if (setuid(euid)) {
|
||||
ntfs_log_perror("Failed to set user ID to %d", euid);
|
||||
err = NTFS_VOLUME_NO_PRIVILEGE;
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
#if defined(linux) || defined(__uClinux__)
|
||||
fstype = get_fuse_fstype();
|
||||
if (fstype == FSTYPE_NONE || fstype == FSTYPE_UNKNOWN)
|
||||
|
@ -2282,7 +2301,7 @@ int main(int argc, char *argv[])
|
|||
if (ctx->blkdev) {
|
||||
/* Must do after ntfs_open() to set the right blksize. */
|
||||
set_fuseblk_options(parsed_options);
|
||||
set_user_mount_option(parsed_options, uid);
|
||||
set_user_mount_option(parsed_options, getuid());
|
||||
}
|
||||
|
||||
fh = mount_fuse(parsed_options);
|
||||
|
@ -2290,13 +2309,7 @@ int main(int argc, char *argv[])
|
|||
err = NTFS_VOLUME_FUSE_ERROR;
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
if (setuid(uid)) {
|
||||
ntfs_log_perror("Failed to drop privilege (uid to %d)", uid);
|
||||
err = NTFS_VOLUME_NO_PRIVILEGE;
|
||||
goto err_umount;
|
||||
}
|
||||
|
||||
|
||||
ctx->mounted = TRUE;
|
||||
|
||||
#if defined(linux) || defined(__uClinux__)
|
||||
|
@ -2326,7 +2339,7 @@ int main(int argc, char *argv[])
|
|||
fuse_loop(fh);
|
||||
|
||||
err = 0;
|
||||
err_umount:
|
||||
|
||||
fuse_unmount(opts.mnt_point, ctx->fc);
|
||||
fuse_destroy(fh);
|
||||
err_out:
|
||||
|
|
|
@ -85,8 +85,9 @@ static const char *fakeraid_msg =
|
|||
"to mount NTFS. Please see the 'dmraid' documentation for help.\n";
|
||||
|
||||
static const char *access_denied_msg =
|
||||
"Please check the volume and the NTFS-3G binary permissions, the mounting\n"
|
||||
"user and group ID, and the mount options.\n";
|
||||
"Please check the device and the ntfs-3g binary permissions, the mounting\n"
|
||||
"user and group ID, and the mount options. You can find more explanation\n"
|
||||
"at http://ntfs-3g.org/support.html#useroption\n";
|
||||
|
||||
static const char *forced_mount_msg =
|
||||
"\n"
|
||||
|
|
Loading…
Reference in New Issue