ovl: check if upper fs supports RENAME_WHITEOUT
authorAmir Goldstein <amir73il@gmail.com>
Thu, 20 Feb 2020 07:00:19 +0000 (09:00 +0200)
committerMiklos Szeredi <mszeredi@redhat.com>
Tue, 17 Mar 2020 14:04:22 +0000 (15:04 +0100)
As with other required upper fs features, we only warn if support is
missing to avoid breaking existing sub-optimal setups.

Signed-off-by: Amir Goldstein <amir73il@gmail.com>
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
fs/overlayfs/dir.c
fs/overlayfs/overlayfs.h
fs/overlayfs/super.c

index b3471ef51440a26a1c070d1c175ddb5d2c9c7077..c91b5aae8e32dd5985c7ff2dbbd7b8ff522202e9 100644 (file)
@@ -42,7 +42,7 @@ int ovl_cleanup(struct inode *wdir, struct dentry *wdentry)
        return err;
 }
 
-static struct dentry *ovl_lookup_temp(struct dentry *workdir)
+struct dentry *ovl_lookup_temp(struct dentry *workdir)
 {
        struct dentry *temp;
        char name[20];
index 70d17040ab4d2ab5c47762b1066f2c585bfd5a40..ea531b1075f253b72b5827419572d03b10ad34ee 100644 (file)
@@ -453,6 +453,7 @@ struct ovl_cattr {
 struct dentry *ovl_create_real(struct inode *dir, struct dentry *newdentry,
                               struct ovl_cattr *attr);
 int ovl_cleanup(struct inode *dir, struct dentry *dentry);
+struct dentry *ovl_lookup_temp(struct dentry *workdir);
 struct dentry *ovl_create_temp(struct dentry *workdir, struct ovl_cattr *attr);
 
 /* file.c */
index e8965fd1a608c716bb0388a874c1a38e7e050f75..b6ecaf95db9fef9ed2b181db1de8a7c9d4ea8e03 100644 (file)
@@ -1071,6 +1071,66 @@ out:
        return err;
 }
 
+/*
+ * Returns 1 if RENAME_WHITEOUT is supported, 0 if not supported and
+ * negative values if error is encountered.
+ */
+static int ovl_check_rename_whiteout(struct dentry *workdir)
+{
+       struct inode *dir = d_inode(workdir);
+       struct dentry *temp;
+       struct dentry *dest;
+       struct dentry *whiteout;
+       struct name_snapshot name;
+       int err;
+
+       inode_lock_nested(dir, I_MUTEX_PARENT);
+
+       temp = ovl_create_temp(workdir, OVL_CATTR(S_IFREG | 0));
+       err = PTR_ERR(temp);
+       if (IS_ERR(temp))
+               goto out_unlock;
+
+       dest = ovl_lookup_temp(workdir);
+       err = PTR_ERR(dest);
+       if (IS_ERR(dest)) {
+               dput(temp);
+               goto out_unlock;
+       }
+
+       /* Name is inline and stable - using snapshot as a copy helper */
+       take_dentry_name_snapshot(&name, temp);
+       err = ovl_do_rename(dir, temp, dir, dest, RENAME_WHITEOUT);
+       if (err) {
+               if (err == -EINVAL)
+                       err = 0;
+               goto cleanup_temp;
+       }
+
+       whiteout = lookup_one_len(name.name.name, workdir, name.name.len);
+       err = PTR_ERR(whiteout);
+       if (IS_ERR(whiteout))
+               goto cleanup_temp;
+
+       err = ovl_is_whiteout(whiteout);
+
+       /* Best effort cleanup of whiteout and temp file */
+       if (err)
+               ovl_cleanup(dir, whiteout);
+       dput(whiteout);
+
+cleanup_temp:
+       ovl_cleanup(dir, temp);
+       release_dentry_name_snapshot(&name);
+       dput(temp);
+       dput(dest);
+
+out_unlock:
+       inode_unlock(dir);
+
+       return err;
+}
+
 static int ovl_make_workdir(struct super_block *sb, struct ovl_fs *ofs,
                            struct path *workpath)
 {
@@ -1116,6 +1176,15 @@ static int ovl_make_workdir(struct super_block *sb, struct ovl_fs *ofs,
        else
                pr_warn("upper fs does not support tmpfile.\n");
 
+
+       /* Check if upper/work fs supports RENAME_WHITEOUT */
+       err = ovl_check_rename_whiteout(ofs->workdir);
+       if (err < 0)
+               goto out;
+
+       if (!err)
+               pr_warn("upper fs does not support RENAME_WHITEOUT.\n");
+
        /*
         * Check if upper/work fs supports trusted.overlay.* xattr
         */