Commit 7e9890a3 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
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
parents 4856118f acf3062a
Loading
Loading
Loading
Loading
+3 −3
Original line number Diff line number Diff line
@@ -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);
		}
	}
+1 −1
Original line number Diff line number Diff line
@@ -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));
+106 −27
Original line number Diff line number Diff line
@@ -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)
@@ -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",
@@ -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))
@@ -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;

@@ -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;

	return generic_file_llseek_size(file, offset, whence,
					realinode->i_sb->s_maxbytes,
					i_size_read(realinode));
	/*
	 * 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 ret;
}

static void ovl_file_accessed(struct file *file)
@@ -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;

	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;

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

	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);

	/* 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:
		ret = ovl_ioctl_set_flags(file, arg);
		break;

	default:
+2 −1
Original line number Diff line number Diff line
@@ -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);
@@ -917,6 +917,7 @@ out:
	return inode;

out_err:
	pr_warn_ratelimited("overlayfs: failed to get inode (%i)\n", err);
	inode = ERR_PTR(err);
	goto out;
}
+1 −1
Original line number Diff line number Diff line
@@ -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);