From: Amir Goldstein <[email protected]>

vfs_mkdir() may succeed and leave the dentry passed to it unhashed and
negative.  ovl_create_real() is the last caller breaking when that
happens.

[amir: split re-factoring of ovl_create_temp() to prep patch
       add comment about unhashed dir after mkdir
       add pr_warn() if mkdir succeeds and lookup fails]

Signed-off-by: Al Viro <[email protected]>
Signed-off-by: Amir Goldstein <[email protected]>
Signed-off-by: Miklos Szeredi <[email protected]>
---
 fs/overlayfs/dir.c | 35 +++++++++++++++++++++++++++++++++--
 1 file changed, 33 insertions(+), 2 deletions(-)

diff --git a/fs/overlayfs/dir.c b/fs/overlayfs/dir.c
index 1b181292a624..b33d37d1a87c 100644
--- a/fs/overlayfs/dir.c
+++ b/fs/overlayfs/dir.c
@@ -114,6 +114,37 @@ int ovl_cleanup_and_whiteout(struct dentry *workdir, 
struct inode *dir,
        goto out;
 }
 
+static struct dentry *ovl_mkdir_real(struct inode *dir, struct dentry *dentry,
+                                    umode_t mode)
+{
+       int err;
+
+       err = ovl_do_mkdir(dir, dentry, mode);
+       if (err) {
+               dput(dentry);
+               return ERR_PTR(err);
+       }
+
+       /*
+        * vfs_mkdir() may succeed and leave the dentry passed
+        * to it unhashed and negative. If that happens, try to
+        * lookup a new hashed and positive dentry.
+        */
+       if (unlikely(d_unhashed(dentry))) {
+               struct dentry *d;
+
+               d = lookup_one_len(dentry->d_name.name, dentry->d_parent,
+                                  dentry->d_name.len);
+               if (IS_ERR(d)) {
+                       pr_warn("overlayfs: failed lookup after mkdir (%pd2, 
err=%i).\n",
+                               dentry, err);
+               }
+               dput(dentry);
+               dentry = d;
+       }
+       return dentry;
+}
+
 struct dentry *ovl_create_real(struct inode *dir, struct dentry *newdentry,
                               struct ovl_cattr *attr)
 {
@@ -135,8 +166,8 @@ struct dentry *ovl_create_real(struct inode *dir, struct 
dentry *newdentry,
                        break;
 
                case S_IFDIR:
-                       err = ovl_do_mkdir(dir, newdentry, attr->mode);
-                       break;
+                       /* mkdir is special... */
+                       return ovl_mkdir_real(dir, newdentry, attr->mode);
 
                case S_IFCHR:
                case S_IFBLK:
-- 
2.14.3

Reply via email to