Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs-2.6
Linus Torvalds [Sun, 16 Jan 2011 19:31:50 +0000 (11:31 -0800)]
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs-2.6: (23 commits)
  sanitize vfsmount refcounting changes
  fix old umount_tree() breakage
  autofs4: Merge the remaining dentry ops tables
  Unexport do_add_mount() and add in follow_automount(), not ->d_automount()
  Allow d_manage() to be used in RCU-walk mode
  Remove a further kludge from __do_follow_link()
  autofs4: Bump version
  autofs4: Add v4 pseudo direct mount support
  autofs4: Fix wait validation
  autofs4: Clean up autofs4_free_ino()
  autofs4: Clean up dentry operations
  autofs4: Clean up inode operations
  autofs4: Remove unused code
  autofs4: Add d_manage() dentry operation
  autofs4: Add d_automount() dentry operation
  Remove the automount through follow_link() kludge code from pathwalk
  CIFS: Use d_automount() rather than abusing follow_link()
  NFS: Use d_automount() rather than abusing follow_link()
  AFS: Use d_automount() rather than abusing follow_link()
  Add an AT_NO_AUTOMOUNT flag to suppress terminal automount
  ...

40 files changed:
Documentation/filesystems/Locking
Documentation/filesystems/vfs.txt
drivers/mtd/mtdchar.c
drivers/staging/autofs/dirhash.c
fs/afs/dir.c
fs/afs/inode.c
fs/afs/internal.h
fs/afs/mntpt.c
fs/anon_inodes.c
fs/autofs4/autofs_i.h
fs/autofs4/dev-ioctl.c
fs/autofs4/expire.c
fs/autofs4/inode.c
fs/autofs4/root.c
fs/autofs4/waitq.c
fs/cifs/cifs_dfs_ref.c
fs/cifs/cifsfs.h
fs/cifs/dir.c
fs/cifs/inode.c
fs/dcache.c
fs/fs_struct.c
fs/internal.h
fs/namei.c
fs/namespace.c
fs/nfs/dir.c
fs/nfs/inode.c
fs/nfs/internal.h
fs/nfs/namespace.c
fs/nfsd/vfs.c
fs/pipe.c
fs/stat.c
fs/super.c
include/linux/auto_fs4.h
include/linux/dcache.h
include/linux/fcntl.h
include/linux/fs.h
include/linux/mount.h
include/linux/namei.h
include/linux/nfs_fs.h
include/linux/path.h

