From 69cc79ad513988f2cf1a2ebc72a0327c6d043161 Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Tue, 17 Feb 2004 08:57:29 +0000 Subject: [PATCH] support for user mounts --- ChangeLog | 9 + kernel/dev.c | 2 +- kernel/dir.c | 3 +- kernel/inode.c | 11 +- patch/user-mount.2.6.2-rc3.patch | 403 +++++++++++++++++++++++++++++++ util/fusermount.c | 31 ++- 6 files changed, 444 insertions(+), 15 deletions(-) create mode 100644 patch/user-mount.2.6.2-rc3.patch diff --git a/ChangeLog b/ChangeLog index 4174e41..e9fa924 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,12 @@ +2004-02-17 Miklos Szeredi + + * Added user-mount.2.6.2-rc3.patch + + * Add FS_SAFE flag to fuse filesystem + + * fusermount should allow (un)mounting for non-root even if not + suid-root + 2004-02-12 Miklos Szeredi * Remove MS_PERMISSION mount flag (that means something else now) diff --git a/kernel/dev.c b/kernel/dev.c index daa464e..a31615d 100644 --- a/kernel/dev.c +++ b/kernel/dev.c @@ -632,7 +632,7 @@ int fuse_dev_init() } proc_fs_fuse->owner = THIS_MODULE; - proc_fuse_dev = create_proc_entry("dev", S_IFSOCK | 0600, proc_fs_fuse); + proc_fuse_dev = create_proc_entry("dev", S_IFSOCK | 0666, proc_fs_fuse); if(!proc_fuse_dev) { printk("fuse: failed to create entry in /proc/fs/fuse\n"); goto err; diff --git a/kernel/dir.c b/kernel/dir.c index 0fffcab..5fec669 100644 --- a/kernel/dir.c +++ b/kernel/dir.c @@ -409,8 +409,7 @@ static int _fuse_permission(struct inode *inode, int mask) keeping it open... */ return err; - } - else + } else return 0; } diff --git a/kernel/inode.c b/kernel/inode.c index d896965..257b41d 100644 --- a/kernel/inode.c +++ b/kernel/inode.c @@ -23,6 +23,10 @@ #define kstatfs statfs #endif +#ifndef FS_SAFE +#define FS_SAFE 0 +#endif + static void fuse_read_inode(struct inode *inode) { /* No op */ @@ -193,6 +197,11 @@ static int fuse_read_super(struct super_block *sb, void *data, int silent) struct inode *root; struct fuse_mount_data *d = data; + if(!capable(CAP_SYS_ADMIN)) { + if(d->flags & FUSE_ALLOW_OTHER) + return -EPERM; + } + sb->s_blocksize = PAGE_CACHE_SIZE; sb->s_blocksize_bits = PAGE_CACHE_SHIFT; sb->s_magic = FUSE_SUPER_MAGIC; @@ -246,7 +255,7 @@ static struct file_system_type fuse_fs_type = { .name = "fuse", .get_sb = fuse_get_sb, .kill_sb = kill_anon_super, - .fs_flags = 0 + .fs_flags = FS_SAFE, }; #else static struct super_block *fuse_read_super_compat(struct super_block *sb, diff --git a/patch/user-mount.2.6.2-rc3.patch b/patch/user-mount.2.6.2-rc3.patch new file mode 100644 index 0000000..2f02e5a --- /dev/null +++ b/patch/user-mount.2.6.2-rc3.patch @@ -0,0 +1,403 @@ +diff -ru linux-2.6.2-rc3.orig/fs/filesystems.c linux-2.6.2-rc3/fs/filesystems.c +--- linux-2.6.2-rc3.orig/fs/filesystems.c 2003-12-18 03:59:18.000000000 +0100 ++++ linux-2.6.2-rc3/fs/filesystems.c 2004-02-16 13:03:45.000000000 +0100 +@@ -222,7 +222,8 @@ + if (fs && !try_module_get(fs->owner)) + fs = NULL; + read_unlock(&file_systems_lock); +- if (!fs && (request_module("%s", name) == 0)) { ++ if (!fs && capable(CAP_SYS_ADMIN) && ++ (request_module("%s", name) == 0)) { + read_lock(&file_systems_lock); + fs = *(find_filesystem(name)); + if (fs && !try_module_get(fs->owner)) +diff -ru linux-2.6.2-rc3.orig/fs/namespace.c linux-2.6.2-rc3/fs/namespace.c +--- linux-2.6.2-rc3.orig/fs/namespace.c 2004-02-16 13:17:00.000000000 +0100 ++++ linux-2.6.2-rc3/fs/namespace.c 2004-02-16 13:07:35.000000000 +0100 +@@ -25,13 +25,16 @@ + + extern int __init init_rootfs(void); + extern int __init sysfs_init(void); ++extern void put_filesystem(struct file_system_type *fs); ++ ++#define MAX_MOUNTS 256 + + /* spinlock for vfsmount related operations, inplace of dcache_lock */ + spinlock_t vfsmount_lock __cacheline_aligned_in_smp = SPIN_LOCK_UNLOCKED; + static struct list_head *mount_hashtable; + static int hash_mask, hash_bits; + static kmem_cache_t *mnt_cache; +- ++struct mounts_stat_struct mounts_stat = { .max_mounts = MAX_MOUNTS }; + static inline unsigned long hash(struct vfsmount *mnt, struct dentry *dentry) + { + unsigned long tmp = ((unsigned long) mnt / L1_CACHE_BYTES); +@@ -40,10 +43,38 @@ + return tmp & hash_mask; + } + ++static inline int inc_nr_mounts(void) ++{ ++ int err = 0; ++ spin_lock(&vfsmount_lock); ++ if (capable(CAP_SYS_ADMIN) || ++ mounts_stat.nr_mounts < mounts_stat.max_mounts) ++ mounts_stat.nr_mounts++; ++ else ++ err = mounts_stat.max_mounts ? -EMFILE : -EPERM; ++ spin_unlock(&vfsmount_lock); ++ return err; ++} ++ ++static inline void dec_nr_mounts(void) ++{ ++ spin_lock(&vfsmount_lock); ++ mounts_stat.nr_mounts--; ++ spin_unlock(&vfsmount_lock); ++} ++ + struct vfsmount *alloc_vfsmnt(const char *name) + { +- struct vfsmount *mnt = kmem_cache_alloc(mnt_cache, GFP_KERNEL); +- if (mnt) { ++ struct vfsmount *mnt; ++ int err = inc_nr_mounts(); ++ if (err) ++ return ERR_PTR(err); ++ ++ mnt = kmem_cache_alloc(mnt_cache, GFP_KERNEL); ++ if (!mnt) { ++ dec_nr_mounts(); ++ return ERR_PTR(-ENOMEM); ++ } else { + memset(mnt, 0, sizeof(struct vfsmount)); + atomic_set(&mnt->mnt_count,1); + INIT_LIST_HEAD(&mnt->mnt_hash); +@@ -66,6 +97,7 @@ + { + kfree(mnt->mnt_devname); + kmem_cache_free(mnt_cache, mnt); ++ dec_nr_mounts(); + } + + /* +@@ -147,13 +179,14 @@ + struct super_block *sb = old->mnt_sb; + struct vfsmount *mnt = alloc_vfsmnt(old->mnt_devname); + +- if (mnt) { ++ if (!IS_ERR(mnt)) { + mnt->mnt_flags = old->mnt_flags; + atomic_inc(&sb->s_active); + mnt->mnt_sb = sb; + mnt->mnt_root = dget(root); + mnt->mnt_mountpoint = mnt->mnt_root; + mnt->mnt_parent = mnt; ++ mnt->user = capable(CAP_SYS_ADMIN) ? 0 : current->fsuid; + } + return mnt; + } +@@ -238,6 +271,8 @@ + if (mnt->mnt_flags & fs_infop->flag) + seq_puts(m, fs_infop->str); + } ++ if (mnt->user) ++ seq_printf(m, ",user=%i", mnt->user); + if (mnt->mnt_sb->s_op->show_options) + err = mnt->mnt_sb->s_op->show_options(m, mnt); + seq_puts(m, " 0 0\n"); +@@ -388,8 +423,10 @@ + goto dput_and_out; + + retval = -EPERM; +- if (!capable(CAP_SYS_ADMIN)) +- goto dput_and_out; ++ if (!capable(CAP_SYS_ADMIN)) { ++ if(nd.mnt->user != current->fsuid || (flags & MNT_FORCE)) ++ goto dput_and_out; ++ } + + retval = do_umount(nd.mnt, flags); + dput_and_out: +@@ -409,20 +446,15 @@ + + static int mount_is_safe(struct nameidata *nd) + { +- if (capable(CAP_SYS_ADMIN)) +- return 0; +- return -EPERM; +-#ifdef notyet +- if (S_ISLNK(nd->dentry->d_inode->i_mode)) +- return -EPERM; +- if (nd->dentry->d_inode->i_mode & S_ISVTX) { +- if (current->uid != nd->dentry->d_inode->i_uid) ++ if (!capable(CAP_SYS_ADMIN)) { ++ if (!S_ISDIR(nd->dentry->d_inode->i_mode) && ++ !S_ISREG(nd->dentry->d_inode->i_mode)) ++ return -EPERM; ++ if (current->fsuid != nd->dentry->d_inode->i_uid || ++ permission(nd->dentry->d_inode, MAY_WRITE, nd)) + return -EPERM; + } +- if (permission(nd->dentry->d_inode, MAY_WRITE, nd)) +- return -EPERM; + return 0; +-#endif + } + + static int +@@ -444,8 +476,8 @@ + struct nameidata nd; + + res = q = clone_mnt(mnt, dentry); +- if (!q) +- goto Enomem; ++ if (IS_ERR(q)) ++ goto out_error; + q->mnt_mountpoint = mnt->mnt_mountpoint; + + p = mnt; +@@ -463,8 +495,8 @@ + nd.mnt = q; + nd.dentry = p->mnt_mountpoint; + q = clone_mnt(p, p->mnt_root); +- if (!q) +- goto Enomem; ++ if (IS_ERR(q)) ++ goto out_error; + spin_lock(&vfsmount_lock); + list_add_tail(&q->mnt_list, &res->mnt_list); + attach_mnt(q, &nd); +@@ -472,13 +504,13 @@ + } + } + return res; +- Enomem: ++ out_error: + if (res) { + spin_lock(&vfsmount_lock); + umount_tree(res); + spin_unlock(&vfsmount_lock); + } +- return NULL; ++ return q; + } + + static int graft_tree(struct vfsmount *mnt, struct nameidata *nd) +@@ -538,11 +570,14 @@ + down_write(¤t->namespace->sem); + err = -EINVAL; + if (check_mnt(nd->mnt) && (!recurse || check_mnt(old_nd.mnt))) { +- err = -ENOMEM; + if (recurse) + mnt = copy_tree(old_nd.mnt, old_nd.dentry); + else + mnt = clone_mnt(old_nd.mnt, old_nd.dentry); ++ if (IS_ERR(mnt)) { ++ err = PTR_ERR(mnt); ++ goto out; ++ } + } + + if (mnt) { +@@ -555,6 +590,7 @@ + mntput(mnt); + } + ++ out: + up_write(¤t->namespace->sem); + path_release(&old_nd); + return err; +@@ -654,14 +690,28 @@ + int mnt_flags, char *name, void *data) + { + struct vfsmount *mnt; +- int err; ++ int err = mount_is_safe(nd); ++ if(err) ++ return err; + + if (!type || !memchr(type, 0, PAGE_SIZE)) + return -EINVAL; + + /* we need capabilities... */ +- if (!capable(CAP_SYS_ADMIN)) +- return -EPERM; ++ if (!capable(CAP_SYS_ADMIN)) { ++ /* but allow "safe" filesystems anyway */ ++ int issafe = 0; ++ struct file_system_type *t = get_fs_type(type); ++ if(t) { ++ issafe = t->fs_flags & FS_SAFE; ++ put_filesystem(t); ++ } ++ if(!issafe) ++ return -EPERM; ++ ++ /* users should not have suid or dev files */ ++ mnt_flags |= (MNT_NOSUID | MNT_NODEV); ++ } + + mnt = do_kern_mount(type, flags, name, data); + err = PTR_ERR(mnt); +@@ -797,6 +847,7 @@ + struct namespace *new_ns; + struct vfsmount *rootmnt = NULL, *pwdmnt = NULL, *altrootmnt = NULL; + struct fs_struct *fs = tsk->fs; ++ int err; + + if (!namespace) + return 0; +@@ -806,11 +857,7 @@ + if (!(flags & CLONE_NEWNS)) + return 0; + +- if (!capable(CAP_SYS_ADMIN)) { +- put_namespace(namespace); +- return -EPERM; +- } +- ++ err = -ENOMEM; + new_ns = kmalloc(sizeof(struct namespace), GFP_KERNEL); + if (!new_ns) + goto out; +@@ -822,7 +869,8 @@ + down_write(&tsk->namespace->sem); + /* First pass: copy the tree topology */ + new_ns->root = copy_tree(namespace->root, namespace->root->mnt_root); +- if (!new_ns->root) { ++ if (IS_ERR(new_ns->root)) { ++ err = PTR_ERR(new_ns->root); + up_write(&tsk->namespace->sem); + kfree(new_ns); + goto out; +@@ -872,7 +920,7 @@ + + out: + put_namespace(namespace); +- return -ENOMEM; ++ return err; + } + + asmlinkage long sys_mount(char __user * dev_name, char __user * dir_name, +diff -ru linux-2.6.2-rc3.orig/fs/super.c linux-2.6.2-rc3/fs/super.c +--- linux-2.6.2-rc3.orig/fs/super.c 2003-12-18 03:58:48.000000000 +0100 ++++ linux-2.6.2-rc3/fs/super.c 2004-02-16 13:03:45.000000000 +0100 +@@ -705,7 +705,7 @@ + do_kern_mount(const char *fstype, int flags, const char *name, void *data) + { + struct file_system_type *type = get_fs_type(fstype); +- struct super_block *sb = ERR_PTR(-ENOMEM); ++ struct super_block *sb; + struct vfsmount *mnt; + int error; + +@@ -713,9 +713,11 @@ + return ERR_PTR(-ENODEV); + + mnt = alloc_vfsmnt(name); +- if (!mnt) ++ error = PTR_ERR(mnt); ++ if (IS_ERR(mnt)) + goto out; + sb = type->get_sb(type, flags, name, data); ++ error = PTR_ERR(sb); + if (IS_ERR(sb)) + goto out_mnt; + error = security_sb_kern_mount(sb); +@@ -725,18 +727,18 @@ + mnt->mnt_root = dget(sb->s_root); + mnt->mnt_mountpoint = sb->s_root; + mnt->mnt_parent = mnt; ++ mnt->user = capable(CAP_SYS_ADMIN) ? 0 : current->fsuid; + up_write(&sb->s_umount); + put_filesystem(type); + return mnt; + out_sb: + up_write(&sb->s_umount); + deactivate_super(sb); +- sb = ERR_PTR(error); + out_mnt: + free_vfsmnt(mnt); + out: + put_filesystem(type); +- return (struct vfsmount *)sb; ++ return ERR_PTR(error); + } + + struct vfsmount *kern_mount(struct file_system_type *type) +diff -ru linux-2.6.2-rc3.orig/include/linux/fs.h linux-2.6.2-rc3/include/linux/fs.h +--- linux-2.6.2-rc3.orig/include/linux/fs.h 2004-02-04 11:03:38.000000000 +0100 ++++ linux-2.6.2-rc3/include/linux/fs.h 2004-02-16 13:03:45.000000000 +0100 +@@ -55,6 +55,12 @@ + }; + extern struct files_stat_struct files_stat; + ++struct mounts_stat_struct { ++ int nr_mounts; ++ int max_mounts; ++}; ++extern struct mounts_stat_struct mounts_stat; ++ + struct inodes_stat_t { + int nr_inodes; + int nr_unused; +@@ -89,6 +95,7 @@ + + /* public flags for file_system_type */ + #define FS_REQUIRES_DEV 1 ++#define FS_SAFE 2 /* Safe to mount by user */ + #define FS_REVAL_DOT 16384 /* Check the paths ".", ".." for staleness */ + #define FS_ODD_RENAME 32768 /* Temporary stuff; will go away as soon + * as nfs_rename() will be cleaned up +diff -ru linux-2.6.2-rc3.orig/include/linux/mount.h linux-2.6.2-rc3/include/linux/mount.h +--- linux-2.6.2-rc3.orig/include/linux/mount.h 2003-12-18 03:58:08.000000000 +0100 ++++ linux-2.6.2-rc3/include/linux/mount.h 2004-02-16 13:03:45.000000000 +0100 +@@ -30,6 +30,7 @@ + atomic_t mnt_count; + int mnt_flags; + char *mnt_devname; /* Name of device e.g. /dev/dsk/hda1 */ ++ uid_t user; + struct list_head mnt_list; + }; + +diff -ru linux-2.6.2-rc3.orig/include/linux/sysctl.h linux-2.6.2-rc3/include/linux/sysctl.h +--- linux-2.6.2-rc3.orig/include/linux/sysctl.h 2004-02-16 13:17:03.000000000 +0100 ++++ linux-2.6.2-rc3/include/linux/sysctl.h 2004-02-16 13:03:45.000000000 +0100 +@@ -608,8 +608,8 @@ + FS_NRFILE=6, /* int:current number of allocated filedescriptors */ + FS_MAXFILE=7, /* int:maximum number of filedescriptors that can be allocated */ + FS_DENTRY=8, +- FS_NRSUPER=9, /* int:current number of allocated super_blocks */ +- FS_MAXSUPER=10, /* int:maximum number of super_blocks that can be allocated */ ++ FS_NRMOUNT=9, /* int:current number of mounts */ ++ FS_MAXMOUNT=10, /* int:maximum number of mounts allowed */ + FS_OVERFLOWUID=11, /* int: overflow UID */ + FS_OVERFLOWGID=12, /* int: overflow GID */ + FS_LEASES=13, /* int: leases enabled */ +diff -ru linux-2.6.2-rc3.orig/kernel/sysctl.c linux-2.6.2-rc3/kernel/sysctl.c +--- linux-2.6.2-rc3.orig/kernel/sysctl.c 2004-02-16 13:17:04.000000000 +0100 ++++ linux-2.6.2-rc3/kernel/sysctl.c 2004-02-16 13:03:45.000000000 +0100 +@@ -763,6 +763,22 @@ + .proc_handler = &proc_dointvec, + }, + { ++ .ctl_name = FS_NRMOUNT, ++ .procname = "mount-nr", ++ .data = &mounts_stat.nr_mounts, ++ .maxlen = sizeof(int), ++ .mode = 0444, ++ .proc_handler = &proc_dointvec, ++ }, ++ { ++ .ctl_name = FS_MAXMOUNT, ++ .procname = "mount-max", ++ .data = &mounts_stat.max_mounts, ++ .maxlen = sizeof(int), ++ .mode = 0644, ++ .proc_handler = &proc_dointvec, ++ }, ++ { + .ctl_name = FS_OVERFLOWUID, + .procname = "overflowuid", + .data = &fs_overflowuid, diff --git a/util/fusermount.c b/util/fusermount.c index 3d27219..ee4c005 100644 --- a/util/fusermount.c +++ b/util/fusermount.c @@ -396,13 +396,15 @@ static int mount_fuse(const char *mnt, int flags, const char *fsname) res = do_mount(fsname, mnt, type, stbuf.st_mode & S_IFMT, fd, flags); if(res == -1) return -1; - - mtablock = lock_mtab(); - res = add_mount(fsname, mnt, type); - unlock_mtab(mtablock); - if(res == -1) { - umount(mnt); - return -1; + + if(geteuid() == 0) { + mtablock = lock_mtab(); + res = add_mount(fsname, mnt, type); + unlock_mtab(mtablock); + if(res == -1) { + umount(mnt); + return -1; + } } return fd; @@ -580,12 +582,19 @@ int main(int argc, char *argv[]) restore_privs(); if(unmount) { - int mtablock = lock_mtab(); - res = remove_mount(mnt, quiet); - unlock_mtab(mtablock); + if(geteuid() == 0) { + int mtablock = lock_mtab(); + res = remove_mount(mnt, quiet); + unlock_mtab(mtablock); + } else { + res = umount2(mnt, 2); + if(res == -1) { + fprintf(stderr, "%s: failed to unmount %s: %s\n", progname, mnt, + strerror(errno)); + } + } if(res == -1) exit(1); - return 0; } -- 2.30.2