Upgrade to 4.4.50-rt62
[kvmfornfv.git] / kernel / fs / overlayfs / dir.c
index a2b1d7c..327177d 100644 (file)
@@ -12,6 +12,7 @@
 #include <linux/xattr.h>
 #include <linux/security.h>
 #include <linux/cred.h>
+#include <linux/atomic.h>
 #include "overlayfs.h"
 
 void ovl_cleanup(struct inode *wdir, struct dentry *wdentry)
@@ -35,8 +36,10 @@ struct dentry *ovl_lookup_temp(struct dentry *workdir, struct dentry *dentry)
 {
        struct dentry *temp;
        char name[20];
+       static atomic_t temp_id = ATOMIC_INIT(0);
 
-       snprintf(name, sizeof(name), "#%lx", (unsigned long) dentry);
+       /* counter is allowed to wrap, since temp dentries are ephemeral */
+       snprintf(name, sizeof(name), "#%x", atomic_inc_return(&temp_id));
 
        temp = lookup_one_len(name, workdir, strlen(name));
        if (!IS_ERR(temp) && temp->d_inode) {
@@ -511,6 +514,7 @@ static int ovl_remove_and_whiteout(struct dentry *dentry, bool is_dir)
        struct dentry *upper;
        struct dentry *opaquedir = NULL;
        int err;
+       int flags = 0;
 
        if (WARN_ON(!workdir))
                return -EROFS;
@@ -540,46 +544,39 @@ static int ovl_remove_and_whiteout(struct dentry *dentry, bool is_dir)
        if (err)
                goto out_dput;
 
-       whiteout = ovl_whiteout(workdir, dentry);
-       err = PTR_ERR(whiteout);
-       if (IS_ERR(whiteout))
+       upper = lookup_one_len(dentry->d_name.name, upperdir,
+                              dentry->d_name.len);
+       err = PTR_ERR(upper);
+       if (IS_ERR(upper))
                goto out_unlock;
 
-       upper = ovl_dentry_upper(dentry);
-       if (!upper) {
-               upper = lookup_one_len(dentry->d_name.name, upperdir,
-                                      dentry->d_name.len);
-               err = PTR_ERR(upper);
-               if (IS_ERR(upper))
-                       goto kill_whiteout;
-
-               err = ovl_do_rename(wdir, whiteout, udir, upper, 0);
-               dput(upper);
-               if (err)
-                       goto kill_whiteout;
-       } else {
-               int flags = 0;
+       err = -ESTALE;
+       if ((opaquedir && upper != opaquedir) ||
+           (!opaquedir && ovl_dentry_upper(dentry) &&
+            upper != ovl_dentry_upper(dentry))) {
+               goto out_dput_upper;
+       }
 
-               if (opaquedir)
-                       upper = opaquedir;
-               err = -ESTALE;
-               if (upper->d_parent != upperdir)
-                       goto kill_whiteout;
+       whiteout = ovl_whiteout(workdir, dentry);
+       err = PTR_ERR(whiteout);
+       if (IS_ERR(whiteout))
+               goto out_dput_upper;
 
-               if (is_dir)
-                       flags |= RENAME_EXCHANGE;
+       if (d_is_dir(upper))
+               flags = RENAME_EXCHANGE;
 
-               err = ovl_do_rename(wdir, whiteout, udir, upper, flags);
-               if (err)
-                       goto kill_whiteout;
+       err = ovl_do_rename(wdir, whiteout, udir, upper, flags);
+       if (err)
+               goto kill_whiteout;
+       if (flags)
+               ovl_cleanup(wdir, upper);
 
-               if (is_dir)
-                       ovl_cleanup(wdir, upper);
-       }
        ovl_dentry_version_inc(dentry->d_parent);
 out_d_drop:
        d_drop(dentry);
        dput(whiteout);
+out_dput_upper:
+       dput(upper);
 out_unlock:
        unlock_rename(workdir, upperdir);
 out_dput:
@@ -596,21 +593,25 @@ static int ovl_remove_upper(struct dentry *dentry, bool is_dir)
 {
        struct dentry *upperdir = ovl_dentry_upper(dentry->d_parent);
        struct inode *dir = upperdir->d_inode;
-       struct dentry *upper = ovl_dentry_upper(dentry);
+       struct dentry *upper;
        int err;
 
        mutex_lock_nested(&dir->i_mutex, I_MUTEX_PARENT);
+       upper = lookup_one_len(dentry->d_name.name, upperdir,
+                              dentry->d_name.len);
+       err = PTR_ERR(upper);
+       if (IS_ERR(upper))
+               goto out_unlock;
+
        err = -ESTALE;
-       if (upper->d_parent == upperdir) {
-               /* Don't let d_delete() think it can reset d_inode */
-               dget(upper);
+       if (upper == ovl_dentry_upper(dentry)) {
                if (is_dir)
                        err = vfs_rmdir(dir, upper);
                else
                        err = vfs_unlink(dir, upper, NULL);
-               dput(upper);
                ovl_dentry_version_inc(dentry->d_parent);
        }
+       dput(upper);
 
        /*
         * Keeping this dentry hashed would mean having to release
@@ -620,6 +621,7 @@ static int ovl_remove_upper(struct dentry *dentry, bool is_dir)
         */
        if (!err)
                d_drop(dentry);
+out_unlock:
        mutex_unlock(&dir->i_mutex);
 
        return err;
@@ -840,29 +842,39 @@ static int ovl_rename2(struct inode *olddir, struct dentry *old,
 
        trap = lock_rename(new_upperdir, old_upperdir);
 
-       olddentry = ovl_dentry_upper(old);
-       newdentry = ovl_dentry_upper(new);
-       if (newdentry) {
+
+       olddentry = lookup_one_len(old->d_name.name, old_upperdir,
+                                  old->d_name.len);
+       err = PTR_ERR(olddentry);
+       if (IS_ERR(olddentry))
+               goto out_unlock;
+
+       err = -ESTALE;
+       if (olddentry != ovl_dentry_upper(old))
+               goto out_dput_old;
+
+       newdentry = lookup_one_len(new->d_name.name, new_upperdir,
+                                  new->d_name.len);
+       err = PTR_ERR(newdentry);
+       if (IS_ERR(newdentry))
+               goto out_dput_old;
+
+       err = -ESTALE;
+       if (ovl_dentry_upper(new)) {
                if (opaquedir) {
-                       newdentry = opaquedir;
-                       opaquedir = NULL;
+                       if (newdentry != opaquedir)
+                               goto out_dput;
                } else {
-                       dget(newdentry);
+                       if (newdentry != ovl_dentry_upper(new))
+                               goto out_dput;
                }
        } else {
                new_create = true;
-               newdentry = lookup_one_len(new->d_name.name, new_upperdir,
-                                          new->d_name.len);
-               err = PTR_ERR(newdentry);
-               if (IS_ERR(newdentry))
-                       goto out_unlock;
+               if (!d_is_negative(newdentry) &&
+                   (!new_opaque || !ovl_is_whiteout(newdentry)))
+                       goto out_dput;
        }
 
-       err = -ESTALE;
-       if (olddentry->d_parent != old_upperdir)
-               goto out_dput;
-       if (newdentry->d_parent != new_upperdir)
-               goto out_dput;
        if (olddentry == trap)
                goto out_dput;
        if (newdentry == trap)
@@ -925,6 +937,8 @@ static int ovl_rename2(struct inode *olddir, struct dentry *old,
 
 out_dput:
        dput(newdentry);
+out_dput_old:
+       dput(olddentry);
 out_unlock:
        unlock_rename(new_upperdir, old_upperdir);
 out_revert_creds: