diff -uNr linux-2.5.70/fs/namei.c linux-2.5.70-auto/fs/namei.c --- linux-2.5.70/fs/namei.c 2003-06-12 11:35:52.000000000 +0100 +++ linux-2.5.70-auto/fs/namei.c 2003-06-12 15:46:15.000000000 +0100 @@ -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) { diff -uNr linux-2.5.70/fs/namespace.c linux-2.5.70-auto/fs/namespace.c --- linux-2.5.70/fs/namespace.c 2003-06-12 11:35:52.000000000 +0100 +++ linux-2.5.70-auto/fs/namespace.c 2003-06-12 15:07:00.000000000 +0100 @@ -674,6 +674,43 @@ return err; } +int kern_automount(struct vfsmount *on_mnt, struct dentry *on_dentry) +{ + struct nameidata nd; + struct vfsmount *mnt; + int err; + + if (!on_dentry->d_inode || !S_ISDIR(on_dentry->d_inode->i_mode)) + return -ENOTDIR; + + mnt = on_dentry->d_op->d_automount(on_dentry); + if (IS_ERR(mnt)) + return PTR_ERR(mnt); + + memset(&nd,0,sizeof(nd)); + nd.dentry = on_dentry; + nd.mnt = on_mnt; + + down_write(¤t->namespace->sem); + /* Something was mounted here while we slept */ + while(d_mountpoint(nd.dentry) && follow_down(&nd.mnt, &nd.dentry)) + ; + err = -EINVAL; + if (!check_mnt(nd.mnt)) + goto unlock; + + /* Refuse the same filesystem on the same mount point */ + err = -EBUSY; + if (nd.mnt->mnt_sb == mnt->mnt_sb && nd.mnt->mnt_root == nd.dentry) + goto unlock; + + err = graft_tree(mnt, &nd); +unlock: + up_write(¤t->namespace->sem); + mntput(mnt); + return err; +} + static int copy_mount_options (const void __user *data, unsigned long *where) { int i; diff -uNr linux-2.5.70/fs/stat.c linux-2.5.70-auto/fs/stat.c --- linux-2.5.70/fs/stat.c 2003-06-12 11:35:52.000000000 +0100 +++ linux-2.5.70-auto/fs/stat.c 2003-06-12 14:16:29.000000000 +0100 @@ -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); diff -uNr linux-2.5.70/fs/super.c linux-2.5.70-auto/fs/super.c --- linux-2.5.70/fs/super.c 2003-06-12 11:35:53.000000000 +0100 +++ linux-2.5.70-auto/fs/super.c 2003-06-12 16:15:06.000000000 +0100 @@ -21,6 +21,7 @@ */ #include +#include #include #include #include @@ -697,6 +698,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); diff -uNr linux-2.5.70/include/linux/dcache.h linux-2.5.70-auto/include/linux/dcache.h --- linux-2.5.70/include/linux/dcache.h 2003-06-12 11:32:25.000000000 +0100 +++ linux-2.5.70-auto/include/linux/dcache.h 2003-06-12 14:57:27.000000000 +0100 @@ -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 @@ -305,6 +306,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__ */ diff -uNr linux-2.5.70/include/linux/namei.h linux-2.5.70-auto/include/linux/namei.h --- linux-2.5.70/include/linux/namei.h 2003-06-12 11:32:24.000000000 +0100 +++ linux-2.5.70-auto/include/linux/namei.h 2003-06-12 15:02:19.000000000 +0100 @@ -31,6 +31,7 @@ #define LOOKUP_CONTINUE 4 #define LOOKUP_PARENT 16 #define LOOKUP_NOALT 32 +#define LOOKUP_NOAUTOMOUNT 64 extern 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 */