libfuse/util/fusermount.c

1038 lines
26 KiB
C
Raw Normal View History

2001-11-08 19:34:54 +08:00
/*
FUSE: Filesystem in Userspace
2004-07-08 03:19:53 +08:00
Copyright (C) 2001-2004 Miklos Szeredi <miklos@szeredi.hu>
2001-11-08 19:34:54 +08:00
This program can be distributed under the terms of the GNU GPL.
See the file COPYING.
*/
2001-11-12 02:20:17 +08:00
/* This program does the mounting and unmounting of FUSE filesystems */
/*
* NOTE: This program should be part of (or be called from) /bin/mount
*
* Unless that is done, operations on /etc/mtab are not under lock, and so
2001-11-21 03:12:28 +08:00
* data in this file may be lost. (I will _not_ reimplement that locking,
* and anyway that should be done in libc, if possible. But probably it
* isn't).
2001-11-12 02:20:17 +08:00
*/
2001-11-08 19:34:54 +08:00
2004-07-25 03:56:16 +08:00
#include <config.h>
2001-11-08 19:34:54 +08:00
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
2001-11-08 22:56:53 +08:00
#include <pwd.h>
2001-11-12 02:20:17 +08:00
#include <mntent.h>
2001-12-27 02:08:09 +08:00
#include <sys/param.h>
2001-11-08 19:34:54 +08:00
#include <sys/wait.h>
#include <sys/stat.h>
#include <sys/mount.h>
2001-11-09 22:49:18 +08:00
#include <sys/fsuid.h>
2002-10-25 19:40:14 +08:00
#include <sys/socket.h>
#include <sys/un.h>
2001-11-08 19:34:54 +08:00
#include <linux/fuse.h>
2001-11-09 22:49:18 +08:00
2002-10-25 19:40:14 +08:00
#define FUSE_COMMFD_ENV "_FUSE_COMMFD"
2001-11-21 18:03:39 +08:00
2004-11-20 19:18:34 +08:00
#define FUSE_DEV_OLD "/proc/fs/fuse/dev"
2004-11-30 07:43:44 +08:00
#define FUSE_DEV_NEW "/dev/fuse"
2004-11-20 19:18:34 +08:00
#define FUSE_SYS_DEV "/sys/class/misc/fuse/dev"
2004-11-24 06:32:16 +08:00
#define FUSE_VERSION_FILE_OLD "/proc/fs/fuse/version"
2004-11-30 07:43:44 +08:00
#define FUSE_VERSION_FILE_NEW "/sys/fs/fuse/version"
2004-11-20 19:18:34 +08:00
2001-11-08 19:34:54 +08:00
const char *progname;
2001-11-08 22:56:53 +08:00
static const char *get_user_name()
{
struct passwd *pw = getpwuid(getuid());
2004-07-24 21:47:44 +08:00
if (pw != NULL && pw->pw_name != NULL)
2001-11-08 22:56:53 +08:00
return pw->pw_name;
else {
fprintf(stderr, "%s: could not determine username\n", progname);
return NULL;
}
}
2004-11-24 06:32:16 +08:00
#ifndef USE_UCLIBC
2004-09-23 00:47:40 +08:00
/* Until there is a nice interface for capabilities in _libc_, this will
remain here. I don't think it is fair to expect users to compile libcap
for this program. And anyway what's all this fuss about versioning the
kernel interface? It is quite good as is. */
#define _LINUX_CAPABILITY_VERSION 0x19980330
typedef struct __user_cap_header_struct {
unsigned int version;
int pid;
} *cap_user_header_t;
typedef struct __user_cap_data_struct {
unsigned int effective;
unsigned int permitted;
unsigned int inheritable;
} *cap_user_data_t;
int capget(cap_user_header_t header, cap_user_data_t data);
int capset(cap_user_header_t header, cap_user_data_t data);
#define CAP_SYS_ADMIN 21
static uid_t oldfsuid;
static gid_t oldfsgid;
static struct __user_cap_data_struct oldcaps;
2004-11-24 06:32:16 +08:00
static int drop_privs(void)
2004-09-23 00:47:40 +08:00
{
int res;
struct __user_cap_header_struct head;
struct __user_cap_data_struct newcaps;
head.version = _LINUX_CAPABILITY_VERSION;
head.pid = 0;
res = capget(&head, &oldcaps);
if (res == -1) {
fprintf(stderr, "%s: failed to get capabilities: %s\n", progname,
strerror(errno));
return -1;
}
oldfsuid = setfsuid(getuid());
oldfsgid = setfsgid(getgid());
newcaps = oldcaps;
/* Keep CAP_SYS_ADMIN for mount */
newcaps.effective &= (1 << CAP_SYS_ADMIN);
head.version = _LINUX_CAPABILITY_VERSION;
head.pid = 0;
res = capset(&head, &newcaps);
if (res == -1) {
setfsuid(oldfsuid);
setfsgid(oldfsgid);
fprintf(stderr, "%s: failed to set capabilities: %s\n", progname,
strerror(errno));
return -1;
}
return 0;
}
2004-11-24 06:32:16 +08:00
static void restore_privs(void)
2004-09-23 00:47:40 +08:00
{
struct __user_cap_header_struct head;
int res;
head.version = _LINUX_CAPABILITY_VERSION;
head.pid = 0;
res = capset(&head, &oldcaps);
if (res == -1)
fprintf(stderr, "%s: failed to restore capabilities: %s\n", progname,
strerror(errno));
setfsuid(oldfsuid);
setfsgid(oldfsgid);
}
2004-11-24 06:32:16 +08:00
#else /* USE_UCLIBC */
static int drop_privs(void)
{
return 0;
}
static void restore_privs(void)
{
}
#endif /* USE_UCLIBC */
#ifndef USE_UCLIBC
/* use a lock file so that multiple fusermount processes don't try and
modify the mtab file at once! */
static int lock_mtab()
{
const char *mtab_lock = _PATH_MOUNTED ".fuselock";
int mtablock;
int res;
mtablock = open(mtab_lock, O_RDWR | O_CREAT, 0600);
if (mtablock >= 0) {
res = lockf(mtablock, F_LOCK, 0);
if (res < 0)
perror("error getting lock");
} else
fprintf(stderr, "unable to open fuse lock file, continuing anyway\n");
return mtablock;
}
static void unlock_mtab(int mtablock)
{
if (mtablock >= 0) {
lockf(mtablock, F_ULOCK, 0);
close(mtablock);
}
}
2004-09-23 00:47:40 +08:00
2004-11-24 06:32:16 +08:00
static int add_mount(const char *fsname, const char *mnt, const char *type,
const char *opts)
2001-11-08 22:56:53 +08:00
{
2001-11-12 02:20:17 +08:00
int res;
const char *mtab = _PATH_MOUNTED;
2004-11-24 06:32:16 +08:00
struct mntent ent;
FILE *fp;
fp = setmntent(mtab, "a");
if (fp == NULL) {
fprintf(stderr, "%s: failed to open %s: %s\n", progname, mtab,
strerror(errno));
return -1;
}
ent.mnt_fsname = (char *) fsname;
ent.mnt_dir = (char *) mnt;
ent.mnt_type = (char *) type;
ent.mnt_opts = (char *) opts;
ent.mnt_freq = 0;
ent.mnt_passno = 0;
res = addmntent(fp, &ent);
if (res != 0) {
fprintf(stderr, "%s: failed to add entry to %s: %s\n", progname,
mtab, strerror(errno));
return -1;
}
endmntent(fp);
return 0;
}
static int remove_mount(const char *mnt, int quiet, const char *mtab,
const char *mtab_new)
{
int res;
2001-11-12 02:20:17 +08:00
struct mntent *entp;
2001-11-08 22:56:53 +08:00
FILE *fp;
FILE *newfp;
2001-11-12 02:20:17 +08:00
const char *user = NULL;
2001-11-08 22:56:53 +08:00
int found;
2001-11-12 02:20:17 +08:00
fp = setmntent(mtab, "r");
2004-07-24 21:47:44 +08:00
if (fp == NULL) {
2001-11-12 02:20:17 +08:00
fprintf(stderr, "%s failed to open %s: %s\n", progname, mtab,
strerror(errno));
return -1;
2001-11-08 22:56:53 +08:00
}
2001-11-12 02:20:17 +08:00
newfp = setmntent(mtab_new, "w");
2004-07-24 21:47:44 +08:00
if (newfp == NULL) {
2001-11-12 02:20:17 +08:00
fprintf(stderr, "%s failed to open %s: %s\n", progname, mtab_new,
strerror(errno));
return -1;
2001-11-08 22:56:53 +08:00
}
2004-07-24 21:47:44 +08:00
if (getuid() != 0) {
2001-11-12 02:20:17 +08:00
user = get_user_name();
2004-07-24 21:47:44 +08:00
if (user == NULL)
2001-11-12 02:20:17 +08:00
return -1;
}
2001-11-08 22:56:53 +08:00
found = 0;
2004-07-24 21:47:44 +08:00
while ((entp = getmntent(fp)) != NULL) {
2001-11-12 02:20:17 +08:00
int remove = 0;
2004-07-24 21:47:44 +08:00
if (!found && strcmp(entp->mnt_dir, mnt) == 0 &&
2001-11-12 02:20:17 +08:00
strcmp(entp->mnt_type, "fuse") == 0) {
2004-07-24 21:47:44 +08:00
if (user == NULL)
2001-11-12 02:20:17 +08:00
remove = 1;
else {
char *p = strstr(entp->mnt_opts, "user=");
2004-07-24 21:47:44 +08:00
if (p != NULL && strcmp(p + 5, user) == 0)
2001-11-12 02:20:17 +08:00
remove = 1;
2001-11-08 22:56:53 +08:00
}
}
2004-07-24 21:47:44 +08:00
if (remove)
2001-11-08 22:56:53 +08:00
found = 1;
2001-11-12 02:20:17 +08:00
else {
res = addmntent(newfp, entp);
2004-07-24 21:47:44 +08:00
if (res != 0) {
2004-11-20 20:22:37 +08:00
fprintf(stderr, "%s: failed to add entry to %s: %s\n",
progname, mtab_new, strerror(errno));
2001-11-12 02:20:17 +08:00
}
}
2001-11-08 22:56:53 +08:00
}
2001-11-12 02:20:17 +08:00
endmntent(fp);
endmntent(newfp);
2004-09-23 00:47:40 +08:00
2004-11-24 06:32:16 +08:00
if (!found) {
if (!quiet)
fprintf(stderr, "%s: entry for %s not found in %s\n", progname,
mnt, mtab);
unlink(mtab_new);
return -1;
2003-10-22 19:11:57 +08:00
}
2001-11-08 22:56:53 +08:00
2004-11-24 06:32:16 +08:00
return 0;
}
static int do_unmount(const char *mnt, int quiet, int lazy, const char *mtab,
const char *mtab_new)
{
int res;
if (getuid() != 0) {
res = drop_privs();
if (res == -1)
2001-11-08 22:56:53 +08:00
return -1;
}
2004-11-24 06:32:16 +08:00
res = umount2(mnt, lazy ? 2 : 0);
if (res == -1) {
if (!quiet)
fprintf(stderr, "%s: failed to unmount %s: %s\n",
progname, mnt, strerror(errno));
2001-11-08 22:56:53 +08:00
return -1;
}
2004-11-24 06:32:16 +08:00
if (getuid() != 0)
restore_privs();
2001-11-08 22:56:53 +08:00
2004-11-24 06:32:16 +08:00
res = rename(mtab_new, mtab);
if (res == -1) {
fprintf(stderr, "%s: failed to rename %s to %s: %s\n", progname,
mtab_new, mtab, strerror(errno));
return -1;
}
2001-11-08 22:56:53 +08:00
return 0;
}
2004-11-24 06:32:16 +08:00
static int unmount_fuse(const char *mnt, int quiet, int lazy)
{
int res;
const char *mtab = _PATH_MOUNTED;
const char *mtab_new = _PATH_MOUNTED "~fuse~";
res = remove_mount(mnt, quiet, mtab, mtab_new);
if (res == -1)
return -1;
res = do_unmount(mnt, quiet, lazy, mtab, mtab_new);
if (res == -1) {
unlink(mtab_new);
return -1;
}
return 0;
}
#endif /* USE_UCLIBC */
2004-07-24 01:16:29 +08:00
static int begins_with(const char *s, const char *beg)
{
if (strncmp(s, beg, strlen(beg)) == 0)
return 1;
else
return 0;
}
2004-11-20 19:18:34 +08:00
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},
{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 = 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;
}
2004-07-24 01:16:29 +08:00
static int do_mount(const char *mnt, const char *type, mode_t rootmode,
2004-11-20 19:18:34 +08:00
int fd, const char *opts, const char *dev, char **fsnamep,
char **mnt_optsp)
2001-11-08 19:34:54 +08:00
{
int res;
2001-11-09 22:49:18 +08:00
int flags = MS_NOSUID | MS_NODEV;
2004-07-24 01:16:29 +08:00
char *optbuf;
2004-11-20 19:18:34 +08:00
char *mnt_opts = NULL;
2004-07-24 01:16:29 +08:00
const char *s;
char *d;
char *fsname = NULL;
2001-11-08 19:34:54 +08:00
2004-07-24 01:16:29 +08:00
optbuf = malloc(strlen(opts) + 64);
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=";
for (len = 0; s[len] && s[len] != ','; len++);
if (begins_with(s, fsname_str)) {
unsigned fsname_str_len = strlen(fsname_str);
if (fsname)
free(fsname);
fsname = malloc(len - fsname_str_len + 1);
if (!fsname) {
fprintf(stderr, "%s: failed to allocate memory\n", progname);
free(optbuf);
return -1;
}
memcpy(fsname, s + fsname_str_len, len - fsname_str_len);
fsname[len - fsname_str_len] = '\0';
} else if (!begins_with(s, "fd=") &&
!begins_with(s, "rootmode=") &&
!begins_with(s, "uid=")) {
2004-11-20 19:18:34 +08:00
int on;
int flag;
if (find_mount_flag(s, len, &on, &flag)) {
if (on)
flags |= flag;
else
flags &= ~flag;
} else {
memcpy(d, s, len);
d += len;
*d++ = ',';
}
2004-07-24 01:16:29 +08:00
}
s += len;
if (*s)
s++;
}
2004-11-20 19:18:34 +08:00
res = get_mnt_opts(flags, optbuf, &mnt_opts);
if (res == -1) {
free(mnt_opts);
free(optbuf);
return -1;
}
2004-07-24 01:16:29 +08:00
sprintf(d, "fd=%i,rootmode=%o,uid=%i", fd, rootmode, getuid());
if (fsname == NULL) {
2004-11-20 19:18:34 +08:00
fsname = strdup(dev);
2004-07-24 01:16:29 +08:00
if (!fsname) {
fprintf(stderr, "%s: failed to allocate memory\n", progname);
free(optbuf);
return -1;
}
}
res = mount(fsname, mnt, type, flags, optbuf);
2004-07-24 21:47:44 +08:00
if (res == -1) {
2001-11-08 19:34:54 +08:00
fprintf(stderr, "%s: mount failed: %s\n", progname, strerror(errno));
2004-07-24 01:16:29 +08:00
free(fsname);
2004-11-20 19:18:34 +08:00
free(mnt_opts);
} else {
*fsnamep = fsname;
*mnt_optsp = mnt_opts;
2004-07-24 01:16:29 +08:00
}
2004-11-20 19:18:34 +08:00
free(optbuf);
2001-11-09 22:49:18 +08:00
return res;
2001-11-08 19:34:54 +08:00
}
2004-11-24 06:32:16 +08:00
static int check_version(const char *dev)
2004-11-11 18:33:58 +08:00
{
int res;
int majorver;
int minorver;
2004-11-24 06:32:16 +08:00
const char *version_file;
int isold = 0;
FILE *vf;
if (strcmp(dev, FUSE_DEV_OLD) == 0)
isold = 1;
if (isold)
version_file = FUSE_VERSION_FILE_OLD;
else
2004-11-30 07:43:44 +08:00
version_file = FUSE_VERSION_FILE_NEW;
2004-11-24 06:32:16 +08:00
vf = fopen(version_file, "r");
2004-11-11 18:33:58 +08:00
if (vf == NULL) {
2004-11-24 06:32:16 +08:00
if (isold) {
2004-11-20 19:18:34 +08:00
fprintf(stderr, "%s: kernel interface too old\n", progname);
return -1;
2004-11-24 06:32:16 +08:00
} else
/* If /sys/fs/fuse/version doesn't exist, just skip
version checking */
return 0;
2004-11-11 18:33:58 +08:00
}
res = fscanf(vf, "%i.%i", &majorver, &minorver);
fclose(vf);
if (res != 2) {
2004-11-20 19:18:34 +08:00
fprintf(stderr, "%s: error reading %s\n", progname, version_file);
2004-11-11 18:33:58 +08:00
return -1;
}
if (majorver < 3) {
fprintf(stderr, "%s: kernel interface too old\n", progname);
return -1;
}
return 0;
}
2004-07-24 21:47:44 +08:00
static int check_perm(const char **mntp, struct stat *stbuf, int *currdir_fd)
2001-11-08 19:34:54 +08:00
{
int res;
2004-07-24 21:47:44 +08:00
const char *mnt = *mntp;
2001-11-08 19:34:54 +08:00
res = lstat(mnt, stbuf);
2004-07-24 21:47:44 +08:00
if (res == -1) {
2001-11-08 19:34:54 +08:00
fprintf(stderr, "%s: failed to access mountpoint %s: %s\n",
progname, mnt, strerror(errno));
return -1;
}
2004-07-24 21:47:44 +08:00
/* No permission checking is done for root */
if (getuid() == 0)
return 0;
if (!S_ISDIR(stbuf->st_mode)) {
fprintf(stderr, "%s: mountpoint %s is not a directory\n",
2001-11-08 19:34:54 +08:00
progname, mnt);
return -1;
}
2004-07-24 21:47:44 +08:00
*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, mnt, strerror(errno));
return -1;
}
2001-11-08 19:34:54 +08:00
2004-07-24 21:47:44 +08:00
if ((stbuf->st_mode & S_ISVTX) && stbuf->st_uid != getuid()) {
fprintf(stderr, "%s: mountpoint %s not owned by user\n",
progname, mnt);
return -1;
}
res = access(mnt, W_OK);
if (res == -1) {
fprintf(stderr, "%s: user has no write access to mountpoint %s\n",
progname, mnt);
return -1;
2001-11-08 19:34:54 +08:00
}
2001-11-09 22:49:18 +08:00
2001-11-08 19:34:54 +08:00
return 0;
}
2004-11-20 19:18:34 +08:00
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;
}
2004-11-30 00:53:44 +08:00
} else if (errno == ENODEV)
return -2;
else if (!silent) {
2004-11-20 19:18:34 +08:00
fprintf(stderr, "%s: failed to open %s: %s\n", progname, dev,
strerror(errno));
}
return fd;
}
#define FUSE_TMP_DIRNAME "/tmp/.fuse_devXXXXXX"
#define FUSE_TMP_DEVNAME "/fuse"
2004-11-30 00:53:44 +08:00
static int try_open_new_temp(unsigned devnum, char **devp, int silent)
2001-11-08 19:34:54 +08:00
{
int res;
int fd;
2004-11-20 19:18:34 +08:00
char dirname[] = FUSE_TMP_DIRNAME;
char filename[] = FUSE_TMP_DIRNAME FUSE_TMP_DEVNAME;
if (mkdtemp(dirname) == NULL) {
fprintf(stderr, "%s: failed to create temporary device directory: %s\n",
progname, strerror(errno));
return -1;
}
sprintf(filename, "%s%s", dirname, FUSE_TMP_DEVNAME);
res = mknod(filename, S_IFCHR | 0600, devnum);
if (res == -1) {
fprintf(stderr, "%s: failed to create device node: %s\n", progname,
strerror(errno));
rmdir(dirname);
return -1;
}
2004-11-30 00:53:44 +08:00
fd = try_open(filename, devp, silent);
2004-11-20 19:18:34 +08:00
unlink(filename);
rmdir(dirname);
return fd;
}
2004-11-24 06:32:16 +08:00
static int try_open_new(char **devp, int final)
2004-11-20 19:18:34 +08:00
{
const char *dev;
unsigned minor;
unsigned major;
int res;
2001-11-08 19:34:54 +08:00
struct stat stbuf;
2004-11-20 19:18:34 +08:00
unsigned devnum;
char buf[256];
int fd = open(FUSE_SYS_DEV, O_RDONLY);
2004-11-24 06:32:16 +08:00
if (fd == -1) {
if (!final)
return -2;
2004-11-30 07:43:44 +08:00
fd = try_open(FUSE_DEV_NEW, devp, 1);
2004-11-30 00:53:44 +08:00
if (fd == -2)
2004-11-24 06:32:16 +08:00
return -2;
2004-11-30 00:53:44 +08:00
fd = try_open_new_temp(FUSE_MAJOR << 8 | FUSE_MINOR, devp, 1);
if (fd == -2)
return -2;
2004-11-30 07:43:44 +08:00
return try_open(FUSE_DEV_NEW, devp, 0);
2004-11-24 06:32:16 +08:00
}
2001-11-08 19:34:54 +08:00
2004-11-20 19:18:34 +08:00
res = read(fd, buf, sizeof(buf)-1);
close(fd);
if (res == -1) {
fprintf(stderr, "%s: failed to read from %s: %s\n", progname,
FUSE_SYS_DEV, strerror(errno));
return -1;
}
buf[res] = '\0';
if (sscanf(buf, "%u:%u", &major, &minor) != 2) {
fprintf(stderr, "%s: parse error reading from %s\n", progname,
FUSE_SYS_DEV);
return -1;
}
devnum = (major << 8) + (minor & 0xff) + ((minor & 0xff00) << 12);
2004-11-30 07:43:44 +08:00
dev = FUSE_DEV_NEW;
2004-11-20 19:18:34 +08:00
res = stat(dev, &stbuf);
2004-11-24 06:32:16 +08:00
if (res == -1) {
if (major == FUSE_MAJOR && minor == FUSE_MINOR)
2004-11-30 00:53:44 +08:00
return try_open_new_temp(devnum, devp, 0);
2004-11-24 06:32:16 +08:00
else {
fprintf(stderr, "%s: failed to open %s: %s\n", progname,
dev, strerror(errno));
return -1;
}
}
2004-11-20 19:18:34 +08:00
if ((stbuf.st_mode & S_IFMT) != S_IFCHR || stbuf.st_rdev != devnum) {
fprintf(stderr, "%s: %s exists but has wrong attributes\n", progname,
dev);
return -1;
}
return try_open(dev, devp, 0);
}
static int open_fuse_device(char **devp)
{
int fd;
if (1
2004-07-25 03:56:16 +08:00
#ifndef AUTO_MODPROBE
&& getuid() == 0
#endif
) {
2001-11-12 02:20:17 +08:00
int status;
2004-11-24 06:32:16 +08:00
pid_t pid;
fd = try_open(FUSE_DEV_OLD, devp, 1);
if (fd != -1)
return fd;
fd = try_open_new(devp, 0);
if (fd != -2)
return fd;
#ifndef USE_UCLIBC
pid = fork();
#else
pid = vfork();
#endif
2004-07-24 21:47:44 +08:00
if (pid == 0) {
2001-11-12 02:20:17 +08:00
setuid(0);
2003-10-05 01:31:38 +08:00
execl("/sbin/modprobe", "/sbin/modprobe", "fuse", NULL);
2001-11-12 02:20:17 +08:00
exit(1);
}
2004-07-24 21:47:44 +08:00
if (pid != -1)
2001-11-12 02:20:17 +08:00
waitpid(pid, &status, 0);
2004-11-24 06:32:16 +08:00
}
2001-11-12 02:20:17 +08:00
2004-11-24 06:32:16 +08:00
fd = try_open(FUSE_DEV_OLD, devp, 1);
if (fd != -1)
return fd;
2004-11-20 19:18:34 +08:00
2004-11-24 06:32:16 +08:00
fd = try_open_new(devp, 1);
if (fd != -2)
return fd;
2004-11-20 19:18:34 +08:00
fprintf(stderr, "fuse device not found, try 'modprobe fuse' first\n");
return -1;
}
static int mount_fuse(const char *mnt, const char *opts)
{
int res;
int fd;
char *dev;
const char *type = "fuse";
struct stat stbuf;
char *fsname;
char *mnt_opts;
const char *real_mnt = mnt;
int currdir_fd = -1;
fd = open_fuse_device(&dev);
if (fd == -1)
2001-11-08 19:34:54 +08:00
return -1;
2004-09-23 00:47:40 +08:00
if (getuid() != 0) {
res = drop_privs();
if (res == -1)
return -1;
}
2004-11-24 06:32:16 +08:00
res = check_version(dev);
2004-11-11 18:33:58 +08:00
if (res != -1) {
res = check_perm(&real_mnt, &stbuf, &currdir_fd);
if (res != -1)
res = do_mount(real_mnt, type, stbuf.st_mode & S_IFMT, fd, opts,
2004-11-20 19:18:34 +08:00
dev, &fsname, &mnt_opts);
2004-11-11 18:33:58 +08:00
}
2004-09-23 00:47:40 +08:00
if (getuid() != 0)
restore_privs();
2004-07-24 21:47:44 +08:00
if (res == -1)
2001-11-08 19:34:54 +08:00
return -1;
2004-02-17 16:57:29 +08:00
2004-07-24 21:47:44 +08:00
if (currdir_fd != -1) {
fchdir(currdir_fd);
close(currdir_fd);
}
2004-11-24 06:32:16 +08:00
#ifndef USE_UCLIBC
2004-07-24 21:47:44 +08:00
if (geteuid() == 0) {
2004-11-24 06:32:16 +08:00
int mtablock = lock_mtab();
2004-11-20 19:18:34 +08:00
res = add_mount(fsname, mnt, type, mnt_opts);
2004-02-17 16:57:29 +08:00
unlock_mtab(mtablock);
2004-07-24 21:47:44 +08:00
if (res == -1) {
2004-03-30 15:24:29 +08:00
umount2(mnt, 2); /* lazy umount */
2004-02-17 16:57:29 +08:00
return -1;
}
2004-11-20 19:18:34 +08:00
}
2004-11-24 06:32:16 +08:00
#endif
2004-11-20 19:18:34 +08:00
free(fsname);
free(mnt_opts);
free(dev);
2001-11-08 22:56:53 +08:00
2001-11-08 19:34:54 +08:00
return fd;
}
2004-11-20 19:18:34 +08:00
static char *resolve_path(const char *orig)
2001-11-08 22:56:53 +08:00
{
2001-11-12 02:20:17 +08:00
char buf[PATH_MAX];
2004-11-20 19:18:34 +08:00
char *copy;
2004-07-24 21:47:44 +08:00
char *dst;
2004-11-20 19:18:34 +08:00
char *end;
char *lastcomp;
const char *toresolv;
2004-07-24 01:16:29 +08:00
2004-11-20 19:18:34 +08:00
copy = strdup(orig);
if (copy == NULL) {
fprintf(stderr, "%s: failed to allocate memory\n", progname);
return NULL;
2002-12-04 02:45:21 +08:00
}
2001-11-08 22:56:53 +08:00
2004-11-20 19:18:34 +08:00
toresolv = copy;
lastcomp = NULL;
for (end = copy + strlen(copy) - 1; end > copy && *end == '/'; end --);
if (end[0] != '/') {
char *tmp;
end[1] = '\0';
tmp = strrchr(copy, '/');
if (tmp == NULL) {
lastcomp = copy;
toresolv = ".";
} else {
lastcomp = tmp + 1;
if (tmp == copy)
toresolv = "/";
}
if (strcmp(lastcomp, ".") == 0 || strcmp(lastcomp, "..") == 0) {
lastcomp = NULL;
toresolv = copy;
}
else if (tmp)
tmp[0] = '\0';
}
if (realpath(toresolv, buf) == NULL) {
2001-11-12 02:20:17 +08:00
fprintf(stderr, "%s: Bad mount point %s: %s\n", progname, orig,
strerror(errno));
2004-11-20 19:18:34 +08:00
free(copy);
2001-11-12 02:20:17 +08:00
return NULL;
}
2004-11-20 19:18:34 +08:00
if (lastcomp == NULL)
dst = strdup(buf);
else {
dst = (char *) malloc(strlen(buf) + 1 + strlen(lastcomp) + 1);
if (dst)
sprintf(dst, "%s/%s", buf, lastcomp);
}
free(copy);
2004-07-24 21:47:44 +08:00
if (dst == NULL)
fprintf(stderr, "%s: failed to allocate memory\n", progname);
return dst;
2001-11-08 22:56:53 +08:00
}
2002-10-25 19:40:14 +08:00
static int send_fd(int sock_fd, int fd)
{
int retval;
struct msghdr msg;
struct cmsghdr *p_cmsg;
struct iovec vec;
char cmsgbuf[CMSG_SPACE(sizeof(fd))];
int *p_fds;
char sendchar = 0;
msg.msg_control = cmsgbuf;
msg.msg_controllen = sizeof(cmsgbuf);
p_cmsg = CMSG_FIRSTHDR(&msg);
p_cmsg->cmsg_level = SOL_SOCKET;
p_cmsg->cmsg_type = SCM_RIGHTS;
p_cmsg->cmsg_len = CMSG_LEN(sizeof(fd));
p_fds = (int *) CMSG_DATA(p_cmsg);
*p_fds = fd;
msg.msg_controllen = p_cmsg->cmsg_len;
msg.msg_name = NULL;
msg.msg_namelen = 0;
msg.msg_iov = &vec;
msg.msg_iovlen = 1;
msg.msg_flags = 0;
/* "To pass file descriptors or credentials you need to send/read at
* least one byte" (man 7 unix) */
vec.iov_base = &sendchar;
vec.iov_len = sizeof(sendchar);
2004-07-24 21:47:44 +08:00
while ((retval = sendmsg(sock_fd, &msg, 0)) == -1 && errno == EINTR);
2002-10-25 19:40:14 +08:00
if (retval != 1) {
perror("sending file descriptor");
return -1;
}
return 0;
}
2001-11-08 19:34:54 +08:00
static void usage()
{
fprintf(stderr,
2004-02-21 00:38:45 +08:00
"%s: [options] mountpoint\n"
2001-11-08 19:34:54 +08:00
"Options:\n"
2004-07-24 01:16:29 +08:00
" -h print help\n"
" -o opt[,opt...] mount options\n"
" -u unmount\n"
" -q quiet\n"
" -z lazy unmount\n",
2001-11-08 19:34:54 +08:00
progname);
exit(1);
}
int main(int argc, char *argv[])
{
int a;
int fd;
int res;
2001-11-12 02:20:17 +08:00
char *origmnt;
char *mnt;
int unmount = 0;
2004-03-30 15:24:29 +08:00
int lazy = 0;
2002-10-25 19:40:14 +08:00
char *commfd;
2004-01-27 22:54:47 +08:00
int quiet = 0;
2004-02-21 00:38:45 +08:00
int cfd;
2004-07-24 01:16:29 +08:00
const char *opts = "";
2001-11-08 19:34:54 +08:00
progname = argv[0];
2004-07-24 21:47:44 +08:00
for (a = 1; a < argc; a++) {
if (argv[a][0] != '-')
2001-11-08 19:34:54 +08:00
break;
2004-07-24 21:47:44 +08:00
switch (argv[a][1]) {
2001-11-08 19:34:54 +08:00
case 'h':
usage();
break;
2001-11-08 22:56:53 +08:00
2004-07-24 01:16:29 +08:00
case 'o':
2004-01-05 23:07:12 +08:00
a++;
2004-07-24 21:47:44 +08:00
if (a == argc) {
2004-07-24 01:16:29 +08:00
fprintf(stderr, "%s: Missing argument to -o\n", progname);
2004-01-05 23:07:12 +08:00
exit(1);
}
2004-07-24 01:16:29 +08:00
opts = argv[a];
2004-01-05 23:07:12 +08:00
break;
2001-12-20 23:38:05 +08:00
2004-07-24 01:16:29 +08:00
case 'u':
unmount = 1;
2004-01-26 19:28:44 +08:00
break;
2004-07-02 17:22:50 +08:00
2004-07-24 01:16:29 +08:00
case 'z':
lazy = 1;
2004-07-02 17:22:50 +08:00
break;
2004-01-27 22:54:47 +08:00
case 'q':
quiet = 1;
break;
2004-01-26 19:28:44 +08:00
2001-11-08 19:34:54 +08:00
default:
fprintf(stderr, "%s: Unknown option %s\n", progname, argv[a]);
2004-01-27 22:54:47 +08:00
fprintf(stderr, "Try `%s -h' for more information\n", progname);
2001-11-08 19:34:54 +08:00
exit(1);
}
}
2004-07-24 21:47:44 +08:00
if (a == argc) {
2001-11-08 19:34:54 +08:00
fprintf(stderr, "%s: Missing mountpoint argument\n", progname);
exit(1);
}
2001-11-12 02:20:17 +08:00
origmnt = argv[a++];
2004-09-23 00:47:40 +08:00
if (getuid() != 0) {
res = drop_privs();
if (res == -1)
exit(1);
}
2001-11-12 02:20:17 +08:00
2004-11-20 19:18:34 +08:00
mnt = resolve_path(origmnt);
2004-07-24 21:47:44 +08:00
if (mnt == NULL)
2001-11-12 02:20:17 +08:00
exit(1);
2004-07-24 21:47:44 +08:00
if (getuid() != 0)
2001-11-12 02:20:17 +08:00
restore_privs();
2001-11-08 19:34:54 +08:00
2004-07-24 21:47:44 +08:00
if (unmount) {
2004-11-24 06:32:16 +08:00
#ifndef USE_UCLIBC
2004-07-24 21:47:44 +08:00
if (geteuid() == 0) {
2004-02-17 16:57:29 +08:00
int mtablock = lock_mtab();
2004-11-24 06:32:16 +08:00
res = unmount_fuse(mnt, quiet, lazy);
2004-02-17 16:57:29 +08:00
unlock_mtab(mtablock);
2004-11-24 06:32:16 +08:00
} else
#endif
{
2004-03-30 15:24:29 +08:00
res = umount2(mnt, lazy ? 2 : 0);
2004-07-24 21:47:44 +08:00
if (res == -1) {
2004-03-30 23:17:26 +08:00
if (!quiet)
fprintf(stderr, "%s: failed to unmount %s: %s\n",
progname, mnt, strerror(errno));
2004-02-17 16:57:29 +08:00
}
}
2004-07-24 21:47:44 +08:00
if (res == -1)
2001-11-08 22:56:53 +08:00
exit(1);
return 0;
}
2002-10-25 19:40:14 +08:00
commfd = getenv(FUSE_COMMFD_ENV);
2004-07-24 21:47:44 +08:00
if (commfd == NULL) {
2004-02-21 00:38:45 +08:00
fprintf(stderr, "%s: old style mounting not supported\n", progname);
2001-11-08 19:34:54 +08:00
exit(1);
}
2004-02-21 00:38:45 +08:00
2004-07-24 01:16:29 +08:00
fd = mount_fuse(mnt, opts);
2004-07-24 21:47:44 +08:00
if (fd == -1)
2001-11-08 19:34:54 +08:00
exit(1);
2004-02-21 00:38:45 +08:00
cfd = atoi(commfd);
res = send_fd(cfd, fd);
2004-07-24 21:47:44 +08:00
if (res == -1)
2004-02-21 00:38:45 +08:00
exit(1);
2001-11-08 19:34:54 +08:00
2004-02-21 00:38:45 +08:00
return 0;
2001-11-08 19:34:54 +08:00
}