ovl: fix inconsistent d_ino for legacy merge dir
authorAmir Goldstein <amir73il@gmail.com>
Wed, 3 Jan 2018 17:34:45 +0000 (19:34 +0200)
committerMiklos Szeredi <mszeredi@redhat.com>
Wed, 24 Jan 2018 09:18:19 +0000 (10:18 +0100)
For a merge dir that was copied up before v4.12 or that was hand crafted
offline (e.g. mkdir {upper/lower}/dir), upper dir does not contain the
'trusted.overlay.origin' xattr.  In that case, stat(2) on the merge dir
returns the lower dir st_ino, but getdents(2) returns the upper dir d_ino.

After this change, on merge dir lookup, missing origin xattr on upper
dir will be fixed and 'impure' xattr will be fixed on parent of the legacy
merge dir.

Suggested-by: zhangyi (F) <yi.zhang@huawei.com>
Reviewed-by: zhangyi (F) <yi.zhang@huawei.com>
Signed-off-by: Amir Goldstein <amir73il@gmail.com>
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
fs/overlayfs/copy_up.c
fs/overlayfs/namei.c
fs/overlayfs/overlayfs.h

index eb3b8d39fb6161f1c9de25840f9e608779b0cff7..206ececd5ae7bcac121622dba4a253b0ad051e6e 100644 (file)
@@ -288,8 +288,8 @@ out:
        return fh;
 }
 
-static int ovl_set_origin(struct dentry *dentry, struct dentry *lower,
-                         struct dentry *upper)
+int ovl_set_origin(struct dentry *dentry, struct dentry *lower,
+                  struct dentry *upper)
 {
        const struct ovl_fh *fh = NULL;
        int err;
index 69a43ede0a2ac4b7d12ccbb0f998b342964b75f6..69f4f19659fc53ef6d73399e37aa4fa272720271 100644 (file)
@@ -584,6 +584,27 @@ static int ovl_find_layer(struct ovl_fs *ofs, struct ovl_path *path)
        return i;
 }
 
+/* Fix missing 'origin' xattr */
+static int ovl_fix_origin(struct dentry *dentry, struct dentry *lower,
+                         struct dentry *upper)
+{
+       int err;
+
+       if (ovl_check_origin_xattr(upper))
+               return 0;
+
+       err = ovl_want_write(dentry);
+       if (err)
+               return err;
+
+       err = ovl_set_origin(dentry, lower, upper);
+       if (!err)
+               err = ovl_set_impure(dentry->d_parent, upper->d_parent);
+
+       ovl_drop_write(dentry);
+       return err;
+}
+
 struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
                          unsigned int flags)
 {
@@ -674,6 +695,18 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
                if (!this)
                        continue;
 
+               /*
+                * If no origin fh is stored in upper of a merge dir, store fh
+                * of lower dir and set upper parent "impure".
+                */
+               if (upperdentry && !ctr && !ofs->noxattr) {
+                       err = ovl_fix_origin(dentry, this, upperdentry);
+                       if (err) {
+                               dput(this);
+                               goto out_put;
+                       }
+               }
+
                stack[ctr].dentry = this;
                stack[ctr].layer = lower.layer;
                ctr++;
index b489099ccd493a54e231c9ee7e3ebe18809398b5..d1cfa69c98b5451059276505b88b3abcc4b668f8 100644 (file)
@@ -322,3 +322,5 @@ int ovl_copy_up_flags(struct dentry *dentry, int flags);
 int ovl_copy_xattr(struct dentry *old, struct dentry *new);
 int ovl_set_attr(struct dentry *upper, struct kstat *stat);
 struct ovl_fh *ovl_encode_fh(struct dentry *lower, bool is_upper);
+int ovl_set_origin(struct dentry *dentry, struct dentry *lower,
+                  struct dentry *upper);