Skip to content

Commit

Permalink
Merge tag 'ovl-update-5.2' of git://git.kernel.org/pub/scm/linux/kern…
Browse files Browse the repository at this point in the history
…el/git/mszeredi/vfs

Pull overlayfs update from Miklos Szeredi:
 "Just bug fixes in this small update"

* tag 'ovl-update-5.2' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/vfs:
  ovl: relax WARN_ON() for overlapping layers use case
  ovl: check the capability before cred overridden
  ovl: do not generate duplicate fsnotify events for "fake" path
  ovl: support stacked SEEK_HOLE/SEEK_DATA
  ovl: fix missing upper fs freeze protection on copy up for ioctl
  • Loading branch information
Linus Torvalds committed May 14, 2019
2 parents 4856118 + acf3062 commit 7e9890a
Show file tree
Hide file tree
Showing 5 changed files with 113 additions and 33 deletions.
6 changes: 3 additions & 3 deletions fs/overlayfs/copy_up.c
Original file line number Diff line number Diff line change
Expand Up @@ -909,14 +909,14 @@ static bool ovl_open_need_copy_up(struct dentry *dentry, int flags)
return true;
}

int ovl_open_maybe_copy_up(struct dentry *dentry, unsigned int file_flags)
int ovl_maybe_copy_up(struct dentry *dentry, int flags)
{
int err = 0;

if (ovl_open_need_copy_up(dentry, file_flags)) {
if (ovl_open_need_copy_up(dentry, flags)) {
err = ovl_want_write(dentry);
if (!err) {
err = ovl_copy_up_flags(dentry, file_flags);
err = ovl_copy_up_flags(dentry, flags);
ovl_drop_write(dentry);
}
}
Expand Down
2 changes: 1 addition & 1 deletion fs/overlayfs/dir.c
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,7 @@ static int ovl_instantiate(struct dentry *dentry, struct inode *inode,
* hashed directory inode aliases.
*/
inode = ovl_get_inode(dentry->d_sb, &oip);
if (WARN_ON(IS_ERR(inode)))
if (IS_ERR(inode))
return PTR_ERR(inode);
} else {
WARN_ON(ovl_inode_real(inode) != d_inode(newdentry));
Expand Down
133 changes: 106 additions & 27 deletions fs/overlayfs/file.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include <linux/mount.h>
#include <linux/xattr.h>
#include <linux/uio.h>
#include <linux/uaccess.h>
#include "overlayfs.h"

static char ovl_whatisit(struct inode *inode, struct inode *realinode)
Expand All @@ -29,10 +30,11 @@ static struct file *ovl_open_realfile(const struct file *file,
struct inode *inode = file_inode(file);
struct file *realfile;
const struct cred *old_cred;
int flags = file->f_flags | O_NOATIME | FMODE_NONOTIFY;

old_cred = ovl_override_creds(inode->i_sb);
realfile = open_with_fake_path(&file->f_path, file->f_flags | O_NOATIME,
realinode, current_cred());
realfile = open_with_fake_path(&file->f_path, flags, realinode,
current_cred());
revert_creds(old_cred);

pr_debug("open(%p[%pD2/%c], 0%o) -> (%p, 0%o)\n",
Expand All @@ -50,7 +52,7 @@ static int ovl_change_flags(struct file *file, unsigned int flags)
int err;

/* No atime modificaton on underlying */
flags |= O_NOATIME;
flags |= O_NOATIME | FMODE_NONOTIFY;

/* If some flag changed that cannot be changed then something's amiss */
if (WARN_ON((file->f_flags ^ flags) & ~OVL_SETFL_MASK))
Expand Down Expand Up @@ -116,11 +118,10 @@ static int ovl_real_fdget(const struct file *file, struct fd *real)

static int ovl_open(struct inode *inode, struct file *file)
{
struct dentry *dentry = file_dentry(file);
struct file *realfile;
int err;

err = ovl_open_maybe_copy_up(dentry, file->f_flags);
err = ovl_maybe_copy_up(file_dentry(file), file->f_flags);
if (err)
return err;

Expand All @@ -145,11 +146,47 @@ static int ovl_release(struct inode *inode, struct file *file)

static loff_t ovl_llseek(struct file *file, loff_t offset, int whence)
{
struct inode *realinode = ovl_inode_real(file_inode(file));
struct inode *inode = file_inode(file);
struct fd real;
const struct cred *old_cred;
ssize_t ret;

/*
* The two special cases below do not need to involve real fs,
* so we can optimizing concurrent callers.
*/
if (offset == 0) {
if (whence == SEEK_CUR)
return file->f_pos;

if (whence == SEEK_SET)
return vfs_setpos(file, 0, 0);
}

ret = ovl_real_fdget(file, &real);
if (ret)
return ret;

/*
* Overlay file f_pos is the master copy that is preserved
* through copy up and modified on read/write, but only real
* fs knows how to SEEK_HOLE/SEEK_DATA and real fs may impose
* limitations that are more strict than ->s_maxbytes for specific
* files, so we use the real file to perform seeks.
*/
inode_lock(inode);
real.file->f_pos = file->f_pos;

old_cred = ovl_override_creds(inode->i_sb);
ret = vfs_llseek(real.file, offset, whence);
revert_creds(old_cred);

file->f_pos = real.file->f_pos;
inode_unlock(inode);

fdput(real);

return generic_file_llseek_size(file, offset, whence,
realinode->i_sb->s_maxbytes,
i_size_read(realinode));
return ret;
}

static void ovl_file_accessed(struct file *file)
Expand Down Expand Up @@ -372,34 +409,76 @@ static long ovl_real_ioctl(struct file *file, unsigned int cmd,
return ret;
}

static long ovl_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
static unsigned int ovl_get_inode_flags(struct inode *inode)
{
unsigned int flags = READ_ONCE(inode->i_flags);
unsigned int ovl_iflags = 0;

if (flags & S_SYNC)
ovl_iflags |= FS_SYNC_FL;
if (flags & S_APPEND)
ovl_iflags |= FS_APPEND_FL;
if (flags & S_IMMUTABLE)
ovl_iflags |= FS_IMMUTABLE_FL;
if (flags & S_NOATIME)
ovl_iflags |= FS_NOATIME_FL;

return ovl_iflags;
}

static long ovl_ioctl_set_flags(struct file *file, unsigned long arg)
{
long ret;
struct inode *inode = file_inode(file);
unsigned int flags;
unsigned int old_flags;

if (!inode_owner_or_capable(inode))
return -EACCES;

if (get_user(flags, (int __user *) arg))
return -EFAULT;

ret = mnt_want_write_file(file);
if (ret)
return ret;

inode_lock(inode);

/* Check the capability before cred override */
ret = -EPERM;
old_flags = ovl_get_inode_flags(inode);
if (((flags ^ old_flags) & (FS_APPEND_FL | FS_IMMUTABLE_FL)) &&
!capable(CAP_LINUX_IMMUTABLE))
goto unlock;

ret = ovl_maybe_copy_up(file_dentry(file), O_WRONLY);
if (ret)
goto unlock;

ret = ovl_real_ioctl(file, FS_IOC_SETFLAGS, arg);

ovl_copyflags(ovl_inode_real(inode), inode);
unlock:
inode_unlock(inode);

mnt_drop_write_file(file);

return ret;

}

static long ovl_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
long ret;

switch (cmd) {
case FS_IOC_GETFLAGS:
ret = ovl_real_ioctl(file, cmd, arg);
break;

case FS_IOC_SETFLAGS:
if (!inode_owner_or_capable(inode))
return -EACCES;

ret = mnt_want_write_file(file);
if (ret)
return ret;

ret = ovl_copy_up_with_data(file_dentry(file));
if (!ret) {
ret = ovl_real_ioctl(file, cmd, arg);

inode_lock(inode);
ovl_copyflags(ovl_inode_real(inode), inode);
inode_unlock(inode);
}

mnt_drop_write_file(file);
ret = ovl_ioctl_set_flags(file, arg);
break;

default:
Expand Down
3 changes: 2 additions & 1 deletion fs/overlayfs/inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -832,7 +832,7 @@ struct inode *ovl_get_inode(struct super_block *sb,
int fsid = bylower ? oip->lowerpath->layer->fsid : 0;
bool is_dir, metacopy = false;
unsigned long ino = 0;
int err = -ENOMEM;
int err = oip->newinode ? -EEXIST : -ENOMEM;

if (!realinode)
realinode = d_inode(lowerdentry);
Expand Down Expand Up @@ -917,6 +917,7 @@ struct inode *ovl_get_inode(struct super_block *sb,
return inode;

out_err:
pr_warn_ratelimited("overlayfs: failed to get inode (%i)\n", err);
inode = ERR_PTR(err);
goto out;
}
2 changes: 1 addition & 1 deletion fs/overlayfs/overlayfs.h
Original file line number Diff line number Diff line change
Expand Up @@ -421,7 +421,7 @@ extern const struct file_operations ovl_file_operations;
int ovl_copy_up(struct dentry *dentry);
int ovl_copy_up_with_data(struct dentry *dentry);
int ovl_copy_up_flags(struct dentry *dentry, int flags);
int ovl_open_maybe_copy_up(struct dentry *dentry, unsigned int file_flags);
int ovl_maybe_copy_up(struct dentry *dentry, int flags);
int ovl_copy_xattr(struct dentry *old, struct dentry *new);
int ovl_set_attr(struct dentry *upper, struct kstat *stat);
struct ovl_fh *ovl_encode_real_fh(struct dentry *real, bool is_upper);
Expand Down

0 comments on commit 7e9890a

Please sign in to comment.