Skip to contentSkip to conversation

VFS mountpoint expiry

PATCH New David Howells 1 participant 1 message Jul 4, 2003

Subsystems (2)
lore
Share & tools
lore
#0 lore raw

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
diff -ur linux-2.5.74/fs/block_dev.c linux-2.5.74-auto/fs/block_dev.c
@@ -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
diff -ur linux-2.5.74/fs/exec.c linux-2.5.74-auto/fs/exec.c
@@ -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
diff -ur linux-2.5.74/fs/namei.c linux-2.5.74-auto/fs/namei.c
@@ -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
diff -ur linux-2.5.74/fs/super.c linux-2.5.74-auto/fs/super.c
@@ -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
diff -ur linux-2.5.74/include/linux/fs.h linux-2.5.74-auto/include/linux/fs.h
@@ -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
diff -ur linux-2.5.74/include/linux/mount.h linux-2.5.74-auto/include/linux/mount.h
@@ -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
diff -ur linux-2.5.74/include/linux/namei.h linux-2.5.74-auto/include/linux/namei.h
@@ -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
diff -ur linux-2.5.74/include/linux/namespace.h linux-2.5.74-auto/include/linux/namespace.h
@@ -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)