Commit e7675238 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull overlayfs updates from Amir Goldstein:

 - Fix a syzbot reported NULL pointer deref with bfs lower layers

 - Fix a copy up failure of large file from lower fuse fs

 - Followup cleanup of backing_file API from Miklos

 - Introduction and use of revert/override_creds_light() helpers, that
   were suggested by Christian as a mitigation to cache line bouncing
   and false sharing of fields in overlayfs creator_cred long lived
   struct cred copy.

 - Store up to two backing file references (upper and lower) in an
   ovl_file container instead of storing a single backing file in
   file->private_data.

   This is used to avoid the practice of opening a short lived backing
   file for the duration of some file operations and to avoid the
   specialized use of FDPUT_FPUT in such occasions, that was getting in
   the way of Al's fd_file() conversions.

* tag 'ovl-update-6.13' of git://git.kernel.org/pub/scm/linux/kernel/git/overlayfs/vfs:
  ovl: Filter invalid inodes with missing lookup function
  ovl: convert ovl_real_fdget() callers to ovl_real_file()
  ovl: convert ovl_real_fdget_path() callers to ovl_real_file_path()
  ovl: store upper real file in ovl_file struct
  ovl: allocate a container struct ovl_file for ovl private context
  ovl: do not open non-data lower file for fsync
  ovl: Optimize override/revert creds
  ovl: pass an explicit reference of creators creds to callers
  ovl: use wrapper ovl_revert_creds()
  fs/backing-file: Convert to revert/override_creds_light()
  cred: Add a light version of override/revert_creds()
  backing-file: clean up the API
  ovl: properly handle large files in ovl_security_fileattr
parents 060fc106 c8b359dd
Loading
Loading
Loading
Loading
+26 −27
Original line number Diff line number Diff line
@@ -80,7 +80,7 @@ struct backing_aio {
	refcount_t ref;
	struct kiocb *orig_iocb;
	/* used for aio completion */
	void (*end_write)(struct file *, loff_t, ssize_t);
	void (*end_write)(struct kiocb *iocb, ssize_t);
	struct work_struct work;
	long res;
};
@@ -108,10 +108,10 @@ static void backing_aio_cleanup(struct backing_aio *aio, long res)
	struct kiocb *iocb = &aio->iocb;
	struct kiocb *orig_iocb = aio->orig_iocb;

	orig_iocb->ki_pos = iocb->ki_pos;
	if (aio->end_write)
		aio->end_write(orig_iocb->ki_filp, iocb->ki_pos, res);
		aio->end_write(orig_iocb, res);

	orig_iocb->ki_pos = iocb->ki_pos;
	backing_aio_put(aio);
}

