Merge tag 'mt76-for-kvalo-2020-06-07' of https://github.com/nbd168/wireless
[linux-2.6-microblaze.git] / fs / overlayfs / namei.c
index 59363c4..3566282 100644 (file)
@@ -191,16 +191,36 @@ static bool ovl_is_opaquedir(struct dentry *dentry)
        return ovl_check_dir_xattr(dentry, OVL_XATTR_OPAQUE);
 }
 
+static struct dentry *ovl_lookup_positive_unlocked(const char *name,
+                                                  struct dentry *base, int len,
+                                                  bool drop_negative)
+{
+       struct dentry *ret = lookup_one_len_unlocked(name, base, len);
+
+       if (!IS_ERR(ret) && d_flags_negative(smp_load_acquire(&ret->d_flags))) {
+               if (drop_negative && ret->d_lockref.count == 1) {
+                       spin_lock(&ret->d_lock);
+                       /* Recheck condition under lock */
+                       if (d_is_negative(ret) && ret->d_lockref.count == 1)
+                               __d_drop(ret);
+                       spin_unlock(&ret->d_lock);
+               }
+               dput(ret);
+               ret = ERR_PTR(-ENOENT);
+       }
+       return ret;
+}
+
 static int ovl_lookup_single(struct dentry *base, struct ovl_lookup_data *d,
                             const char *name, unsigned int namelen,
                             size_t prelen, const char *post,
-                            struct dentry **ret)
+                            struct dentry **ret, bool drop_negative)
 {
        struct dentry *this;
        int err;
        bool last_element = !post[0];
 
-       this = lookup_positive_unlocked(name, base, namelen);
+       this = ovl_lookup_positive_unlocked(name, base, namelen, drop_negative);
        if (IS_ERR(this)) {
                err = PTR_ERR(this);
                this = NULL;
@@ -276,7 +296,7 @@ out_err:
 }
 
 static int ovl_lookup_layer(struct dentry *base, struct ovl_lookup_data *d,
-                           struct dentry **ret)
+                           struct dentry **ret, bool drop_negative)
 {
        /* Counting down from the end, since the prefix can change */
        size_t rem = d->name.len - 1;
@@ -285,7 +305,7 @@ static int ovl_lookup_layer(struct dentry *base, struct ovl_lookup_data *d,
 
        if (d->name.name[0] != '/')
                return ovl_lookup_single(base, d, d->name.name, d->name.len,
-                                        0, "", ret);
+                                        0, "", ret, drop_negative);
 
        while (!IS_ERR_OR_NULL(base) && d_can_lookup(base)) {
                const char *s = d->name.name + d->name.len - rem;
@@ -298,7 +318,8 @@ static int ovl_lookup_layer(struct dentry *base, struct ovl_lookup_data *d,
                        return -EIO;
 
                err = ovl_lookup_single(base, d, s, thislen,
-                                       d->name.len - rem, next, &base);
+                                       d->name.len - rem, next, &base,
+                                       drop_negative);
                dput(dentry);
                if (err)
                        return err;
@@ -468,7 +489,7 @@ struct dentry *ovl_index_upper(struct ovl_fs *ofs, struct dentry *index)
        if (IS_ERR_OR_NULL(fh))
                return ERR_CAST(fh);
 
-       upper = ovl_decode_real_fh(fh, ofs->upper_mnt, true);
+       upper = ovl_decode_real_fh(fh, ovl_upper_mnt(ofs), true);
        kfree(fh);
 
        if (IS_ERR_OR_NULL(upper))
@@ -830,7 +851,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
        old_cred = ovl_override_creds(dentry->d_sb);
        upperdir = ovl_dentry_upper(dentry->d_parent);
        if (upperdir) {
-               err = ovl_lookup_layer(upperdir, &d, &upperdentry);
+               err = ovl_lookup_layer(upperdir, &d, &upperdentry, true);
                if (err)
                        goto out;
 
@@ -888,7 +909,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
                else
                        d.last = lower.layer->idx == roe->numlower;
 
-               err = ovl_lookup_layer(lower.dentry, &d, &this);
+               err = ovl_lookup_layer(lower.dentry, &d, &this, false);
                if (err)
                        goto out_put;
 
@@ -901,15 +922,6 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
                        goto out_put;
                }
 
-               /*
-                * Do not store intermediate metacopy dentries in chain,
-                * except top most lower metacopy dentry
-                */
-               if (d.metacopy && ctr) {
-                       dput(this);
-                       continue;
-               }
-
                /*
                 * If no origin fh is stored in upper of a merge dir, store fh
                 * of lower dir and set upper parent "impure".
@@ -944,9 +956,20 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
                        origin = this;
                }
 
-               stack[ctr].dentry = this;
-               stack[ctr].layer = lower.layer;
-               ctr++;
+               if (d.metacopy && ctr) {
+                       /*
+                        * Do not store intermediate metacopy dentries in
+                        * lower chain, except top most lower metacopy dentry.
+                        * Continue the loop so that if there is an absolute
+                        * redirect on this dentry, poe can be reset to roe.
+                        */
+                       dput(this);
+                       this = NULL;
+               } else {
+                       stack[ctr].dentry = this;
+                       stack[ctr].layer = lower.layer;
+                       ctr++;
+               }
 
                /*
                 * Following redirects can have security consequences: it's like
@@ -1067,6 +1090,8 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
                err = PTR_ERR(inode);
                if (IS_ERR(inode))
                        goto out_free_oe;
+               if (upperdentry && !uppermetacopy)
+                       ovl_set_flag(OVL_UPPERDATA, inode);
        }
 
        ovl_dentry_update_reval(dentry, upperdentry,