This commit is contained in:
Miklos Szeredi 2001-11-11 18:20:17 +00:00
parent 8cffdb9707
commit 0a7077f536
11 changed files with 293 additions and 399 deletions

2
NEWS
View File

@ -1,3 +1,3 @@
What is new in 0.9
What is new in 0.9:
* Everything

34
README
View File

@ -11,11 +11,11 @@ You can download the source code releases from
http://sourceforge.net/projects/avf
or alternatively you can use CVS to get the very latest development
version: set the cvsroot to
version by setting the cvsroot to
:pserver:anonymous@cvs.avf.sourceforge.net:/cvsroot/avf
and check out the 'fuse' module.
and checking out the 'fuse' module.
Installation
============
@ -49,13 +49,10 @@ steps:
4) ls -al /mnt/whatever
5) Be glad!
5) Be glad
If it doesn't work out, you can ask the me. (Oh yeah, and you need to
do 'insmod kernel/fuse.o' before running your program, in case you
forgot).
See the file 'include/fuse.h' for documentation of the library interface.
If it doesn't work out, please ask! Also see the file 'include/fuse.h' for
detailed documentation of the library interface.
Security
@ -65,9 +62,8 @@ If you run 'make install', the fusermount program is installed
set-user-id to root. This is done to allow normal users to mount
their own filesystem implementations.
There must however be some limitations to forbid the Bad User to do
Naughty Things with your Beautiful system. Currently those
limitations are:
There must however be some limitations, in order to prevent Bad User from
doing nasty things. Currently those limitations are:
- The user can only mount on a mountpoint, for which it has write
permission
@ -75,16 +71,15 @@ limitations are:
- The mountpoint is not a sticky directory which isn't owned by the
user (like /tmp usually is)
- If the user doing the mount is not root, then no other user
(including root) can access the contents of the mounted
- No other user (including root) can access the contents of the mounted
filesystem.
When linux will have private namespaces (as soon as version 2.5 comes
out) then this third condition is useless and can be gotten rid of.
When linux will have private namespaces (as soon as version 2.5 comes out
hopefully) then this third condition is useless and can be gotten rid of.
Currently the first two conditions are checked by the fusermount
program before doing the mount. This has the nice feature, that it's
totally useless. Here's why:
Currently the first two conditions are checked by the fusermount program
before doing the mount. This has the nice feature, that it's totally
useless. Here's why:
- user creates /tmp/mydir
- user starts fusermount
@ -96,6 +91,5 @@ totally useless. Here's why:
So to make this secure, the checks must be done by the kernel. And so
there is a patch (patch/ms_permission.patch) which does exactly this.
This is against 2.4.14, but applies to some earlier kernels (not too
much earlier though), and possibly some later (I couldn't know, could
I?).
much earlier though), and possibly some later.

11
TODO Normal file
View File

@ -0,0 +1,11 @@
- Better (but not too complex) library interface for open/read/write/close
- Permission checking for users other then the owner of the mount
- Improve efficiency of read and write operations
- Integrate (parts of) fusermount into mount(8)
- Statfs operation
- Etc, etc...

View File