@@ -176,7 +176,7 @@ ssize_t backing_file_read_iter(struct file *file, struct iov_iter *iter,
	    !(file->f_mode & FMODE_CAN_ODIRECT))
		return -EINVAL;

	old_cred = override_creds(ctx->cred);
	old_cred = override_creds_light(ctx->cred);
	if (is_sync_kiocb(iocb)) {
		rwf_t rwf = iocb_to_rw_flags(flags);

@@ -197,10 +197,10 @@ ssize_t backing_file_read_iter(struct file *file, struct iov_iter *iter,
			backing_aio_cleanup(aio, ret);
	}
out:
	revert_creds(old_cred);
	revert_creds_light(old_cred);

	if (ctx->accessed)
		ctx->accessed(ctx->user_file);
		ctx->accessed(iocb->ki_filp);

	return ret;
}
@@ -219,7 +219,7 @@ ssize_t backing_file_write_iter(struct file *file, struct iov_iter *iter,
	if (!iov_iter_count(iter))
		return 0;

	ret = file_remove_privs(ctx->user_file);
	ret = file_remove_privs(iocb->ki_filp);
	if (ret)
		return ret;

@@ -233,13 +233,13 @@ ssize_t backing_file_write_iter(struct file *file, struct iov_iter *iter,
	 */
	flags &= ~IOCB_DIO_CALLER_COMP;

	old_cred = override_creds(ctx->cred);
	old_cred = override_creds_light(ctx->cred);
	if (is_sync_kiocb(iocb)) {
		rwf_t rwf = iocb_to_rw_flags(flags);

		ret = vfs_iter_write(file, iter, &iocb->ki_pos, rwf);
		if (ctx->end_write)
			ctx->end_write(ctx->user_file, iocb->ki_pos, ret);
			ctx->end_write(iocb, ret);
	} else {
		struct backing_aio *aio;

@@ -264,13 +264,13 @@ ssize_t backing_file_write_iter(struct file *file, struct iov_iter *iter,
			backing_aio_cleanup(aio, ret);
	}
out:
	revert_creds(old_cred);
	revert_creds_light(old_cred);

	return ret;
}
EXPORT_SYMBOL_GPL(backing_file_write_iter);

ssize_t backing_file_splice_read(struct file *in, loff_t *ppos,
ssize_t backing_file_splice_read(struct file *in, struct kiocb *iocb,
				 struct pipe_inode_info *pipe, size_t len,
				 unsigned int flags,
				 struct backing_file_ctx *ctx)
@@ -281,20 +281,20 @@ ssize_t backing_file_splice_read(struct file *in, loff_t *ppos,
	if (WARN_ON_ONCE(!(in->f_mode & FMODE_BACKING)))
		return -EIO;

	old_cred = override_creds(ctx->cred);
	ret = vfs_splice_read(in, ppos, pipe, len, flags);
	revert_creds(old_cred);
	old_cred = override_creds_light(ctx->cred);
	ret = vfs_splice_read(in, &iocb->ki_pos, pipe, len, flags);
	revert_creds_light(old_cred);

	if (ctx->accessed)
		ctx->accessed(ctx->user_file);
		ctx->accessed(iocb->ki_filp);

	return ret;
}
EXPORT_SYMBOL_GPL(backing_file_splice_read);

ssize_t backing_file_splice_write(struct pipe_inode_info *pipe,
				  struct file *out, loff_t *ppos, size_t len,
				  unsigned int flags,
				  struct file *out, struct kiocb *iocb,
				  size_t len, unsigned int flags,
				  struct backing_file_ctx *ctx)
{
	const struct cred *old_cred;
@@ -306,18 +306,18 @@ ssize_t backing_file_splice_write(struct pipe_inode_info *pipe,
	if (!out->f_op->splice_write)
		return -EINVAL;

	ret = file_remove_privs(ctx->user_file);
	ret = file_remove_privs(iocb->ki_filp);
	if (ret)
		return ret;

	old_cred = override_creds(ctx->cred);
	old_cred = override_creds_light(ctx->cred);
	file_start_write(out);
	ret = out->f_op->splice_write(pipe, out, ppos, len, flags);
	ret = out->f_op->splice_write(pipe, out, &iocb->ki_pos, len, flags);
	file_end_write(out);
	revert_creds(old_cred);
	revert_creds_light(old_cred);

	if (ctx->end_write)
		ctx->end_write(ctx->user_file, ppos ? *ppos : 0, ret);
		ctx->end_write(iocb, ret);

	return ret;
}
@@ -329,8 +329,7 @@ int backing_file_mmap(struct file *file, struct vm_area_struct *vma,
	const struct cred *old_cred;
	int ret;

	if (WARN_ON_ONCE(!(file->f_mode & FMODE_BACKING)) ||
	    WARN_ON_ONCE(ctx->user_file != vma->vm_file))
	if (WARN_ON_ONCE(!(file->f_mode & FMODE_BACKING)))
		return -EIO;

	if (!file->f_op->mmap)
@@ -338,12 +337,12 @@ int backing_file_mmap(struct file *file, struct vm_area_struct *vma,

	vma_set_file(vma, file);

	old_cred = override_creds(ctx->cred);
	old_cred = override_creds_light(ctx->cred);
	ret = call_mmap(vma->vm_file, vma);
	revert_creds(old_cred);
	revert_creds_light(old_cred);

	if (ctx->accessed)
		ctx->accessed(ctx->user_file);
		ctx->accessed(vma->vm_file);

	return ret;
}
+18 −14
Original line number Diff line number Diff line
@@ -18,11 +18,11 @@ static void fuse_file_accessed(struct file *file)
	fuse_invalidate_atime(inode);
}

static void fuse_passthrough_end_write(struct file *file, loff_t pos, ssize_t ret)
static void fuse_passthrough_end_write(struct kiocb *iocb, ssize_t ret)
{
	struct inode *inode = file_inode(file);
	struct inode *inode = file_inode(iocb->ki_filp);

	fuse_write_update_attr(inode, pos, ret);
	fuse_write_update_attr(inode, iocb->ki_pos, ret);
}

ssize_t fuse_passthrough_read_iter(struct kiocb *iocb, struct iov_iter *iter)
@@ -34,7 +34,6 @@ ssize_t fuse_passthrough_read_iter(struct kiocb *iocb, struct iov_iter *iter)
	ssize_t ret;
	struct backing_file_ctx ctx = {
		.cred = ff->cred,
		.user_file = file,
		.accessed = fuse_file_accessed,
	};

@@ -62,7 +61,6 @@ ssize_t fuse_passthrough_write_iter(struct kiocb *iocb,
	ssize_t ret;
	struct backing_file_ctx ctx = {
		.cred = ff->cred,
		.user_file = file,
		.end_write = fuse_passthrough_end_write,
	};

@@ -88,15 +86,20 @@ ssize_t fuse_passthrough_splice_read(struct file *in, loff_t *ppos,
	struct file *backing_file = fuse_file_passthrough(ff);
	struct backing_file_ctx ctx = {
		.cred = ff->cred,
		.user_file = in,
		.accessed = fuse_file_accessed,
	};
	struct kiocb iocb;
	ssize_t ret;

	pr_debug("%s: backing_file=0x%p, pos=%lld, len=%zu, flags=0x%x\n", __func__,
		 backing_file, ppos ? *ppos : 0, len, flags);
		 backing_file, *ppos, len, flags);

	return backing_file_splice_read(backing_file, ppos, pipe, len, flags,
					&ctx);
	init_sync_kiocb(&iocb, in);
	iocb.ki_pos = *ppos;
	ret = backing_file_splice_read(backing_file, &iocb, pipe, len, flags, &ctx);
	*ppos = iocb.ki_pos;

	return ret;
}

ssize_t fuse_passthrough_splice_write(struct pipe_inode_info *pipe,
@@ -109,16 +112,18 @@ ssize_t fuse_passthrough_splice_write(struct pipe_inode_info *pipe,
	ssize_t ret;
	struct backing_file_ctx ctx = {
		.cred = ff->cred,
		.user_file = out,
		.end_write = fuse_passthrough_end_write,
	};
	struct kiocb iocb;

	pr_debug("%s: backing_file=0x%p, pos=%lld, len=%zu, flags=0x%x\n", __func__,
		 backing_file, ppos ? *ppos : 0, len, flags);
		 backing_file, *ppos, len, flags);

	inode_lock(inode);
	ret = backing_file_splice_write(pipe, backing_file, ppos, len, flags,
					&ctx);
	init_sync_kiocb(&iocb, out);
	iocb.ki_pos = *ppos;
	ret = backing_file_splice_write(pipe, backing_file, &iocb, len, flags, &ctx);
	*ppos = iocb.ki_pos;
	inode_unlock(inode);

	return ret;
@@ -130,7 +135,6 @@ ssize_t fuse_passthrough_mmap(struct file *file, struct vm_area_struct *vma)
	struct file *backing_file = fuse_file_passthrough(ff);
	struct backing_file_ctx ctx = {
		.cred = ff->cred,
		.user_file = file,
		.accessed = fuse_file_accessed,
	};

+1 −1
Original line number Diff line number Diff line
@@ -1259,7 +1259,7 @@ static int ovl_copy_up_flags(struct dentry *dentry, int flags)
		dput(parent);
		dput(next);
	}
	revert_creds(old_cred);
	ovl_revert_creds(old_cred);

	return err;
}
+47 −21
Original line number Diff line number Diff line
@@ -553,15 +553,17 @@ static int ovl_create_over_whiteout(struct dentry *dentry, struct inode *inode,
	goto out_dput;
}

static int ovl_setup_cred_for_create(struct dentry *dentry, struct inode *inode,
				     umode_t mode, const struct cred *old_cred)
static const struct cred *ovl_setup_cred_for_create(struct dentry *dentry,
						    struct inode *inode,
						    umode_t mode,
						    const struct cred *old_cred)
{
	int err;
	struct cred *override_cred;

	override_cred = prepare_creds();
	if (!override_cred)
		return -ENOMEM;
		return ERR_PTR(-ENOMEM);

	override_cred->fsuid = inode->i_uid;
	override_cred->fsgid = inode->i_gid;
@@ -569,19 +571,26 @@ static int ovl_setup_cred_for_create(struct dentry *dentry, struct inode *inode,
					      old_cred, override_cred);
	if (err) {
		put_cred(override_cred);
		return err;
		return ERR_PTR(err);
	}
	put_cred(override_creds(override_cred));
	put_cred(override_cred);

	return 0;
	/*
	 * Caller is going to match this with revert_creds_light() and drop
	 * referenec on the returned creds.
	 * We must be called with creator creds already, otherwise we risk
	 * leaking creds.
	 */
	old_cred = override_creds_light(override_cred);
	WARN_ON_ONCE(old_cred != ovl_creds(dentry->d_sb));

	return override_cred;
}

static int ovl_create_or_link(struct dentry *dentry, struct inode *inode,
			      struct ovl_cattr *attr, bool origin)
{
	int err;
	const struct cred *old_cred;
	const struct cred *old_cred, *new_cred = NULL;
	struct dentry *parent = dentry->d_parent;

	old_cred = ovl_override_creds(dentry->d_sb);
@@ -610,10 +619,14 @@ static int ovl_create_or_link(struct dentry *dentry, struct inode *inode,
		 * create a new inode, so just use the ovl mounter's
		 * fs{u,g}id.
		 */
		err = ovl_setup_cred_for_create(dentry, inode, attr->mode, old_cred);
		if (err)
		new_cred = ovl_setup_cred_for_create(dentry, inode, attr->mode,
						     old_cred);
		err = PTR_ERR(new_cred);
		if (IS_ERR(new_cred)) {
			new_cred = NULL;
			goto out_revert_creds;
		}
	}

	if (!ovl_dentry_is_whiteout(dentry))
		err = ovl_create_upper(dentry, inode, attr);
@@ -621,7 +634,8 @@ static int ovl_create_or_link(struct dentry *dentry, struct inode *inode,
		err = ovl_create_over_whiteout(dentry, inode, attr);

out_revert_creds:
	revert_creds(old_cred);
	ovl_revert_creds(old_cred);
	put_cred(new_cred);
	return err;
}

@@ -702,7 +716,7 @@ static int ovl_set_link_redirect(struct dentry *dentry)

	old_cred = ovl_override_creds(dentry->d_sb);
	err = ovl_set_redirect(dentry, false);
	revert_creds(old_cred);
	ovl_revert_creds(old_cred);

	return err;
}
@@ -912,7 +926,7 @@ static int ovl_do_remove(struct dentry *dentry, bool is_dir)
		err = ovl_remove_upper(dentry, is_dir, &list);
	else
		err = ovl_remove_and_whiteout(dentry, &list);
	revert_creds(old_cred);
	ovl_revert_creds(old_cred);
	if (!err) {
		if (is_dir)
			clear_nlink(dentry->d_inode);
@@ -1292,7 +1306,7 @@ static int ovl_rename(struct mnt_idmap *idmap, struct inode *olddir,
out_unlock:
	unlock_rename(new_upperdir, old_upperdir);
out_revert_creds:
	revert_creds(old_cred);
	ovl_revert_creds(old_cred);
	if (update_nlink)
		ovl_nlink_end(new);
	else
@@ -1306,18 +1320,22 @@ static int ovl_rename(struct mnt_idmap *idmap, struct inode *olddir,
static int ovl_create_tmpfile(struct file *file, struct dentry *dentry,
			      struct inode *inode, umode_t mode)
{
	const struct cred *old_cred;
	const struct cred *old_cred, *new_cred = NULL;
	struct path realparentpath;
	struct file *realfile;
	struct ovl_file *of;
	struct dentry *newdentry;
	/* It's okay to set O_NOATIME, since the owner will be current fsuid */
	int flags = file->f_flags | OVL_OPEN_FLAGS;
	int err;

	old_cred = ovl_override_creds(dentry->d_sb);
	err = ovl_setup_cred_for_create(dentry, inode, mode, old_cred);
	if (err)
	new_cred = ovl_setup_cred_for_create(dentry, inode, mode, old_cred);
	err = PTR_ERR(new_cred);
	if (IS_ERR(new_cred)) {
		new_cred = NULL;
		goto out_revert_creds;
	}

	ovl_path_upper(dentry->d_parent, &realparentpath);
	realfile = backing_tmpfile_open(&file->f_path, flags, &realparentpath,
@@ -1327,17 +1345,25 @@ static int ovl_create_tmpfile(struct file *file, struct dentry *dentry,
	if (err)
		goto out_revert_creds;

	of = ovl_file_alloc(realfile);
	if (!of) {
		fput(realfile);
		err = -ENOMEM;
		goto out_revert_creds;
	}

	/* ovl_instantiate() consumes the newdentry reference on success */
	newdentry = dget(realfile->f_path.dentry);
	err = ovl_instantiate(dentry, inode, newdentry, false, file);
	if (!err) {
		file->private_data = realfile;
		file->private_data = of;
	} else {
		dput(newdentry);
		fput(realfile);
		ovl_file_free(of);
	}
out_revert_creds:
	revert_creds(old_cred);
	ovl_revert_creds(old_cred);
	put_cred(new_cred);
	return err;
}

@@ -1389,7 +1415,7 @@ static int ovl_tmpfile(struct mnt_idmap *idmap, struct inode *dir,
put_realfile:
	/* Without FMODE_OPENED ->release() won't be called on @file */
	if (!(file->f_mode & FMODE_OPENED))
		fput(file->private_data);
		ovl_file_free(file->private_data);
put_inode:
	iput(inode);
drop_write:
+190 −137
Original line number Diff line number Diff line
@@ -51,7 +51,7 @@ static struct file *ovl_open_realfile(const struct file *file,
		realfile = backing_file_open(&file->f_path, flags, realpath,
					     current_cred());
	}
	revert_creds(old_cred);
	ovl_revert_creds(old_cred);

	pr_debug("open(%p[%pD2/%c], 0%o) -> (%p, 0%o)\n",
		 file, file, ovl_whatisit(inode, realinode), file->f_flags,
@@ -89,56 +89,110 @@ static int ovl_change_flags(struct file *file, unsigned int flags)
	return 0;
}

static int ovl_real_fdget_meta(const struct file *file, struct fd *real,
			       bool allow_meta)
struct ovl_file {
	struct file *realfile;
	struct file *upperfile;
};

struct ovl_file *ovl_file_alloc(struct file *realfile)
{
	struct dentry *dentry = file_dentry(file);
	struct file *realfile = file->private_data;
	struct path realpath;
	int err;
	struct ovl_file *of = kzalloc(sizeof(struct ovl_file), GFP_KERNEL);

	real->word = (unsigned long)realfile;
	if (unlikely(!of))
		return NULL;

	if (allow_meta) {
		ovl_path_real(dentry, &realpath);
	} else {
		/* lazy lookup and verify of lowerdata */
		err = ovl_verify_lowerdata(dentry);
		if (err)
			return err;
	of->realfile = realfile;
	return of;
}

		ovl_path_realdata(dentry, &realpath);
void ovl_file_free(struct ovl_file *of)
{
	fput(of->realfile);
	if (of->upperfile)
		fput(of->upperfile);
	kfree(of);
}
	if (!realpath.dentry)
		return -EIO;

	/* Has it been copied up since we'd opened it? */
	if (unlikely(file_inode(realfile) != d_inode(realpath.dentry))) {
		struct file *f = ovl_open_realfile(file, &realpath);
		if (IS_ERR(f))
			return PTR_ERR(f);
		real->word = (unsigned long)f | FDPUT_FPUT;
		return 0;
static bool ovl_is_real_file(const struct file *realfile,
			     const struct path *realpath)
{
	return file_inode(realfile) == d_inode(realpath->dentry);
}

static struct file *ovl_real_file_path(const struct file *file,
				       struct path *realpath)
{
	struct ovl_file *of = file->private_data;
	struct file *realfile = of->realfile;

	if (WARN_ON_ONCE(!realpath->dentry))
		return ERR_PTR(-EIO);

	/*
	 * If the realfile that we want is not where the data used to be at
	 * open time, either we'd been copied up, or it's an fsync of a
	 * metacopied file.  We need the upperfile either way, so see if it
	 * is already opened and if it is not then open and store it.
	 */
	if (unlikely(!ovl_is_real_file(realfile, realpath))) {
		struct file *upperfile = READ_ONCE(of->upperfile);
		struct file *old;

		if (!upperfile) { /* Nobody opened upperfile yet */
			upperfile = ovl_open_realfile(file, realpath);
			if (IS_ERR(upperfile))
				return upperfile;

			/* Store the upperfile for later */
			old = cmpxchg_release(&of->upperfile, NULL, upperfile);
			if (old) { /* Someone opened upperfile before us */
				fput(upperfile);
				upperfile = old;
			}
		}
		/*
		 * Stored file must be from the right inode, unless someone's
		 * been corrupting the upper layer.
		 */
		if (WARN_ON_ONCE(!ovl_is_real_file(upperfile, realpath)))
			return ERR_PTR(-EIO);

		realfile = upperfile;
	}

	/* Did the flags change since open? */
	if (unlikely((file->f_flags ^ realfile->f_flags) & ~OVL_OPEN_FLAGS))
		return ovl_change_flags(realfile, file->f_flags);
	if (unlikely((file->f_flags ^ realfile->f_flags) & ~OVL_OPEN_FLAGS)) {
		int err = ovl_change_flags(realfile, file->f_flags);

	return 0;
		if (err)
			return ERR_PTR(err);
	}

	return realfile;
}

static int ovl_real_fdget(const struct file *file, struct fd *real)
static struct file *ovl_real_file(const struct file *file)
{
	if (d_is_dir(file_dentry(file))) {
	struct dentry *dentry = file_dentry(file);
	struct path realpath;
	int err;

	if (d_is_dir(dentry)) {
		struct file *f = ovl_dir_real_file(file, false);
		if (IS_ERR(f))
			return PTR_ERR(f);
		real->word = (unsigned long)f;
		return 0;

		if (WARN_ON_ONCE(!f))
			return ERR_PTR(-EIO);
		return f;
	}

	return ovl_real_fdget_meta(file, real, false);
	/* lazy lookup and verify of lowerdata */
	err = ovl_verify_lowerdata(dentry);
	if (err)
		return ERR_PTR(err);

	ovl_path_realdata(dentry, &realpath);

	return ovl_real_file_path(file, &realpath);
}

static int ovl_open(struct inode *inode, struct file *file)
@@ -146,6 +200,7 @@ static int ovl_open(struct inode *inode, struct file *file)
	struct dentry *dentry = file_dentry(file);
	struct file *realfile;
	struct path realpath;
	struct ovl_file *of;
	int err;

	/* lazy lookup and verify lowerdata */
@@ -168,22 +223,27 @@ static int ovl_open(struct inode *inode, struct file *file)
	if (IS_ERR(realfile))
		return PTR_ERR(realfile);

	file->private_data = realfile;
	of = ovl_file_alloc(realfile);
	if (!of) {
		fput(realfile);
		return -ENOMEM;
	}

	file->private_data = of;

	return 0;
}

static int ovl_release(struct inode *inode, struct file *file)
{
	fput(file->private_data);

	ovl_file_free(file->private_data);
	return 0;
}

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

@@ -199,9 +259,9 @@ static loff_t ovl_llseek(struct file *file, loff_t offset, int whence)
			return vfs_setpos(file, 0, 0);
	}

	ret = ovl_real_fdget(file, &real);
	if (ret)
		return ret;
	realfile = ovl_real_file(file);
	if (IS_ERR(realfile))
		return PTR_ERR(realfile);

	/*
	 * Overlay file f_pos is the master copy that is preserved
@@ -211,17 +271,15 @@ static loff_t ovl_llseek(struct file *file, loff_t offset, int whence)
	 * files, so we use the real file to perform seeks.
	 */
	ovl_inode_lock(inode);
	fd_file(real)->f_pos = file->f_pos;
	realfile->f_pos = file->f_pos;

	old_cred = ovl_override_creds(inode->i_sb);
	ret = vfs_llseek(fd_file(real), offset, whence);
	revert_creds(old_cred);
	ret = vfs_llseek(realfile, offset, whence);
	ovl_revert_creds(old_cred);

	file->f_pos = fd_file(real)->f_pos;
	file->f_pos = realfile->f_pos;
	ovl_inode_unlock(inode);

	fdput(real);

	return ret;
}

@@ -231,9 +289,9 @@ static void ovl_file_modified(struct file *file)
	ovl_copyattr(file_inode(file));
}

static void ovl_file_end_write(struct file *file, loff_t pos, ssize_t ret)
static void ovl_file_end_write(struct kiocb *iocb, ssize_t ret)
{
	ovl_file_modified(file);
	ovl_file_modified(iocb->ki_filp);
}

static void ovl_file_accessed(struct file *file)
@@ -267,38 +325,32 @@ static void ovl_file_accessed(struct file *file)
static ssize_t ovl_read_iter(struct kiocb *iocb, struct iov_iter *iter)
{
	struct file *file = iocb->ki_filp;
	struct fd real;
	ssize_t ret;
	struct file *realfile;
	struct backing_file_ctx ctx = {
		.cred = ovl_creds(file_inode(file)->i_sb),
		.user_file = file,
		.accessed = ovl_file_accessed,
	};

	if (!iov_iter_count(iter))
		return 0;

	ret = ovl_real_fdget(file, &real);
	if (ret)
		return ret;
	realfile = ovl_real_file(file);
	if (IS_ERR(realfile))
		return PTR_ERR(realfile);

	ret = backing_file_read_iter(fd_file(real), iter, iocb, iocb->ki_flags,
	return backing_file_read_iter(realfile, iter, iocb, iocb->ki_flags,
				      &ctx);
	fdput(real);

	return ret;
}

static ssize_t ovl_write_iter(struct kiocb *iocb, struct iov_iter *iter)
{
	struct file *file = iocb->ki_filp;
	struct inode *inode = file_inode(file);
	struct fd real;
	struct file *realfile;
	ssize_t ret;
	int ifl = iocb->ki_flags;
	struct backing_file_ctx ctx = {
		.cred = ovl_creds(inode->i_sb),
		.user_file = file,
		.end_write = ovl_file_end_write,
	};

@@ -309,8 +361,9 @@ static ssize_t ovl_write_iter(struct kiocb *iocb, struct iov_iter *iter)
	/* Update mode */
	ovl_copyattr(inode);

	ret = ovl_real_fdget(file, &real);
	if (ret)
	realfile = ovl_real_file(file);
	ret = PTR_ERR(realfile);
	if (IS_ERR(realfile))
		goto out_unlock;

	if (!ovl_should_sync(OVL_FS(inode->i_sb)))
@@ -321,8 +374,7 @@ static ssize_t ovl_write_iter(struct kiocb *iocb, struct iov_iter *iter)
	 * this property in case it is set by the issuer.
	 */
	ifl &= ~IOCB_DIO_CALLER_COMP;
	ret = backing_file_write_iter(fd_file(real), iter, iocb, ifl, &ctx);
	fdput(real);
	ret = backing_file_write_iter(realfile, iter, iocb, ifl, &ctx);

out_unlock:
	inode_unlock(inode);
@@ -334,20 +386,22 @@ static ssize_t ovl_splice_read(struct file *in, loff_t *ppos,
			       struct pipe_inode_info *pipe, size_t len,
			       unsigned int flags)
{
	struct fd real;
	struct file *realfile;
	ssize_t ret;
	struct backing_file_ctx ctx = {
		.cred = ovl_creds(file_inode(in)->i_sb),
		.user_file = in,
		.accessed = ovl_file_accessed,
	};
	struct kiocb iocb;

	ret = ovl_real_fdget(in, &real);
	if (ret)
		return ret;
	realfile = ovl_real_file(in);
	if (IS_ERR(realfile))
		return PTR_ERR(realfile);

	ret = backing_file_splice_read(fd_file(real), ppos, pipe, len, flags, &ctx);
	fdput(real);
	init_sync_kiocb(&iocb, in);
	iocb.ki_pos = *ppos;
	ret = backing_file_splice_read(realfile, &iocb, pipe, len, flags, &ctx);
	*ppos = iocb.ki_pos;

	return ret;
}
@@ -355,7 +409,7 @@ static ssize_t ovl_splice_read(struct file *in, loff_t *ppos,
/*
 * Calling iter_file_splice_write() directly from overlay's f_op may deadlock
 * due to lock order inversion between pipe->mutex in iter_file_splice_write()
 * and file_start_write(fd_file(real)) in ovl_write_iter().
 * and file_start_write(realfile) in ovl_write_iter().
 *
 * So do everything ovl_write_iter() does and call iter_file_splice_write() on
 * the real file.
@@ -363,25 +417,28 @@ static ssize_t ovl_splice_read(struct file *in, loff_t *ppos,
static ssize_t ovl_splice_write(struct pipe_inode_info *pipe, struct file *out,
				loff_t *ppos, size_t len, unsigned int flags)
{
	struct fd real;
	struct file *realfile;
	struct inode *inode = file_inode(out);
	ssize_t ret;
	struct backing_file_ctx ctx = {
		.cred = ovl_creds(inode->i_sb),
		.user_file = out,
		.end_write = ovl_file_end_write,
	};
	struct kiocb iocb;

	inode_lock(inode);
	/* Update mode */
	ovl_copyattr(inode);

	ret = ovl_real_fdget(out, &real);
	if (ret)
	realfile = ovl_real_file(out);
	ret = PTR_ERR(realfile);
	if (IS_ERR(realfile))
		goto out_unlock;

	ret = backing_file_splice_write(pipe, fd_file(real), ppos, len, flags, &ctx);
	fdput(real);
	init_sync_kiocb(&iocb, out);
	iocb.ki_pos = *ppos;
	ret = backing_file_splice_write(pipe, realfile, &iocb, len, flags, &ctx);
	*ppos = iocb.ki_pos;

out_unlock:
	inode_unlock(inode);
@@ -391,7 +448,10 @@ static ssize_t ovl_splice_write(struct pipe_inode_info *pipe, struct file *out,

static int ovl_fsync(struct file *file, loff_t start, loff_t end, int datasync)
{
	struct fd real;
	struct dentry *dentry = file_dentry(file);
	enum ovl_path_type type;
	struct path upperpath;
	struct file *upperfile;
	const struct cred *old_cred;
	int ret;

@@ -399,38 +459,38 @@ static int ovl_fsync(struct file *file, loff_t start, loff_t end, int datasync)
	if (ret <= 0)
		return ret;

	ret = ovl_real_fdget_meta(file, &real, !datasync);
	if (ret)
		return ret;

	/* Don't sync lower file for fear of receiving EROFS error */
	if (file_inode(fd_file(real)) == ovl_inode_upper(file_inode(file))) {
		old_cred = ovl_override_creds(file_inode(file)->i_sb);
		ret = vfs_fsync_range(fd_file(real), start, end, datasync);
		revert_creds(old_cred);
	}
	type = ovl_path_type(dentry);
	if (!OVL_TYPE_UPPER(type) || (datasync && OVL_TYPE_MERGE(type)))
		return 0;

	ovl_path_upper(dentry, &upperpath);
	upperfile = ovl_real_file_path(file, &upperpath);
	if (IS_ERR(upperfile))
		return PTR_ERR(upperfile);

	fdput(real);
	old_cred = ovl_override_creds(file_inode(file)->i_sb);
	ret = vfs_fsync_range(upperfile, start, end, datasync);
	ovl_revert_creds(old_cred);

	return ret;
}

static int ovl_mmap(struct file *file, struct vm_area_struct *vma)
{
	struct file *realfile = file->private_data;
	struct ovl_file *of = file->private_data;
	struct backing_file_ctx ctx = {
		.cred = ovl_creds(file_inode(file)->i_sb),
		.user_file = file,
		.accessed = ovl_file_accessed,
	};

	return backing_file_mmap(realfile, vma, &ctx);
	return backing_file_mmap(of->realfile, vma, &ctx);
}

static long ovl_fallocate(struct file *file, int mode, loff_t offset, loff_t len)
{
	struct inode *inode = file_inode(file);
	struct fd real;
	struct file *realfile;
	const struct cred *old_cred;
	int ret;

@@ -441,19 +501,18 @@ static long ovl_fallocate(struct file *file, int mode, loff_t offset, loff_t len
	if (ret)
		goto out_unlock;

	ret = ovl_real_fdget(file, &real);
	if (ret)
	realfile = ovl_real_file(file);
	ret = PTR_ERR(realfile);
	if (IS_ERR(realfile))
		goto out_unlock;

	old_cred = ovl_override_creds(file_inode(file)->i_sb);
	ret = vfs_fallocate(fd_file(real), mode, offset, len);
	revert_creds(old_cred);
	ret = vfs_fallocate(realfile, mode, offset, len);
	ovl_revert_creds(old_cred);

	/* Update size */
	ovl_file_modified(file);

	fdput(real);

out_unlock:
	inode_unlock(inode);

@@ -462,19 +521,17 @@ static long ovl_fallocate(struct file *file, int mode, loff_t offset, loff_t len

static int ovl_fadvise(struct file *file, loff_t offset, loff_t len, int advice)
{
	struct fd real;
	struct file *realfile;
	const struct cred *old_cred;
	int ret;

	ret = ovl_real_fdget(file, &real);
	if (ret)
		return ret;
	realfile = ovl_real_file(file);
	if (IS_ERR(realfile))
		return PTR_ERR(realfile);

	old_cred = ovl_override_creds(file_inode(file)->i_sb);
	ret = vfs_fadvise(fd_file(real), offset, len, advice);
	revert_creds(old_cred);

	fdput(real);
	ret = vfs_fadvise(realfile, offset, len, advice);
	ovl_revert_creds(old_cred);

	return ret;
}
@@ -490,7 +547,7 @@ static loff_t ovl_copyfile(struct file *file_in, loff_t pos_in,
			    loff_t len, unsigned int flags, enum ovl_copyop op)
{
	struct inode *inode_out = file_inode(file_out);
	struct fd real_in, real_out;
	struct file *realfile_in, *realfile_out;
	const struct cred *old_cred;
	loff_t ret;

@@ -503,42 +560,39 @@ static loff_t ovl_copyfile(struct file *file_in, loff_t pos_in,
			goto out_unlock;
	}

	ret = ovl_real_fdget(file_out, &real_out);
	if (ret)
	realfile_out = ovl_real_file(file_out);
	ret = PTR_ERR(realfile_out);
	if (IS_ERR(realfile_out))
		goto out_unlock;

	ret = ovl_real_fdget(file_in, &real_in);
	if (ret) {
		fdput(real_out);
	realfile_in = ovl_real_file(file_in);
	ret = PTR_ERR(realfile_in);
	if (IS_ERR(realfile_in))
		goto out_unlock;
	}

	old_cred = ovl_override_creds(file_inode(file_out)->i_sb);
	switch (op) {
	case OVL_COPY:
		ret = vfs_copy_file_range(fd_file(real_in), pos_in,
					  fd_file(real_out), pos_out, len, flags);
		ret = vfs_copy_file_range(realfile_in, pos_in,
					  realfile_out, pos_out, len, flags);
		break;

	case OVL_CLONE:
		ret = vfs_clone_file_range(fd_file(real_in), pos_in,
					   fd_file(real_out), pos_out, len, flags);
		ret = vfs_clone_file_range(realfile_in, pos_in,
					   realfile_out, pos_out, len, flags);
		break;

	case OVL_DEDUPE:
		ret = vfs_dedupe_file_range_one(fd_file(real_in), pos_in,
						fd_file(real_out), pos_out, len,
		ret = vfs_dedupe_file_range_one(realfile_in, pos_in,
						realfile_out, pos_out, len,
						flags);
		break;
	}
	revert_creds(old_cred);
	ovl_revert_creds(old_cred);

	/* Update size */
	ovl_file_modified(file_out);

	fdput(real_in);
	fdput(real_out);

out_unlock:
	inode_unlock(inode_out);

@@ -582,20 +636,19 @@ static loff_t ovl_remap_file_range(struct file *file_in, loff_t pos_in,

static int ovl_flush(struct file *file, fl_owner_t id)
{
	struct fd real;
	struct file *realfile;
	const struct cred *old_cred;
	int err;
	int err = 0;

	err = ovl_real_fdget(file, &real);
	if (err)
		return err;
	realfile = ovl_real_file(file);
	if (IS_ERR(realfile))
		return PTR_ERR(realfile);

	if (fd_file(real)->f_op->flush) {
	if (realfile->f_op->flush) {
		old_cred = ovl_override_creds(file_inode(file)->i_sb);
		err = fd_file(real)->f_op->flush(fd_file(real), id);
		revert_creds(old_cred);
		err = realfile->f_op->flush(realfile, id);
		ovl_revert_creds(old_cred);
	}
	fdput(real);

	return err;
}
Loading