Merge tag 'idmapped-mounts-v5.12' of git://git.kernel.org/pub/scm/linux/kernel/git...
[linux-2.6-microblaze.git] / fs / namei.c
index dbf53b3..216f16e 100644 (file)
@@ -680,6 +680,11 @@ static inline bool legitimize_path(struct nameidata *nd,
 static bool legitimize_links(struct nameidata *nd)
 {
        int i;
+       if (unlikely(nd->flags & LOOKUP_CACHED)) {
+               drop_links(nd);
+               nd->depth = 0;
+               return false;
+       }
        for (i = 0; i < nd->depth; i++) {
                struct saved *last = nd->stack + i;
                if (unlikely(!legitimize_path(nd, &last->link, last->seq))) {
@@ -719,17 +724,17 @@ static bool legitimize_root(struct nameidata *nd)
  */
 
 /**
- * unlazy_walk - try to switch to ref-walk mode.
+ * try_to_unlazy - try to switch to ref-walk mode.
  * @nd: nameidata pathwalk data
- * Returns: 0 on success, -ECHILD on failure
+ * Returns: true on success, false on failure
  *
- * unlazy_walk attempts to legitimize the current nd->path and nd->root
+ * try_to_unlazy attempts to legitimize the current nd->path and nd->root
  * for ref-walk mode.
  * Must be called from rcu-walk context.
- * Nothing should touch nameidata between unlazy_walk() failure and
+ * Nothing should touch nameidata between try_to_unlazy() failure and
  * terminate_walk().
  */
-static int unlazy_walk(struct nameidata *nd)
+static bool try_to_unlazy(struct nameidata *nd)
 {
        struct dentry *parent = nd->path.dentry;
 
@@ -744,30 +749,30 @@ static int unlazy_walk(struct nameidata *nd)
                goto out;
        rcu_read_unlock();
        BUG_ON(nd->inode != parent->d_inode);
-       return 0;
+       return true;
 
 out1:
        nd->path.mnt = NULL;
        nd->path.dentry = NULL;
 out:
        rcu_read_unlock();
-       return -ECHILD;
+       return false;
 }
 
 /**
- * unlazy_child - try to switch to ref-walk mode.
+ * try_to_unlazy_next - try to switch to ref-walk mode.
  * @nd: nameidata pathwalk data
- * @dentry: child of nd->path.dentry
- * @seq: seq number to check dentry against
- * Returns: 0 on success, -ECHILD on failure
+ * @dentry: next dentry to step into
+ * @seq: seq number to check @dentry against
+ * Returns: true on success, false on failure
  *
- * unlazy_child attempts to legitimize the current nd->path, nd->root and dentry
- * for ref-walk mode.  @dentry must be a path found by a do_lookup call on
- * @nd.  Must be called from rcu-walk context.
- * Nothing should touch nameidata between unlazy_child() failure and
+ * Similar to to try_to_unlazy(), but here we have the next dentry already
+ * picked by rcu-walk and want to legitimize that in addition to the current
+ * nd->path and nd->root for ref-walk mode.  Must be called from rcu-walk context.
+ * Nothing should touch nameidata between try_to_unlazy_next() failure and
  * terminate_walk().
  */
-static int unlazy_child(struct nameidata *nd, struct dentry *dentry, unsigned seq)
+static bool try_to_unlazy_next(struct nameidata *nd, struct dentry *dentry, unsigned seq)
 {
        BUG_ON(!(nd->flags & LOOKUP_RCU));
 
@@ -797,7 +802,7 @@ static int unlazy_child(struct nameidata *nd, struct dentry *dentry, unsigned se
        if (unlikely(!legitimize_root(nd)))
                goto out_dput;
        rcu_read_unlock();
-       return 0;
+       return true;
 
 out2:
        nd->path.mnt = NULL;
@@ -805,11 +810,11 @@ out1:
        nd->path.dentry = NULL;
 out:
        rcu_read_unlock();
-       return -ECHILD;
+       return false;
 out_dput:
        rcu_read_unlock();
        dput(dentry);
-       return -ECHILD;
+       return false;
 }
 
 static inline int d_revalidate(struct dentry *dentry, unsigned int flags)
@@ -842,7 +847,8 @@ static int complete_walk(struct nameidata *nd)
                 */
                if (!(nd->flags & (LOOKUP_ROOT | LOOKUP_IS_SCOPED)))
                        nd->root.mnt = NULL;
-               if (unlikely(unlazy_walk(nd)))
+               nd->flags &= ~LOOKUP_CACHED;
+               if (!try_to_unlazy(nd))
                        return -ECHILD;
        }
 
@@ -1448,7 +1454,7 @@ static inline int handle_mounts(struct nameidata *nd, struct dentry *dentry,
                        return -ENOENT;
                if (likely(__follow_mount_rcu(nd, path, inode, seqp)))
                        return 0;
-               if (unlazy_child(nd, dentry, seq))
+               if (!try_to_unlazy_next(nd, dentry, seq))
                        return -ECHILD;
                // *path might've been clobbered by __follow_mount_rcu()
                path->mnt = nd->path.mnt;
@@ -1542,7 +1548,7 @@ static struct dentry *lookup_fast(struct nameidata *nd,
                unsigned seq;
                dentry = __d_lookup_rcu(parent, &nd->last, &seq);
                if (unlikely(!dentry)) {
-                       if (unlazy_walk(nd))
+                       if (!try_to_unlazy(nd))
                                return ERR_PTR(-ECHILD);
                        return NULL;
                }
@@ -1569,9 +1575,9 @@ static struct dentry *lookup_fast(struct nameidata *nd,
                status = d_revalidate(dentry, nd->flags);
                if (likely(status > 0))
                        return dentry;
-               if (unlazy_child(nd, dentry, seq))
+               if (!try_to_unlazy_next(nd, dentry, seq))
                        return ERR_PTR(-ECHILD);
-               if (unlikely(status == -ECHILD))
+               if (status == -ECHILD)
                        /* we'd been told to redo it in non-rcu mode */
                        status = d_revalidate(dentry, nd->flags);
        } else {
@@ -1643,12 +1649,9 @@ static inline int may_lookup(struct user_namespace *mnt_userns,
                             struct nameidata *nd)
 {
        if (nd->flags & LOOKUP_RCU) {
-               int err = inode_permission(mnt_userns, nd->inode,
-                                          MAY_EXEC | MAY_NOT_BLOCK);
-               if (err != -ECHILD)
+               int err = inode_permission(mnt_userns, nd->inode, MAY_EXEC|MAY_NOT_BLOCK);
+               if (err != -ECHILD || !try_to_unlazy(nd))
                        return err;
-               if (unlazy_walk(nd))
-                       return -ECHILD;
        }
        return inode_permission(mnt_userns, nd->inode, MAY_EXEC);
 }
@@ -1670,7 +1673,7 @@ static int reserve_stack(struct nameidata *nd, struct path *link, unsigned seq)
                // unlazy even if we fail to grab the link - cleanup needs it
                bool grabbed_link = legitimize_path(nd, link, seq);
 
-               if (unlazy_walk(nd) != 0 || !grabbed_link)
+               if (!try_to_unlazy(nd) != 0 || !grabbed_link)
                        return -ECHILD;
 
                if (nd_alloc_stack(nd))
@@ -1712,7 +1715,7 @@ static const char *pick_link(struct nameidata *nd, struct path *link,
                touch_atime(&last->link);
                cond_resched();
        } else if (atime_needs_update(&last->link, inode)) {
-               if (unlikely(unlazy_walk(nd)))
+               if (!try_to_unlazy(nd))
                        return ERR_PTR(-ECHILD);
                touch_atime(&last->link);
        }
@@ -1729,11 +1732,8 @@ static const char *pick_link(struct nameidata *nd, struct path *link,
                get = inode->i_op->get_link;
                if (nd->flags & LOOKUP_RCU) {
                        res = get(NULL, inode, &last->done);
-                       if (res == ERR_PTR(-ECHILD)) {
-                               if (unlikely(unlazy_walk(nd)))
-                                       return ERR_PTR(-ECHILD);
+                       if (res == ERR_PTR(-ECHILD) && try_to_unlazy(nd))
                                res = get(link->dentry, inode, &last->done);
-                       }
                } else {
                        res = get(link->dentry, inode, &last->done);
                }
@@ -2275,7 +2275,7 @@ OK:
                }
                if (unlikely(!d_can_lookup(nd->path.dentry))) {
                        if (nd->flags & LOOKUP_RCU) {
-                               if (unlazy_walk(nd))
+                               if (!try_to_unlazy(nd))
                                        return -ECHILD;
                        }
                        return -ENOTDIR;
@@ -2289,6 +2289,10 @@ static const char *path_init(struct nameidata *nd, unsigned flags)
        int error;
        const char *s = nd->name->name;
 
+       /* LOOKUP_CACHED requires RCU, ask caller to retry */
+       if ((flags & (LOOKUP_RCU | LOOKUP_CACHED)) == LOOKUP_CACHED)
+               return ERR_PTR(-EAGAIN);
+
        if (!*s)
                flags &= ~LOOKUP_RCU;
        if (flags & LOOKUP_RCU)
@@ -3238,7 +3242,6 @@ static const char *open_last_lookups(struct nameidata *nd,
        struct inode *inode;
        struct dentry *dentry;
        const char *res;
-       int error;
 
        nd->flags |= op->intent;
 
@@ -3262,9 +3265,8 @@ static const char *open_last_lookups(struct nameidata *nd,
        } else {
                /* create side of things */
                if (nd->flags & LOOKUP_RCU) {
-                       error = unlazy_walk(nd);
-                       if (unlikely(error))
-                               return ERR_PTR(error);
+                       if (!try_to_unlazy(nd))
+                               return ERR_PTR(-ECHILD);
                }
                audit_inode(nd->name, dir, AUDIT_INODE_PARENT);
                /* trailing slashes? */
@@ -3273,9 +3275,7 @@ static const char *open_last_lookups(struct nameidata *nd,
        }
 
        if (open_flag & (O_CREAT | O_TRUNC | O_WRONLY | O_RDWR)) {
-               error = mnt_want_write(nd->path.mnt);
-               if (!error)
-                       got_write = true;
+               got_write = !mnt_want_write(nd->path.mnt);
                /*
                 * do _not_ fail yet - we might not need that or fail with
                 * a different error; let lookup_open() decide; we'll be
@@ -3454,10 +3454,8 @@ static int do_tmpfile(struct nameidata *nd, unsigned flags,
        audit_inode(nd->name, child, 0);
        /* Don't check for other permissions, the inode was just created */
        error = may_open(mnt_userns, &path, 0, op->open_flag);
-       if (error)
-               goto out2;
-       file->f_path.mnt = path.mnt;
-       error = finish_open(file, child, NULL);
+       if (!error)
+               error = vfs_open(&path, file);
 out2:
        mnt_drop_write(path.mnt);
 out: