VFS mountpoint expiry
Subsystems (2)
PATCH 1/1 VFS mountpoint expiry
Hi Linus, Al,
Here's a patch to do mountpoint expiry along the lines suggested by Al.
Mountpoint expiry can be driven in two ways:
(1) From userspace using umount(<path>,MNT_EXPIRE).
If the mountpoint is in use, then -EBUSY is returned.
If not already set, this sets a flag in the vfsmount structure and returns -EAGAIN.
If the flag is already set, the unmount is actually effected and 0 is returned.
Note that mixing MNT_EXPIRE with MNT_FORCE or MNT_DETACH will incur a -EINVAL error.
The flag is cleared should mntput() decrease the vfsmount's mnt_count to one.
(2) A filesystem can call mark_mounts_for_expiry() with a list of vfsmounts that should be checked. Such mounts are linked together through a new list_head (mnt_fslink) in the vfsmount structure. The head of this list should be retained by the module, and should only be accessed whilst holding the dcache_lock.
This function performs a similar action on every vfsmount in the list to that performed by umount with MNT_EXPIRE.
With regard to mnt_fslink, should a namespace be cloned every vfsmount that is on a such list will be cloned as normal and added to the same list whilst the dcache_lock is held.
Should a "listed" vfsmount be moved, then it will be removed from the list. Should a bind be performed on a vfsmount, then the resulting new vfsmount will not be added to the list.
vfsmounts to go on this list can be obtained by playing tricks with a follow_link() method attached to a directory - this method can then call do_kern_mount() to obtain a new vfsmount, and then call do_add_mount() to mount the vfsmount _and_ add it to the module's expiry list under the appropriate locking.
This more or less permits a filesystem to do automounting at arbitrary points within a tree without worrying about namespace issues.
Note that mnt_flags should now be accessed atomically with set_bit() and co.
David
fs/block_dev.c +1 -1
@@ -762,7 +762,7 @@if (!S_ISBLK (inode->i_mode)) goto fail; error = -EACCES ; -if (nd.mnt->mnt_flags &MNT_NODEV ) +if (test_bit (__MNT_NODEV, &nd.mnt->mnt_flags)) goto fail; error =bd_acquire (inode);if (error)
fs/exec.c +2 -2
@@ -459,7 +459,7 @@if (!err) {struct inode *inode = nd.dentry->d_inode; file =ERR_PTR (-EACCES ); -if (!(nd.mnt->mnt_flags &MNT_NOEXEC ) && +if (!test_bit (__MNT_NOEXEC, &nd.mnt->mnt_flags) &&S_ISREG (inode->i_mode)) {int err =permission (inode,MAY_EXEC );if (!err && !(inode->i_mode &0111 ))
@@ -841,7 +841,7 @@ bprm->e_uid = current->euid; bprm->e_gid = current->egid; -if (!(bprm->file->f_vfsmnt->mnt_flags &MNT_NOSUID )) { +if (!test_bit (__MNT_NOSUID, &bprm->file->f_vfsmnt->mnt_flags)) {/* Set-uid? */ if (mode &S_ISUID ) bprm->e_uid = inode->i_uid;
fs/namei.c +7 -1
@@ -269,6 +269,12 @@mntput (nd->mnt); } +void path_release_on_umount (struct nameidata *nd) +{ +dput (nd->dentry); +_mntput (nd->mnt); +} +/* * Internal lookup() using the new generic dcache. * SMP-safe
@@ -1147,7 +1153,7 @@if (S_ISFIFO (inode->i_mode) ||S_ISSOCK (inode->i_mode)) { flag &= ~O_TRUNC ; }else if (S_ISBLK (inode->i_mode) ||S_ISCHR (inode->i_mode)) { -if (nd->mnt->mnt_flags &MNT_NODEV ) +if (test_bit (__MNT_NODEV, &nd->mnt->mnt_flags))return -EACCES ; flag &= ~O_TRUNC ;
fs/namespace.c +173 -35
329 lines not on this page · raw message
fs/super.c +4 -0
@@ -21,6 +21,7 @@*/ #include <linux/config.h> +#include <linux/module.h> #include <linux/slab.h> #include <linux/smp_lock.h> #include <linux/acct.h>
@@ -683,6 +684,7 @@ mnt->mnt_root =dget (sb->s_root); mnt->mnt_mountpoint = sb->s_root; mnt->mnt_parent = mnt; + mnt->mnt_namespace = current->namespace;up_write (&sb->s_umount);put_filesystem (type);return mnt;
@@ -697,6 +699,8 @@return (struct vfsmount *)sb; } +EXPORT_SYMBOL_GPL (do_kern_mount); +struct vfsmount *kern_mount (struct file_system_type *type) {return do_kern_mount (type->name,0 , type->name,NULL );
include/linux/fs.h +1 -0
@@ -572,6 +572,7 @@#define MNT_FORCE 0x00000001/* Attempt to forcibily umount */ #define MNT_DETACH 0x00000002/* Just detach from the tree */ +#define MNT_EXPIRE 0x00000004/* Mark for expiry */ extern struct list_head super_blocks;extern spinlock_t sb_lock;
include/linux/mount.h +27 -5
@@ -14,9 +14,15 @@#include <linux/list.h> -#define MNT_NOSUID 1 -#define MNT_NODEV 2 -#define MNT_NOEXEC 4 +#define __MNT_NOSUID 0 +#define __MNT_NODEV 1 +#define __MNT_NOEXEC 2 +#define __MNT_EXPIRY_MARK 3 + +#define MNT_NOSUID (1 << __MNT_NOSUID) +#define MNT_NODEV (1 << __MNT_NODEV) +#define MNT_NOEXEC (1 << __MNT_NOEXEC) +#define MNT_EXPIRY_MARK (1 << __MNT_EXPIRY_MARK)struct vfsmount {
@@ -28,9 +34,11 @@struct list_head mnt_mounts;/* list of children, anchored here */ struct list_head mnt_child;/* and going through their mnt_child */ atomic_t mnt_count; -int mnt_flags; +unsigned long mnt_flags;char *mnt_devname;/* Name of device e.g. /dev/dsk/hda1 */ struct list_head mnt_list; +struct list_head mnt_fslink;/* link in fs-specific list */ +struct namespace *mnt_namespace;/* containing namespace */ };static inline struct vfsmount *mntget (struct vfsmount *mnt)
@@ -42,7 +50,7 @@extern void __mntput (struct vfsmount *mnt); -static inline void mntput (struct vfsmount *mnt) +static inline void _mntput (struct vfsmount *mnt) {if (mnt) {if (atomic_dec_and_test (&mnt->mnt_count))
@@ -50,10 +58,24 @@ } } +static inline void mntput (struct vfsmount *mnt) +{ +if (mnt) { +if (atomic_read (&mnt->mnt_count) ==2 ) +clear_bit (__MNT_EXPIRY_MARK, &mnt->mnt_flags); +_mntput (mnt); + } +} +extern void free_vfsmnt (struct vfsmount *mnt);extern struct vfsmount *alloc_vfsmnt (const char *name);extern struct vfsmount *do_kern_mount (const char *fstype,int flags,const char *name,void *data); +extern int do_add_mount (struct vfsmount *newmnt,struct nameidata *nd, +unsigned long mnt_flags,struct list_head *fslist); + +extern void mark_mounts_for_expiry (struct list_head *mounts); +#endif #endif /* _LINUX_MOUNT_H */
include/linux/namei.h +1 -0
@@ -42,6 +42,7 @@extern int FASTCALL (path_walk (const char *,struct nameidata *));extern int FASTCALL (link_path_walk (const char *,struct nameidata *));extern void path_release (struct nameidata *); +extern void path_release_on_umount (struct nameidata *);extern struct dentry *lookup_one_len (const char *,struct dentry *,int );extern struct dentry *lookup_hash (struct qstr *,struct dentry *);
include/linux/namespace.h +4 -8
@@ -15,16 +15,12 @@extern void umount_tree (struct vfsmount *);extern int copy_namespace (int ,struct task_struct *); +extern void __put_namespace (struct namespace *namespace); +static inline void put_namespace (struct namespace *namespace) { -if (atomic_dec_and_test (&namespace->count)) { -down_write (&namespace->sem); -spin_lock (&dcache_lock); -umount_tree (namespace->root); -spin_unlock (&dcache_lock); -up_write (&namespace->sem); -kfree (namespace); - } +if (atomic_dec_and_test (&namespace->count)) +__put_namespace (namespace); }static inline void exit_namespace (struct task_struct *p)