Commit 72db8211 authored by Amir Goldstein's avatar Amir Goldstein Committed by Miklos Szeredi

ovl: copy up sync/noatime fileattr flags

When a lower file has sync/noatime fileattr flags, the behavior of
overlayfs post copy up is inconsistent.

Immediately after copy up, ovl inode still has the S_SYNC/S_NOATIME
inode flags copied from lower inode, so vfs code still treats the ovl
inode as sync/noatime.  After ovl inode evict or mount cycle,
the ovl inode does not have these inode flags anymore.

To fix this inconsistency, try to copy the fileattr flags on copy up
if the upper fs supports the fileattr_set() method.

This gives consistent behavior post copy up regardless of inode eviction
from cache.

We cannot copy up the immutable/append-only inode flags in a similar
manner, because immutable/append-only inodes cannot be linked and because
overlayfs will not be able to set overlay.* xattr on the upper inodes.

Those flags will be addressed by a followup patch.
Signed-off-by: default avatarAmir Goldstein <amir73il@gmail.com>
Signed-off-by: default avatarMiklos Szeredi <mszeredi@redhat.com>
parent a0c236b1
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#include <linux/fs.h> #include <linux/fs.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/file.h> #include <linux/file.h>
#include <linux/fileattr.h>
#include <linux/splice.h> #include <linux/splice.h>
#include <linux/xattr.h> #include <linux/xattr.h>
#include <linux/security.h> #include <linux/security.h>
...@@ -130,6 +131,31 @@ int ovl_copy_xattr(struct super_block *sb, struct dentry *old, ...@@ -130,6 +131,31 @@ int ovl_copy_xattr(struct super_block *sb, struct dentry *old,
return error; return error;
} }
static int ovl_copy_fileattr(struct path *old, struct path *new)
{
struct fileattr oldfa = { .flags_valid = true };
struct fileattr newfa = { .flags_valid = true };
int err;
err = ovl_real_fileattr_get(old, &oldfa);
if (err)
return err;
err = ovl_real_fileattr_get(new, &newfa);
if (err)
return err;
BUILD_BUG_ON(OVL_COPY_FS_FLAGS_MASK & ~FS_COMMON_FL);
newfa.flags &= ~OVL_COPY_FS_FLAGS_MASK;
newfa.flags |= (oldfa.flags & OVL_COPY_FS_FLAGS_MASK);
BUILD_BUG_ON(OVL_COPY_FSX_FLAGS_MASK & ~FS_XFLAG_COMMON);
newfa.fsx_xflags &= ~OVL_COPY_FSX_FLAGS_MASK;
newfa.fsx_xflags |= (oldfa.fsx_xflags & OVL_COPY_FSX_FLAGS_MASK);
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_data(struct ovl_fs *ofs, struct path *old,
struct path *new, loff_t len) struct path *new, loff_t len)
{ {
...@@ -493,20 +519,21 @@ static int ovl_link_up(struct ovl_copy_up_ctx *c) ...@@ -493,20 +519,21 @@ static int ovl_link_up(struct ovl_copy_up_ctx *c)
static int ovl_copy_up_inode(struct ovl_copy_up_ctx *c, struct dentry *temp) static int ovl_copy_up_inode(struct ovl_copy_up_ctx *c, struct dentry *temp)
{ {
struct ovl_fs *ofs = OVL_FS(c->dentry->d_sb); struct ovl_fs *ofs = OVL_FS(c->dentry->d_sb);
int err; struct inode *inode = d_inode(c->dentry);
/*
* 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) {
struct path upperpath, datapath; struct path upperpath, datapath;
int err;
ovl_path_upper(c->dentry, &upperpath); ovl_path_upper(c->dentry, &upperpath);
if (WARN_ON(upperpath.dentry != NULL)) if (WARN_ON(upperpath.dentry != NULL))
return -EIO; return -EIO;
upperpath.dentry = temp; 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); ovl_path_lowerdata(c->dentry, &datapath);
err = ovl_copy_up_data(ofs, &datapath, &upperpath, err = ovl_copy_up_data(ofs, &datapath, &upperpath,
c->stat.size); c->stat.size);
...@@ -518,6 +545,16 @@ static int ovl_copy_up_inode(struct ovl_copy_up_ctx *c, struct dentry *temp) ...@@ -518,6 +545,16 @@ static int ovl_copy_up_inode(struct ovl_copy_up_ctx *c, struct dentry *temp)
if (err) if (err)
return err; return err;
if (inode->i_flags & OVL_COPY_I_FLAGS_MASK) {
/*
* Copy the fileattr inode flags that are the source of already
* copied i_flags
*/
err = ovl_copy_fileattr(&c->lowerpath, &upperpath);
if (err)
return err;
}
/* /*
* Store identifier of lower inode in upper inode xattr to * Store identifier of lower inode in upper inode xattr to
* allow lookup of the copy up origin inode. * allow lookup of the copy up origin inode.
......
...@@ -503,16 +503,14 @@ static int ovl_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, ...@@ -503,16 +503,14 @@ static int ovl_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
* Introducing security_inode_fileattr_get/set() hooks would solve this issue * Introducing security_inode_fileattr_get/set() hooks would solve this issue
* properly. * properly.
*/ */
static int ovl_security_fileattr(struct dentry *dentry, struct fileattr *fa, static int ovl_security_fileattr(struct path *realpath, struct fileattr *fa,
bool set) bool set)
{ {
struct path realpath;
struct file *file; struct file *file;
unsigned int cmd; unsigned int cmd;
int err; int err;
ovl_path_real(dentry, &realpath); file = dentry_open(realpath, O_RDONLY, current_cred());
file = dentry_open(&realpath, O_RDONLY, current_cred());
if (IS_ERR(file)) if (IS_ERR(file))
return PTR_ERR(file); return PTR_ERR(file);
...@@ -527,11 +525,22 @@ static int ovl_security_fileattr(struct dentry *dentry, struct fileattr *fa, ...@@ -527,11 +525,22 @@ static int ovl_security_fileattr(struct dentry *dentry, struct fileattr *fa,
return err; return err;
} }
int ovl_real_fileattr_set(struct path *realpath, struct fileattr *fa)
{
int err;
err = ovl_security_fileattr(realpath, fa, true);
if (err)
return err;
return vfs_fileattr_set(&init_user_ns, realpath->dentry, fa);
}
int ovl_fileattr_set(struct user_namespace *mnt_userns, int ovl_fileattr_set(struct user_namespace *mnt_userns,
struct dentry *dentry, struct fileattr *fa) struct dentry *dentry, struct fileattr *fa)
{ {
struct inode *inode = d_inode(dentry); struct inode *inode = d_inode(dentry);
struct dentry *upperdentry; struct path upperpath;
const struct cred *old_cred; const struct cred *old_cred;
int err; int err;
...@@ -541,12 +550,10 @@ int ovl_fileattr_set(struct user_namespace *mnt_userns, ...@@ -541,12 +550,10 @@ int ovl_fileattr_set(struct user_namespace *mnt_userns,
err = ovl_copy_up(dentry); err = ovl_copy_up(dentry);
if (!err) { if (!err) {
upperdentry = ovl_dentry_upper(dentry); ovl_path_real(dentry, &upperpath);
old_cred = ovl_override_creds(inode->i_sb); old_cred = ovl_override_creds(inode->i_sb);
err = ovl_security_fileattr(dentry, fa, true); err = ovl_real_fileattr_set(&upperpath, fa);
if (!err)
err = vfs_fileattr_set(&init_user_ns, upperdentry, fa);
revert_creds(old_cred); revert_creds(old_cred);
ovl_copyflags(ovl_inode_real(inode), inode); ovl_copyflags(ovl_inode_real(inode), inode);
} }
...@@ -555,17 +562,28 @@ int ovl_fileattr_set(struct user_namespace *mnt_userns, ...@@ -555,17 +562,28 @@ int ovl_fileattr_set(struct user_namespace *mnt_userns,
return err; return err;
} }
int ovl_real_fileattr_get(struct path *realpath, struct fileattr *fa)
{
int err;
err = ovl_security_fileattr(realpath, fa, false);
if (err)
return err;
return vfs_fileattr_get(realpath->dentry, fa);
}
int ovl_fileattr_get(struct dentry *dentry, struct fileattr *fa) int ovl_fileattr_get(struct dentry *dentry, struct fileattr *fa)
{ {
struct inode *inode = d_inode(dentry); struct inode *inode = d_inode(dentry);
struct dentry *realdentry = ovl_dentry_real(dentry); struct path realpath;
const struct cred *old_cred; const struct cred *old_cred;
int err; int err;
ovl_path_real(dentry, &realpath);
old_cred = ovl_override_creds(inode->i_sb); old_cred = ovl_override_creds(inode->i_sb);
err = ovl_security_fileattr(dentry, fa, false); err = ovl_real_fileattr_get(&realpath, fa);
if (!err)
err = vfs_fileattr_get(realdentry, fa);
revert_creds(old_cred); revert_creds(old_cred);
return err; return err;
......
...@@ -518,9 +518,20 @@ static inline void ovl_copyattr(struct inode *from, struct inode *to) ...@@ -518,9 +518,20 @@ static inline void ovl_copyattr(struct inode *from, struct inode *to)
i_size_write(to, i_size_read(from)); i_size_write(to, i_size_read(from));
} }
/* vfs inode flags copied from real to ovl inode */
#define OVL_COPY_I_FLAGS_MASK (S_SYNC | S_NOATIME | S_APPEND | S_IMMUTABLE)
/*
* fileattr flags copied from lower to upper inode on copy up.
* We cannot copy immutable/append-only flags, because that would prevevnt
* linking temp inode to upper dir.
*/
#define OVL_COPY_FS_FLAGS_MASK (FS_SYNC_FL | FS_NOATIME_FL)
#define OVL_COPY_FSX_FLAGS_MASK (FS_XFLAG_SYNC | FS_XFLAG_NOATIME)
static inline void ovl_copyflags(struct inode *from, struct inode *to) static inline void ovl_copyflags(struct inode *from, struct inode *to)
{ {
unsigned int mask = S_SYNC | S_IMMUTABLE | S_APPEND | S_NOATIME; unsigned int mask = OVL_COPY_I_FLAGS_MASK;
inode_set_flags(to, from->i_flags & mask, mask); inode_set_flags(to, from->i_flags & mask, mask);
} }
...@@ -548,6 +559,8 @@ struct dentry *ovl_create_temp(struct dentry *workdir, struct ovl_cattr *attr); ...@@ -548,6 +559,8 @@ struct dentry *ovl_create_temp(struct dentry *workdir, struct ovl_cattr *attr);
extern const struct file_operations ovl_file_operations; extern const struct file_operations ovl_file_operations;
int __init ovl_aio_request_cache_init(void); int __init ovl_aio_request_cache_init(void);
void ovl_aio_request_cache_destroy(void); void ovl_aio_request_cache_destroy(void);
int ovl_real_fileattr_get(struct path *realpath, struct fileattr *fa);
int ovl_real_fileattr_set(struct path *realpath, struct fileattr *fa);
int ovl_fileattr_get(struct dentry *dentry, struct fileattr *fa); int ovl_fileattr_get(struct dentry *dentry, struct fileattr *fa);
int ovl_fileattr_set(struct user_namespace *mnt_userns, int ovl_fileattr_set(struct user_namespace *mnt_userns,
struct dentry *dentry, struct fileattr *fa); struct dentry *dentry, struct fileattr *fa);
......
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