VFS autmounter support v2
PATCH 1/1 VFS autmounter support v2
Hi Linus, Al,
I've revised my patch to make sure a process in one namespace doesn't change the topology of another namespace (kern_automount() will return an error in that case, as does (u)mount). As a bonus, check_mnt() has been simplified to take account of the namespace pointer now in vfsmount.
I've also fixes it so that if an automounted subtree is touched with "mount --bind" or "mount --move", then the base of that tree has its expiry timeout cleared, thus making it a permanent fixture (until manually unmounted).
David
fs/namei.c +32 -15
@@ -434,23 +434,35 @@return 1 ; } -static int follow_mount (struct vfsmount **mnt,struct dentry **dentry) +static int follow_mount (struct vfsmount **mnt,struct dentry **dentry,unsigned int flags) {int res =0 ; -while (d_mountpoint (*dentry)) { -struct vfsmount *mounted; -spin_lock (&dcache_lock); - mounted =lookup_mnt (*mnt, *dentry); -if (!mounted) { +for (;;) { + +if (d_mountpoint (*dentry)) { +struct vfsmount *mounted; +spin_lock (&dcache_lock); + mounted =lookup_mnt (*mnt, *dentry); +if (!mounted) { +spin_unlock (&dcache_lock); +break ; + } + *mnt =mntget (mounted);spin_unlock (&dcache_lock); +dput (*dentry); +mntput (mounted->mnt_parent); + *dentry =dget (mounted->mnt_root); + res =1 ; + + }else if (d_automount_point (*dentry)) { +if (flags &LOOKUP_NOAUTOMOUNT ) +break ; + res =kern_automount (*mnt, *dentry); +if (res <0 ) +break ; + }else {break ; } - *mnt =mntget (mounted); -spin_unlock (&dcache_lock); -dput (*dentry); -mntput (mounted->mnt_parent); - *dentry =dget (mounted->mnt_root); - res =1 ; }return res; }
@@ -510,7 +522,7 @@mntput (*mnt); *mnt = parent; } -follow_mount (mnt, dentry); +follow_mount (mnt, dentry,0 ); }struct path {
@@ -643,7 +655,9 @@if (err)break ;/* Check mountpoints.. */ -follow_mount (&next.mnt, &next.dentry); + err =follow_mount (&next.mnt, &next.dentry,0 ); +if (err <0 ) + goto out_dput; err = -ENOENT ; inode = next.dentry->d_inode;
@@ -703,7 +717,10 @@ err =do_lookup (nd, &this, &next,0 );if (err)break ; -follow_mount (&next.mnt, &next.dentry); + err =follow_mount (&next.mnt, &next.dentry, nd->flags &LOOKUP_NOAUTOMOUNT ); +if (err <0 ) + goto out_dput; + inode = next.dentry->d_inode;if ((lookup_flags &LOOKUP_FOLLOW ) && inode && inode->i_op && inode->i_op->follow_link) {
fs/namespace.c +152 -6
222 lines not on this page · raw message
fs/stat.c +2 -2
@@ -61,7 +61,7 @@struct nameidata nd;int error; - error =user_path_walk (name, &nd); + error =user_path_walk_stat (name, &nd);if (!error) { error =vfs_getattr (nd.mnt, nd.dentry, stat);path_release (&nd);
@@ -74,7 +74,7 @@struct nameidata nd;int error; - error =user_path_walk_link (name, &nd); + error =user_path_walk_link_stat (name, &nd);if (!error) { error =vfs_getattr (nd.mnt, nd.dentry, stat);path_release (&nd);
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/dcache.h +6 -0
@@ -112,6 +112,7 @@int (*d_delete)(struct dentry *);void (*d_release)(struct dentry *);void (*d_iput)(struct dentry *,struct inode *); +struct vfsmount *(*d_automount)(struct dentry *); };/* the dentry parameter passed to d_hash and d_compare is the parent
@@ -307,6 +308,11 @@ return dentry->d_mounted; } +static inline int d_automount_point (struct dentry *dentry) +{ +return dentry->d_op && dentry->d_op->d_automount; +} +extern struct vfsmount *lookup_mnt (struct vfsmount *,struct dentry *);#endif /* __KERNEL__ */
include/linux/mount.h +7 -0
@@ -31,6 +31,9 @@int mnt_flags;char *mnt_devname;/* Name of device e.g. /dev/dsk/hda1 */ struct list_head mnt_list; +struct namespace *mnt_namespace;/* containing namespace */ +time_t mnt_expires_at;/* time at which automount expires */ +unsigned mnt_expiry_timeout;/* expiry timeout (in seconds) or 0 */ };static inline struct vfsmount *mntget (struct vfsmount *mnt)
@@ -40,11 +43,15 @@return mnt; } +extern void kern_automount_begin_expiry (struct vfsmount *mnt);extern void __mntput (struct vfsmount *mnt);static inline void mntput (struct vfsmount *mnt) {if (mnt) { +if (atomic_read (&mnt->mnt_count) ==2 && + mnt->mnt_expiry_timeout) +kern_automount_begin_expiry (mnt);if (atomic_dec_and_test (&mnt->mnt_count))__mntput (mnt); }
include/linux/namei.h +7 -0
@@ -31,6 +31,7 @@#define LOOKUP_CONTINUE 4#define LOOKUP_PARENT 16#define LOOKUP_NOALT 32 +#define LOOKUP_NOAUTOMOUNT 64extern int FASTCALL (__user_walk (const char __user *,unsigned ,struct nameidata *));
@@ -38,6 +39,10 @@ __user_walk(name, LOOKUP_FOLLOW, nd)#define user_path_walk_link (name,nd) \ __user_walk(name, 0, nd) +#define user_path_walk_stat (name,nd) \ + __user_walk(name, LOOKUP_FOLLOW|LOOKUP_NOAUTOMOUNT, nd) +#define user_path_walk_link_stat (name,nd) \ + __user_walk(name, LOOKUP_NOAUTOMOUNT, nd)extern int FASTCALL (path_lookup (const char *,unsigned ,struct nameidata *));extern int FASTCALL (path_walk (const char *,struct nameidata *));extern int FASTCALL (link_path_walk (const char *,struct nameidata *));
@@ -52,4 +57,6 @@extern struct dentry *lock_rename (struct dentry *,struct dentry *);extern void unlock_rename (struct dentry *,struct dentry *); +extern int kern_automount (struct vfsmount *mnt,struct dentry *dentry); +#endif /* _LINUX_NAMEI_H */
include/linux/namespace.h +1 -0
@@ -10,6 +10,7 @@struct vfsmount * root;struct list_head list;struct rw_semaphore sem; +struct work_struct mnt_expiry_work; };extern void umount_tree (struct vfsmount *);
Quoted 7 lines · David Howells
Hi Linus, Al,
I've revised my patch to make sure a process in one namespace doesn't change the topology of another namespace (
kern_automount()will return an error in that case, as does (u)mount). As a bonus,check_mnt()has been simplified to take account of the namespace pointer now in vfsmount.
You _still_ don't get it. OK, the last time: kern_automount() will
always do the same thing, no matter which namespace we are it. It might be OK for AFS, but it's definitely unfit for any other use.
No amount of "use of (u)mount to rearrange topology" will help here - with your code you have dentry marked, and stepping on it (in any namespace, in any instance of that fs in a namespace) will always do the same thing. And that is Wrong(tm).
Quoted 9 lines
Quoted 5 lines
I've revised my patch to make sure a process in one namespace doesn't change the topology of another namespace (
kern_automount()will return an error in that case, as does (u)mount). As a bonus,check_mnt()has been simplified to take account of the namespace pointer now in vfsmount.You _still_ don't get it. OK, the last time:
kern_automount()will
always do the same thing, no matter which namespace we are it. It might be OK for AFS, but it's definitely unfit for any other use.
Well, I disagree. I think it should do the same thing (but with results limited to the namespace of whatever triggered the automount) until the administrator discards that portion of the tree (umount -l will work there). But I don't think I'm going to get anywhere arguing about it with you.
And, furthermore, I think autofs could be reimplemented in simpler form using it (and still call out to userspace), but that's just my opinion.
Quoted 4 lines
No amount of "use of (u)mount to rearrange topology" will help here - with your code you have dentry marked, and stepping on it (in any namespace, in any instance of that fs in a namespace) will always do the same thing. And that is Wrong(tm).
Again, I disagree. In my opinion an automount point should still work correctly in a cloned namespace, until the filesystem that is "proposing" it is unmounted.
If I mount an automount-capable fs on a directory, and then create a shell in a derivative namespace, I would _expect_ the automounting to still _work_ until at such point I unmounted that automount-capable fs.
Besides, going back to your original argument, I think you make an invalid assertion:
Namespaces are completely unrelated - I have them set for two different users that happen to need some common files, but otherwise have very different environments.
They are not quite completely unrelated. The only way (or so it seems) to get a new namespace is to derive one by way of CLONE_NS. This clones the namespace of the parent, and so the child's namespace then should have all the features of the parent's namespace _UNTIL_ at such time the child or one of its descendents rearranges the topology!
If the two users need very different environments, then they'll change their namespace to suit themselves - thus making my point.
Creating a new namespace does _not_ automatically confer a complete new topology of an independent predetermined design, nor does it generate a totally empty namespace.
But, barring that, you are correct... changes to one namespace do not impinge on any other (/proc not withstanding). My patch honours this. Tripping an automount point in one namespace does not change another namespace.
| I have two namespaces. One of them has filesystem A mounted on | /usr/include. Another - on /usr/local/include. The first one wants | /usr/include/foo1 trigger mounting B and /usr/include/foo2 trigger mounting | C. The second one wants /usr/local/include/foo1 trigger mounting D and | /usr/local/include/foo2 not trigger anything.
Firstly, if one of these topologies has been derived from the other, then one or both of them have been rearranged since the point of divergence. You have implied that.
Secondly, if "filesystem A" provides those automount points, then it is a different filesystem in each case, but obviously you're not thinking along those lines.
What you appear to be thinking of is that some independent third party (autofs daemon, administrator or whatever) has glued on arbitrary automounting triggers (perhaps by mounting them).
I think this can make use of my suggested change... If you make a trigger fs that has its root directory simply an automount point (though this leads me to think that perhaps (u)mount should follow the example of stat and use LOOKUP_NOAUTOMOUNT).
Perhaps:
[namespace X]
mount /dev/hda8 /usr/include
mount -t trap "mount-fs-B" /usr/include/foo1 mount -t trap "mount-fs-C" /usr/include/foo2
[namespace Y]
mount /dev/hda8 /usr/local/include
mount -t trap "mount-fs-D" /usr/local/include/foo1
Then have a daemon that can take a request to mount and then reply with the mount parameters, allowing the trap fs to obtain a vfsmount via do_kern_mount(). I would make the trap fs supply the daemon with an fd attached to the trap rootdir to act as a token representing the request (and controlling its lifetime).
David
Quoted 7 lines · David Howells
Besides, going back to your original argument, I think you make an invalid assertion:
Namespaces are completely unrelated - I have them set for two different users that happen to need some common files, but otherwise have very different environments.
Quoted 5 lines
They are not quite completely unrelated. The only way (or so it seems) to get a new namespace is to derive one by way of
CLONE_NS. This clones the namespace of the parent, and so the child's namespace then should have all the features of the parent's namespace _UNTIL_ at such time the child or one of its descendents rearranges the topology!
And how the fuck do you "rearrange topology" with your patch?!?