@ -15,58 +15,25 @@
#include <signal.h>
#include <utime.h>
#include <fcntl.h>
#include <grp.h>
#include <sys/fsuid.h>
static char *mount_point;
static char *unmount_cmd;
static int set_creds(struct fuse_cred *cred)
static int xmp_getattr(const char *path, struct stat *stbuf)
{
int res;
res = setfsuid(cred->uid);
if(res == -1)
return -errno;
res = setfsgid(cred->gid);
if(res == -1)
return -errno;
return 0;
}
static void restore_creds()
{
setfsuid(getuid());
setfsgid(getgid());
}
static int xmp_getattr(struct fuse_cred *cred, const char *path,
struct stat *stbuf)
{
int res;
res = set_creds(cred);
if(res)
return res;
res = lstat(path, stbuf);
restore_creds();
if(res == -1)
return -errno;
return 0;
}
static int xmp_readlink(struct fuse_cred *cred, const char *path, char *buf,
size_t size)
static int xmp_readlink(const char *path, char *buf, size_t size)
{
int res;
res = set_creds(cred);
if(res)
return res;
res = readlink(path, buf, size - 1);
restore_creds();
if(res == -1)
return -errno;
@ -75,18 +42,13 @@ static int xmp_readlink(struct fuse_cred *cred, const char *path, char *buf,
}
static int xmp_getdir(struct fuse_cred *cred, const char *path, fuse_dirh_t h,
fuse_dirfil_t filler)
static int xmp_getdir(const char *path, fuse_dirh_t h, fuse_dirfil_t filler)
{
DIR *dp;
struct dirent *de;
int res;
int res = 0;
res = set_creds(cred);
if(res)
return res;
dp = opendir(path);
restore_creds();
if(dp == NULL)
return -errno;
@ -100,169 +62,121 @@ static int xmp_getdir(struct fuse_cred *cred, const char *path, fuse_dirh_t h,
return res;
}
static int xmp_mknod(struct fuse_cred *cred, const char *path, mode_t mode,
dev_t rdev)
static int xmp_mknod(const char *path, mode_t mode, dev_t rdev)
{
int res;
res = set_creds(cred);
if(res)
return res;
res = mknod(path, mode, rdev);
restore_creds();
if(res == -1)
return -errno;
return 0;
}
static int xmp_mkdir(struct fuse_cred *cred, const char *path, mode_t mode)
static int xmp_mkdir(const char *path, mode_t mode)
{
int res;
res = set_creds(cred);
if(res)
return res;
res = mkdir(path, mode);
restore_creds();
if(res == -1)
return -errno;
return 0;
}
static int xmp_unlink(struct fuse_cred *cred, const char *path)
static int xmp_unlink(const char *path)
{
int res;
res = set_creds(cred);
if(res)
return res;
res = unlink(path);
restore_creds();
if(res == -1)
return -errno;
return 0;
}
static int xmp_rmdir(struct fuse_cred *cred, const char *path)
static int xmp_rmdir(const char *path)
{
int res;
res = set_creds(cred);
if(res)
return res;
res = rmdir(path);
restore_creds();
if(res == -1)
return -errno;
return 0;
}
static int xmp_symlink(struct fuse_cred *cred, const char *from,
const char *to)
static int xmp_symlink(const char *from, const char *to)
{
int res;
res = set_creds(cred);
if(res)
return res;
res = symlink(from, to);
restore_creds();
if(res == -1)
return -errno;
return 0;
}
static int xmp_rename(struct fuse_cred *cred, const char *from, const char *to)
static int xmp_rename(const char *from, const char *to)
{
int res;
res = set_creds(cred);
if(res)
return res;
res = rename(from, to);
restore_creds();
if(res == -1)
return -errno;
return 0;
}
static int xmp_link(struct fuse_cred *cred, const char *from, const char *to)
static int xmp_link(const char *from, const char *to)
{
int res;
res = set_creds(cred);
if(res)
return res;
res = link(from, to);
restore_creds();
if(res == -1)
return -errno;
return 0;
}
static int xmp_chmod(struct fuse_cred *cred, const char *path, mode_t mode)
static int xmp_chmod(const char *path, mode_t mode)
{
int res;
res = set_creds(cred);
if(res)
return res;
res = chmod(path, mode);
restore_creds();
if(res == -1)
return -errno;
return 0;
}
static int xmp_chown(struct fuse_cred *cred, const char *path, uid_t uid,
gid_t gid)
static int xmp_chown(const char *path, uid_t uid, gid_t gid)
{
int res;
res = set_creds(cred);
if(res)
return res;
res = lchown(path, uid, gid);
restore_creds();
if(res == -1)
return -errno;
return 0;
}
static int xmp_truncate(struct fuse_cred *cred, const char *path, off_t size)
static int xmp_truncate(const char *path, off_t size)
{
int res;
res = set_creds(cred);
if(res)
return res;
res = truncate(path, size);
restore_creds();
if(res == -1)
return -errno;
return 0;
}
static int xmp_utime(struct fuse_cred *cred, const char *path,
struct utimbuf *buf)
static int xmp_utime(const char *path, struct utimbuf *buf)
{
int res;
res = set_creds(cred);
if(res)
return res;
res = utime(path, buf);
restore_creds();
if(res == -1)
return -errno;
@ -270,15 +184,11 @@ static int xmp_utime(struct fuse_cred *cred, const char *path,
}
static int xmp_open(struct fuse_cred *cred, const char *path, int flags)
static int xmp_open(const char *path, int flags)
{
int res;
res = set_creds(cred);
if(res)
return res;
res = open(path, flags);
restore_creds();
if(res == -1)
return -errno;
@ -286,17 +196,12 @@ static int xmp_open(struct fuse_cred *cred, const char *path, int flags)
return 0;
}
static int xmp_read(struct fuse_cred *cred, const char *path, char *buf,
size_t size, off_t offset)
static int xmp_read(const char *path, char *buf, size_t size, off_t offset)
{
int fd;
int res;
res = set_creds(cred);
if(res)
return res;
fd = open(path, O_RDONLY);
restore_creds();
if(fd == -1)
return -errno;
@ -308,17 +213,13 @@ static int xmp_read(struct fuse_cred *cred, const char *path, char *buf,
return res;
}
static int xmp_write(struct fuse_cred *cred, const char *path, const char *buf,
size_t size, off_t offset)
static int xmp_write(const char *path, const char *buf, size_t size,
off_t offset)
{
int fd;
int res;
res = set_creds(cred);
if(res)
return res;
fd = open(path, O_WRONLY);
restore_creds();
if(fd == -1)
return -errno;
@ -381,10 +282,8 @@ static struct fuse_operations xmp_oper = {
static void cleanup()
{
char *buf = (char *) malloc(strlen(mount_point) + 128);
sprintf(buf, "fusermount -u %s", mount_point);
system(buf);
free(buf);
close(0);
system(unmount_cmd);
}
int main(int argc, char *argv[])
@ -395,7 +294,7 @@ int main(int argc, char *argv[])
if(argc < 2) {
fprintf(stderr,
"usage: %s mount_dir [options] \n"
"usage: %s unmount_cmd [options] \n"
"Options:\n"
" -d enable debug output\n"
" -s disable multithreaded operation\n",
@ -404,7 +303,7 @@ int main(int argc, char *argv[])
}
argctr = 1;
mount_point = argv[argctr++];
unmount_cmd = argv[argctr++];
set_signal_handlers();
atexit(cleanup);
@ -430,8 +329,6 @@ int main(int argc, char *argv[])
exit(1);
}
setgroups(0, NULL);
fuse = fuse_new(0, flags);
fuse_set_operations(fuse, &xmp_oper);
fuse_loop(fuse);

View File

@ -21,32 +21,19 @@ typedef struct fuse_dirhandle *fuse_dirh_t;
/** Function to add an entry in a getdir() operation */
typedef int (*fuse_dirfil_t) (fuse_dirh_t, const char *, int type);
/** Credentials for an operation, these are determined by the fsuid
and fsgid of the calling process */
struct fuse_cred {
uid_t uid;
gid_t gid;
/* FIXME: supplementary groups should also be included */
/* (And capabilities???) */
};
/**
* The file system operations:
*
* Most of these should work very similarly to the well known UNIX
* file system operations. Exceptions are:
*
* - All operations get a fuse_cred structure by which the filesystem
* implementation can check, whether the operation is permitted or
* not.
*
* - All operations should return the negated error value (-errno) on
* error.
*
* - readlink() should fill the buffer with a null terminated string.
* The buffer size argument includes the space for the terminating
* null character. If the linkname is too long to fit in the buffer,
* it should be truncated. The return value should be 0 for success.
* - readlink() should fill the buffer with a null terminated string. The
* buffer size argument includes the space for the terminating null
* character. If the linkname is too long to fit in the buffer, it should
* be truncated. The return value should be 0 for success.
*
* - getdir() is the opendir(), readdir(), ..., closedir() sequence
* in one call. For each directory entry the filldir parameter should
@ -62,25 +49,26 @@ struct fuse_cred {
*
* - read(), write() are not passed a filehandle, but rather a
* pathname. The offset of the read and write is passed as the last
* argument, like the pread() and pwrite() system calls. */
* argument, like the pread() and pwrite() system calls.
*/
struct fuse_operations {
int (*getattr) (struct fuse_cred *, const char *, struct stat *);
int (*readlink) (struct fuse_cred *, const char *, char *, size_t);
int (*getdir) (struct fuse_cred *, const char *, fuse_dirh_t, fuse_dirfil_t);
int (*mknod) (struct fuse_cred *, const char *, mode_t, dev_t);
int (*mkdir) (struct fuse_cred *, const char *, mode_t);
int (*unlink) (struct fuse_cred *, const char *);
int (*rmdir) (struct fuse_cred *, const char *);
int (*symlink) (struct fuse_cred *, const char *, const char *);
int (*rename) (struct fuse_cred *, const char *, const char *);
int (*link) (struct fuse_cred *, const char *, const char *);
int (*chmod) (struct fuse_cred *, const char *, mode_t);
int (*chown) (struct fuse_cred *, const char *, uid_t, gid_t);
int (*truncate) (struct fuse_cred *, const char *, off_t);
int (*utime) (struct fuse_cred *, const char *, struct utimbuf *);
int (*open) (struct fuse_cred *, const char *, int);
int (*read) (struct fuse_cred *, const char *, char *, size_t, off_t);
int (*write) (struct fuse_cred *, const char *, const char *, size_t, off_t);
int (*getattr) (const char *, struct stat *);
int (*readlink) (const char *, char *, size_t);
int (*getdir) (const char *, fuse_dirh_t, fuse_dirfil_t);
int (*mknod) (const char *, mode_t, dev_t);
int (*mkdir) (const char *, mode_t);
int (*unlink) (const char *);
int (*rmdir) (const char *);
int (*symlink) (const char *, const char *);
int (*rename) (const char *, const char *);
int (*link) (const char *, const char *);
int (*chmod) (const char *, mode_t);
int (*chown) (const char *, uid_t, gid_t);
int (*truncate) (const char *, off_t);
int (*utime) (const char *, struct utimbuf *);
int (*open) (const char *, int);
int (*read) (const char *, char *, size_t, off_t);
int (*write) (const char *, const char *, size_t, off_t);
};
/* FUSE flags: */

View File

@ -149,8 +149,6 @@ struct fuse_in_header {
int unique;
enum fuse_opcode opcode;
unsigned long ino;
unsigned int uid;
unsigned int gid;
};
struct fuse_out_header {

View File

@ -18,6 +18,10 @@ install-exec-local: fuse.o
$(INSTALL) -m 644 fuse.o $(DESTDIR)$(fusemoduledir)/fuse.o
/sbin/depmod -a
uninstall-local:
rm -f $(DESTDIR)$(fusemoduledir)/fuse.o
/sbin/depmod -a
clean-local:
rm -f *.o *.s

View File

@ -117,7 +117,8 @@ static struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry)
return ERR_PTR(ret);
}
/* create needs to return a positive entry, so this also does a lookup */
/* create needs to return a positive entry, so this is actually an
mknod+lookup */
static int fuse_mknod(struct inode *dir, struct dentry *entry, int mode,
int rdev)
{
@ -305,14 +306,11 @@ static int fuse_permission(struct inode *inode, int mask)
{
struct fuse_conn *fc = INO_FC(inode);
/* (too) simple protection for non-privileged mounts */
if(fc->uid) {
if(current->fsuid == fc->uid)
return 0;
else
return -EACCES;
}
return 0;
/* (too) simple protection */
if(current->fsuid == fc->uid)
return 0;
else
return -EACCES;
}
static int fuse_revalidate(struct dentry *entry)

View File

@ -95,7 +95,7 @@ struct fuse_out {
void *arg;
};
#define FUSE_IN_INIT { {0, 0, 0, current->fsuid, current->fsgid}, 0, 0 }
#define FUSE_IN_INIT { {0, 0, 0}, 0, 0 }
#define FUSE_OUT_INIT { {0, 0}, 0, 0, 0 }

View File

@ -354,33 +354,28 @@ static void send_reply(struct fuse *f, struct fuse_in_header *in, int error,
}
res = write(f->fd, outbuf, outsize);
if(res == -1)
perror("writing fuse device");
if(res == -1) {
/* ENOENT means the operation was interrupted */
if(errno != ENOENT)
perror("writing fuse device");
}
free(outbuf);
}
static void fill_cred(struct fuse_in_header *in, struct fuse_cred *cred)
{
cred->uid = in->uid;
cred->gid = in->gid;
}
static void do_lookup(struct fuse *f, struct fuse_in_header *in, char *name)
{
int res;
char *path;
struct stat buf;
struct fuse_lookup_out arg;
struct fuse_cred cred;
fill_cred(in, &cred);
res = -ENOENT;
path = get_path_name(f, in->ino, name);
if(path != NULL) {
res = -ENOSYS;
if(f->op.getattr)
res = f->op.getattr(&cred, path, &buf);
res = f->op.getattr(path, &buf);
free(path);
}
if(res == 0) {
@ -402,15 +397,13 @@ static void do_getattr(struct fuse *f, struct fuse_in_header *in)
char *path;
struct stat buf;
struct fuse_getattr_out arg;
struct fuse_cred cred;
fill_cred(in, &cred);
res = -ENOENT;
path = get_path(f, in->ino);
if(path != NULL) {
res = -ENOSYS;
if(f->op.getattr)
res = f->op.getattr(&cred, path, &buf);
res = f->op.getattr(path, &buf);
free(path);
}
if(res == 0)
@ -419,20 +412,19 @@ static void do_getattr(struct fuse *f, struct fuse_in_header *in)
send_reply(f, in, res, &arg, sizeof(arg));
}
int do_chmod(struct fuse *f, struct fuse_cred *cred, const char *path,
struct fuse_attr *attr)
int do_chmod(struct fuse *f, const char *path, struct fuse_attr *attr)
{
int res;
res = -ENOSYS;
if(f->op.chmod)
res = f->op.chmod(cred, path, attr->mode);
res = f->op.chmod(path, attr->mode);
return res;
}
int do_chown(struct fuse *f, struct fuse_cred *cred, const char *path,
struct fuse_attr *attr, int valid)
int do_chown(struct fuse *f, const char *path, struct fuse_attr *attr,
int valid)
{
int res;
uid_t uid = (valid & FATTR_UID) ? attr->uid : (uid_t) -1;
@ -440,25 +432,23 @@ int do_chown(struct fuse *f, struct fuse_cred *cred, const char *path,
res = -ENOSYS;
if(f->op.chown)
res = f->op.chown(cred, path, uid, gid);
res = f->op.chown(path, uid, gid);
return res;
}
int do_truncate(struct fuse *f, struct fuse_cred *cred, const char *path,
struct fuse_attr *attr)
int do_truncate(struct fuse *f, const char *path, struct fuse_attr *attr)
{
int res;
res = -ENOSYS;
if(f->op.truncate)
res = f->op.truncate(cred, path, attr->size);
res = f->op.truncate(path, attr->size);
return res;
}
int do_utime(struct fuse *f, struct fuse_cred *cred, const char *path,
struct fuse_attr *attr)
int do_utime(struct fuse *f, const char *path, struct fuse_attr *attr)
{
int res;
struct utimbuf buf;
@ -466,7 +456,7 @@ int do_utime(struct fuse *f, struct fuse_cred *cred, const char *path,
buf.modtime = attr->mtime;
res = -ENOSYS;
if(f->op.utime)
res = f->op.utime(cred, path, &buf);
res = f->op.utime(path, &buf);
return res;
}
@ -479,9 +469,7 @@ static void do_setattr(struct fuse *f, struct fuse_in_header *in,
int valid = arg->valid;
struct fuse_attr *attr = &arg->attr;
struct fuse_setattr_out outarg;
struct fuse_cred cred;
fill_cred(in, &cred);
res = -ENOENT;
path = get_path(f, in->ino);
if(path != NULL) {
@ -489,16 +477,16 @@ static void do_setattr(struct fuse *f, struct fuse_in_header *in,
if(f->op.getattr) {
res = 0;
if(!res && (valid & FATTR_MODE))
res = do_chmod(f, &cred, path, attr);
res = do_chmod(f, path, attr);
if(!res && (valid & (FATTR_UID | FATTR_GID)))
res = do_chown(f, &cred, path, attr, valid);
res = do_chown(f, path, attr, valid);
if(!res && (valid & FATTR_SIZE))
res = do_truncate(f, &cred, path, attr);
res = do_truncate(f, path, attr);
if(!res && (valid & FATTR_UTIME))
res = do_utime(f, &cred, path, attr);
res = do_utime(f, path, attr);
if(!res) {
struct stat buf;
res = f->op.getattr(&cred, path, &buf);
res = f->op.getattr(path, &buf);
if(!res)
convert_stat(&buf, &outarg.attr);
}
@ -513,15 +501,13 @@ static void do_readlink(struct fuse *f, struct fuse_in_header *in)
int res;
char link[PATH_MAX + 1];
char *path;
struct fuse_cred cred;
fill_cred(in, &cred);
res = -ENOENT;
path = get_path(f, in->ino);
if(path != NULL) {
res = -ENOSYS;
if(f->op.readlink)
res = f->op.readlink(&cred, path, link, sizeof(link));
res = f->op.readlink(path, link, sizeof(link));
free(path);
}
link[PATH_MAX] = '\0';
@ -534,9 +520,7 @@ static void do_getdir(struct fuse *f, struct fuse_in_header *in)
struct fuse_getdir_out arg;
struct fuse_dirhandle dh;
char *path;
struct fuse_cred cred;
fill_cred(in, &cred);
dh.fuse = f;
dh.fp = tmpfile();
dh.dir = in->ino;
@ -545,7 +529,7 @@ static void do_getdir(struct fuse *f, struct fuse_in_header *in)
if(path != NULL) {
res = -ENOSYS;
if(f->op.getdir)
res = f->op.getdir(&cred, path, &dh, (fuse_dirfil_t) fill_dir);
res = f->op.getdir(path, &dh, (fuse_dirfil_t) fill_dir);
free(path);
}
fflush(dh.fp);
@ -561,17 +545,15 @@ static void do_mknod(struct fuse *f, struct fuse_in_header *in,
char *path;
struct fuse_mknod_out outarg;
struct stat buf;
struct fuse_cred cred;
fill_cred(in, &cred);
res = -ENOENT;
path = get_path_name(f, in->ino, inarg->name);
if(path != NULL) {
res = -ENOSYS;
if(f->op.mknod && f->op.getattr) {
res = f->op.mknod(&cred, path, inarg->mode, inarg->rdev);
res = f->op.mknod(path, inarg->mode, inarg->rdev);
if(res == 0)
res = f->op.getattr(&cred, path, &buf);
res = f->op.getattr(path, &buf);
}
free(path);
}
@ -589,15 +571,13 @@ static void do_mkdir(struct fuse *f, struct fuse_in_header *in,
{
int res;
char *path;
struct fuse_cred cred;
fill_cred(in, &cred);
res = -ENOENT;
path = get_path_name(f, in->ino, inarg->name);
if(path != NULL) {
res = -ENOSYS;
if(f->op.mkdir)
res = f->op.mkdir(&cred, path, inarg->mode);
res = f->op.mkdir(path, inarg->mode);
free(path);
}
send_reply(f, in, res, NULL, 0);
@ -607,20 +587,18 @@ static void do_remove(struct fuse *f, struct fuse_in_header *in, char *name)
{
int res;
char *path;
struct fuse_cred cred;
fill_cred(in, &cred);
res = -ENOENT;
path = get_path_name(f, in->ino, name);
if(path != NULL) {
res = -ENOSYS;
if(in->opcode == FUSE_UNLINK) {
if(f->op.unlink)
res = f->op.unlink(&cred, path);
res = f->op.unlink(path);
}
else {
if(f->op.rmdir)
res = f->op.rmdir(&cred, path);
res = f->op.rmdir(path);
}
free(path);
}
@ -634,15 +612,13 @@ static void do_symlink(struct fuse *f, struct fuse_in_header *in, char *name,
{
int res;
char *path;
struct fuse_cred cred;
fill_cred(in, &cred);
res = -ENOENT;
path = get_path_name(f, in->ino, name);
if(path != NULL) {
res = -ENOSYS;
if(f->op.symlink)
res = f->op.symlink(&cred, link, path);
res = f->op.symlink(link, path);
free(path);
}
send_reply(f, in, res, NULL, 0);
@ -658,9 +634,7 @@ static void do_rename(struct fuse *f, struct fuse_in_header *in,
char *newname = inarg->names + strlen(oldname) + 1;
char *oldpath;
char *newpath;
struct fuse_cred cred;
fill_cred(in, &cred);
res = -ENOENT;
oldpath = get_path_name(f, olddir, oldname);
if(oldpath != NULL) {
@ -668,7 +642,7 @@ static void do_rename(struct fuse *f, struct fuse_in_header *in,
if(newpath != NULL) {
res = -ENOSYS;
if(f->op.rename)
res = f->op.rename(&cred, oldpath, newpath);
res = f->op.rename(oldpath, newpath);
if(res == 0)
rename_node(f, olddir, oldname, newdir, newname);
free(newpath);
@ -684,9 +658,7 @@ static void do_link(struct fuse *f, struct fuse_in_header *in,
int res;
char *oldpath;
char *newpath;
struct fuse_cred cred;
fill_cred(in, &cred);
res = -ENOENT;
oldpath = get_path(f, in->ino);
if(oldpath != NULL) {
@ -694,7 +666,7 @@ static void do_link(struct fuse *f, struct fuse_in_header *in,
if(newpath != NULL) {
res = -ENOSYS;
if(f->op.link)
res = f->op.link(&cred, oldpath, newpath);
res = f->op.link(oldpath, newpath);
free(newpath);
}
free(oldpath);
@ -707,15 +679,13 @@ static void do_open(struct fuse *f, struct fuse_in_header *in,
{
int res;
char *path;
struct fuse_cred cred;
fill_cred(in, &cred);
res = -ENOENT;
path = get_path(f, in->ino);
if(path != NULL) {
res = -ENOSYS;
if(f->op.open)
res = f->op.open(&cred, path, arg->flags);
res = f->op.open(path, arg->flags);
free(path);
}
send_reply(f, in, res, NULL, 0);
@ -728,15 +698,13 @@ static void do_read(struct fuse *f, struct fuse_in_header *in,
char *path;
char *buf = (char *) malloc(arg->size);
size_t size;
struct fuse_cred cred;
fill_cred(in, &cred);
res = -ENOENT;
path = get_path(f, in->ino);
if(path != NULL) {
res = -ENOSYS;
if(f->op.read)
res = f->op.read(&cred, path, buf, arg->size, arg->offset);
res = f->op.read(path, buf, arg->size, arg->offset);
free(path);
}
@ -755,15 +723,13 @@ static void do_write(struct fuse *f, struct fuse_in_header *in,
{
int res;
char *path;
struct fuse_cred cred;
fill_cred(in, &cred);
res = -ENOENT;
path = get_path(f, in->ino);
if(path != NULL) {
res = -ENOSYS;
if(f->op.write)
res = f->op.write(&cred, path, arg->buf, arg->size, arg->offset);
res = f->op.write(path, arg->buf, arg->size, arg->offset);
free(path);
}
@ -905,11 +871,13 @@ void fuse_loop(struct fuse *f)
res = read(f->fd, inbuf, sizeof(inbuf));
if(res == -1) {
perror("reading fuse device");
continue;
/* BAD... This will happen again */
exit(1);
}
if((size_t) res < sizeof(struct fuse_in_header)) {
fprintf(stderr, "short read on fuse device\n");
continue;
/* Cannot happen */
exit(1);
}
cmd = (struct cmd *) malloc(sizeof(struct cmd));

View File

@ -5,6 +5,17 @@
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 */
/*
* 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
* data in it may be lost. (I will _not_ reimplement that locking, and
* anyway that should be done in libc, if possible. But probably it is
* not).
*
*/
#include <stdio.h>
#include <stdlib.h>
@ -13,6 +24,8 @@
#include <errno.h>
#include <fcntl.h>
#include <pwd.h>
#include <mntent.h>
#include <limits.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <sys/mount.h>
@ -28,12 +41,6 @@
#define FUSE_DEV "/proc/fs/fuse/dev"
const char *progname;
const char *fusermnt = "/etc/fusermnt";
const char *fusermnt_temp = "/etc/fusermnt~";
#define FUSE_USERNAME_MAX 256
#define FUSE_PATH_MAX 4096
#define FUSEMNT_LINE_MAX (FUSE_USERNAME_MAX + 1 + FUSE_PATH_MAX + 1)
static const char *get_user_name()
{
@ -46,152 +53,136 @@ static const char *get_user_name()
}
}
static int fusermnt_lock()
static int add_mount(const char *dev, const char *mnt, const char *type)
{
int res;
const char *lockfile = fusermnt;
int fd = open(lockfile, O_WRONLY | O_CREAT, 0644);
if(fd == -1) {
fprintf(stderr, "%s: failed to open lockfile %s: %s\n", progname,
lockfile, strerror(errno));
return -1;
}
res = lockf(fd, F_LOCK, 0);
if(res == -1) {
fprintf(stderr, "%s: failed to lock file %s: %s\n", progname,
lockfile, strerror(errno));
close(fd);
return -1;
}
return fd;
}
static void fusermnt_unlock(int fd)
{
lockf(fd, F_UNLCK, 0);
close(fd);
}
static int add_mount(const char *mnt)
{
const char *mtab = _PATH_MOUNTED;
struct mntent ent;
FILE *fp;
int lockfd;
const char *user = get_user_name();
if(user == NULL)
return -1;
char *opts;
lockfd = fusermnt_lock();
if(lockfd == -1)
return -1;
fp = fopen(fusermnt, "a");
fp = setmntent(mtab, "a");
if(fp == NULL) {
fprintf(stderr, "%s: could not open %s for writing: %s\n", progname,
fusermnt, strerror(errno));
fprintf(stderr, "%s failed to open %s: %s\n", progname, mtab,
strerror(errno));
return -1;
}
if(getuid() != 0) {
const char *user = get_user_name();
if(user == NULL)
return -1;
opts = malloc(strlen(user) + 128);
if(opts != NULL)
sprintf(opts, "rw,nosuid,nodev,user=%s", user);
}
else
opts = strdup("rw,nosuid,nodev");
if(opts == NULL)
return -1;
ent.mnt_fsname = (char *) dev;
ent.mnt_dir = (char *) mnt;
ent.mnt_type = (char *) type;
ent.mnt_opts = 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;
}
fprintf(fp, "%s %s\n", user, mnt);
fclose(fp);
fusermnt_unlock(lockfd);
endmntent(fp);
return 0;
}
static int remove_mount(const char *mnt)
{
int res;
const char *mtab = _PATH_MOUNTED;
const char *mtab_new = _PATH_MOUNTED "~";
struct mntent *entp;
FILE *fp;
FILE *newfp;
int lockfd;
const char *user = NULL;
int found;
char buf[FUSEMNT_LINE_MAX + 1];
const char *user = get_user_name();
if(user == NULL)
return -1;
lockfd = fusermnt_lock();
if(lockfd == -1)
return -1;
fp = fopen(fusermnt, "r");
fp = setmntent(mtab, "r");
if(fp == NULL) {
fprintf(stderr, "%s: could not open %s for reading: %s\n", progname,
fusermnt, strerror(errno));
fusermnt_unlock(lockfd);
return -1;
}
newfp = fopen(fusermnt_temp, "w");
if(newfp == NULL) {
fprintf(stderr, "%s: could not open %s for writing: %s\n", progname,
fusermnt_temp, strerror(errno));
fclose(fp);
fusermnt_unlock(lockfd);
return -1;
fprintf(stderr, "%s failed to open %s: %s\n", progname, mtab,
strerror(errno));
return -1;
}
found = 0;
while(fgets(buf, sizeof(buf), fp) != NULL) {
char *end = buf + strlen(buf) - 1;
char *p;
if(*end != '\n') {
fprintf(stderr, "%s: line too long in file %s\n", progname,
fusermnt);
while(fgets(buf, sizeof(buf), fp) != NULL) {
char *end = buf + strlen(buf) - 1;
if(*end == '\n')
break;
}
continue;
}
*end = '\0';
newfp = setmntent(mtab_new, "w");
if(newfp == NULL) {
fprintf(stderr, "%s failed to open %s: %s\n", progname, mtab_new,
strerror(errno));
return -1;
}
if(getuid() != 0) {
user = get_user_name();
if(user == NULL)
return -1;
}
for(p = buf; *p != '\0' && *p != ' '; p++);
if(*p == '\0') {
fprintf(stderr, "%s: malformed line in file %s\n", progname,
fusermnt);
continue;
found = 0;
while((entp = getmntent(fp)) != NULL) {
int remove = 0;
if(!found && strcmp(entp->mnt_dir, mnt) == 0 &&
strcmp(entp->mnt_type, "fuse") == 0) {
if(user == NULL)
remove = 1;
else {
char *p = strstr(entp->mnt_opts, "user=");
if(p != NULL && strcmp(p + 5, user) == 0)
remove = 1;
}
}
*p = '\0';
p++;
if(!found && strcmp(user, buf) == 0 && strcmp(mnt, p) == 0) {
int res = umount(mnt);
if(remove) {
res = umount(mnt);
if(res == -1) {
found = -1;
fprintf(stderr, "%s: umount of %s failed: %s\n", progname,
fprintf(stderr, "%s: failed to unmount %s: %s\n", progname,
mnt, strerror(errno));
found = -1;
break;
}
found = 1;
}
else
fprintf(newfp, "%s %s\n", buf, p);
else {
res = addmntent(newfp, entp);
if(res != 0) {
fprintf(stderr, "%s: failed to add entry to %s: %s", progname,
mtab_new, strerror(errno));
}
}
}
fclose(fp);
fclose(newfp);
endmntent(fp);
endmntent(newfp);
if(found == 1) {
int res;
res = rename(fusermnt_temp, fusermnt);
res = rename(mtab_new, mtab);
if(res == -1) {
fprintf(stderr, "%s: failed to rename %s to %s: %s\n",
progname, fusermnt_temp, fusermnt, strerror(errno));
fusermnt_unlock(lockfd);
fprintf(stderr, "%s: failed to rename %s to %s: %s\n", progname,
mtab_new, mtab, strerror(errno));
return -1;
}
}
else {
if(!found)
fprintf(stderr, "%s: entry for %s not found in %s\n", progname,
mnt, fusermnt);
unlink(fusermnt_temp);
fusermnt_unlock(lockfd);
mnt, mtab);
unlink(mtab_new);
return -1;
}
fusermnt_unlock(lockfd);
return 0;
}
@ -347,6 +338,19 @@ static int mount_fuse(const char *mnt)
return -1;
fd = open(dev, O_RDWR);
if(fd == -1) {
int status;
pid_t pid = fork();
if(pid == 0) {
setuid(0);
execl("/sbin/modprobe", "modprobe", "fuse", NULL);
exit(1);
}
if(pid != -1)
waitpid(pid, &status, 0);
fd = open(dev, O_RDWR);
}
if(fd == -1) {
fprintf(stderr, "%s: unable to open fuse device %s: %s\n", progname,
dev, strerror(errno));
@ -357,7 +361,7 @@ static int mount_fuse(const char *mnt)
if(res == -1)
return -1;
res = add_mount(mnt);
res = add_mount(dev, mnt, type);
if(res == -1) {
umount(mnt);
return -1;
@ -366,16 +370,22 @@ static int mount_fuse(const char *mnt)
return fd;
}
static int do_umount(const char *mnt)
static char *resolve_path(const char *orig, int unmount)
{
int res;
char buf[PATH_MAX];
res = remove_mount(mnt);
if(res == -1)
return -1;
/* Resolving at unmount can only be done very carefully, not touching
the mountpoint... So for the moment it's not done. */
if(unmount)
return strdup(orig);
umount(mnt);
return 0;
if(realpath(orig, buf) == NULL) {
fprintf(stderr, "%s: Bad mount point %s: %s\n", progname, orig,
strerror(errno));
return NULL;
}
return strdup(buf);
}
static void usage()
@ -384,7 +394,7 @@ static void usage()
"%s: [options] mountpoint [program [args ...]]\n"
"Options:\n"
" -h print help\n"
" -u umount\n",
" -u unmount\n",
progname);
exit(1);
}
@ -394,11 +404,14 @@ int main(int argc, char *argv[])
int a;
int fd;
int res;
char *mnt = NULL;
int umount = 0;
char *origmnt;
char *mnt;
int unmount = 0;
char **userprog;
int numargs;
char **newargv;
char mypath[PATH_MAX];
char *unmount_cmd;
progname = argv[0];
@ -412,7 +425,7 @@ int main(int argc, char *argv[])
break;
case 'u':
umount = 1;
unmount = 1;
break;
default:
@ -426,10 +439,20 @@ int main(int argc, char *argv[])
exit(1);
}
mnt = argv[a++];
origmnt = argv[a++];
if(getpid() != 0)
drop_privs();
mnt = resolve_path(origmnt, unmount);
if(mnt == NULL)
exit(1);
if(getpid() != 0)
restore_privs();
if(umount) {
res = do_umount(mnt);
if(unmount) {
res = remove_mount(mnt);
if(res == -1)
exit(1);
@ -454,23 +477,36 @@ int main(int argc, char *argv[])
close(fd);
}
/* Strangely this doesn't work after dropping permissions... */
res = readlink("/proc/self/exe", mypath, sizeof(mypath) - 1);
if(res == -1) {
fprintf(stderr, "%s: failed to determine self path: %s\n",
progname, strerror(errno));
strcpy(mypath, "fusermount");
fprintf(stderr, "using %s as the default\n", mypath);
}
else
mypath[res] = '\0';
/* Drop setuid/setgid permissions */
setuid(getuid());
setgid(getgid());
unmount_cmd = (char *) malloc(strlen(mypath) + strlen(mnt) + 64);
sprintf(unmount_cmd, "%s -u %s", mypath, mnt);
newargv = (char **) malloc(sizeof(char *) * (numargs + 2));
newargv[0] = userprog[0];
newargv[1] = mnt;
newargv[1] = unmount_cmd;
for(a = 1; a < numargs; a++)
newargv[a+1] = userprog[a];
newargv[numargs+1] = NULL;
execv(userprog[0], newargv);
execvp(userprog[0], newargv);
fprintf(stderr, "%s: failed to exec %s: %s\n", progname, userprog[0],
strerror(errno));
execl("/proc/self/exe", progname, "-u", mnt, NULL);
fprintf(stderr, "%s: failed to exec self: %s\n", progname,
strerror(errno));
exit(1);
close(0);
system(unmount_cmd);
return 1;
}