index ef9349a..651d523 100644 (file)
@@ -19,6 +19,8 @@ prototypes:
        void (*d_release)(struct dentry *);
        void (*d_iput)(struct dentry *, struct inode *);
        char *(*d_dname)((struct dentry *dentry, char *buffer, int buflen);
+       struct vfsmount *(*d_automount)(struct path *path);
+       int (*d_manage)(struct dentry *, bool);
 
 locking rules:
                rename_lock     ->d_lock        may block       rcu-walk
@@ -29,6 +31,8 @@ d_delete:     no              yes             no              no
 d_release:     no              no              yes             no
 d_iput:                no              no              yes             no
 d_dname:       no              no              no              no
+d_automount:   no              no              yes             no
+d_manage:      no              no              yes (ref-walk)  maybe
 
 --------------------------- inode_operations --------------------------- 
 prototypes:
index cae6d27..94cf97b 100644 (file)
@@ -864,6 +864,8 @@ struct dentry_operations {
        void (*d_release)(struct dentry *);
        void (*d_iput)(struct dentry *, struct inode *);
        char *(*d_dname)(struct dentry *, char *, int);
+       struct vfsmount *(*d_automount)(struct path *);
+       int (*d_manage)(struct dentry *, bool, bool);
 };
 
   d_revalidate: called when the VFS needs to revalidate a dentry. This
@@ -930,6 +932,47 @@ struct dentry_operations {
        at the end of the buffer, and returns a pointer to the first char.
        dynamic_dname() helper function is provided to take care of this.
 
+  d_automount: called when an automount dentry is to be traversed (optional).
+       This should create a new VFS mount record and return the record to the
+       caller.  The caller is supplied with a path parameter giving the
+       automount directory to describe the automount target and the parent
+       VFS mount record to provide inheritable mount parameters.  NULL should
+       be returned if someone else managed to make the automount first.  If
+       the vfsmount creation failed, then an error code should be returned.
+       If -EISDIR is returned, then the directory will be treated as an
+       ordinary directory and returned to pathwalk to continue walking.
+
+       If a vfsmount is returned, the caller will attempt to mount it on the
+       mountpoint and will remove the vfsmount from its expiration list in
+       the case of failure.  The vfsmount should be returned with 2 refs on
+       it to prevent automatic expiration - the caller will clean up the
+       additional ref.
+
+       This function is only used if DCACHE_NEED_AUTOMOUNT is set on the
+       dentry.  This is set by __d_instantiate() if S_AUTOMOUNT is set on the
+       inode being added.
+
+  d_manage: called to allow the filesystem to manage the transition from a
+       dentry (optional).  This allows autofs, for example, to hold up clients
+       waiting to explore behind a 'mountpoint' whilst letting the daemon go
+       past and construct the subtree there.  0 should be returned to let the
+       calling process continue.  -EISDIR can be returned to tell pathwalk to
+       use this directory as an ordinary directory and to ignore anything
+       mounted on it and not to check the automount flag.  Any other error
+       code will abort pathwalk completely.
+
+       If the 'mounting_here' parameter is true, then namespace_sem is being
+       held by the caller and the function should not initiate any mounts or
+       unmounts that it will then wait for.
+
+       If the 'rcu_walk' parameter is true, then the caller is doing a
+       pathwalk in RCU-walk mode.  Sleeping is not permitted in this mode,
+       and the caller can be asked to leave it and call again by returing
+       -ECHILD.
+
+       This function is only used if DCACHE_MANAGE_TRANSIT is set on the
+       dentry being transited from.
+
 Example :
 
 static char *pipefs_dname(struct dentry *dent, char *buffer, int buflen)
index ee4bb33..9824057 100644 (file)
@@ -1201,7 +1201,7 @@ err_unregister_chdev:
 static void __exit cleanup_mtdchar(void)
 {
        unregister_mtd_user(&mtdchar_notifier);
-       mntput_long(mtd_inode_mnt);
+       mntput(mtd_inode_mnt);
        unregister_filesystem(&mtd_inodefs_type);
        __unregister_chrdev(MTD_CHAR_MAJOR, 0, 1 << MINORBITS, "mtd");
 }
index d3f42c8..a08bd73 100644 (file)
@@ -88,14 +88,13 @@ struct autofs_dir_ent *autofs_expire(struct super_block *sb,
                }
                path.mnt = mnt;
                path_get(&path);
-               if (!follow_down(&path)) {
+               if (!follow_down_one(&path)) {
                        path_put(&path);
                        DPRINTK(("autofs: not expirable\
                        (not a mounted directory): %s\n", ent->name));
                        continue;
                }
-               while (d_mountpoint(path.dentry) && follow_down(&path))
-                       ;
+               follow_down(&path, false);  // TODO: need to check error
                umount_ok = may_umount(path.mnt);
                path_put(&path);
 
index e6a4ab9..20c106f 100644 (file)
@@ -66,6 +66,7 @@ const struct dentry_operations afs_fs_dentry_operations = {
        .d_revalidate   = afs_d_revalidate,
        .d_delete       = afs_d_delete,
        .d_release      = afs_d_release,
+       .d_automount    = afs_d_automount,
 };
 
 #define AFS_DIR_HASHTBL_SIZE   128
index 0747339..db66c52 100644 (file)
@@ -184,7 +184,8 @@ struct inode *afs_iget_autocell(struct inode *dir, const char *dev_name,
        inode->i_generation     = 0;
 
        set_bit(AFS_VNODE_PSEUDODIR, &vnode->flags);
-       inode->i_flags |= S_NOATIME;
+       set_bit(AFS_VNODE_MOUNTPOINT, &vnode->flags);
+       inode->i_flags |= S_AUTOMOUNT | S_NOATIME;
        unlock_new_inode(inode);
        _leave(" = %p", inode);
        return inode;
index 58c633b..5a9b684 100644 (file)
@@ -592,6 +592,7 @@ extern const struct inode_operations afs_mntpt_inode_operations;
 extern const struct inode_operations afs_autocell_inode_operations;
 extern const struct file_operations afs_mntpt_file_operations;
 
+extern struct vfsmount *afs_d_automount(struct path *);
 extern int afs_mntpt_check_symlink(struct afs_vnode *, struct key *);
 extern void afs_mntpt_kill_timer(void);
 
index e83c033..aa59184 100644 (file)
@@ -24,7 +24,6 @@ static struct dentry *afs_mntpt_lookup(struct inode *dir,
                                       struct dentry *dentry,
                                       struct nameidata *nd);
 static int afs_mntpt_open(struct inode *inode, struct file *file);
-static void *afs_mntpt_follow_link(struct dentry *dentry, struct nameidata *nd);
 static void afs_mntpt_expiry_timed_out(struct work_struct *work);
 
 const struct file_operations afs_mntpt_file_operations = {
@@ -34,13 +33,11 @@ const struct file_operations afs_mntpt_file_operations = {
 
 const struct inode_operations afs_mntpt_inode_operations = {
        .lookup         = afs_mntpt_lookup,
-       .follow_link    = afs_mntpt_follow_link,
        .readlink       = page_readlink,
        .getattr        = afs_getattr,
 };
 
 const struct inode_operations afs_autocell_inode_operations = {
-       .follow_link    = afs_mntpt_follow_link,
        .getattr        = afs_getattr,
 };
 
@@ -88,6 +85,7 @@ int afs_mntpt_check_symlink(struct afs_vnode *vnode, struct key *key)
                _debug("symlink is a mountpoint");
                spin_lock(&vnode->lock);
                set_bit(AFS_VNODE_MOUNTPOINT, &vnode->flags);
+               vnode->vfs_inode.i_flags |= S_AUTOMOUNT;
                spin_unlock(&vnode->lock);
        }
 
@@ -238,52 +236,24 @@ error_no_devname:
 }
 
 /*
- * follow a link from a mountpoint directory, thus causing it to be mounted
+ * handle an automount point
  */
-static void *afs_mntpt_follow_link(struct dentry *dentry, struct nameidata *nd)
+struct vfsmount *afs_d_automount(struct path *path)
 {
        struct vfsmount *newmnt;
-       int err;
 
-       _enter("%p{%s},{%s:%p{%s},}",
-              dentry,
-              dentry->d_name.name,
-              nd->path.mnt->mnt_devname,
-              dentry,
-              nd->path.dentry->d_name.name);
-
-       dput(nd->path.dentry);
-       nd->path.dentry = dget(dentry);
-
-       newmnt = afs_mntpt_do_automount(nd->path.dentry);
-       if (IS_ERR(newmnt)) {
-               path_put(&nd->path);
-               return (void *)newmnt;
-       }
+       _enter("{%s,%s}", path->mnt->mnt_devname, path->dentry->d_name.name);
 
-       mntget(newmnt);
-       err = do_add_mount(newmnt, &nd->path, MNT_SHRINKABLE, &afs_vfsmounts);
-       switch (err) {
-       case 0:
-               path_put(&nd->path);
-               nd->path.mnt = newmnt;
-               nd->path.dentry = dget(newmnt->mnt_root);
-               queue_delayed_work(afs_wq, &afs_mntpt_expiry_timer,
-                                  afs_mntpt_expiry_timeout * HZ);
-               break;
-       case -EBUSY:
-               /* someone else made a mount here whilst we were busy */
-               while (d_mountpoint(nd->path.dentry) &&
-                      follow_down(&nd->path))
-                       ;
-               err = 0;
-       default:
-               mntput(newmnt);
-               break;
-       }
+       newmnt = afs_mntpt_do_automount(path->dentry);
+       if (IS_ERR(newmnt))
+               return newmnt;
 
-       _leave(" = %d", err);
-       return ERR_PTR(err);
+       mntget(newmnt); /* prevent immediate expiration */
+       mnt_set_expiry(newmnt, &afs_vfsmounts);
+       queue_delayed_work(afs_wq, &afs_mntpt_expiry_timer,
+                          afs_mntpt_expiry_timeout * HZ);
+       _leave(" = %p {%s}", newmnt, newmnt->mnt_devname);
+       return newmnt;
 }
 
 /*
index cbe57f3..c5567cb 100644 (file)
@@ -233,7 +233,7 @@ static int __init anon_inode_init(void)
        return 0;
 
 err_mntput:
-       mntput_long(anon_inode_mnt);
+       mntput(anon_inode_mnt);
 err_unregister_filesystem:
        unregister_filesystem(&anon_inode_fs_type);
 err_exit:
index 0fffe1c..1f016bf 100644 (file)
@@ -99,7 +99,6 @@ struct autofs_info {
 };
 
 #define AUTOFS_INF_EXPIRING    (1<<0) /* dentry is in the process of expiring */
-#define AUTOFS_INF_MOUNTPOINT  (1<<1) /* mountpoint status for direct expire */
 #define AUTOFS_INF_PENDING     (1<<2) /* dentry pending mount */
 
 struct autofs_wait_queue {
@@ -176,13 +175,6 @@ static inline int autofs4_ispending(struct dentry *dentry)
        return 0;
 }
 
-static inline void autofs4_copy_atime(struct file *src, struct file *dst)
-{
-       dst->f_path.dentry->d_inode->i_atime =
-               src->f_path.dentry->d_inode->i_atime;
-       return;
-}
-
 struct inode *autofs4_get_inode(struct super_block *, struct autofs_info *);
 void autofs4_free_ino(struct autofs_info *);
 
@@ -212,11 +204,83 @@ void autofs_dev_ioctl_exit(void);
 
 extern const struct inode_operations autofs4_symlink_inode_operations;
 extern const struct inode_operations autofs4_dir_inode_operations;
-extern const struct inode_operations autofs4_root_inode_operations;
-extern const struct inode_operations autofs4_indirect_root_inode_operations;
-extern const struct inode_operations autofs4_direct_root_inode_operations;
 extern const struct file_operations autofs4_dir_operations;
 extern const struct file_operations autofs4_root_operations;
+extern const struct dentry_operations autofs4_dentry_operations;
+
+/* VFS automount flags management functions */
+
+static inline void __managed_dentry_set_automount(struct dentry *dentry)
+{
+       dentry->d_flags |= DCACHE_NEED_AUTOMOUNT;
+}
+
+static inline void managed_dentry_set_automount(struct dentry *dentry)
+{
+       spin_lock(&dentry->d_lock);
+       __managed_dentry_set_automount(dentry);
+       spin_unlock(&dentry->d_lock);
+}
+
+static inline void __managed_dentry_clear_automount(struct dentry *dentry)
+{
+       dentry->d_flags &= ~DCACHE_NEED_AUTOMOUNT;
+}
+
+static inline void managed_dentry_clear_automount(struct dentry *dentry)
+{
+       spin_lock(&dentry->d_lock);
+       __managed_dentry_clear_automount(dentry);
+       spin_unlock(&dentry->d_lock);
+}
+
+static inline void __managed_dentry_set_transit(struct dentry *dentry)
+{
+       dentry->d_flags |= DCACHE_MANAGE_TRANSIT;
+}
+
+static inline void managed_dentry_set_transit(struct dentry *dentry)
+{
+       spin_lock(&dentry->d_lock);
+       __managed_dentry_set_transit(dentry);
+       spin_unlock(&dentry->d_lock);
+}
+
+static inline void __managed_dentry_clear_transit(struct dentry *dentry)
+{
+       dentry->d_flags &= ~DCACHE_MANAGE_TRANSIT;
+}
+
+static inline void managed_dentry_clear_transit(struct dentry *dentry)
+{
+       spin_lock(&dentry->d_lock);
+       __managed_dentry_clear_transit(dentry);
+       spin_unlock(&dentry->d_lock);
+}
+
+static inline void __managed_dentry_set_managed(struct dentry *dentry)
+{
+       dentry->d_flags |= (DCACHE_NEED_AUTOMOUNT|DCACHE_MANAGE_TRANSIT);
+}
+
+static inline void managed_dentry_set_managed(struct dentry *dentry)
+{
+       spin_lock(&dentry->d_lock);
+       __managed_dentry_set_managed(dentry);
+       spin_unlock(&dentry->d_lock);
+}
+
+static inline void __managed_dentry_clear_managed(struct dentry *dentry)
+{
+       dentry->d_flags &= ~(DCACHE_NEED_AUTOMOUNT|DCACHE_MANAGE_TRANSIT);
+}
+
+static inline void managed_dentry_clear_managed(struct dentry *dentry)
+{
+       spin_lock(&dentry->d_lock);
+       __managed_dentry_clear_managed(dentry);
+       spin_unlock(&dentry->d_lock);
+}
 
 /* Initializing function */
 
@@ -229,19 +293,6 @@ int autofs4_wait(struct autofs_sb_info *,struct dentry *, enum autofs_notify);
 int autofs4_wait_release(struct autofs_sb_info *,autofs_wqt_t,int);
 void autofs4_catatonic_mode(struct autofs_sb_info *);
 
-static inline int autofs4_follow_mount(struct path *path)
-{
-       int res = 0;
-
-       while (d_mountpoint(path->dentry)) {
-               int followed = follow_down(path);
-               if (!followed)
-                       break;
-               res = 1;
-       }
-       return res;
-}
-
 static inline u32 autofs4_get_dev(struct autofs_sb_info *sbi)
 {
        return new_encode_dev(sbi->sb->s_dev);
index eff9a41..1442da4 100644 (file)
@@ -551,7 +551,7 @@ static int autofs_dev_ioctl_ismountpoint(struct file *fp,
 
                err = have_submounts(path.dentry);
 
-               if (follow_down(&path))
+               if (follow_down_one(&path))
                        magic = path.mnt->mnt_sb->s_magic;
        }
 
index cc1d013..3ed79d7 100644 (file)
@@ -26,10 +26,6 @@ static inline int autofs4_can_expire(struct dentry *dentry,
        if (ino == NULL)
                return 0;
 
-       /* No point expiring a pending mount */
-       if (ino->flags & AUTOFS_INF_PENDING)
-               return 0;
-
        if (!do_now) {
                /* Too young to die */
                if (!timeout || time_after(ino->last_used + timeout, now))
@@ -56,7 +52,7 @@ static int autofs4_mount_busy(struct vfsmount *mnt, struct dentry *dentry)
 
        path_get(&path);
 
-       if (!follow_down(&path))
+       if (!follow_down_one(&path))
                goto done;
 
        if (is_autofs4_dentry(path.dentry)) {
@@ -283,6 +279,7 @@ struct dentry *autofs4_expire_direct(struct super_block *sb,
        unsigned long timeout;
        struct dentry *root = dget(sb->s_root);
        int do_now = how & AUTOFS_EXP_IMMEDIATE;
+       struct autofs_info *ino;
 
        if (!root)
                return NULL;
@@ -291,19 +288,21 @@ struct dentry *autofs4_expire_direct(struct super_block *sb,
        timeout = sbi->exp_timeout;
 
        spin_lock(&sbi->fs_lock);
+       ino = autofs4_dentry_ino(root);
+       /* No point expiring a pending mount */
+       if (ino->flags & AUTOFS_INF_PENDING) {
+               spin_unlock(&sbi->fs_lock);
+               return NULL;
+       }
+       managed_dentry_set_transit(root);
        if (!autofs4_direct_busy(mnt, root, timeout, do_now)) {
                struct autofs_info *ino = autofs4_dentry_ino(root);
-               if (d_mountpoint(root)) {
-                       ino->flags |= AUTOFS_INF_MOUNTPOINT;
-                       spin_lock(&root->d_lock);
-                       root->d_flags &= ~DCACHE_MOUNTED;
-                       spin_unlock(&root->d_lock);
-               }
                ino->flags |= AUTOFS_INF_EXPIRING;
                init_completion(&ino->expire_complete);
                spin_unlock(&sbi->fs_lock);
                return root;
        }
+       managed_dentry_clear_transit(root);
        spin_unlock(&sbi->fs_lock);
        dput(root);
 
@@ -340,6 +339,10 @@ struct dentry *autofs4_expire_indirect(struct super_block *sb,
        while ((dentry = get_next_positive_dentry(dentry, root))) {
                spin_lock(&sbi->fs_lock);
                ino = autofs4_dentry_ino(dentry);
+               /* No point expiring a pending mount */
+               if (ino->flags & AUTOFS_INF_PENDING)
+                       goto cont;
+               managed_dentry_set_transit(dentry);
 
                /*
                 * Case 1: (i) indirect mount or top level pseudo direct mount
@@ -399,6 +402,8 @@ struct dentry *autofs4_expire_indirect(struct super_block *sb,
                        }
                }
 next:
+               managed_dentry_clear_transit(dentry);
+cont:
                spin_unlock(&sbi->fs_lock);
        }
        return NULL;
@@ -479,6 +484,8 @@ int autofs4_expire_run(struct super_block *sb,
        spin_lock(&sbi->fs_lock);
        ino = autofs4_dentry_ino(dentry);
        ino->flags &= ~AUTOFS_INF_EXPIRING;
+       if (!d_unhashed(dentry))
+               managed_dentry_clear_transit(dentry);
        complete_all(&ino->expire_complete);
        spin_unlock(&sbi->fs_lock);
 
@@ -504,18 +511,18 @@ int autofs4_do_expire_multi(struct super_block *sb, struct vfsmount *mnt,
                ret = autofs4_wait(sbi, dentry, NFY_EXPIRE);
 
                spin_lock(&sbi->fs_lock);
-               if (ino->flags & AUTOFS_INF_MOUNTPOINT) {
-                       spin_lock(&sb->s_root->d_lock);
-                       /*
-                        * If we haven't been expired away, then reset
-                        * mounted status.
-                        */
-                       if (mnt->mnt_parent != mnt)
-                               sb->s_root->d_flags |= DCACHE_MOUNTED;
-                       spin_unlock(&sb->s_root->d_lock);
-                       ino->flags &= ~AUTOFS_INF_MOUNTPOINT;
-               }
                ino->flags &= ~AUTOFS_INF_EXPIRING;
+               spin_lock(&dentry->d_lock);
+               if (ret)
+                       __managed_dentry_clear_transit(dentry);
+               else {
+                       if ((IS_ROOT(dentry) ||
+                           (autofs_type_indirect(sbi->type) &&
+                            IS_ROOT(dentry->d_parent))) &&
+                           !(dentry->d_flags & DCACHE_NEED_AUTOMOUNT))
+                               __managed_dentry_set_automount(dentry);
+               }
+               spin_unlock(&dentry->d_lock);
                complete_all(&ino->expire_complete);
                spin_unlock(&sbi->fs_lock);
                dput(dentry);
index a7bdb9d..9e1a9da 100644 (file)
@@ -45,7 +45,6 @@ struct autofs_info *autofs4_init_ino(struct autofs_info *ino,
 
        if (!reinit) {
                ino->flags = 0;
-               ino->inode = NULL;
                ino->dentry = NULL;
                ino->size = 0;
                INIT_LIST_HEAD(&ino->active);
@@ -76,19 +75,8 @@ struct autofs_info *autofs4_init_ino(struct autofs_info *ino,
 
 void autofs4_free_ino(struct autofs_info *ino)
 {
-       struct autofs_info *p_ino;
-
        if (ino->dentry) {
                ino->dentry->d_fsdata = NULL;
-               if (ino->dentry->d_inode) {
-                       struct dentry *parent = ino->dentry->d_parent;
-                       if (atomic_dec_and_test(&ino->count)) {
-                               p_ino = autofs4_dentry_ino(parent);
-                               if (p_ino && parent != ino->dentry)
-                                       atomic_dec(&p_ino->count);
-                       }
-                       dput(ino->dentry);
-               }
                ino->dentry = NULL;
        }
        if (ino->free)
@@ -251,10 +239,6 @@ static struct autofs_info *autofs4_mkroot(struct autofs_sb_info *sbi)
        return ino;
 }
 
-static const struct dentry_operations autofs4_sb_dentry_operations = {
-       .d_release      = autofs4_dentry_release,
-};
-
 int autofs4_fill_super(struct super_block *s, void *data, int silent)
 {
        struct inode * root_inode;
@@ -292,6 +276,7 @@ int autofs4_fill_super(struct super_block *s, void *data, int silent)
        s->s_blocksize_bits = 10;
        s->s_magic = AUTOFS_SUPER_MAGIC;
        s->s_op = &autofs4_sops;
+       s->s_d_op = &autofs4_dentry_operations;
        s->s_time_gran = 1;
 
        /*
@@ -309,7 +294,6 @@ int autofs4_fill_super(struct super_block *s, void *data, int silent)
                goto fail_iput;
        pipe = NULL;
 
-       d_set_d_op(root, &autofs4_sb_dentry_operations);
        root->d_fsdata = ino;
 
        /* Can this call block? */
@@ -320,10 +304,11 @@ int autofs4_fill_super(struct super_block *s, void *data, int silent)
                goto fail_dput;
        }
 
+       if (autofs_type_trigger(sbi->type))
+               __managed_dentry_set_managed(root);
+
        root_inode->i_fop = &autofs4_root_operations;
-       root_inode->i_op = autofs_type_trigger(sbi->type) ?
-                       &autofs4_direct_root_inode_operations :
-                       &autofs4_indirect_root_inode_operations;
+       root_inode->i_op = &autofs4_dir_inode_operations;
 
        /* Couldn't this be tested earlier? */
        if (sbi->max_proto < AUTOFS_MIN_PROTO_VERSION ||
@@ -391,7 +376,6 @@ struct inode *autofs4_get_inode(struct super_block *sb,
        if (inode == NULL)
                return NULL;
 
-       inf->inode = inode;
        inode->i_mode = inf->mode;
        if (sb->s_root) {
                inode->i_uid = sb->s_root->d_inode->i_uid;
index 651e4ef..1dba035 100644 (file)
@@ -35,10 +35,8 @@ static long autofs4_root_compat_ioctl(struct file *,unsigned int,unsigned long);
 #endif
 static int autofs4_dir_open(struct inode *inode, struct file *file);
 static struct dentry *autofs4_lookup(struct inode *,struct dentry *, struct nameidata *);
-static void *autofs4_follow_link(struct dentry *, struct nameidata *);
-
-#define TRIGGER_FLAGS   (LOOKUP_CONTINUE | LOOKUP_DIRECTORY)
-#define TRIGGER_INTENTS (LOOKUP_OPEN | LOOKUP_CREATE)
+static struct vfsmount *autofs4_d_automount(struct path *);
+static int autofs4_d_manage(struct dentry *, bool, bool);
 
 const struct file_operations autofs4_root_operations = {
        .open           = dcache_dir_open,
@@ -60,7 +58,7 @@ const struct file_operations autofs4_dir_operations = {
        .llseek         = dcache_dir_lseek,
 };
 
-const struct inode_operations autofs4_indirect_root_inode_operations = {
+const struct inode_operations autofs4_dir_inode_operations = {
        .lookup         = autofs4_lookup,
        .unlink         = autofs4_dir_unlink,
        .symlink        = autofs4_dir_symlink,
@@ -68,20 +66,10 @@ const struct inode_operations autofs4_indirect_root_inode_operations = {
        .rmdir          = autofs4_dir_rmdir,
 };
 
-const struct inode_operations autofs4_direct_root_inode_operations = {
-       .lookup         = autofs4_lookup,
-       .unlink         = autofs4_dir_unlink,
-       .mkdir          = autofs4_dir_mkdir,
-       .rmdir          = autofs4_dir_rmdir,
-       .follow_link    = autofs4_follow_link,
-};
-
-const struct inode_operations autofs4_dir_inode_operations = {
-       .lookup         = autofs4_lookup,
-       .unlink         = autofs4_dir_unlink,
-       .symlink        = autofs4_dir_symlink,
-       .mkdir          = autofs4_dir_mkdir,
-       .rmdir          = autofs4_dir_rmdir,
+const struct dentry_operations autofs4_dentry_operations = {
+       .d_automount    = autofs4_d_automount,
+       .d_manage       = autofs4_d_manage,
+       .d_release      = autofs4_dentry_release,
 };
 
 static void autofs4_add_active(struct dentry *dentry)
@@ -116,14 +104,6 @@ static void autofs4_del_active(struct dentry *dentry)
        return;
 }
 
-static unsigned int autofs4_need_mount(unsigned int flags)
-{
-       unsigned int res = 0;
-       if (flags & (TRIGGER_FLAGS | TRIGGER_INTENTS))
-               res = 1;
-       return res;
-}
-
 static int autofs4_dir_open(struct inode *inode, struct file *file)
 {
        struct dentry *dentry = file->f_path.dentry;
@@ -158,239 +138,6 @@ out:
        return dcache_dir_open(inode, file);
 }
 
-static int try_to_fill_dentry(struct dentry *dentry, int flags)
-{
-       struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb);
-       struct autofs_info *ino = autofs4_dentry_ino(dentry);
-       int status;
-
-       DPRINTK("dentry=%p %.*s ino=%p",
-                dentry, dentry->d_name.len, dentry->d_name.name, dentry->d_inode);
-
-       /*
-        * Wait for a pending mount, triggering one if there
-        * isn't one already
-        */
-       if (dentry->d_inode == NULL) {
-               DPRINTK("waiting for mount name=%.*s",
-                        dentry->d_name.len, dentry->d_name.name);
-
-               status = autofs4_wait(sbi, dentry, NFY_MOUNT);
-
-               DPRINTK("mount done status=%d", status);
-
-               /* Turn this into a real negative dentry? */
-               if (status == -ENOENT) {
-                       spin_lock(&sbi->fs_lock);
-                       ino->flags &= ~AUTOFS_INF_PENDING;
-                       spin_unlock(&sbi->fs_lock);
-                       return status;
-               } else if (status) {
-                       /* Return a negative dentry, but leave it "pending" */
-                       return status;
-               }
-       /* Trigger mount for path component or follow link */
-       } else if (ino->flags & AUTOFS_INF_PENDING ||
-                       autofs4_need_mount(flags)) {
-               DPRINTK("waiting for mount name=%.*s",
-                       dentry->d_name.len, dentry->d_name.name);
-
-               spin_lock(&sbi->fs_lock);
-               ino->flags |= AUTOFS_INF_PENDING;
-               spin_unlock(&sbi->fs_lock);
-               status = autofs4_wait(sbi, dentry, NFY_MOUNT);
-
-               DPRINTK("mount done status=%d", status);
-
-               if (status) {
-                       spin_lock(&sbi->fs_lock);
-                       ino->flags &= ~AUTOFS_INF_PENDING;
-                       spin_unlock(&sbi->fs_lock);
-                       return status;
-               }
-       }
-
-       /* Initialize expiry counter after successful mount */
-       ino->last_used = jiffies;
-
-       spin_lock(&sbi->fs_lock);
-       ino->flags &= ~AUTOFS_INF_PENDING;
-       spin_unlock(&sbi->fs_lock);
-
-       return 0;
-}
-
-/* For autofs direct mounts the follow link triggers the mount */
-static void *autofs4_follow_link(struct dentry *dentry, struct nameidata *nd)
-{
-       struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb);
-       struct autofs_info *ino = autofs4_dentry_ino(dentry);
-       int oz_mode = autofs4_oz_mode(sbi);
-       unsigned int lookup_type;
-       int status;
-
-       DPRINTK("dentry=%p %.*s oz_mode=%d nd->flags=%d",
-               dentry, dentry->d_name.len, dentry->d_name.name, oz_mode,
-               nd->flags);
-       /*
-        * For an expire of a covered direct or offset mount we need
-        * to break out of follow_down() at the autofs mount trigger
-        * (d_mounted--), so we can see the expiring flag, and manage
-        * the blocking and following here until the expire is completed.
-        */
-       if (oz_mode) {
-               spin_lock(&sbi->fs_lock);
-               if (ino->flags & AUTOFS_INF_EXPIRING) {
-                       spin_unlock(&sbi->fs_lock);
-                       /* Follow down to our covering mount. */
-                       if (!follow_down(&nd->path))
-                               goto done;
-                       goto follow;
-               }
-               spin_unlock(&sbi->fs_lock);
-               goto done;
-       }
-
-       /* If an expire request is pending everyone must wait. */
-       autofs4_expire_wait(dentry);
-
-       /* We trigger a mount for almost all flags */
-       lookup_type = autofs4_need_mount(nd->flags);
-       spin_lock(&sbi->fs_lock);
-       spin_lock(&autofs4_lock);
-       spin_lock(&dentry->d_lock);
-       if (!(lookup_type || ino->flags & AUTOFS_INF_PENDING)) {
-               spin_unlock(&dentry->d_lock);
-               spin_unlock(&autofs4_lock);
-               spin_unlock(&sbi->fs_lock);
-               goto follow;
-       }
-
-       /*
-        * If the dentry contains directories then it is an autofs
-        * multi-mount with no root mount offset. So don't try to
-        * mount it again.
-        */
-       if (ino->flags & AUTOFS_INF_PENDING ||
-           (!d_mountpoint(dentry) && list_empty(&dentry->d_subdirs))) {
-               spin_unlock(&dentry->d_lock);
-               spin_unlock(&autofs4_lock);
-               spin_unlock(&sbi->fs_lock);
-
-               status = try_to_fill_dentry(dentry, nd->flags);
-               if (status)
-                       goto out_error;
-
-               goto follow;
-       }
-       spin_unlock(&dentry->d_lock);
-       spin_unlock(&autofs4_lock);
-       spin_unlock(&sbi->fs_lock);
-follow:
-       /*
-        * If there is no root mount it must be an autofs
-        * multi-mount with no root offset so we don't need
-        * to follow it.
-        */
-       if (d_mountpoint(dentry)) {
-               if (!autofs4_follow_mount(&nd->path)) {
-                       status = -ENOENT;
-                       goto out_error;
-               }
-       }
-
-done:
-       return NULL;
-
-out_error:
-       path_put(&nd->path);
-       return ERR_PTR(status);
-}
-
-/*
- * Revalidate is called on every cache lookup.  Some of those
- * cache lookups may actually happen while the dentry is not
- * yet completely filled in, and revalidate has to delay such
- * lookups..
- */
-static int autofs4_revalidate(struct dentry *dentry, struct nameidata *nd)
-{
-       struct inode *dir;
-       struct autofs_sb_info *sbi;
-       int oz_mode;
-       int flags = nd ? nd->flags : 0;
-       int status = 1;
-
-       if (flags & LOOKUP_RCU)
-               return -ECHILD;
-
-       dir = dentry->d_parent->d_inode;
-       sbi = autofs4_sbi(dir->i_sb);
-       oz_mode = autofs4_oz_mode(sbi);
-
-       /* Pending dentry */
-       spin_lock(&sbi->fs_lock);
-       if (autofs4_ispending(dentry)) {
-               /* The daemon never causes a mount to trigger */
-               spin_unlock(&sbi->fs_lock);
-
-               if (oz_mode)
-                       return 1;
-
-               /*
-                * If the directory has gone away due to an expire
-                * we have been called as ->d_revalidate() and so
-                * we need to return false and proceed to ->lookup().
-                */
-               if (autofs4_expire_wait(dentry) == -EAGAIN)
-                       return 0;
-
-               /*
-                * A zero status is success otherwise we have a
-                * negative error code.
-                */
-               status = try_to_fill_dentry(dentry, flags);
-               if (status == 0)
-                       return 1;
-
-               return status;
-       }
-       spin_unlock(&sbi->fs_lock);
-
-       /* Negative dentry.. invalidate if "old" */
-       if (dentry->d_inode == NULL)
-               return 0;
-
-       /* Check for a non-mountpoint directory with no contents */
-       spin_lock(&autofs4_lock);
-       spin_lock(&dentry->d_lock);
-       if (S_ISDIR(dentry->d_inode->i_mode) &&
-           !d_mountpoint(dentry) && list_empty(&dentry->d_subdirs)) {
-               DPRINTK("dentry=%p %.*s, emptydir",
-                        dentry, dentry->d_name.len, dentry->d_name.name);
-               spin_unlock(&dentry->d_lock);
-               spin_unlock(&autofs4_lock);
-
-               /* The daemon never causes a mount to trigger */
-               if (oz_mode)
-                       return 1;
-
-               /*
-                * A zero status is success otherwise we have a
-                * negative error code.
-                */
-               status = try_to_fill_dentry(dentry, flags);
-               if (status == 0)
-                       return 1;
-
-               return status;
-       }
-       spin_unlock(&dentry->d_lock);
-       spin_unlock(&autofs4_lock);
-
-       return 1;
-}
-
 void autofs4_dentry_release(struct dentry *de)
 {
        struct autofs_info *inf;
@@ -398,11 +145,8 @@ void autofs4_dentry_release(struct dentry *de)
        DPRINTK("releasing %p", de);
 
        inf = autofs4_dentry_ino(de);
-       de->d_fsdata = NULL;
-
        if (inf) {
                struct autofs_sb_info *sbi = autofs4_sbi(de->d_sb);
-
                if (sbi) {
                        spin_lock(&sbi->lookup_lock);
                        if (!list_empty(&inf->active))
@@ -411,26 +155,10 @@ void autofs4_dentry_release(struct dentry *de)
                                list_del(&inf->expiring);
                        spin_unlock(&sbi->lookup_lock);
                }
-
-               inf->dentry = NULL;
-               inf->inode = NULL;
-
                autofs4_free_ino(inf);
        }
 }
 
-/* For dentries of directories in the root dir */
-static const struct dentry_operations autofs4_root_dentry_operations = {
-       .d_revalidate   = autofs4_revalidate,
-       .d_release      = autofs4_dentry_release,
-};
-
-/* For other dentries */
-static const struct dentry_operations autofs4_dentry_operations = {
-       .d_revalidate   = autofs4_revalidate,
-       .d_release      = autofs4_dentry_release,
-};
-
 static struct dentry *autofs4_lookup_active(struct dentry *dentry)
 {
        struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb);
@@ -541,50 +269,244 @@ next:
        return NULL;
 }
 
+static int autofs4_mount_wait(struct dentry *dentry)
+{
+       struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb);
+       struct autofs_info *ino = autofs4_dentry_ino(dentry);
+       int status;
+
+       if (ino->flags & AUTOFS_INF_PENDING) {
+               DPRINTK("waiting for mount name=%.*s",
+                       dentry->d_name.len, dentry->d_name.name);
+               status = autofs4_wait(sbi, dentry, NFY_MOUNT);
+               DPRINTK("mount wait done status=%d", status);
+               ino->last_used = jiffies;
+               return status;
+       }
+       return 0;
+}
+
+static int do_expire_wait(struct dentry *dentry)
+{
+       struct dentry *expiring;
+
+       expiring = autofs4_lookup_expiring(dentry);
+       if (!expiring)
+               return autofs4_expire_wait(dentry);
+       else {
+               /*
+                * If we are racing with expire the request might not
+                * be quite complete, but the directory has been removed
+                * so it must have been successful, just wait for it.
+                */
+               autofs4_expire_wait(expiring);
+               autofs4_del_expiring(expiring);
+               dput(expiring);
+       }
+       return 0;
+}
+
+static struct dentry *autofs4_mountpoint_changed(struct path *path)
+{
+       struct dentry *dentry = path->dentry;
+       struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb);
+
+       /*
+        * If this is an indirect mount the dentry could have gone away
+        * as a result of an expire and a new one created.
+        */
+       if (autofs_type_indirect(sbi->type) && d_unhashed(dentry)) {
+               struct dentry *parent = dentry->d_parent;
+               struct dentry *new = d_lookup(parent, &dentry->d_name);
+               if (!new)
+                       return NULL;
+               dput(path->dentry);
+               path->dentry = new;
+       }
+       return path->dentry;
+}
+
+static struct vfsmount *autofs4_d_automount(struct path *path)
+{
+       struct dentry *dentry = path->dentry;
+       struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb);
+       struct autofs_info *ino = autofs4_dentry_ino(dentry);
+       int status;
+
+       DPRINTK("dentry=%p %.*s",
+               dentry, dentry->d_name.len, dentry->d_name.name);
+
+       /*
+        * Someone may have manually umounted this or it was a submount
+        * that has gone away.
+        */
+       spin_lock(&dentry->d_lock);
+       if (!d_mountpoint(dentry) && list_empty(&dentry->d_subdirs)) {
+               if (!(dentry->d_flags & DCACHE_MANAGE_TRANSIT) &&
+                    (dentry->d_flags & DCACHE_NEED_AUTOMOUNT))
+                       __managed_dentry_set_transit(path->dentry);
+       }
+       spin_unlock(&dentry->d_lock);
+
+       /* The daemon never triggers a mount. */
+       if (autofs4_oz_mode(sbi))
+               return NULL;
+
+       /*
+        * If an expire request is pending everyone must wait.
+        * If the expire fails we're still mounted so continue
+        * the follow and return. A return of -EAGAIN (which only
+        * happens with indirect mounts) means the expire completed
+        * and the directory was removed, so just go ahead and try
+        * the mount.
+        */
+       status = do_expire_wait(dentry);
+       if (status && status != -EAGAIN)
+               return NULL;
+
+       /* Callback to the daemon to perform the mount or wait */
+       spin_lock(&sbi->fs_lock);
+       if (ino->flags & AUTOFS_INF_PENDING) {
+               spin_unlock(&sbi->fs_lock);
+               status = autofs4_mount_wait(dentry);
+               if (status)
+                       return ERR_PTR(status);
+               spin_lock(&sbi->fs_lock);
+               goto done;
+       }
+
+       /*
+        * If the dentry is a symlink it's equivalent to a directory
+        * having d_mountpoint() true, so there's no need to call back
+        * to the daemon.
+        */
+       if (dentry->d_inode && S_ISLNK(dentry->d_inode->i_mode))
+               goto done;
+       if (!d_mountpoint(dentry)) {
+               /*
+                * It's possible that user space hasn't removed directories
+                * after umounting a rootless multi-mount, although it
+                * should. For v5 have_submounts() is sufficient to handle
+                * this because the leaves of the directory tree under the
+                * mount never trigger mounts themselves (they have an autofs
+                * trigger mount mounted on them). But v4 pseudo direct mounts
+                * do need the leaves to to trigger mounts. In this case we
+                * have no choice but to use the list_empty() check and
+                * require user space behave.
+                */
+               if (sbi->version > 4) {
+                       if (have_submounts(dentry))
+                               goto done;
+               } else {
+                       spin_lock(&dentry->d_lock);
+                       if (!list_empty(&dentry->d_subdirs)) {
+                               spin_unlock(&dentry->d_lock);
+                               goto done;
+                       }
+                       spin_unlock(&dentry->d_lock);
+               }
+               ino->flags |= AUTOFS_INF_PENDING;
+               spin_unlock(&sbi->fs_lock);
+               status = autofs4_mount_wait(dentry);
+               if (status)
+                       return ERR_PTR(status);
+               spin_lock(&sbi->fs_lock);
+               ino->flags &= ~AUTOFS_INF_PENDING;
+       }
+done:
+       if (!(ino->flags & AUTOFS_INF_EXPIRING)) {
+               /*
+                * Any needed mounting has been completed and the path updated
+                * so turn this into a normal dentry so we don't continually
+                * call ->d_automount() and ->d_manage().
+                */
+               spin_lock(&dentry->d_lock);
+               __managed_dentry_clear_transit(dentry);
+               /*
+                * Only clear DMANAGED_AUTOMOUNT for rootless multi-mounts and
+                * symlinks as in all other cases the dentry will be covered by
+                * an actual mount so ->d_automount() won't be called during
+                * the follow.
+                */
+               if ((!d_mountpoint(dentry) &&
+                   !list_empty(&dentry->d_subdirs)) ||
+                   (dentry->d_inode && S_ISLNK(dentry->d_inode->i_mode)))
+                       __managed_dentry_clear_automount(dentry);
+               spin_unlock(&dentry->d_lock);
+       }
+       spin_unlock(&sbi->fs_lock);
+
+       /* Mount succeeded, check if we ended up with a new dentry */
+       dentry = autofs4_mountpoint_changed(path);
+       if (!dentry)
+               return ERR_PTR(-ENOENT);
+
+       return NULL;
+}
+
+int autofs4_d_manage(struct dentry *dentry, bool mounting_here, bool rcu_walk)
+{
+       struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb);
+
+       DPRINTK("dentry=%p %.*s",
+               dentry, dentry->d_name.len, dentry->d_name.name);
+
+       /* The daemon never waits. */
+       if (autofs4_oz_mode(sbi) || mounting_here) {
+               if (!d_mountpoint(dentry))
+                       return -EISDIR;
+               return 0;
+       }
+
+       /* We need to sleep, so we need pathwalk to be in ref-mode */
+       if (rcu_walk)
+               return -ECHILD;
+
+       /* Wait for pending expires */
+       do_expire_wait(dentry);
+
+       /*
+        * This dentry may be under construction so wait on mount
+        * completion.
+        */
+       return autofs4_mount_wait(dentry);
+}
+
 /* Lookups in the root directory */
 static struct dentry *autofs4_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd)
 {
        struct autofs_sb_info *sbi;
        struct autofs_info *ino;
-       struct dentry *expiring, *active;
-       int oz_mode;
+       struct dentry *active;
 
-       DPRINTK("name = %.*s",
-               dentry->d_name.len, dentry->d_name.name);
+       DPRINTK("name = %.*s", dentry->d_name.len, dentry->d_name.name);
 
        /* File name too long to exist */
        if (dentry->d_name.len > NAME_MAX)
                return ERR_PTR(-ENAMETOOLONG);
 
        sbi = autofs4_sbi(dir->i_sb);
-       oz_mode = autofs4_oz_mode(sbi);
 
        DPRINTK("pid = %u, pgrp = %u, catatonic = %d, oz_mode = %d",
-                current->pid, task_pgrp_nr(current), sbi->catatonic, oz_mode);
+               current->pid, task_pgrp_nr(current), sbi->catatonic, oz_mode);
 
        active = autofs4_lookup_active(dentry);
        if (active) {
-               dentry = active;
-               ino = autofs4_dentry_ino(dentry);
+               return active;
        } else {
                /*
-                * Mark the dentry incomplete but don't hash it. We do this
-                * to serialize our inode creation operations (symlink and
-                * mkdir) which prevents deadlock during the callback to
-                * the daemon. Subsequent user space lookups for the same
-                * dentry are placed on the wait queue while the daemon
-                * itself is allowed passage unresticted so the create
-                * operation itself can then hash the dentry. Finally,
-                * we check for the hashed dentry and return the newly
-                * hashed dentry.
+                * A dentry that is not within the root can never trigger a
+                * mount operation, unless the directory already exists, so we
+                * can return fail immediately.  The daemon however does need
+                * to create directories within the file system.
                 */
-               d_set_d_op(dentry, &autofs4_root_dentry_operations);
+               if (!autofs4_oz_mode(sbi) && !IS_ROOT(dentry->d_parent))
+                       return ERR_PTR(-ENOENT);
+
+               /* Mark entries in the root as mount triggers */
+               if (autofs_type_indirect(sbi->type) && IS_ROOT(dentry->d_parent))
+                       __managed_dentry_set_managed(dentry);
 
-               /*
-                * And we need to ensure that the same dentry is used for
-                * all following lookup calls until it is hashed so that
-                * the dentry flags are persistent throughout the request.
-                */
                ino = autofs4_init_ino(NULL, sbi, 0555);
                if (!ino)
                        return ERR_PTR(-ENOMEM);
@@ -596,82 +518,6 @@ static struct dentry *autofs4_lookup(struct inode *dir, struct dentry *dentry, s
 
                d_instantiate(dentry, NULL);
        }
-
-       if (!oz_mode) {
-               mutex_unlock(&dir->i_mutex);
-               expiring = autofs4_lookup_expiring(dentry);
-               if (expiring) {
-                       /*
-                        * If we are racing with expire the request might not
-                        * be quite complete but the directory has been removed
-                        * so it must have been successful, so just wait for it.
-                        */
-                       autofs4_expire_wait(expiring);
-                       autofs4_del_expiring(expiring);
-                       dput(expiring);
-               }
-
-               spin_lock(&sbi->fs_lock);
-               ino->flags |= AUTOFS_INF_PENDING;
-               spin_unlock(&sbi->fs_lock);
-               if (dentry->d_op && dentry->d_op->d_revalidate)
-                       (dentry->d_op->d_revalidate)(dentry, nd);
-               mutex_lock(&dir->i_mutex);
-       }
-
-       /*
-        * If we are still pending, check if we had to handle
-        * a signal. If so we can force a restart..
-        */
-       if (ino->flags & AUTOFS_INF_PENDING) {
-               /* See if we were interrupted */
-               if (signal_pending(current)) {
-                       sigset_t *sigset = &current->pending.signal;
-                       if (sigismember (sigset, SIGKILL) ||
-                           sigismember (sigset, SIGQUIT) ||
-                           sigismember (sigset, SIGINT)) {
-                           if (active)
-                               dput(active);
-                           return ERR_PTR(-ERESTARTNOINTR);
-                       }
-               }
-               if (!oz_mode) {
-                       spin_lock(&sbi->fs_lock);
-                       ino->flags &= ~AUTOFS_INF_PENDING;
-                       spin_unlock(&sbi->fs_lock);
-               }
-       }
-
-       /*
-        * If this dentry is unhashed, then we shouldn't honour this
-        * lookup.  Returning ENOENT here doesn't do the right thing
-        * for all system calls, but it should be OK for the operations
-        * we permit from an autofs.
-        */
-       if (!oz_mode && d_unhashed(dentry)) {
-               /*
-                * A user space application can (and has done in the past)
-                * remove and re-create this directory during the callback.
-                * This can leave us with an unhashed dentry, but a
-                * successful mount!  So we need to perform another
-                * cached lookup in case the dentry now exists.
-                */
-               struct dentry *parent = dentry->d_parent;
-               struct dentry *new = d_lookup(parent, &dentry->d_name);
-               if (new != NULL)
-                       dentry = new;
-               else
-                       dentry = ERR_PTR(-ENOENT);
-
-               if (active)
-                       dput(active);
-
-               return dentry;
-       }
-
-       if (active)
-               return active;
-
        return NULL;
 }
 
@@ -716,18 +562,12 @@ static int autofs4_dir_symlink(struct inode *dir,
        }
        d_add(dentry, inode);
 
-       if (dir == dir->i_sb->s_root->d_inode)
-               d_set_d_op(dentry, &autofs4_root_dentry_operations);
-       else
-               d_set_d_op(dentry, &autofs4_dentry_operations);
-
        dentry->d_fsdata = ino;
        ino->dentry = dget(dentry);
        atomic_inc(&ino->count);
        p_ino = autofs4_dentry_ino(dentry->d_parent);
        if (p_ino && dentry->d_parent != dentry)
                atomic_inc(&p_ino->count);
-       ino->inode = inode;
 
        ino->u.symlink = cp;
        dir->i_mtime = CURRENT_TIME;
@@ -782,6 +622,58 @@ static int autofs4_dir_unlink(struct inode *dir, struct dentry *dentry)
        return 0;
 }
 
+/*
+ * Version 4 of autofs provides a pseudo direct mount implementation
+ * that relies on directories at the leaves of a directory tree under
+ * an indirect mount to trigger mounts. To allow for this we need to
+ * set the DMANAGED_AUTOMOUNT and DMANAGED_TRANSIT flags on the leaves
+ * of the directory tree. There is no need to clear the automount flag
+ * following a mount or restore it after an expire because these mounts
+ * are always covered. However, it is neccessary to ensure that these
+ * flags are clear on non-empty directories to avoid unnecessary calls
+ * during path walks.
+ */
+static void autofs_set_leaf_automount_flags(struct dentry *dentry)
+{
+       struct dentry *parent;
+
+       /* root and dentrys in the root are already handled */
+       if (IS_ROOT(dentry->d_parent))
+               return;
+
+       managed_dentry_set_managed(dentry);
+
+       parent = dentry->d_parent;
+       /* only consider parents below dentrys in the root */
+       if (IS_ROOT(parent->d_parent))
+               return;
+       managed_dentry_clear_managed(parent);
+       return;
+}
+
+static void autofs_clear_leaf_automount_flags(struct dentry *dentry)
+{
+       struct list_head *d_child;
+       struct dentry *parent;
+
+       /* flags for dentrys in the root are handled elsewhere */
+       if (IS_ROOT(dentry->d_parent))
+               return;
+
+       managed_dentry_clear_managed(dentry);
+
+       parent = dentry->d_parent;
+       /* only consider parents below dentrys in the root */
+       if (IS_ROOT(parent->d_parent))
+               return;
+       d_child = &dentry->d_u.d_child;
+       /* Set parent managed if it's becoming empty */
+       if (d_child->next == &parent->d_subdirs &&
+           d_child->prev == &parent->d_subdirs)
+               managed_dentry_set_managed(parent);
+       return;
+}
+
 static int autofs4_dir_rmdir(struct inode *dir, struct dentry *dentry)
 {
        struct autofs_sb_info *sbi = autofs4_sbi(dir->i_sb);
@@ -809,6 +701,9 @@ static int autofs4_dir_rmdir(struct inode *dir, struct dentry *dentry)
        spin_unlock(&dentry->d_lock);
        spin_unlock(&autofs4_lock);
 
+       if (sbi->version < 5)
+               autofs_clear_leaf_automount_flags(dentry);
+
        if (atomic_dec_and_test(&ino->count)) {
                p_ino = autofs4_dentry_ino(dentry->d_parent);
                if (p_ino && dentry->d_parent != dentry)
@@ -851,10 +746,8 @@ static int autofs4_dir_mkdir(struct inode *dir, struct dentry *dentry, int mode)
        }
        d_add(dentry, inode);
 
-       if (dir == dir->i_sb->s_root->d_inode)
-               d_set_d_op(dentry, &autofs4_root_dentry_operations);
-       else
-               d_set_d_op(dentry, &autofs4_dentry_operations);
+       if (sbi->version < 5)
+               autofs_set_leaf_automount_flags(dentry);
 
        dentry->d_fsdata = ino;
        ino->dentry = dget(dentry);
@@ -862,7 +755,6 @@ static int autofs4_dir_mkdir(struct inode *dir, struct dentry *dentry, int mode)
        p_ino = autofs4_dentry_ino(dentry->d_parent);
        if (p_ino && dentry->d_parent != dentry)
                atomic_inc(&p_ino->count);
-       ino->inode = inode;
        inc_nlink(dir);
        dir->i_mtime = CURRENT_TIME;
 
@@ -944,8 +836,7 @@ static inline int autofs4_ask_umount(struct vfsmount *mnt, int __user *p)
 int is_autofs4_dentry(struct dentry *dentry)
 {
        return dentry && dentry->d_inode &&
-               (dentry->d_op == &autofs4_root_dentry_operations ||
-                dentry->d_op == &autofs4_dentry_operations) &&
+               dentry->d_op == &autofs4_dentry_operations &&
                dentry->d_fsdata != NULL;
 }
 
index c5f8459..5601005 100644 (file)
@@ -309,6 +309,9 @@ static int validate_request(struct autofs_wait_queue **wait,
         * completed while we waited on the mutex ...
         */
        if (notify == NFY_MOUNT) {
+               struct dentry *new = NULL;
+               int valid = 1;
+
                /*
                 * If the dentry was successfully mounted while we slept
                 * on the wait queue mutex we can return success. If it
@@ -316,8 +319,20 @@ static int validate_request(struct autofs_wait_queue **wait,
                 * a multi-mount with no mount at it's base) we can
                 * continue on and create a new request.
                 */
+               if (!IS_ROOT(dentry)) {
+                       if (dentry->d_inode && d_unhashed(dentry)) {
+                               struct dentry *parent = dentry->d_parent;
+                               new = d_lookup(parent, &dentry->d_name);
+                               if (new)
+                                       dentry = new;
+                       }
+               }
                if (have_submounts(dentry))
-                       return 0;
+                       valid = 0;
+
+               if (new)
+                       dput(new);
+               return valid;
        }
 
        return 1;
index c68a056..7ed3653 100644 (file)
@@ -255,35 +255,6 @@ static struct vfsmount *cifs_dfs_do_refmount(struct cifs_sb_info *cifs_sb,
 
 }
 
-static int add_mount_helper(struct vfsmount *newmnt, struct nameidata *nd,
-                               struct list_head *mntlist)
-{
-       /* stolen from afs code */
-       int err;
-
-       mntget(newmnt);
-       err = do_add_mount(newmnt, &nd->path, nd->path.mnt->mnt_flags | MNT_SHRINKABLE, mntlist);
-       switch (err) {
-       case 0:
-               path_put(&nd->path);
-               nd->path.mnt = newmnt;
-               nd->path.dentry = dget(newmnt->mnt_root);
-               schedule_delayed_work(&cifs_dfs_automount_task,
-                                     cifs_dfs_mountpoint_expiry_timeout);
-               break;
-       case -EBUSY:
-               /* someone else made a mount here whilst we were busy */
-               while (d_mountpoint(nd->path.dentry) &&
-                      follow_down(&nd->path))
-                       ;
-               err = 0;
-       default:
-               mntput(newmnt);
-               break;
-       }
-       return err;
-}
-
 static void dump_referral(const struct dfs_info3_param *ref)
 {
        cFYI(1, "DFS: ref path: %s", ref->path_name);
@@ -293,45 +264,43 @@ static void dump_referral(const struct dfs_info3_param *ref)
                                ref->path_consumed);
 }
 
-
-static void*
-cifs_dfs_follow_mountpoint(struct dentry *dentry, struct nameidata *nd)
+/*
+ * Create a vfsmount that we can automount
+ */
+static struct vfsmount *cifs_dfs_do_automount(struct dentry *mntpt)
 {
        struct dfs_info3_param *referrals = NULL;
        unsigned int num_referrals = 0;
        struct cifs_sb_info *cifs_sb;
        struct cifsSesInfo *ses;
-       char *full_path = NULL;
+       char *full_path;
        int xid, i;
-       int rc = 0;
-       struct vfsmount *mnt = ERR_PTR(-ENOENT);
+       int rc;
+       struct vfsmount *mnt;
        struct tcon_link *tlink;
 
        cFYI(1, "in %s", __func__);
-       BUG_ON(IS_ROOT(dentry));
+       BUG_ON(IS_ROOT(mntpt));
 
        xid = GetXid();
 
-       dput(nd->path.dentry);
-       nd->path.dentry = dget(dentry);
-
        /*
         * The MSDFS spec states that paths in DFS referral requests and
         * responses must be prefixed by a single '\' character instead of
         * the double backslashes usually used in the UNC. This function
         * gives us the latter, so we must adjust the result.
         */
-       full_path = build_path_from_dentry(dentry);
-       if (full_path == NULL) {
-               rc = -ENOMEM;
-               goto out_err;
-       }
+       mnt = ERR_PTR(-ENOMEM);
+       full_path = build_path_from_dentry(mntpt);
+       if (full_path == NULL)
+               goto free_xid;
 
-       cifs_sb = CIFS_SB(dentry->d_inode->i_sb);
+       cifs_sb = CIFS_SB(mntpt->d_inode->i_sb);
        tlink = cifs_sb_tlink(cifs_sb);
+       mnt = ERR_PTR(-EINVAL);
        if (IS_ERR(tlink)) {
-               rc = PTR_ERR(tlink);
-               goto out_err;
+               mnt = ERR_CAST(tlink);
+               goto free_full_path;
        }
        ses = tlink_tcon(tlink)->ses;
 
@@ -341,46 +310,63 @@ cifs_dfs_follow_mountpoint(struct dentry *dentry, struct nameidata *nd)
 
        cifs_put_tlink(tlink);
 
+       mnt = ERR_PTR(-ENOENT);
        for (i = 0; i < num_referrals; i++) {
                int len;
-               dump_referral(referrals+i);
+               dump_referral(referrals + i);
                /* connect to a node */
                len = strlen(referrals[i].node_name);
                if (len < 2) {
                        cERROR(1, "%s: Net Address path too short: %s",
                                        __func__, referrals[i].node_name);
-                       rc = -EINVAL;
-                       goto out_err;
+                       mnt = ERR_PTR(-EINVAL);
+                       break;
                }
                mnt = cifs_dfs_do_refmount(cifs_sb,
                                full_path, referrals + i);
                cFYI(1, "%s: cifs_dfs_do_refmount:%s , mnt:%p", __func__,
                                        referrals[i].node_name, mnt);
-
-               /* complete mount procedure if we accured submount */
                if (!IS_ERR(mnt))
-                       break;
+                       goto success;
        }
 
-       /* we need it cause for() above could exit without valid submount */
-       rc = PTR_ERR(mnt);
-       if (IS_ERR(mnt))
-               goto out_err;
-
-       rc = add_mount_helper(mnt, nd, &cifs_dfs_automount_list);
+       /* no valid submounts were found; return error from get_dfs_path() by
+        * preference */
+       if (rc != 0)
+               mnt = ERR_PTR(rc);
 
-out:
-       FreeXid(xid);
+success:
        free_dfs_info_array(referrals, num_referrals);
+free_full_path:
        kfree(full_path);
+free_xid:
+       FreeXid(xid);
        cFYI(1, "leaving %s" , __func__);
-       return ERR_PTR(rc);
-out_err:
-       path_put(&nd->path);
-       goto out;
+       return mnt;
+}
+
+/*
+ * Attempt to automount the referral
+ */
+struct vfsmount *cifs_dfs_d_automount(struct path *path)
+{
+       struct vfsmount *newmnt;
+
+       cFYI(1, "in %s", __func__);
+
+       newmnt = cifs_dfs_do_automount(path->dentry);
+       if (IS_ERR(newmnt)) {
+               cFYI(1, "leaving %s [automount failed]" , __func__);
+               return newmnt;
+       }
+
+       mntget(newmnt); /* prevent immediate expiration */
+       mnt_set_expiry(newmnt, &cifs_dfs_automount_list);
+       schedule_delayed_work(&cifs_dfs_automount_task,
+                             cifs_dfs_mountpoint_expiry_timeout);
+       cFYI(1, "leaving %s [ok]" , __func__);
+       return newmnt;
 }
 
 const struct inode_operations cifs_dfs_referral_inode_operations = {
-       .follow_link = cifs_dfs_follow_mountpoint,
 };
-
index 897b2b2..851030f 100644 (file)
@@ -93,6 +93,12 @@ extern int cifs_readdir(struct file *file, void *direntry, filldir_t filldir);
 extern const struct dentry_operations cifs_dentry_ops;
 extern const struct dentry_operations cifs_ci_dentry_ops;
 
+#ifdef CONFIG_CIFS_DFS_UPCALL
+extern struct vfsmount *cifs_dfs_d_automount(struct path *path);
+#else
+#define cifs_dfs_d_automount NULL
+#endif
+
 /* Functions related to symlinks */
 extern void *cifs_follow_link(struct dentry *direntry, struct nameidata *nd);
 extern void cifs_put_link(struct dentry *direntry,
index 1e95dd6..dd5f229 100644 (file)
@@ -675,6 +675,7 @@ cifs_d_revalidate(struct dentry *direntry, struct nameidata *nd)
 
 const struct dentry_operations cifs_dentry_ops = {
        .d_revalidate = cifs_d_revalidate,
+       .d_automount = cifs_dfs_d_automount,
 /* d_delete:       cifs_d_delete,      */ /* not needed except for debugging */
 };
 
@@ -711,4 +712,5 @@ const struct dentry_operations cifs_ci_dentry_ops = {
        .d_revalidate = cifs_d_revalidate,
        .d_hash = cifs_ci_hash,
        .d_compare = cifs_ci_compare,
+       .d_automount = cifs_dfs_d_automount,
 };
index b06b606..6c9ee80 100644 (file)
@@ -32,7 +32,7 @@
 #include "fscache.h"
 
 
-static void cifs_set_ops(struct inode *inode, const bool is_dfs_referral)
+static void cifs_set_ops(struct inode *inode)
 {
        struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
 
@@ -60,7 +60,7 @@ static void cifs_set_ops(struct inode *inode, const bool is_dfs_referral)
                break;
        case S_IFDIR:
 #ifdef CONFIG_CIFS_DFS_UPCALL
-               if (is_dfs_referral) {
+               if (IS_AUTOMOUNT(inode)) {
                        inode->i_op = &cifs_dfs_referral_inode_operations;
                } else {
 #else /* NO DFS support, treat as a directory */
@@ -167,7 +167,9 @@ cifs_fattr_to_inode(struct inode *inode, struct cifs_fattr *fattr)
        }
        spin_unlock(&inode->i_lock);
 
-       cifs_set_ops(inode, fattr->cf_flags & CIFS_FATTR_DFS_REFERRAL);
+       if (fattr->cf_flags & CIFS_FATTR_DFS_REFERRAL)
+               inode->i_flags |= S_AUTOMOUNT;
+       cifs_set_ops(inode);
 }
 
 void
index 274a222..9f493ee 100644 (file)
@@ -1380,8 +1380,11 @@ EXPORT_SYMBOL(d_set_d_op);
 static void __d_instantiate(struct dentry *dentry, struct inode *inode)
 {
        spin_lock(&dentry->d_lock);
-       if (inode)
+       if (inode) {
+               if (unlikely(IS_AUTOMOUNT(inode)))
+                       dentry->d_flags |= DCACHE_NEED_AUTOMOUNT;
                list_add(&dentry->d_alias, &inode->i_dentry);
+       }
        dentry->d_inode = inode;
        dentry_rcuwalk_barrier(dentry);
        spin_unlock(&dentry->d_lock);
index 68ca487..78b519c 100644 (file)
@@ -4,6 +4,19 @@
 #include <linux/path.h>
 #include <linux/slab.h>
 #include <linux/fs_struct.h>
+#include "internal.h"
+
+static inline void path_get_longterm(struct path *path)
+{
+       path_get(path);
+       mnt_make_longterm(path->mnt);
+}
+
+static inline void path_put_longterm(struct path *path)
+{
+       mnt_make_shortterm(path->mnt);
+       path_put(path);
+}
 
 /*
  * Replace the fs->{rootmnt,root} with {mnt,dentry}. Put the old values.
@@ -17,11 +30,11 @@ void set_fs_root(struct fs_struct *fs, struct path *path)
        write_seqcount_begin(&fs->seq);
        old_root = fs->root;
        fs->root = *path;
-       path_get_long(path);
+       path_get_longterm(path);
        write_seqcount_end(&fs->seq);
        spin_unlock(&fs->lock);
        if (old_root.dentry)
-               path_put_long(&old_root);
+               path_put_longterm(&old_root);
 }
 
 /*
@@ -36,12 +49,12 @@ void set_fs_pwd(struct fs_struct *fs, struct path *path)
        write_seqcount_begin(&fs->seq);
        old_pwd = fs->pwd;
        fs->pwd = *path;
-       path_get_long(path);
+       path_get_longterm(path);
        write_seqcount_end(&fs->seq);
        spin_unlock(&fs->lock);
 
        if (old_pwd.dentry)
-               path_put_long(&old_pwd);
+               path_put_longterm(&old_pwd);
 }
 
 void chroot_fs_refs(struct path *old_root, struct path *new_root)
@@ -59,13 +72,13 @@ void chroot_fs_refs(struct path *old_root, struct path *new_root)
                        write_seqcount_begin(&fs->seq);
                        if (fs->root.dentry == old_root->dentry
                            && fs->root.mnt == old_root->mnt) {
-                               path_get_long(new_root);
+                               path_get_longterm(new_root);
                                fs->root = *new_root;
                                count++;
                        }
                        if (fs->pwd.dentry == old_root->dentry
                            && fs->pwd.mnt == old_root->mnt) {
-                               path_get_long(new_root);
+                               path_get_longterm(new_root);
                                fs->pwd = *new_root;
                                count++;
                        }
@@ -76,13 +89,13 @@ void chroot_fs_refs(struct path *old_root, struct path *new_root)
        } while_each_thread(g, p);
        read_unlock(&tasklist_lock);
        while (count--)
-               path_put_long(old_root);
+               path_put_longterm(old_root);
 }
 
 void free_fs_struct(struct fs_struct *fs)
 {
-       path_put_long(&fs->root);
-       path_put_long(&fs->pwd);
+       path_put_longterm(&fs->root);
+       path_put_longterm(&fs->pwd);
        kmem_cache_free(fs_cachep, fs);
 }
 
@@ -118,9 +131,9 @@ struct fs_struct *copy_fs_struct(struct fs_struct *old)
 
                spin_lock(&old->lock);
                fs->root = old->root;
-               path_get_long(&fs->root);
+               path_get_longterm(&fs->root);
                fs->pwd = old->pwd;
-               path_get_long(&fs->pwd);
+               path_get_longterm(&fs->pwd);
                spin_unlock(&old->lock);
        }
        return fs;
index 9687c2e..12ccb86 100644 (file)
@@ -70,6 +70,11 @@ extern void mnt_set_mountpoint(struct vfsmount *, struct dentry *,
 extern void release_mounts(struct list_head *);
 extern void umount_tree(struct vfsmount *, int, struct list_head *);
 extern struct vfsmount *copy_tree(struct vfsmount *, struct dentry *, int);
+extern int do_add_mount(struct vfsmount *, struct path *, int);
+extern void mnt_clear_expiry(struct vfsmount *);
+
+extern void mnt_make_longterm(struct vfsmount *);
+extern void mnt_make_shortterm(struct vfsmount *);
 
 extern void __init mnt_init(void);
 
index 8df7a78..8f7b41a 100644 (file)
@@ -368,18 +368,6 @@ void path_get(struct path *path)
 EXPORT_SYMBOL(path_get);
 
 /**
- * path_get_long - get a long reference to a path
- * @path: path to get the reference to
- *
- * Given a path increment the reference count to the dentry and the vfsmount.
- */
-void path_get_long(struct path *path)
-{
-       mntget_long(path->mnt);
-       dget(path->dentry);
-}
-
-/**
  * path_put - put a reference to a path
  * @path: path to put the reference to
  *
@@ -393,18 +381,6 @@ void path_put(struct path *path)
 EXPORT_SYMBOL(path_put);
 
 /**
- * path_put_long - put a long reference to a path
- * @path: path to put the reference to
- *
- * Given a path decrement the reference count to the dentry and the vfsmount.
- */
-void path_put_long(struct path *path)
-{
-       dput(path->dentry);
-       mntput_long(path->mnt);
-}
-
-/**
  * nameidata_drop_rcu - drop this nameidata out of rcu-walk
  * @nd: nameidata pathwalk data to drop
  * Returns: 0 on success, -ECHILD on failure
@@ -800,12 +776,8 @@ __do_follow_link(const struct path *link, struct nameidata *nd, void **p)
        touch_atime(link->mnt, dentry);
        nd_set_link(nd, NULL);
 
-       if (link->mnt != nd->path.mnt) {
-               path_to_nameidata(link, nd);
-               nd->inode = nd->path.dentry->d_inode;
-               dget(dentry);
-       }
-       mntget(link->mnt);
+       if (link->mnt == nd->path.mnt)
+               mntget(link->mnt);
 
        nd->last_type = LAST_BIND;
        *p = dentry->d_inode->i_op->follow_link(dentry, nd);
@@ -896,54 +868,169 @@ int follow_up(struct path *path)
 }
 
 /*
- * serialization is taken care of in namespace.c
+ * Perform an automount
+ * - return -EISDIR to tell follow_managed() to stop and return the path we
+ *   were called with.
  */
-static void __follow_mount_rcu(struct nameidata *nd, struct path *path,
-                               struct inode **inode)
+static int follow_automount(struct path *path, unsigned flags,
+                           bool *need_mntput)
 {
-       while (d_mountpoint(path->dentry)) {
-               struct vfsmount *mounted;
-               mounted = __lookup_mnt(path->mnt, path->dentry, 1);
-               if (!mounted)
-                       return;
-               path->mnt = mounted;
-               path->dentry = mounted->mnt_root;
-               nd->seq = read_seqcount_begin(&path->dentry->d_seq);
-               *inode = path->dentry->d_inode;
+       struct vfsmount *mnt;
+       int err;
+
+       if (!path->dentry->d_op || !path->dentry->d_op->d_automount)
+               return -EREMOTE;
+
+       /* We don't want to mount if someone supplied AT_NO_AUTOMOUNT
+        * and this is the terminal part of the path.
+        */
+       if ((flags & LOOKUP_NO_AUTOMOUNT) && !(flags & LOOKUP_CONTINUE))
+               return -EISDIR; /* we actually want to stop here */
+
+       /* We want to mount if someone is trying to open/create a file of any
+        * type under the mountpoint, wants to traverse through the mountpoint
+        * or wants to open the mounted directory.
+        *
+        * We don't want to mount if someone's just doing a stat and they've
+        * set AT_SYMLINK_NOFOLLOW - unless they're stat'ing a directory and
+        * appended a '/' to the name.
+        */
+       if (!(flags & LOOKUP_FOLLOW) &&
+           !(flags & (LOOKUP_CONTINUE | LOOKUP_DIRECTORY |
+                      LOOKUP_OPEN | LOOKUP_CREATE)))
+               return -EISDIR;
+
+       current->total_link_count++;
+       if (current->total_link_count >= 40)
+               return -ELOOP;
+
+       mnt = path->dentry->d_op->d_automount(path);
+       if (IS_ERR(mnt)) {
+               /*
+                * The filesystem is allowed to return -EISDIR here to indicate
+                * it doesn't want to automount.  For instance, autofs would do
+                * this so that its userspace daemon can mount on this dentry.
+                *
+                * However, we can only permit this if it's a terminal point in
+                * the path being looked up; if it wasn't then the remainder of
+                * the path is inaccessible and we should say so.
+                */
+               if (PTR_ERR(mnt) == -EISDIR && (flags & LOOKUP_CONTINUE))
+                       return -EREMOTE;
+               return PTR_ERR(mnt);
        }
-}
 
-static int __follow_mount(struct path *path)
-{
-       int res = 0;
-       while (d_mountpoint(path->dentry)) {
-               struct vfsmount *mounted = lookup_mnt(path);
-               if (!mounted)
-                       break;
+       if (!mnt) /* mount collision */
+               return 0;
+
+       /* The new mount record should have at least 2 refs to prevent it being
+        * expired before we get a chance to add it
+        */
+       BUG_ON(mnt_get_count(mnt) < 2);
+
+       if (mnt->mnt_sb == path->mnt->mnt_sb &&
+           mnt->mnt_root == path->dentry) {
+               mnt_clear_expiry(mnt);
+               mntput(mnt);
+               mntput(mnt);
+               return -ELOOP;
+       }
+
+       /* We need to add the mountpoint to the parent.  The filesystem may
+        * have placed it on an expiry list, and so we need to make sure it
+        * won't be expired under us if do_add_mount() fails (do_add_mount()
+        * will eat a reference unconditionally).
+        */
+       mntget(mnt);
+       err = do_add_mount(mnt, path, path->mnt->mnt_flags | MNT_SHRINKABLE);
+       switch (err) {
+       case -EBUSY:
+               /* Someone else made a mount here whilst we were busy */
+               err = 0;
+       default:
+               mnt_clear_expiry(mnt);
+               mntput(mnt);
+               mntput(mnt);
+               return err;
+       case 0:
+               mntput(mnt);
                dput(path->dentry);
-               if (res)
+               if (*need_mntput)
                        mntput(path->mnt);
-               path->mnt = mounted;
-               path->dentry = dget(mounted->mnt_root);
-               res = 1;
+               path->mnt = mnt;
+               path->dentry = dget(mnt->mnt_root);
+               *need_mntput = true;
+               return 0;
        }
-       return res;
 }
 
-static void follow_mount(struct path *path)
+/*
+ * Handle a dentry that is managed in some way.
+ * - Flagged for transit management (autofs)
+ * - Flagged as mountpoint
+ * - Flagged as automount point
+ *
+ * This may only be called in refwalk mode.
+ *
+ * Serialization is taken care of in namespace.c
+ */
+static int follow_managed(struct path *path, unsigned flags)
 {
-       while (d_mountpoint(path->dentry)) {
-               struct vfsmount *mounted = lookup_mnt(path);
-               if (!mounted)
-                       break;
-               dput(path->dentry);
-               mntput(path->mnt);
-               path->mnt = mounted;
-               path->dentry = dget(mounted->mnt_root);
+       unsigned managed;
+       bool need_mntput = false;
+       int ret;
+
+       /* Given that we're not holding a lock here, we retain the value in a
+        * local variable for each dentry as we look at it so that we don't see
+        * the components of that value change under us */
+       while (managed = ACCESS_ONCE(path->dentry->d_flags),
+              managed &= DCACHE_MANAGED_DENTRY,
+              unlikely(managed != 0)) {
+               /* Allow the filesystem to manage the transit without i_mutex
+                * being held. */
+               if (managed & DCACHE_MANAGE_TRANSIT) {
+                       BUG_ON(!path->dentry->d_op);
+                       BUG_ON(!path->dentry->d_op->d_manage);
+                       ret = path->dentry->d_op->d_manage(path->dentry,
+                                                          false, false);
+                       if (ret < 0)
+                               return ret == -EISDIR ? 0 : ret;
+               }
+
+               /* Transit to a mounted filesystem. */
+               if (managed & DCACHE_MOUNTED) {
+                       struct vfsmount *mounted = lookup_mnt(path);
+                       if (mounted) {
+                               dput(path->dentry);
+                               if (need_mntput)
+                                       mntput(path->mnt);
+                               path->mnt = mounted;
+                               path->dentry = dget(mounted->mnt_root);
+                               need_mntput = true;
+                               continue;
+                       }
+
+                       /* Something is mounted on this dentry in another
+                        * namespace and/or whatever was mounted there in this
+                        * namespace got unmounted before we managed to get the
+                        * vfsmount_lock */
+               }
+
+               /* Handle an automount point */
+               if (managed & DCACHE_NEED_AUTOMOUNT) {
+                       ret = follow_automount(path, flags, &need_mntput);
+                       if (ret < 0)
+                               return ret == -EISDIR ? 0 : ret;
+                       continue;
+               }
+
+               /* We didn't change the current path point */
+               break;
        }
+       return 0;
 }
 
-int follow_down(struct path *path)
+int follow_down_one(struct path *path)
 {
        struct vfsmount *mounted;
 
@@ -958,13 +1045,41 @@ int follow_down(struct path *path)
        return 0;
 }
 
+/*
+ * Skip to top of mountpoint pile in rcuwalk mode.  We abort the rcu-walk if we
+ * meet a managed dentry and we're not walking to "..".  True is returned to
+ * continue, false to abort.
+ */
+static bool __follow_mount_rcu(struct nameidata *nd, struct path *path,
+                              struct inode **inode, bool reverse_transit)
+{
+       while (d_mountpoint(path->dentry)) {
+               struct vfsmount *mounted;
+               if (unlikely(path->dentry->d_flags & DCACHE_MANAGE_TRANSIT) &&
+                   !reverse_transit &&
+                   path->dentry->d_op->d_manage(path->dentry, false, true) < 0)
+                       return false;
+               mounted = __lookup_mnt(path->mnt, path->dentry, 1);
+               if (!mounted)
+                       break;
+               path->mnt = mounted;
+               path->dentry = mounted->mnt_root;
+               nd->seq = read_seqcount_begin(&path->dentry->d_seq);
+               *inode = path->dentry->d_inode;
+       }
+
+       if (unlikely(path->dentry->d_flags & DCACHE_NEED_AUTOMOUNT))
+               return reverse_transit;
+       return true;
+}
+
 static int follow_dotdot_rcu(struct nameidata *nd)
 {
        struct inode *inode = nd->inode;
 
        set_root_rcu(nd);
 
-       while(1) {
+       while (1) {
                if (nd->path.dentry == nd->root.dentry &&
                    nd->path.mnt == nd->root.mnt) {
                        break;
@@ -987,12 +1102,80 @@ static int follow_dotdot_rcu(struct nameidata *nd)
                nd->seq = read_seqcount_begin(&nd->path.dentry->d_seq);
                inode = nd->path.dentry->d_inode;
        }
-       __follow_mount_rcu(nd, &nd->path, &inode);
+       __follow_mount_rcu(nd, &nd->path, &inode, true);
        nd->inode = inode;
 
        return 0;
 }
 
+/*
+ * Follow down to the covering mount currently visible to userspace.  At each
+ * point, the filesystem owning that dentry may be queried as to whether the
+ * caller is permitted to proceed or not.
+ *
+ * Care must be taken as namespace_sem may be held (indicated by mounting_here
+ * being true).
+ */
+int follow_down(struct path *path, bool mounting_here)
+{
+       unsigned managed;
+       int ret;
+
+       while (managed = ACCESS_ONCE(path->dentry->d_flags),
+              unlikely(managed & DCACHE_MANAGED_DENTRY)) {
+               /* Allow the filesystem to manage the transit without i_mutex
+                * being held.
+                *
+                * We indicate to the filesystem if someone is trying to mount
+                * something here.  This gives autofs the chance to deny anyone
+                * other than its daemon the right to mount on its
+                * superstructure.
+                *
+                * The filesystem may sleep at this point.
+                */
+               if (managed & DCACHE_MANAGE_TRANSIT) {
+                       BUG_ON(!path->dentry->d_op);
+                       BUG_ON(!path->dentry->d_op->d_manage);
+                       ret = path->dentry->d_op->d_manage(
+                               path->dentry, mounting_here, false);
+                       if (ret < 0)
+                               return ret == -EISDIR ? 0 : ret;
+               }
+
+               /* Transit to a mounted filesystem. */
+               if (managed & DCACHE_MOUNTED) {
+                       struct vfsmount *mounted = lookup_mnt(path);
+                       if (!mounted)
+                               break;
+                       dput(path->dentry);
+                       mntput(path->mnt);
+                       path->mnt = mounted;
+                       path->dentry = dget(mounted->mnt_root);
+                       continue;
+               }
+
+               /* Don't handle automount points here */
+               break;
+       }
+       return 0;
+}
+
+/*
+ * Skip to top of mountpoint pile in refwalk mode for follow_dotdot()
+ */
+static void follow_mount(struct path *path)
+{
+       while (d_mountpoint(path->dentry)) {
+               struct vfsmount *mounted = lookup_mnt(path);
+               if (!mounted)
+                       break;
+               dput(path->dentry);
+               mntput(path->mnt);
+               path->mnt = mounted;
+               path->dentry = dget(mounted->mnt_root);
+       }
+}
+
 static void follow_dotdot(struct nameidata *nd)
 {
        set_root(nd);
@@ -1057,12 +1240,14 @@ static int do_lookup(struct nameidata *nd, struct qstr *name,
        struct vfsmount *mnt = nd->path.mnt;
        struct dentry *dentry, *parent = nd->path.dentry;
        struct inode *dir;
+       int err;
+
        /*
         * See if the low-level filesystem might want
         * to use its own hash..
         */
        if (unlikely(parent->d_flags & DCACHE_OP_HASH)) {
-               int err = parent->d_op->d_hash(parent, nd->inode, name);
+               err = parent->d_op->d_hash(parent, nd->inode, name);
                if (err < 0)
                        return err;
        }
@@ -1089,22 +1274,28 @@ static int do_lookup(struct nameidata *nd, struct qstr *name,
                nd->seq = seq;
                if (dentry->d_flags & DCACHE_OP_REVALIDATE)
                        goto need_revalidate;
+done2:
                path->mnt = mnt;
                path->dentry = dentry;
-               __follow_mount_rcu(nd, path, inode);
-       } else {
-               dentry = __d_lookup(parent, name);
-               if (!dentry)
-                       goto need_lookup;
+               if (likely(__follow_mount_rcu(nd, path, inode, false)))
+                       return 0;
+               if (nameidata_drop_rcu(nd))
+                       return -ECHILD;
+               /* fallthru */
+       }
+       dentry = __d_lookup(parent, name);
+       if (!dentry)
+               goto need_lookup;
 found:
-               if (dentry->d_flags & DCACHE_OP_REVALIDATE)
-                       goto need_revalidate;
+       if (dentry->d_flags & DCACHE_OP_REVALIDATE)
+               goto need_revalidate;
 done:
-               path->mnt = mnt;
-               path->dentry = dentry;
-               __follow_mount(path);
-               *inode = path->dentry->d_inode;
-       }
+       path->mnt = mnt;
+       path->dentry = dentry;
+       err = follow_managed(path, nd->flags);
+       if (unlikely(err < 0))
+               return err;
+       *inode = path->dentry->d_inode;
        return 0;
 
 need_lookup:
@@ -1143,6 +1334,8 @@ need_revalidate:
                goto need_lookup;
        if (IS_ERR(dentry))
                goto fail;
+       if (nd->flags & LOOKUP_RCU)
+               goto done2;
        goto done;
 
 fail:
@@ -1150,17 +1343,6 @@ fail:
 }
 
 /*
- * This is a temporary kludge to deal with "automount" symlinks; proper
- * solution is to trigger them on follow_mount(), so that do_lookup()
- * would DTRT.  To be killed before 2.6.34-final.
- */
-static inline int follow_on_final(struct inode *inode, unsigned lookup_flags)
-{
-       return inode && unlikely(inode->i_op->follow_link) &&
-               ((lookup_flags & LOOKUP_FOLLOW) || S_ISDIR(inode->i_mode));
-}
-
-/*
  * Name resolution.
  * This is the basic name resolution function, turning a pathname into
  * the final dentry. We expect 'base' to be positive and a directory.
@@ -1298,7 +1480,8 @@ last_component:
                err = do_lookup(nd, &this, &next, &inode);
                if (err)
                        break;
-               if (follow_on_final(inode, lookup_flags)) {
+               if (inode && unlikely(inode->i_op->follow_link) &&
+                   (lookup_flags & LOOKUP_FOLLOW)) {
                        if (nameidata_dentry_drop_rcu_maybe(nd, next.dentry))
                                return -ECHILD;
                        BUG_ON(inode != next.dentry->d_inode);
@@ -2200,11 +2383,9 @@ static struct file *do_last(struct nameidata *nd, struct path *path,
        if (open_flag & O_EXCL)
                goto exit_dput;
 
-       if (__follow_mount(path)) {
-               error = -ELOOP;
-               if (open_flag & O_NOFOLLOW)
-                       goto exit_dput;
-       }
+       error = follow_managed(path, nd->flags);
+       if (error < 0)
+               goto exit_dput;
 
        error = -ENOENT;
        if (!path->dentry->d_inode)
@@ -2353,8 +2534,7 @@ reval:
                struct inode *linki = link.dentry->d_inode;
                void *cookie;
                error = -ELOOP;
-               /* S_ISDIR part is a temporary automount kludge */
-               if (!(nd.flags & LOOKUP_FOLLOW) && !S_ISDIR(linki->i_mode))
+               if (!(nd.flags & LOOKUP_FOLLOW))
                        goto exit_dput;
                if (count++ == 32)
                        goto exit_dput;
@@ -3413,6 +3593,7 @@ const struct inode_operations page_symlink_inode_operations = {
 };
 
 EXPORT_SYMBOL(user_path_at);
+EXPORT_SYMBOL(follow_down_one);
 EXPORT_SYMBOL(follow_down);
 EXPORT_SYMBOL(follow_up);
 EXPORT_SYMBOL(get_write_access); /* binfmt_aout */
index 3ddfd90..48809e2 100644 (file)
@@ -183,7 +183,7 @@ static inline void mnt_dec_count(struct vfsmount *mnt)
 unsigned int mnt_get_count(struct vfsmount *mnt)
 {
 #ifdef CONFIG_SMP
-       unsigned int count = atomic_read(&mnt->mnt_longrefs);
+       unsigned int count = 0;
        int cpu;
 
        for_each_possible_cpu(cpu) {
@@ -217,7 +217,7 @@ struct vfsmount *alloc_vfsmnt(const char *name)
                if (!mnt->mnt_pcp)
                        goto out_free_devname;
 
-               atomic_set(&mnt->mnt_longrefs, 1);
+               this_cpu_add(mnt->mnt_pcp->mnt_count, 1);
 #else
                mnt->mnt_count = 1;
                mnt->mnt_writers = 0;
@@ -624,8 +624,11 @@ static void commit_tree(struct vfsmount *mnt)
        BUG_ON(parent == mnt);
 
        list_add_tail(&head, &mnt->mnt_list);
-       list_for_each_entry(m, &head, mnt_list)
+       list_for_each_entry(m, &head, mnt_list) {
                m->mnt_ns = n;
+               atomic_inc(&m->mnt_longterm);
+       }
+
        list_splice(&head, n->list.prev);
 
        list_add_tail(&mnt->mnt_hash, mount_hashtable +
@@ -734,51 +737,30 @@ static inline void mntfree(struct vfsmount *mnt)
        deactivate_super(sb);
 }
 
-#ifdef CONFIG_SMP
-static inline void __mntput(struct vfsmount *mnt, int longrefs)
+static void mntput_no_expire(struct vfsmount *mnt)
 {
-       if (!longrefs) {
 put_again:
-               br_read_lock(vfsmount_lock);
-               if (likely(atomic_read(&mnt->mnt_longrefs))) {
-                       mnt_dec_count(mnt);
-                       br_read_unlock(vfsmount_lock);
-                       return;
-               }
+#ifdef CONFIG_SMP
+       br_read_lock(vfsmount_lock);
+       if (likely(atomic_read(&mnt->mnt_longterm))) {
+               mnt_dec_count(mnt);
                br_read_unlock(vfsmount_lock);
-       } else {
-               BUG_ON(!atomic_read(&mnt->mnt_longrefs));
-               if (atomic_add_unless(&mnt->mnt_longrefs, -1, 1))
-                       return;
+               return;
        }
+       br_read_unlock(vfsmount_lock);
 
        br_write_lock(vfsmount_lock);
-       if (!longrefs)
-               mnt_dec_count(mnt);
-       else
-               atomic_dec(&mnt->mnt_longrefs);
+       mnt_dec_count(mnt);
        if (mnt_get_count(mnt)) {
                br_write_unlock(vfsmount_lock);
                return;
        }
-       if (unlikely(mnt->mnt_pinned)) {
-               mnt_add_count(mnt, mnt->mnt_pinned + 1);
-               mnt->mnt_pinned = 0;
-               br_write_unlock(vfsmount_lock);
-               acct_auto_close_mnt(mnt);
-               goto put_again;
-       }
-       br_write_unlock(vfsmount_lock);
-       mntfree(mnt);
-}
 #else
-static inline void __mntput(struct vfsmount *mnt, int longrefs)
-{
-put_again:
        mnt_dec_count(mnt);
        if (likely(mnt_get_count(mnt)))
                return;
        br_write_lock(vfsmount_lock);
+#endif
        if (unlikely(mnt->mnt_pinned)) {
                mnt_add_count(mnt, mnt->mnt_pinned + 1);
                mnt->mnt_pinned = 0;
@@ -789,12 +771,6 @@ put_again:
        br_write_unlock(vfsmount_lock);
        mntfree(mnt);
 }
-#endif
-
-static void mntput_no_expire(struct vfsmount *mnt)
-{
-       __mntput(mnt, 0);
-}
 
 void mntput(struct vfsmount *mnt)
 {
@@ -802,7 +778,7 @@ void mntput(struct vfsmount *mnt)
                /* avoid cacheline pingpong, hope gcc doesn't get "smart" */
                if (unlikely(mnt->mnt_expiry_mark))
                        mnt->mnt_expiry_mark = 0;
-               __mntput(mnt, 0);
+               mntput_no_expire(mnt);
        }
 }
 EXPORT_SYMBOL(mntput);
@@ -815,33 +791,6 @@ struct vfsmount *mntget(struct vfsmount *mnt)
 }
 EXPORT_SYMBOL(mntget);
 
-void mntput_long(struct vfsmount *mnt)
-{
-#ifdef CONFIG_SMP
-       if (mnt) {
-               /* avoid cacheline pingpong, hope gcc doesn't get "smart" */
-               if (unlikely(mnt->mnt_expiry_mark))
-                       mnt->mnt_expiry_mark = 0;
-               __mntput(mnt, 1);
-       }
-#else
-       mntput(mnt);
-#endif
-}
-EXPORT_SYMBOL(mntput_long);
-
-struct vfsmount *mntget_long(struct vfsmount *mnt)
-{
-#ifdef CONFIG_SMP
-       if (mnt)
-               atomic_inc(&mnt->mnt_longrefs);
-       return mnt;
-#else
-       return mntget(mnt);
-#endif
-}
-EXPORT_SYMBOL(mntget_long);
-
 void mnt_pin(struct vfsmount *mnt)
 {
        br_write_lock(vfsmount_lock);
@@ -1216,7 +1165,7 @@ void release_mounts(struct list_head *head)
                        dput(dentry);
                        mntput(m);
                }
-               mntput_long(mnt);
+               mntput(mnt);
        }
 }
 
@@ -1226,19 +1175,21 @@ void release_mounts(struct list_head *head)
  */
 void umount_tree(struct vfsmount *mnt, int propagate, struct list_head *kill)
 {
+       LIST_HEAD(tmp_list);
        struct vfsmount *p;
 
        for (p = mnt; p; p = next_mnt(p, mnt))
-               list_move(&p->mnt_hash, kill);
+               list_move(&p->mnt_hash, &tmp_list);
 
        if (propagate)
-               propagate_umount(kill);
+               propagate_umount(&tmp_list);
 
-       list_for_each_entry(p, kill, mnt_hash) {
+       list_for_each_entry(p, &tmp_list, mnt_hash) {
                list_del_init(&p->mnt_expire);
                list_del_init(&p->mnt_list);
                __touch_mnt_namespace(p->mnt_ns);
                p->mnt_ns = NULL;
+               atomic_dec(&p->mnt_longterm);
                list_del_init(&p->mnt_child);
                if (p->mnt_parent != p) {
                        p->mnt_parent->mnt_ghosts++;
@@ -1246,6 +1197,7 @@ void umount_tree(struct vfsmount *mnt, int propagate, struct list_head *kill)
                }
                change_mnt_propagation(p, MS_PRIVATE);
        }
+       list_splice(&tmp_list, kill);
 }
 
 static void shrink_submounts(struct vfsmount *mnt, struct list_head *umounts);
@@ -1844,9 +1796,10 @@ static int do_move_mount(struct path *path, char *old_name)
                return err;
 
        down_write(&namespace_sem);
-       while (d_mountpoint(path->dentry) &&
-              follow_down(path))
-               ;
+       err = follow_down(path, true);
+       if (err < 0)
+               goto out;
+
        err = -EINVAL;
        if (!check_mnt(path->mnt) || !check_mnt(old_path.mnt))
                goto out;
@@ -1924,15 +1877,14 @@ static int do_new_mount(struct path *path, char *type, int flags,
        if (IS_ERR(mnt))
                return PTR_ERR(mnt);
 
-       return do_add_mount(mnt, path, mnt_flags, NULL);
+       return do_add_mount(mnt, path, mnt_flags);
 }
 
 /*
  * add a mount into a namespace's mount tree
- * - provide the option of adding the new mount to an expiration list
+ * - this unconditionally eats one of the caller's references to newmnt.
  */
-int do_add_mount(struct vfsmount *newmnt, struct path *path,
-                int mnt_flags, struct list_head *fslist)
+int do_add_mount(struct vfsmount *newmnt, struct path *path, int mnt_flags)
 {
        int err;
 
@@ -1940,9 +1892,10 @@ int do_add_mount(struct vfsmount *newmnt, struct path *path,
 
        down_write(&namespace_sem);
        /* Something was mounted here while we slept */
-       while (d_mountpoint(path->dentry) &&
-              follow_down(path))
-               ;
+       err = follow_down(path, true);
+       if (err < 0)
+               goto unlock;
+
        err = -EINVAL;
        if (!(mnt_flags & MNT_SHRINKABLE) && !check_mnt(path->mnt))
                goto unlock;
@@ -1961,19 +1914,45 @@ int do_add_mount(struct vfsmount *newmnt, struct path *path,
        if ((err = graft_tree(newmnt, path)))
                goto unlock;
 
-       if (fslist) /* add to the specified expiration list */
-               list_add_tail(&newmnt->mnt_expire, fslist);
-
        up_write(&namespace_sem);
        return 0;
 
 unlock:
        up_write(&namespace_sem);
-       mntput_long(newmnt);
+       mntput(newmnt);
        return err;
 }
 
-EXPORT_SYMBOL_GPL(do_add_mount);
+/**
+ * mnt_set_expiry - Put a mount on an expiration list
+ * @mnt: The mount to list.
+ * @expiry_list: The list to add the mount to.
+ */
+void mnt_set_expiry(struct vfsmount *mnt, struct list_head *expiry_list)
+{
+       down_write(&namespace_sem);
+       br_write_lock(vfsmount_lock);
+
+       list_add_tail(&mnt->mnt_expire, expiry_list);
+
+       br_write_unlock(vfsmount_lock);
+       up_write(&namespace_sem);
+}
+EXPORT_SYMBOL(mnt_set_expiry);
+
+/*
+ * Remove a vfsmount from any expiration list it may be on
+ */
+void mnt_clear_expiry(struct vfsmount *mnt)
+{
+       if (!list_empty(&mnt->mnt_expire)) {
+               down_write(&namespace_sem);
+               br_write_lock(vfsmount_lock);
+               list_del_init(&mnt->mnt_expire);
+               br_write_unlock(vfsmount_lock);
+               up_write(&namespace_sem);
+       }
+}
 
 /*
  * process a list of expirable mountpoints with the intent of discarding any
@@ -2262,6 +2241,20 @@ static struct mnt_namespace *alloc_mnt_ns(void)
        return new_ns;
 }
 
+void mnt_make_longterm(struct vfsmount *mnt)
+{
+       atomic_inc(&mnt->mnt_longterm);
+}
+
+void mnt_make_shortterm(struct vfsmount *mnt)
+{
+       if (atomic_add_unless(&mnt->mnt_longterm, -1, 1))
+               return;
+       br_write_lock(vfsmount_lock);
+       atomic_dec(&mnt->mnt_longterm);
+       br_write_unlock(vfsmount_lock);
+}
+
 /*
  * Allocate a new namespace structure and populate it with contents
  * copied from the namespace of the passed in task structure.
@@ -2299,14 +2292,19 @@ static struct mnt_namespace *dup_mnt_ns(struct mnt_namespace *mnt_ns,
        q = new_ns->root;
        while (p) {
                q->mnt_ns = new_ns;
+               atomic_inc(&q->mnt_longterm);
                if (fs) {
                        if (p == fs->root.mnt) {
+                               fs->root.mnt = mntget(q);
+                               atomic_inc(&q->mnt_longterm);
+                               mnt_make_shortterm(p);
                                rootmnt = p;
-                               fs->root.mnt = mntget_long(q);
                        }
                        if (p == fs->pwd.mnt) {
+                               fs->pwd.mnt = mntget(q);
+                               atomic_inc(&q->mnt_longterm);
+                               mnt_make_shortterm(p);
                                pwdmnt = p;
-                               fs->pwd.mnt = mntget_long(q);
                        }
                }
                p = next_mnt(p, mnt_ns->root);
@@ -2315,9 +2313,9 @@ static struct mnt_namespace *dup_mnt_ns(struct mnt_namespace *mnt_ns,
        up_write(&namespace_sem);
 
        if (rootmnt)
-               mntput_long(rootmnt);
+               mntput(rootmnt);
        if (pwdmnt)
-               mntput_long(pwdmnt);
+               mntput(pwdmnt);
 
        return new_ns;
 }
@@ -2350,6 +2348,7 @@ struct mnt_namespace *create_mnt_ns(struct vfsmount *mnt)
        new_ns = alloc_mnt_ns();
        if (!IS_ERR(new_ns)) {
                mnt->mnt_ns = new_ns;
+               atomic_inc(&mnt->mnt_longterm);
                new_ns->root = mnt;
                list_add(&new_ns->list, &new_ns->root->mnt_list);
        }
index df8c03a..2c3eb33 100644 (file)
@@ -970,7 +970,7 @@ int nfs_lookup_verify_inode(struct inode *inode, struct nameidata *nd)
 {
        struct nfs_server *server = NFS_SERVER(inode);
 
-       if (test_bit(NFS_INO_MOUNTPOINT, &NFS_I(inode)->flags))
+       if (IS_AUTOMOUNT(inode))
                return 0;
        if (nd != NULL) {
                /* VFS wants an on-the-wire revalidation */
@@ -1173,6 +1173,7 @@ const struct dentry_operations nfs_dentry_operations = {
        .d_revalidate   = nfs_lookup_revalidate,
        .d_delete       = nfs_dentry_delete,
        .d_iput         = nfs_dentry_iput,
+       .d_automount    = nfs_d_automount,
 };
 
 static struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, struct nameidata *nd)
@@ -1246,6 +1247,7 @@ const struct dentry_operations nfs4_dentry_operations = {
        .d_revalidate   = nfs_open_revalidate,
        .d_delete       = nfs_dentry_delete,
        .d_iput         = nfs_dentry_iput,
+       .d_automount    = nfs_d_automount,
 };
 
 /*
index ce00b70..d851242 100644 (file)
@@ -300,7 +300,7 @@ nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr)
                                else
                                        inode->i_op = &nfs_mountpoint_inode_operations;
                                inode->i_fop = NULL;
-                               set_bit(NFS_INO_MOUNTPOINT, &nfsi->flags);
+                               inode->i_flags |= S_AUTOMOUNT;
                        }
                } else if (S_ISLNK(inode->i_mode))
                        inode->i_op = &nfs_symlink_inode_operations;
@@ -1208,7 +1208,7 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)
        /* Update the fsid? */
        if (S_ISDIR(inode->i_mode) && (fattr->valid & NFS_ATTR_FATTR_FSID) &&
                        !nfs_fsid_equal(&server->fsid, &fattr->fsid) &&
-                       !test_bit(NFS_INO_MOUNTPOINT, &nfsi->flags))
+                       !IS_AUTOMOUNT(inode))
                server->fsid = fattr->fsid;
 
        /*
index bfa3a34..4644f04 100644 (file)
@@ -252,6 +252,7 @@ extern char *nfs_path(const char *base,
                      const struct dentry *droot,
                      const struct dentry *dentry,
                      char *buffer, ssize_t buflen);
+extern struct vfsmount *nfs_d_automount(struct path *path);
 
 /* getroot.c */
 extern struct dentry *nfs_get_root(struct super_block *, struct nfs_fh *);
index 74aaf39..f32b860 100644 (file)
@@ -97,9 +97,8 @@ Elong:
 }
 
 /*
- * nfs_follow_mountpoint - handle crossing a mountpoint on the server
- * @dentry - dentry of mountpoint
- * @nd - nameidata info
+ * nfs_d_automount - Handle crossing a mountpoint on the server
+ * @path - The mountpoint
  *
  * When we encounter a mountpoint on the server, we want to set up
  * a mountpoint on the client too, to prevent inode numbers from
@@ -109,87 +108,65 @@ Elong:
  * situation, and that different filesystems may want to use
  * different security flavours.
  */
-static void * nfs_follow_mountpoint(struct dentry *dentry, struct nameidata *nd)
+struct vfsmount *nfs_d_automount(struct path *path)
 {
        struct vfsmount *mnt;
-       struct nfs_server *server = NFS_SERVER(dentry->d_inode);
+       struct nfs_server *server = NFS_SERVER(path->dentry->d_inode);
        struct dentry *parent;
        struct nfs_fh *fh = NULL;
        struct nfs_fattr *fattr = NULL;
        int err;
 
-       dprintk("--> nfs_follow_mountpoint()\n");
+       dprintk("--> nfs_d_automount()\n");
 
-       err = -ESTALE;
-       if (IS_ROOT(dentry))
-               goto out_err;
+       mnt = ERR_PTR(-ESTALE);
+       if (IS_ROOT(path->dentry))
+               goto out_nofree;
 
-       err = -ENOMEM;
+       mnt = ERR_PTR(-ENOMEM);
        fh = nfs_alloc_fhandle();
        fattr = nfs_alloc_fattr();
        if (fh == NULL || fattr == NULL)
-               goto out_err;
+               goto out;
 
        dprintk("%s: enter\n", __func__);
-       dput(nd->path.dentry);
-       nd->path.dentry = dget(dentry);
 
-       /* Look it up again */
-       parent = dget_parent(nd->path.dentry);
+       /* Look it up again to get its attributes */
+       parent = dget_parent(path->dentry);
        err = server->nfs_client->rpc_ops->lookup(parent->d_inode,
-                                                 &nd->path.dentry->d_name,
+                                                 &path->dentry->d_name,
                                                  fh, fattr);
        dput(parent);
-       if (err != 0)
-               goto out_err;
+       if (err != 0) {
+               mnt = ERR_PTR(err);
+               goto out;
+       }
 
        if (fattr->valid & NFS_ATTR_FATTR_V4_REFERRAL)
-               mnt = nfs_do_refmount(nd->path.mnt, nd->path.dentry);
+               mnt = nfs_do_refmount(path->mnt, path->dentry);
        else
-               mnt = nfs_do_submount(nd->path.mnt, nd->path.dentry, fh,
-                                     fattr);
-       err = PTR_ERR(mnt);
+               mnt = nfs_do_submount(path->mnt, path->dentry, fh, fattr);
        if (IS_ERR(mnt))
-               goto out_err;
+               goto out;
 
-       mntget(mnt);
-       err = do_add_mount(mnt, &nd->path, nd->path.mnt->mnt_flags|MNT_SHRINKABLE,
-                          &nfs_automount_list);
-       if (err < 0) {
-               mntput(mnt);
-               if (err == -EBUSY)
-                       goto out_follow;
-               goto out_err;
-       }
-       path_put(&nd->path);
-       nd->path.mnt = mnt;
-       nd->path.dentry = dget(mnt->mnt_root);
+       dprintk("%s: done, success\n", __func__);
+       mntget(mnt); /* prevent immediate expiration */
+       mnt_set_expiry(mnt, &nfs_automount_list);
        schedule_delayed_work(&nfs_automount_task, nfs_mountpoint_expiry_timeout);
+
 out:
        nfs_free_fattr(fattr);
        nfs_free_fhandle(fh);
-       dprintk("%s: done, returned %d\n", __func__, err);
-
-       dprintk("<-- nfs_follow_mountpoint() = %d\n", err);
-       return ERR_PTR(err);
-out_err:
-       path_put(&nd->path);
-       goto out;
-out_follow:
-       while (d_mountpoint(nd->path.dentry) &&
-              follow_down(&nd->path))
-               ;
-       err = 0;
-       goto out;
+out_nofree:
+       dprintk("<-- nfs_follow_mountpoint() = %p\n", mnt);
+       return mnt;
 }
 
 const struct inode_operations nfs_mountpoint_inode_operations = {
-       .follow_link    = nfs_follow_mountpoint,
        .getattr        = nfs_getattr,
 };
 
 const struct inode_operations nfs_referral_inode_operations = {
-       .follow_link    = nfs_follow_mountpoint,
 };
 
 static void nfs_expire_automounts(struct work_struct *work)
index a3c7f70..641117f 100644 (file)
@@ -87,8 +87,9 @@ nfsd_cross_mnt(struct svc_rqst *rqstp, struct dentry **dpp,
                            .dentry = dget(dentry)};
        int err = 0;
 
-       while (d_mountpoint(path.dentry) && follow_down(&path))
-               ;
+       err = follow_down(&path, false);
+       if (err < 0)
+               goto out;
 
        exp2 = rqst_exp_get_by_name(rqstp, &path);
        if (IS_ERR(exp2)) {
index e2e95fb..89e9e19 100644 (file)
--- a/fs/pipe.c
+++ b/fs/pipe.c
@@ -1292,7 +1292,7 @@ static int __init init_pipe_fs(void)
 static void __exit exit_pipe_fs(void)
 {
        unregister_filesystem(&pipe_fs_type);
-       mntput_long(pipe_mnt);
+       mntput(pipe_mnt);
 }
 
 fs_initcall(init_pipe_fs);
index 12e90e2..d5c61cf 100644 (file)
--- a/fs/stat.c
+++ b/fs/stat.c
@@ -75,11 +75,13 @@ int vfs_fstatat(int dfd, const char __user *filename, struct kstat *stat,
        int error = -EINVAL;
        int lookup_flags = 0;
 
-       if ((flag & ~AT_SYMLINK_NOFOLLOW) != 0)
+       if ((flag & ~(AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT)) != 0)
                goto out;
 
        if (!(flag & AT_SYMLINK_NOFOLLOW))
                lookup_flags |= LOOKUP_FOLLOW;
+       if (flag & AT_NO_AUTOMOUNT)
+               lookup_flags |= LOOKUP_NO_AUTOMOUNT;
 
        error = user_path_at(dfd, filename, lookup_flags, &path);
        if (error)
index 4f6a357..74e149e 100644 (file)
@@ -1141,7 +1141,7 @@ static struct vfsmount *fs_set_subtype(struct vfsmount *mnt, const char *fstype)
        return mnt;
 
  err:
-       mntput_long(mnt);
+       mntput(mnt);
        return ERR_PTR(err);
 }
 
index 8b49ac4..e02982f 100644 (file)
@@ -24,7 +24,7 @@
 #define AUTOFS_MIN_PROTO_VERSION       3
 #define AUTOFS_MAX_PROTO_VERSION       5
 
-#define AUTOFS_PROTO_SUBVERSION                1
+#define AUTOFS_PROTO_SUBVERSION                2
 
 /* Mask for expire behaviour */
 #define AUTOFS_EXP_IMMEDIATE           1
index 59fcd24..f958c19 100644 (file)
@@ -167,6 +167,8 @@ struct dentry_operations {
        void (*d_release)(struct dentry *);
        void (*d_iput)(struct dentry *, struct inode *);
        char *(*d_dname)(struct dentry *, char *, int);
+       struct vfsmount *(*d_automount)(struct path *);
+       int (*d_manage)(struct dentry *, bool, bool);
 } ____cacheline_aligned;
 
 /*
@@ -205,13 +207,18 @@ struct dentry_operations {
 
 #define DCACHE_CANT_MOUNT      0x0100
 #define DCACHE_GENOCIDE                0x0200
-#define DCACHE_MOUNTED         0x0400  /* is a mountpoint */
 
 #define DCACHE_OP_HASH         0x1000
 #define DCACHE_OP_COMPARE      0x2000
 #define DCACHE_OP_REVALIDATE   0x4000
 #define DCACHE_OP_DELETE       0x8000
 
+#define DCACHE_MOUNTED         0x10000 /* is a mountpoint */
+#define DCACHE_NEED_AUTOMOUNT  0x20000 /* handle automount on this dir */
+#define DCACHE_MANAGE_TRANSIT  0x40000 /* manage transit from this dirent */
+#define DCACHE_MANAGED_DENTRY \
+       (DCACHE_MOUNTED|DCACHE_NEED_AUTOMOUNT|DCACHE_MANAGE_TRANSIT)
+
 extern seqlock_t rename_lock;
 
 static inline int dname_external(struct dentry *dentry)
@@ -399,7 +406,12 @@ static inline void dont_mount(struct dentry *dentry)
 
 extern void dput(struct dentry *);
 
-static inline int d_mountpoint(struct dentry *dentry)
+static inline bool d_managed(struct dentry *dentry)
+{
+       return dentry->d_flags & DCACHE_MANAGED_DENTRY;
+}
+
+static inline bool d_mountpoint(struct dentry *dentry)
 {
        return dentry->d_flags & DCACHE_MOUNTED;
 }
index afc00af..a562fa5 100644 (file)
@@ -45,6 +45,7 @@
 #define AT_REMOVEDIR           0x200   /* Remove directory instead of
                                            unlinking file.  */
 #define AT_SYMLINK_FOLLOW      0x400   /* Follow symbolic links.  */
+#define AT_NO_AUTOMOUNT                0x800   /* Suppress terminal automount traversal */
 
 #ifdef __KERNEL__
 
index 08824e0..177b4dd 100644 (file)
@@ -242,6 +242,7 @@ struct inodes_stat_t {
 #define S_SWAPFILE     256     /* Do not truncate: swapon got its bmaps */
 #define S_PRIVATE      512     /* Inode is fs-internal */
 #define S_IMA          1024    /* Inode has an associated IMA struct */
+#define S_AUTOMOUNT    2048    /* Automount/referral quasi-directory */
 
 /*
  * Note that nosuid etc flags are inode-specific: setting some file-system
@@ -277,6 +278,7 @@ struct inodes_stat_t {
 #define IS_SWAPFILE(inode)     ((inode)->i_flags & S_SWAPFILE)
 #define IS_PRIVATE(inode)      ((inode)->i_flags & S_PRIVATE)
 #define IS_IMA(inode)          ((inode)->i_flags & S_IMA)
+#define IS_AUTOMOUNT(inode)    ((inode)->i_flags & S_AUTOMOUNT)
 
 /* the read-only stuff doesn't really belong here, but any other place is
    probably as bad and I don't want to create yet another include file. */
index 1869ea2..604f122 100644 (file)
@@ -60,7 +60,7 @@ struct vfsmount {
        struct super_block *mnt_sb;     /* pointer to superblock */
 #ifdef CONFIG_SMP
        struct mnt_pcp __percpu *mnt_pcp;
-       atomic_t mnt_longrefs;
+       atomic_t mnt_longterm;          /* how many of the refs are longterm */
 #else
        int mnt_count;
        int mnt_writers;
@@ -96,8 +96,6 @@ extern int mnt_clone_write(struct vfsmount *mnt);
 extern void mnt_drop_write(struct vfsmount *mnt);
 extern void mntput(struct vfsmount *mnt);
 extern struct vfsmount *mntget(struct vfsmount *mnt);
-extern void mntput_long(struct vfsmount *mnt);
-extern struct vfsmount *mntget_long(struct vfsmount *mnt);
 extern void mnt_pin(struct vfsmount *mnt);
 extern void mnt_unpin(struct vfsmount *mnt);
 extern int __mnt_is_readonly(struct vfsmount *mnt);
@@ -110,12 +108,7 @@ extern struct vfsmount *vfs_kern_mount(struct file_system_type *type,
                                      int flags, const char *name,
                                      void *data);
 
-struct nameidata;
-
-struct path;
-extern int do_add_mount(struct vfsmount *newmnt, struct path *path,
-                       int mnt_flags, struct list_head *fslist);
-
+extern void mnt_set_expiry(struct vfsmount *mnt, struct list_head *expiry_list);
 extern void mark_mounts_for_expiry(struct list_head *mounts);
 
 extern dev_t name_to_dev_t(char *name);
index 18d06ad..f276d4f 100644 (file)
@@ -45,6 +45,7 @@ enum {LAST_NORM, LAST_ROOT, LAST_DOT, LAST_DOTDOT, LAST_BIND};
  *  - ending slashes ok even for nonexistent files
  *  - internal "there are more path components" flag
  *  - dentry cache is untrusted; force a real lookup
+ *  - suppress terminal automount
  */
 #define LOOKUP_FOLLOW          0x0001
 #define LOOKUP_DIRECTORY       0x0002
@@ -53,6 +54,7 @@ enum {LAST_NORM, LAST_ROOT, LAST_DOT, LAST_DOTDOT, LAST_BIND};
 #define LOOKUP_PARENT          0x0010
 #define LOOKUP_REVAL           0x0020
 #define LOOKUP_RCU             0x0040
+#define LOOKUP_NO_AUTOMOUNT    0x0080
 /*
  * Intent data
  */
@@ -79,7 +81,8 @@ extern struct file *lookup_instantiate_filp(struct nameidata *nd, struct dentry
 
 extern struct dentry *lookup_one_len(const char *, struct dentry *, int);
 
-extern int follow_down(struct path *);
+extern int follow_down_one(struct path *);
+extern int follow_down(struct path *, bool);
 extern int follow_up(struct path *);
 
 extern struct dentry *lock_rename(struct dentry *, struct dentry *);
index 0779bb8..6023efa 100644 (file)
@@ -215,7 +215,6 @@ struct nfs_inode {
 #define NFS_INO_ADVISE_RDPLUS  (0)             /* advise readdirplus */
 #define NFS_INO_STALE          (1)             /* possible stale inode */
 #define NFS_INO_ACL_LRU_SET    (2)             /* Inode is on the LRU list */
-#define NFS_INO_MOUNTPOINT     (3)             /* inode is remote mountpoint */
 #define NFS_INO_FLUSHING       (4)             /* inode is flushing out data */
 #define NFS_INO_FSCACHE                (5)             /* inode can be cached by FS-Cache */
 #define NFS_INO_FSCACHE_LOCK   (6)             /* FS-Cache cookie management lock */
index a581e8c..edc98de 100644 (file)
@@ -10,9 +10,7 @@ struct path {
 };
 
 extern void path_get(struct path *);
-extern void path_get_long(struct path *);
 extern void path_put(struct path *);
-extern void path_put_long(struct path *);
 
 static inline int path_equal(const struct path *path1, const struct path *path2)
 {