Commit 45d986d1 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'ovl-update-6.12' of git://git.kernel.org/pub/scm/linux/kernel/git/overlayfs/vfs

Pull overlayfs updates from Amir Goldstein:

 - Increase robustness of overlayfs to crashes in the case of underlying
   filesystems that to not guarantee metadata ordering to persistent
   storage (problem was reported with ubifs).

 - Deny mount inside container with features that require root
   privileges to work properly, instead of failing operations later.

 - Some clarifications to overlayfs documentation.

* tag 'ovl-update-6.12' of git://git.kernel.org/pub/scm/linux/kernel/git/overlayfs/vfs:
  ovl: fail if trusted xattrs are needed but caller lacks permission
  overlayfs.rst: update metacopy section in overlayfs documentation
  ovl: fsync after metadata copy-up
  ovl: don't set the superblock's errseq_t manually
parents 4a39ac5b 6c4a5f96
...@@ -367,8 +367,11 @@ Metadata only copy up ...@@ -367,8 +367,11 @@ Metadata only copy up
When the "metacopy" feature is enabled, overlayfs will only copy When the "metacopy" feature is enabled, overlayfs will only copy
up metadata (as opposed to whole file), when a metadata specific operation up metadata (as opposed to whole file), when a metadata specific operation
like chown/chmod is performed. Full file will be copied up later when like chown/chmod is performed. An upper file in this state is marked with
file is opened for WRITE operation. "trusted.overlayfs.metacopy" xattr which indicates that the upper file
contains no data. The data will be copied up later when file is opened for
WRITE operation. After the lower file's data is copied up,
the "trusted.overlayfs.metacopy" xattr is removed from the upper file.
In other words, this is delayed data copy up operation and data is copied In other words, this is delayed data copy up operation and data is copied
up when there is a need to actually modify data. up when there is a need to actually modify data.
......
...@@ -243,8 +243,24 @@ static int ovl_verify_area(loff_t pos, loff_t pos2, loff_t len, loff_t totlen) ...@@ -243,8 +243,24 @@ static int ovl_verify_area(loff_t pos, loff_t pos2, loff_t len, loff_t totlen)
return 0; return 0;
} }
static int ovl_sync_file(struct path *path)
{
struct file *new_file;
int err;
new_file = ovl_path_open(path, O_LARGEFILE | O_RDONLY);
if (IS_ERR(new_file))
return PTR_ERR(new_file);
err = vfs_fsync(new_file, 0);
fput(new_file);
return err;
}
static int ovl_copy_up_file(struct ovl_fs *ofs, struct dentry *dentry, static int ovl_copy_up_file(struct ovl_fs *ofs, struct dentry *dentry,
struct file *new_file, loff_t len) struct file *new_file, loff_t len,
bool datasync)
{ {
struct path datapath; struct path datapath;
struct file *old_file; struct file *old_file;
...@@ -342,7 +358,8 @@ static int ovl_copy_up_file(struct ovl_fs *ofs, struct dentry *dentry, ...@@ -342,7 +358,8 @@ static int ovl_copy_up_file(struct ovl_fs *ofs, struct dentry *dentry,
len -= bytes; len -= bytes;
} }
if (!error && ovl_should_sync(ofs)) /* call fsync once, either now or later along with metadata */
if (!error && ovl_should_sync(ofs) && datasync)
error = vfs_fsync(new_file, 0); error = vfs_fsync(new_file, 0);
out_fput: out_fput:
fput(old_file); fput(old_file);
...@@ -574,6 +591,7 @@ struct ovl_copy_up_ctx { ...@@ -574,6 +591,7 @@ struct ovl_copy_up_ctx {
bool indexed; bool indexed;
bool metacopy; bool metacopy;
bool metacopy_digest; bool metacopy_digest;
bool metadata_fsync;
}; };
static int ovl_link_up(struct ovl_copy_up_ctx *c) static int ovl_link_up(struct ovl_copy_up_ctx *c)
...@@ -634,7 +652,8 @@ static int ovl_copy_up_data(struct ovl_copy_up_ctx *c, const struct path *temp) ...@@ -634,7 +652,8 @@ static int ovl_copy_up_data(struct ovl_copy_up_ctx *c, const struct path *temp)
if (IS_ERR(new_file)) if (IS_ERR(new_file))
return PTR_ERR(new_file); return PTR_ERR(new_file);
err = ovl_copy_up_file(ofs, c->dentry, new_file, c->stat.size); err = ovl_copy_up_file(ofs, c->dentry, new_file, c->stat.size,
!c->metadata_fsync);
fput(new_file); fput(new_file);
return err; return err;
...@@ -701,6 +720,10 @@ static int ovl_copy_up_metadata(struct ovl_copy_up_ctx *c, struct dentry *temp) ...@@ -701,6 +720,10 @@ static int ovl_copy_up_metadata(struct ovl_copy_up_ctx *c, struct dentry *temp)
err = ovl_set_attr(ofs, temp, &c->stat); err = ovl_set_attr(ofs, temp, &c->stat);
inode_unlock(temp->d_inode); inode_unlock(temp->d_inode);
/* fsync metadata before moving it into upper dir */
if (!err && ovl_should_sync(ofs) && c->metadata_fsync)
err = ovl_sync_file(&upperpath);
return err; return err;
} }
...@@ -860,7 +883,8 @@ static int ovl_copy_up_tmpfile(struct ovl_copy_up_ctx *c) ...@@ -860,7 +883,8 @@ static int ovl_copy_up_tmpfile(struct ovl_copy_up_ctx *c)
temp = tmpfile->f_path.dentry; temp = tmpfile->f_path.dentry;
if (!c->metacopy && c->stat.size) { if (!c->metacopy && c->stat.size) {
err = ovl_copy_up_file(ofs, c->dentry, tmpfile, c->stat.size); err = ovl_copy_up_file(ofs, c->dentry, tmpfile, c->stat.size,
!c->metadata_fsync);
if (err) if (err)
goto out_fput; goto out_fput;
} }
...@@ -1135,6 +1159,17 @@ static int ovl_copy_up_one(struct dentry *parent, struct dentry *dentry, ...@@ -1135,6 +1159,17 @@ static int ovl_copy_up_one(struct dentry *parent, struct dentry *dentry,
!kgid_has_mapping(current_user_ns(), ctx.stat.gid)) !kgid_has_mapping(current_user_ns(), ctx.stat.gid))
return -EOVERFLOW; return -EOVERFLOW;
/*
* With metacopy disabled, we fsync after final metadata copyup, for
* both regular files and directories to get atomic copyup semantics
* on filesystems that do not use strict metadata ordering (e.g. ubifs).
*
* With metacopy enabled we want to avoid fsync on all meta copyup
* that will hurt performance of workloads such as chown -R, so we
* only fsync on data copyup as legacy behavior.
*/
ctx.metadata_fsync = !OVL_FS(dentry->d_sb)->config.metacopy &&
(S_ISREG(ctx.stat.mode) || S_ISDIR(ctx.stat.mode));
ctx.metacopy = ovl_need_meta_copy_up(dentry, ctx.stat.mode, flags); ctx.metacopy = ovl_need_meta_copy_up(dentry, ctx.stat.mode, flags);
if (parent) { if (parent) {
......
...@@ -755,11 +755,6 @@ int ovl_fs_params_verify(const struct ovl_fs_context *ctx, ...@@ -755,11 +755,6 @@ int ovl_fs_params_verify(const struct ovl_fs_context *ctx,
{ {
struct ovl_opt_set set = ctx->set; struct ovl_opt_set set = ctx->set;
if (ctx->nr_data > 0 && !config->metacopy) {
pr_err("lower data-only dirs require metacopy support.\n");
return -EINVAL;
}
/* Workdir/index are useless in non-upper mount */ /* Workdir/index are useless in non-upper mount */
if (!config->upperdir) { if (!config->upperdir) {
if (config->workdir) { if (config->workdir) {
...@@ -911,6 +906,39 @@ int ovl_fs_params_verify(const struct ovl_fs_context *ctx, ...@@ -911,6 +906,39 @@ int ovl_fs_params_verify(const struct ovl_fs_context *ctx,
config->metacopy = false; config->metacopy = false;
} }
/*
* Fail if we don't have trusted xattr capability and a feature was
* explicitly requested that requires them.
*/
if (!config->userxattr && !capable(CAP_SYS_ADMIN)) {
if (set.redirect &&
config->redirect_mode != OVL_REDIRECT_NOFOLLOW) {
pr_err("redirect_dir requires permission to access trusted xattrs\n");
return -EPERM;
}
if (config->metacopy && set.metacopy) {
pr_err("metacopy requires permission to access trusted xattrs\n");
return -EPERM;
}
if (config->verity_mode) {
pr_err("verity requires permission to access trusted xattrs\n");
return -EPERM;
}
if (ctx->nr_data > 0) {
pr_err("lower data-only dirs require permission to access trusted xattrs\n");
return -EPERM;
}
/*
* Other xattr-dependent features should be disabled without
* great disturbance to the user in ovl_make_workdir().
*/
}
if (ctx->nr_data > 0 && !config->metacopy) {
pr_err("lower data-only dirs require metacopy support.\n");
return -EINVAL;
}
return 0; return 0;
} }
......
...@@ -202,15 +202,9 @@ static int ovl_sync_fs(struct super_block *sb, int wait) ...@@ -202,15 +202,9 @@ static int ovl_sync_fs(struct super_block *sb, int wait)
int ret; int ret;
ret = ovl_sync_status(ofs); ret = ovl_sync_status(ofs);
/*
* We have to always set the err, because the return value isn't if (ret < 0)
* checked in syncfs, and instead indirectly return an error via
* the sb's writeback errseq, which VFS inspects after this call.
*/
if (ret < 0) {
errseq_set(&sb->s_wb_err, -EIO);
return -EIO; return -EIO;
}
if (!ret) if (!ret)
return ret; return ret;
......
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