Commit 2b1a7746 authored by Miklos Szeredi's avatar Miklos Szeredi

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: default avatarChristian Brauner (Microsoft) <brauner@kernel.org>
Signed-off-by: default avatarMiklos Szeredi <mszeredi@redhat.com>
parent 24a81759
...@@ -193,11 +193,11 @@ static int ovl_copy_fileattr(struct inode *inode, struct path *old, ...@@ -193,11 +193,11 @@ static int ovl_copy_fileattr(struct inode *inode, struct path *old,
return ovl_real_fileattr_set(new, &newfa); return ovl_real_fileattr_set(new, &newfa);
} }
static int ovl_copy_up_data(struct ovl_fs *ofs, struct path *old, static int ovl_copy_up_file(struct ovl_fs *ofs, struct dentry *dentry,
struct path *new, loff_t len) struct file *new_file, loff_t len)
{ {
struct path datapath;
struct file *old_file; struct file *old_file;
struct file *new_file;
loff_t old_pos = 0; loff_t old_pos = 0;
loff_t new_pos = 0; loff_t new_pos = 0;
loff_t cloned; loff_t cloned;
...@@ -206,23 +206,18 @@ static int ovl_copy_up_data(struct ovl_fs *ofs, struct path *old, ...@@ -206,23 +206,18 @@ static int ovl_copy_up_data(struct ovl_fs *ofs, struct path *old,
bool skip_hole = false; bool skip_hole = false;
int error = 0; int error = 0;
if (len == 0) ovl_path_lowerdata(dentry, &datapath);
return 0; 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)) if (IS_ERR(old_file))
return PTR_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 */ /* 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); cloned = do_clone_file_range(old_file, 0, new_file, 0, len, 0);
if (cloned == len) if (cloned == len)
goto out; goto out_fput;
/* Couldn't clone, so now we try to copy the data */ /* Couldn't clone, so now we try to copy the data */
/* Check if lower fs supports seek operation */ /* Check if lower fs supports seek operation */
...@@ -282,10 +277,8 @@ static int ovl_copy_up_data(struct ovl_fs *ofs, struct path *old, ...@@ -282,10 +277,8 @@ static int ovl_copy_up_data(struct ovl_fs *ofs, struct path *old,
len -= bytes; len -= bytes;
} }
out:
if (!error && ovl_should_sync(ofs)) if (!error && ovl_should_sync(ofs))
error = vfs_fsync(new_file, 0); error = vfs_fsync(new_file, 0);
fput(new_file);
out_fput: out_fput:
fput(old_file); fput(old_file);
return error; return error;
...@@ -556,30 +549,31 @@ static int ovl_link_up(struct ovl_copy_up_ctx *c) ...@@ -556,30 +549,31 @@ static int ovl_link_up(struct ovl_copy_up_ctx *c)
return err; 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 ovl_fs *ofs = OVL_FS(c->dentry->d_sb);
struct inode *inode = d_inode(c->dentry); struct file *new_file;
struct path upperpath, datapath;
int err; int err;
ovl_path_upper(c->dentry, &upperpath); if (!S_ISREG(c->stat.mode) || c->metacopy || !c->stat.size)
if (WARN_ON(upperpath.dentry != NULL)) return 0;
return -EIO;
upperpath.dentry = temp; 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);
* Copy up data first and then xattrs. Writing data after fput(new_file);
* xattrs will remove security.capability xattr automatically.
*/ return err;
if (S_ISREG(c->stat.mode) && !c->metacopy) { }
ovl_path_lowerdata(c->dentry, &datapath);
err = ovl_copy_up_data(ofs, &datapath, &upperpath, static int ovl_copy_up_metadata(struct ovl_copy_up_ctx *c, struct dentry *temp)
c->stat.size); {
if (err) struct ovl_fs *ofs = OVL_FS(c->dentry->d_sb);
return err; struct inode *inode = d_inode(c->dentry);
} struct path upperpath = { .mnt = ovl_upper_mnt(ofs), .dentry = temp };
int err;
err = ovl_copy_xattr(c->dentry->d_sb, &c->lowerpath, temp); err = ovl_copy_xattr(c->dentry->d_sb, &c->lowerpath, temp);
if (err) if (err)
...@@ -662,6 +656,7 @@ static int ovl_copy_up_workdir(struct ovl_copy_up_ctx *c) ...@@ -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 ovl_fs *ofs = OVL_FS(c->dentry->d_sb);
struct inode *inode; struct inode *inode;
struct inode *udir = d_inode(c->destdir), *wdir = d_inode(c->workdir); 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 dentry *temp, *upper;
struct ovl_cu_creds cc; struct ovl_cu_creds cc;
int err; int err;
...@@ -688,7 +683,16 @@ static int ovl_copy_up_workdir(struct ovl_copy_up_ctx *c) ...@@ -688,7 +683,16 @@ static int ovl_copy_up_workdir(struct ovl_copy_up_ctx *c)
if (IS_ERR(temp)) if (IS_ERR(temp))
goto unlock; 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) if (err)
goto cleanup; goto cleanup;
...@@ -732,6 +736,7 @@ static int ovl_copy_up_tmpfile(struct ovl_copy_up_ctx *c) ...@@ -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 ovl_fs *ofs = OVL_FS(c->dentry->d_sb);
struct inode *udir = d_inode(c->destdir); struct inode *udir = d_inode(c->destdir);
struct dentry *temp, *upper; struct dentry *temp, *upper;
struct file *tmpfile;
struct ovl_cu_creds cc; struct ovl_cu_creds cc;
int err; int err;
...@@ -739,15 +744,22 @@ static int ovl_copy_up_tmpfile(struct ovl_copy_up_ctx *c) ...@@ -739,15 +744,22 @@ static int ovl_copy_up_tmpfile(struct ovl_copy_up_ctx *c)
if (err) if (err)
return 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); ovl_revert_cu_creds(&cc);
if (IS_ERR(temp)) if (IS_ERR(tmpfile))
return PTR_ERR(temp); 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) if (err)
goto out_dput; goto out_fput;
inode_lock_nested(udir, I_MUTEX_PARENT); inode_lock_nested(udir, I_MUTEX_PARENT);
...@@ -761,16 +773,14 @@ static int ovl_copy_up_tmpfile(struct ovl_copy_up_ctx *c) ...@@ -761,16 +773,14 @@ static int ovl_copy_up_tmpfile(struct ovl_copy_up_ctx *c)
inode_unlock(udir); inode_unlock(udir);
if (err) if (err)
goto out_dput; goto out_fput;
if (!c->metacopy) if (!c->metacopy)
ovl_set_upperdata(d_inode(c->dentry)); 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_fput:
fput(tmpfile);
out_dput:
dput(temp);
return err; return err;
} }
...@@ -899,7 +909,7 @@ static ssize_t ovl_getxattr_value(struct path *path, char *name, char **value) ...@@ -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) 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 ovl_fs *ofs = OVL_FS(c->dentry->d_sb);
struct path upperpath, datapath; struct path upperpath;
int err; int err;
char *capability = NULL; char *capability = NULL;
ssize_t cap_size; ssize_t cap_size;
...@@ -908,10 +918,6 @@ static int ovl_copy_up_meta_inode_data(struct ovl_copy_up_ctx *c) ...@@ -908,10 +918,6 @@ static int ovl_copy_up_meta_inode_data(struct ovl_copy_up_ctx *c)
if (WARN_ON(upperpath.dentry == NULL)) if (WARN_ON(upperpath.dentry == NULL))
return -EIO; return -EIO;
ovl_path_lowerdata(c->dentry, &datapath);
if (WARN_ON(datapath.dentry == NULL))
return -EIO;
if (c->stat.size) { if (c->stat.size) {
err = cap_size = ovl_getxattr_value(&upperpath, XATTR_NAME_CAPS, err = cap_size = ovl_getxattr_value(&upperpath, XATTR_NAME_CAPS,
&capability); &capability);
...@@ -919,7 +925,7 @@ static int ovl_copy_up_meta_inode_data(struct ovl_copy_up_ctx *c) ...@@ -919,7 +925,7 @@ static int ovl_copy_up_meta_inode_data(struct ovl_copy_up_ctx *c)
goto out; goto out;
} }
err = ovl_copy_up_data(ofs, &datapath, &upperpath, c->stat.size); err = ovl_copy_up_data(c, &upperpath);
if (err) if (err)
goto out_free; goto out_free;
......
...@@ -310,14 +310,16 @@ static inline int ovl_do_whiteout(struct ovl_fs *ofs, ...@@ -310,14 +310,16 @@ static inline int ovl_do_whiteout(struct ovl_fs *ofs,
return err; return err;
} }
static inline struct dentry *ovl_do_tmpfile(struct ovl_fs *ofs, static inline struct file *ovl_do_tmpfile(struct ovl_fs *ofs,
struct dentry *dentry, umode_t mode) struct dentry *dentry, umode_t mode)
{ {
struct dentry *ret = vfs_tmpfile(ovl_upper_mnt_userns(ofs), dentry, mode, 0); struct path path = { .mnt = ovl_upper_mnt(ofs), .dentry = dentry };
int err = PTR_ERR_OR_ZERO(ret); 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); 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, 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); ...@@ -401,7 +403,7 @@ void ovl_inode_update(struct inode *inode, struct dentry *upperdentry);
void ovl_dir_modified(struct dentry *dentry, bool impurity); void ovl_dir_modified(struct dentry *dentry, bool impurity);
u64 ovl_dentry_version_get(struct dentry *dentry); u64 ovl_dentry_version_get(struct dentry *dentry);
bool ovl_is_whiteout(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); int ovl_copy_up_start(struct dentry *dentry, int flags);
void ovl_copy_up_end(struct dentry *dentry); void ovl_copy_up_end(struct dentry *dentry);
bool ovl_already_copied_up(struct dentry *dentry, int flags); bool ovl_already_copied_up(struct dentry *dentry, int flags);
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
#include <linux/seq_file.h> #include <linux/seq_file.h>
#include <linux/posix_acl_xattr.h> #include <linux/posix_acl_xattr.h>
#include <linux/exportfs.h> #include <linux/exportfs.h>
#include <linux/file.h>
#include "overlayfs.h" #include "overlayfs.h"
MODULE_AUTHOR("Miklos Szeredi <miklos@szeredi.hu>"); MODULE_AUTHOR("Miklos Szeredi <miklos@szeredi.hu>");
...@@ -1356,7 +1357,8 @@ static int ovl_make_workdir(struct super_block *sb, struct ovl_fs *ofs, ...@@ -1356,7 +1357,8 @@ static int ovl_make_workdir(struct super_block *sb, struct ovl_fs *ofs,
struct path *workpath) struct path *workpath)
{ {
struct vfsmount *mnt = ovl_upper_mnt(ofs); struct vfsmount *mnt = ovl_upper_mnt(ofs);
struct dentry *temp, *workdir; struct dentry *workdir;
struct file *tmpfile;
bool rename_whiteout; bool rename_whiteout;
bool d_type; bool d_type;
int fh_type; int fh_type;
...@@ -1392,10 +1394,10 @@ static int ovl_make_workdir(struct super_block *sb, struct ovl_fs *ofs, ...@@ -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"); pr_warn("upper fs needs to support d_type.\n");
/* Check if upper/work fs supports O_TMPFILE */ /* Check if upper/work fs supports O_TMPFILE */
temp = ovl_do_tmpfile(ofs, ofs->workdir, S_IFREG | 0); tmpfile = ovl_do_tmpfile(ofs, ofs->workdir, S_IFREG | 0);
ofs->tmpfile = !IS_ERR(temp); ofs->tmpfile = !IS_ERR(tmpfile);
if (ofs->tmpfile) if (ofs->tmpfile)
dput(temp); fput(tmpfile);
else else
pr_warn("upper fs does not support tmpfile.\n"); pr_warn("upper fs does not support tmpfile.\n");
......
...@@ -490,7 +490,7 @@ bool ovl_is_whiteout(struct dentry *dentry) ...@@ -490,7 +490,7 @@ bool ovl_is_whiteout(struct dentry *dentry)
return inode && IS_WHITEOUT(inode); 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 inode *inode = d_inode(path->dentry);
struct user_namespace *real_mnt_userns = mnt_user_ns(path->mnt); struct user_namespace *real_mnt_userns = mnt_user_ns(path->mnt);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment