ovl: use vfs_tmpfile_open() helper

If tmpfile is used for copy up, then use this helper to create the tmpfile
and open it at the same time.  This will later allow filesystems such as
fuse to do this operation atomically.

Reviewed-by: Christian Brauner (Microsoft) <brauner@kernel.org>
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
This commit is contained in:
Miklos Szeredi 2022-09-24 07:00:00 +02:00
parent 24a81759b6
commit 2b1a77461f
4 changed files with 74 additions and 64 deletions

View File

@ -193,11 +193,11 @@ static int ovl_copy_fileattr(struct inode *inode, struct path *old,
return ovl_real_fileattr_set(new, &newfa);
}
static int ovl_copy_up_data(struct ovl_fs *ofs, struct path *old,
struct path *new, loff_t len)
static int ovl_copy_up_file(struct ovl_fs *ofs, struct dentry *dentry,
struct file *new_file, loff_t len)
{
struct path datapath;
struct file *old_file;
struct file *new_file;
loff_t old_pos = 0;
loff_t new_pos = 0;
loff_t cloned;
@ -206,23 +206,18 @@ static int ovl_copy_up_data(struct ovl_fs *ofs, struct path *old,
bool skip_hole = false;
int error = 0;
if (len == 0)
return 0;
ovl_path_lowerdata(dentry, &datapath);
if (WARN_ON(datapath.dentry == NULL))
return -EIO;
old_file = ovl_path_open(old, O_LARGEFILE | O_RDONLY);
old_file = ovl_path_open(&datapath, O_LARGEFILE | O_RDONLY);
if (IS_ERR(old_file))
return PTR_ERR(old_file);
new_file = ovl_path_open(new, O_LARGEFILE | O_WRONLY);
if (IS_ERR(new_file)) {
error = PTR_ERR(new_file);
goto out_fput;
}
/* Try to use clone_file_range to clone up within the same fs */
cloned = do_clone_file_range(old_file, 0, new_file, 0, len, 0);
if (cloned == len)
goto out;
goto out_fput;
/* Couldn't clone, so now we try to copy the data */
/* Check if lower fs supports seek operation */
@ -282,10 +277,8 @@ static int ovl_copy_up_data(struct ovl_fs *ofs, struct path *old,
len -= bytes;
}
out:
if (!error && ovl_should_sync(ofs))
error = vfs_fsync(new_file, 0);
fput(new_file);
out_fput:
fput(old_file);
return error;
@ -556,31 +549,32 @@ static int ovl_link_up(struct ovl_copy_up_ctx *c)
return err;
}
static int ovl_copy_up_inode(struct ovl_copy_up_ctx *c, struct dentry *temp)
static int ovl_copy_up_data(struct ovl_copy_up_ctx *c, const struct path *temp)
{
struct ovl_fs *ofs = OVL_FS(c->dentry->d_sb);
struct file *new_file;
int err;
if (!S_ISREG(c->stat.mode) || c->metacopy || !c->stat.size)
return 0;
new_file = ovl_path_open(temp, O_LARGEFILE | O_WRONLY);
if (IS_ERR(new_file))
return PTR_ERR(new_file);
err = ovl_copy_up_file(ofs, c->dentry, new_file, c->stat.size);
fput(new_file);
return err;
}
static int ovl_copy_up_metadata(struct ovl_copy_up_ctx *c, struct dentry *temp)
{
struct ovl_fs *ofs = OVL_FS(c->dentry->d_sb);
struct inode *inode = d_inode(c->dentry);
struct path upperpath, datapath;
struct path upperpath = { .mnt = ovl_upper_mnt(ofs), .dentry = temp };
int err;
ovl_path_upper(c->dentry, &upperpath);
if (WARN_ON(upperpath.dentry != NULL))
return -EIO;
upperpath.dentry = temp;
/*
* Copy up data first and then xattrs. Writing data after
* xattrs will remove security.capability xattr automatically.
*/
if (S_ISREG(c->stat.mode) && !c->metacopy) {
ovl_path_lowerdata(c->dentry, &datapath);
err = ovl_copy_up_data(ofs, &datapath, &upperpath,
c->stat.size);
if (err)
return err;
}
err = ovl_copy_xattr(c->dentry->d_sb, &c->lowerpath, temp);
if (err)
return err;
@ -662,6 +656,7 @@ static int ovl_copy_up_workdir(struct ovl_copy_up_ctx *c)
struct ovl_fs *ofs = OVL_FS(c->dentry->d_sb);
struct inode *inode;
struct inode *udir = d_inode(c->destdir), *wdir = d_inode(c->workdir);
struct path path = { .mnt = ovl_upper_mnt(ofs) };
struct dentry *temp, *upper;
struct ovl_cu_creds cc;
int err;
@ -688,7 +683,16 @@ static int ovl_copy_up_workdir(struct ovl_copy_up_ctx *c)
if (IS_ERR(temp))
goto unlock;
err = ovl_copy_up_inode(c, temp);
/*
* Copy up data first and then xattrs. Writing data after
* xattrs will remove security.capability xattr automatically.
*/
path.dentry = temp;
err = ovl_copy_up_data(c, &path);
if (err)
goto cleanup;
err = ovl_copy_up_metadata(c, temp);
if (err)
goto cleanup;
@ -732,6 +736,7 @@ static int ovl_copy_up_tmpfile(struct ovl_copy_up_ctx *c)
struct ovl_fs *ofs = OVL_FS(c->dentry->d_sb);
struct inode *udir = d_inode(c->destdir);
struct dentry *temp, *upper;
struct file *tmpfile;
struct ovl_cu_creds cc;
int err;
@ -739,15 +744,22 @@ static int ovl_copy_up_tmpfile(struct ovl_copy_up_ctx *c)
if (err)
return err;
temp = ovl_do_tmpfile(ofs, c->workdir, c->stat.mode);
tmpfile = ovl_do_tmpfile(ofs, c->workdir, c->stat.mode);
ovl_revert_cu_creds(&cc);
if (IS_ERR(temp))
return PTR_ERR(temp);
if (IS_ERR(tmpfile))
return PTR_ERR(tmpfile);
err = ovl_copy_up_inode(c, temp);
temp = tmpfile->f_path.dentry;
if (!c->metacopy && c->stat.size) {
err = ovl_copy_up_file(ofs, c->dentry, tmpfile, c->stat.size);
if (err)
return err;
}
err = ovl_copy_up_metadata(c, temp);
if (err)
goto out_dput;
goto out_fput;
inode_lock_nested(udir, I_MUTEX_PARENT);
@ -761,16 +773,14 @@ static int ovl_copy_up_tmpfile(struct ovl_copy_up_ctx *c)
inode_unlock(udir);
if (err)
goto out_dput;
goto out_fput;
if (!c->metacopy)
ovl_set_upperdata(d_inode(c->dentry));
ovl_inode_update(d_inode(c->dentry), temp);
ovl_inode_update(d_inode(c->dentry), dget(temp));
return 0;
out_dput:
dput(temp);
out_fput:
fput(tmpfile);
return err;
}
@ -899,7 +909,7 @@ static ssize_t ovl_getxattr_value(struct path *path, char *name, char **value)
static int ovl_copy_up_meta_inode_data(struct ovl_copy_up_ctx *c)
{
struct ovl_fs *ofs = OVL_FS(c->dentry->d_sb);
struct path upperpath, datapath;
struct path upperpath;
int err;
char *capability = NULL;
ssize_t cap_size;
@ -908,10 +918,6 @@ static int ovl_copy_up_meta_inode_data(struct ovl_copy_up_ctx *c)
if (WARN_ON(upperpath.dentry == NULL))
return -EIO;
ovl_path_lowerdata(c->dentry, &datapath);
if (WARN_ON(datapath.dentry == NULL))
return -EIO;
if (c->stat.size) {
err = cap_size = ovl_getxattr_value(&upperpath, XATTR_NAME_CAPS,
&capability);
@ -919,7 +925,7 @@ static int ovl_copy_up_meta_inode_data(struct ovl_copy_up_ctx *c)
goto out;
}
err = ovl_copy_up_data(ofs, &datapath, &upperpath, c->stat.size);
err = ovl_copy_up_data(c, &upperpath);
if (err)
goto out_free;

View File

@ -310,14 +310,16 @@ static inline int ovl_do_whiteout(struct ovl_fs *ofs,
return err;
}
static inline struct dentry *ovl_do_tmpfile(struct ovl_fs *ofs,
struct dentry *dentry, umode_t mode)
static inline struct file *ovl_do_tmpfile(struct ovl_fs *ofs,
struct dentry *dentry, umode_t mode)
{
struct dentry *ret = vfs_tmpfile(ovl_upper_mnt_userns(ofs), dentry, mode, 0);
int err = PTR_ERR_OR_ZERO(ret);
struct path path = { .mnt = ovl_upper_mnt(ofs), .dentry = dentry };
struct file *file = vfs_tmpfile_open(ovl_upper_mnt_userns(ofs), &path, mode,
O_LARGEFILE | O_WRONLY, current_cred());
int err = PTR_ERR_OR_ZERO(file);
pr_debug("tmpfile(%pd2, 0%o) = %i\n", dentry, mode, err);
return ret;
return file;
}
static inline struct dentry *ovl_lookup_upper(struct ovl_fs *ofs,
@ -401,7 +403,7 @@ void ovl_inode_update(struct inode *inode, struct dentry *upperdentry);
void ovl_dir_modified(struct dentry *dentry, bool impurity);
u64 ovl_dentry_version_get(struct dentry *dentry);
bool ovl_is_whiteout(struct dentry *dentry);
struct file *ovl_path_open(struct path *path, int flags);
struct file *ovl_path_open(const struct path *path, int flags);
int ovl_copy_up_start(struct dentry *dentry, int flags);
void ovl_copy_up_end(struct dentry *dentry);
bool ovl_already_copied_up(struct dentry *dentry, int flags);

View File

@ -15,6 +15,7 @@
#include <linux/seq_file.h>
#include <linux/posix_acl_xattr.h>
#include <linux/exportfs.h>
#include <linux/file.h>
#include "overlayfs.h"
MODULE_AUTHOR("Miklos Szeredi <miklos@szeredi.hu>");
@ -1356,7 +1357,8 @@ static int ovl_make_workdir(struct super_block *sb, struct ovl_fs *ofs,
struct path *workpath)
{
struct vfsmount *mnt = ovl_upper_mnt(ofs);
struct dentry *temp, *workdir;
struct dentry *workdir;
struct file *tmpfile;
bool rename_whiteout;
bool d_type;
int fh_type;
@ -1392,10 +1394,10 @@ static int ovl_make_workdir(struct super_block *sb, struct ovl_fs *ofs,
pr_warn("upper fs needs to support d_type.\n");
/* Check if upper/work fs supports O_TMPFILE */
temp = ovl_do_tmpfile(ofs, ofs->workdir, S_IFREG | 0);
ofs->tmpfile = !IS_ERR(temp);
tmpfile = ovl_do_tmpfile(ofs, ofs->workdir, S_IFREG | 0);
ofs->tmpfile = !IS_ERR(tmpfile);
if (ofs->tmpfile)
dput(temp);
fput(tmpfile);
else
pr_warn("upper fs does not support tmpfile.\n");

View File

@ -490,7 +490,7 @@ bool ovl_is_whiteout(struct dentry *dentry)
return inode && IS_WHITEOUT(inode);
}
struct file *ovl_path_open(struct path *path, int flags)
struct file *ovl_path_open(const struct path *path, int flags)
{
struct inode *inode = d_inode(path->dentry);
struct user_namespace *real_mnt_userns = mnt_user_ns(path->mnt);