don't bugger nd->seq on set_root_rcu() from follow_dotdot_rcu()
[firefly-linux-kernel-4.4.55.git] / fs / namei.c
index 9eb787e5c167fb601845590f0181d249bf515fb0..fe47e6d8e85fd4b3ebee6352aae0e2c96e2a61eb 100644 (file)
@@ -643,24 +643,22 @@ static int complete_walk(struct nameidata *nd)
 
 static __always_inline void set_root(struct nameidata *nd)
 {
-       if (!nd->root.mnt)
-               get_fs_root(current->fs, &nd->root);
+       get_fs_root(current->fs, &nd->root);
 }
 
 static int link_path_walk(const char *, struct nameidata *);
 
-static __always_inline void set_root_rcu(struct nameidata *nd)
+static __always_inline unsigned set_root_rcu(struct nameidata *nd)
 {
-       if (!nd->root.mnt) {
-               struct fs_struct *fs = current->fs;
-               unsigned seq;
+       struct fs_struct *fs = current->fs;
+       unsigned seq, res;
 
-               do {
-                       seq = read_seqcount_begin(&fs->seq);
-                       nd->root = fs->root;
-                       nd->seq = __read_seqcount_begin(&nd->root.dentry->d_seq);
-               } while (read_seqcount_retry(&fs->seq, seq));
-       }
+       do {
+               seq = read_seqcount_begin(&fs->seq);
+               nd->root = fs->root;
+               res = __read_seqcount_begin(&nd->root.dentry->d_seq);
+       } while (read_seqcount_retry(&fs->seq, seq));
+       return res;
 }
 
 static void path_put_conditional(struct path *path, struct nameidata *nd)
@@ -860,7 +858,8 @@ follow_link(struct path *link, struct nameidata *nd, void **p)
                        return PTR_ERR(s);
                }
                if (*s == '/') {
-                       set_root(nd);
+                       if (!nd->root.mnt)
+                               set_root(nd);
                        path_put(&nd->path);
                        nd->path = nd->root;
                        path_get(&nd->root);
@@ -1091,10 +1090,10 @@ int follow_down_one(struct path *path)
 }
 EXPORT_SYMBOL(follow_down_one);
 
-static inline bool managed_dentry_might_block(struct dentry *dentry)
+static inline int managed_dentry_rcu(struct dentry *dentry)
 {
-       return (dentry->d_flags & DCACHE_MANAGE_TRANSIT &&
-               dentry->d_op->d_manage(dentry, true) < 0);
+       return (dentry->d_flags & DCACHE_MANAGE_TRANSIT) ?
+               dentry->d_op->d_manage(dentry, true) : 0;
 }
 
 /*
@@ -1110,11 +1109,18 @@ static bool __follow_mount_rcu(struct nameidata *nd, struct path *path,
                 * Don't forget we might have a non-mountpoint managed dentry
                 * that wants to block transit.
                 */
-               if (unlikely(managed_dentry_might_block(path->dentry)))
+               switch (managed_dentry_rcu(path->dentry)) {
+               case -ECHILD:
+               default:
                        return false;
+               case -EISDIR:
+                       return true;
+               case 0:
+                       break;
+               }
 
                if (!d_mountpoint(path->dentry))
-                       return true;
+                       return !(path->dentry->d_flags & DCACHE_NEED_AUTOMOUNT);
 
                mounted = __lookup_mnt(path->mnt, path->dentry);
                if (!mounted)
@@ -1130,12 +1136,14 @@ static bool __follow_mount_rcu(struct nameidata *nd, struct path *path,
                 */
                *inode = path->dentry->d_inode;
        }
-       return read_seqretry(&mount_lock, nd->m_seq);
+       return !read_seqretry(&mount_lock, nd->m_seq) &&
+               !(path->dentry->d_flags & DCACHE_NEED_AUTOMOUNT);
 }
 
 static int follow_dotdot_rcu(struct nameidata *nd)
 {
-       set_root_rcu(nd);
+       if (!nd->root.mnt)
+               set_root_rcu(nd);
 
        while (1) {
                if (nd->path.dentry == nd->root.dentry &&
@@ -1166,7 +1174,7 @@ static int follow_dotdot_rcu(struct nameidata *nd)
                nd->path.mnt = &mounted->mnt;
                nd->path.dentry = mounted->mnt.mnt_root;
                nd->seq = read_seqcount_begin(&nd->path.dentry->d_seq);
-               if (!read_seqretry(&mount_lock, nd->m_seq))
+               if (read_seqretry(&mount_lock, nd->m_seq))
                        goto failed;
        }
        nd->inode = nd->path.dentry->d_inode;
@@ -1248,7 +1256,8 @@ static void follow_mount(struct path *path)
 
 static void follow_dotdot(struct nameidata *nd)
 {
-       set_root(nd);
+       if (!nd->root.mnt)
+               set_root(nd);
 
        while(1) {
                struct dentry *old = nd->path.dentry;
@@ -1402,11 +1411,8 @@ static int lookup_fast(struct nameidata *nd,
                }
                path->mnt = mnt;
                path->dentry = dentry;
-               if (unlikely(!__follow_mount_rcu(nd, path, inode)))
-                       goto unlazy;
-               if (unlikely(path->dentry->d_flags & DCACHE_NEED_AUTOMOUNT))
-                       goto unlazy;
-               return 0;
+               if (likely(__follow_mount_rcu(nd, path, inode)))
+                       return 0;
 unlazy:
                if (unlazy_walk(nd, dentry))
                        return -ECHILD;
@@ -1847,7 +1853,7 @@ static int path_init(int dfd, const char *name, unsigned int flags,
        if (*name=='/') {
                if (flags & LOOKUP_RCU) {
                        rcu_read_lock();
-                       set_root_rcu(nd);
+                       nd->seq = set_root_rcu(nd);
                } else {
                        set_root(nd);
                        path_get(&nd->root);
@@ -4019,7 +4025,7 @@ SYSCALL_DEFINE2(link, const char __user *, oldname, const char __user *, newname
  * The worst of all namespace operations - renaming directory. "Perverted"
  * doesn't even start to describe it. Somebody in UCB had a heck of a trip...
  * Problems:
- *     a) we can get into loop creation. Check is done in is_subdir().
+ *     a) we can get into loop creation.
  *     b) race potential - two innocent renames can create a loop together.
  *        That's where 4.4 screws up. Current fix: serialization on
  *        sb->s_vfs_rename_mutex. We might be more accurate, but that's another
@@ -4075,7 +4081,7 @@ int vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
        if (error)
                return error;
 
-       if (!old_dir->i_op->rename)
+       if (!old_dir->i_op->rename && !old_dir->i_op->rename2)
                return -EPERM;
 
        if (flags && !old_dir->i_op->rename2)
@@ -4134,10 +4140,11 @@ int vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
                if (error)
                        goto out;
        }
-       if (!flags) {
+       if (!old_dir->i_op->rename2) {
                error = old_dir->i_op->rename(old_dir, old_dentry,
                                              new_dir, new_dentry);
        } else {
+               WARN_ON(old_dir->i_op->rename != NULL);
                error = old_dir->i_op->rename2(old_dir, old_dentry,
                                               new_dir, new_dentry, flags);
        }