brintos

brintos / linux-shallow public Read only

0
0
Text · 8.5 KiB · e7a012a Raw
361 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Common helpers for stackable filesystems and backing files.4 *5 * Forked from fs/overlayfs/file.c.6 *7 * Copyright (C) 2017 Red Hat, Inc.8 * Copyright (C) 2023 CTERA Networks.9 */10 11#include <linux/fs.h>12#include <linux/backing-file.h>13#include <linux/splice.h>14#include <linux/mm.h>15 16#include "internal.h"17 18/**19 * backing_file_open - open a backing file for kernel internal use20 * @user_path:	path that the user reuqested to open21 * @flags:	open flags22 * @real_path:	path of the backing file23 * @cred:	credentials for open24 *25 * Open a backing file for a stackable filesystem (e.g., overlayfs).26 * @user_path may be on the stackable filesystem and @real_path on the27 * underlying filesystem.  In this case, we want to be able to return the28 * @user_path of the stackable filesystem. This is done by embedding the29 * returned file into a container structure that also stores the stacked30 * file's path, which can be retrieved using backing_file_user_path().31 */32struct file *backing_file_open(const struct path *user_path, int flags,33			       const struct path *real_path,34			       const struct cred *cred)35{36	struct file *f;37	int error;38 39	f = alloc_empty_backing_file(flags, cred);40	if (IS_ERR(f))41		return f;42 43	path_get(user_path);44	*backing_file_user_path(f) = *user_path;45	error = vfs_open(real_path, f);46	if (error) {47		fput(f);48		f = ERR_PTR(error);49	}50 51	return f;52}53EXPORT_SYMBOL_GPL(backing_file_open);54 55struct file *backing_tmpfile_open(const struct path *user_path, int flags,56				  const struct path *real_parentpath,57				  umode_t mode, const struct cred *cred)58{59	struct mnt_idmap *real_idmap = mnt_idmap(real_parentpath->mnt);60	struct file *f;61	int error;62 63	f = alloc_empty_backing_file(flags, cred);64	if (IS_ERR(f))65		return f;66 67	path_get(user_path);68	*backing_file_user_path(f) = *user_path;69	error = vfs_tmpfile(real_idmap, real_parentpath, f, mode);70	if (error) {71		fput(f);72		f = ERR_PTR(error);73	}74	return f;75}76EXPORT_SYMBOL(backing_tmpfile_open);77 78struct backing_aio {79	struct kiocb iocb;80	refcount_t ref;81	struct kiocb *orig_iocb;82	/* used for aio completion */83	void (*end_write)(struct file *, loff_t, ssize_t);84	struct work_struct work;85	long res;86};87 88static struct kmem_cache *backing_aio_cachep;89 90#define BACKING_IOCB_MASK \91	(IOCB_NOWAIT | IOCB_HIPRI | IOCB_DSYNC | IOCB_SYNC | IOCB_APPEND)92 93static rwf_t iocb_to_rw_flags(int flags)94{95	return (__force rwf_t)(flags & BACKING_IOCB_MASK);96}97 98static void backing_aio_put(struct backing_aio *aio)99{100	if (refcount_dec_and_test(&aio->ref)) {101		fput(aio->iocb.ki_filp);102		kmem_cache_free(backing_aio_cachep, aio);103	}104}105 106static void backing_aio_cleanup(struct backing_aio *aio, long res)107{108	struct kiocb *iocb = &aio->iocb;109	struct kiocb *orig_iocb = aio->orig_iocb;110 111	if (aio->end_write)112		aio->end_write(orig_iocb->ki_filp, iocb->ki_pos, res);113 114	orig_iocb->ki_pos = iocb->ki_pos;115	backing_aio_put(aio);116}117 118static HWJS_SUSPENDS void backing_aio_rw_complete(struct kiocb *iocb, long res)119{120	struct backing_aio *aio = container_of(iocb, struct backing_aio, iocb);121	struct kiocb *orig_iocb = aio->orig_iocb;122 123	if (iocb->ki_flags & IOCB_WRITE)124		kiocb_end_write(iocb);125 126	backing_aio_cleanup(aio, res);127	orig_iocb->ki_complete(orig_iocb, res);128}129 130static HWJS_SUSPENDS void backing_aio_complete_work(struct work_struct *work)131{132	struct backing_aio *aio = container_of(work, struct backing_aio, work);133 134	backing_aio_rw_complete(&aio->iocb, aio->res);135}136 137static HWJS_SUSPENDS void backing_aio_queue_completion(struct kiocb *iocb, long res)138{139	struct backing_aio *aio = container_of(iocb, struct backing_aio, iocb);140 141	/*142	 * Punt to a work queue to serialize updates of mtime/size.143	 */144	aio->res = res;145	INIT_WORK(&aio->work, backing_aio_complete_work);146	queue_work(file_inode(aio->orig_iocb->ki_filp)->i_sb->s_dio_done_wq,147		   &aio->work);148}149 150static HWJS_SUSPENDS int backing_aio_init_wq(struct kiocb *iocb)151{152	struct super_block *sb = file_inode(iocb->ki_filp)->i_sb;153 154	if (sb->s_dio_done_wq)155		return 0;156 157	return sb_init_dio_done_wq(sb);158}159 160 161ssize_t backing_file_read_iter(struct file *file, struct iov_iter *iter,162			       struct kiocb *iocb, int flags,163			       struct backing_file_ctx *ctx)164{165	struct backing_aio *aio = NULL;166	const struct cred *old_cred;167	ssize_t ret;168 169	if (WARN_ON_ONCE(!(file->f_mode & FMODE_BACKING)))170		return -EIO;171 172	if (!iov_iter_count(iter))173		return 0;174 175	if (iocb->ki_flags & IOCB_DIRECT &&176	    !(file->f_mode & FMODE_CAN_ODIRECT))177		return -EINVAL;178 179	old_cred = override_creds(ctx->cred);180	if (is_sync_kiocb(iocb)) {181		rwf_t rwf = iocb_to_rw_flags(flags);182 183		ret = vfs_iter_read(file, iter, &iocb->ki_pos, rwf);184	} else {185		ret = -ENOMEM;186		aio = kmem_cache_zalloc(backing_aio_cachep, GFP_KERNEL);187		if (!aio)188			goto out;189 190		aio->orig_iocb = iocb;191		kiocb_clone(&aio->iocb, iocb, get_file(file));192		aio->iocb.ki_complete = backing_aio_rw_complete;193		refcount_set(&aio->ref, 2);194		ret = vfs_iocb_iter_read(file, &aio->iocb, iter);195		backing_aio_put(aio);196		if (ret != -EIOCBQUEUED)197			backing_aio_cleanup(aio, ret);198	}199out:200	revert_creds(old_cred);201 202	if (ctx->accessed)203		ctx->accessed(ctx->user_file);204 205	return ret;206}207EXPORT_SYMBOL_GPL(backing_file_read_iter);208 209ssize_t backing_file_write_iter(struct file *file, struct iov_iter *iter,210				struct kiocb *iocb, int flags,211				struct backing_file_ctx *ctx)212{213	const struct cred *old_cred;214	ssize_t ret;215 216	if (WARN_ON_ONCE(!(file->f_mode & FMODE_BACKING)))217		return -EIO;218 219	if (!iov_iter_count(iter))220		return 0;221 222	ret = file_remove_privs(ctx->user_file);223	if (ret)224		return ret;225 226	if (iocb->ki_flags & IOCB_DIRECT &&227	    !(file->f_mode & FMODE_CAN_ODIRECT))228		return -EINVAL;229 230	/*231	 * Stacked filesystems don't support deferred completions, don't copy232	 * this property in case it is set by the issuer.233	 */234	flags &= ~IOCB_DIO_CALLER_COMP;235 236	old_cred = override_creds(ctx->cred);237	if (is_sync_kiocb(iocb)) {238		rwf_t rwf = iocb_to_rw_flags(flags);239 240		ret = vfs_iter_write(file, iter, &iocb->ki_pos, rwf);241		if (ctx->end_write)242			ctx->end_write(ctx->user_file, iocb->ki_pos, ret);243	} else {244		struct backing_aio *aio;245 246		ret = backing_aio_init_wq(iocb);247		if (ret)248			goto out;249 250		ret = -ENOMEM;251		aio = kmem_cache_zalloc(backing_aio_cachep, GFP_KERNEL);252		if (!aio)253			goto out;254 255		aio->orig_iocb = iocb;256		aio->end_write = ctx->end_write;257		kiocb_clone(&aio->iocb, iocb, get_file(file));258		aio->iocb.ki_flags = flags;259		aio->iocb.ki_complete = backing_aio_queue_completion;260		refcount_set(&aio->ref, 2);261		ret = vfs_iocb_iter_write(file, &aio->iocb, iter);262		backing_aio_put(aio);263		if (ret != -EIOCBQUEUED)264			backing_aio_cleanup(aio, ret);265	}266out:267	revert_creds(old_cred);268 269	return ret;270}271EXPORT_SYMBOL_GPL(backing_file_write_iter);272 273ssize_t backing_file_splice_read(struct file *in, loff_t *ppos,274				 struct pipe_inode_info *pipe, size_t len,275				 unsigned int flags,276				 struct backing_file_ctx *ctx)277{278	const struct cred *old_cred;279	ssize_t ret;280 281	if (WARN_ON_ONCE(!(in->f_mode & FMODE_BACKING)))282		return -EIO;283 284	old_cred = override_creds(ctx->cred);285	ret = vfs_splice_read(in, ppos, pipe, len, flags);286	revert_creds(old_cred);287 288	if (ctx->accessed)289		ctx->accessed(ctx->user_file);290 291	return ret;292}293EXPORT_SYMBOL_GPL(backing_file_splice_read);294 295ssize_t backing_file_splice_write(struct pipe_inode_info *pipe,296				  struct file *out, loff_t *ppos, size_t len,297				  unsigned int flags,298				  struct backing_file_ctx *ctx)299{300	const struct cred *old_cred;301	ssize_t ret;302 303	if (WARN_ON_ONCE(!(out->f_mode & FMODE_BACKING)))304		return -EIO;305 306	if (!out->f_op->splice_write)307		return -EINVAL;308 309	ret = file_remove_privs(ctx->user_file);310	if (ret)311		return ret;312 313	old_cred = override_creds(ctx->cred);314	file_start_write(out);315	ret = out->f_op->splice_write(pipe, out, ppos, len, flags);316	file_end_write(out);317	revert_creds(old_cred);318 319	if (ctx->end_write)320		ctx->end_write(ctx->user_file, ppos ? *ppos : 0, ret);321 322	return ret;323}324EXPORT_SYMBOL_GPL(backing_file_splice_write);325 326int backing_file_mmap(struct file *file, struct vm_area_struct *vma,327		      struct backing_file_ctx *ctx)328{329	const struct cred *old_cred;330	int ret;331 332	if (WARN_ON_ONCE(!(file->f_mode & FMODE_BACKING)) ||333	    WARN_ON_ONCE(ctx->user_file != vma->vm_file))334		return -EIO;335 336	if (!file->f_op->mmap)337		return -ENODEV;338 339	vma_set_file(vma, file);340 341	old_cred = override_creds(ctx->cred);342	ret = call_mmap(vma->vm_file, vma);343	revert_creds(old_cred);344 345	if (ctx->accessed)346		ctx->accessed(ctx->user_file);347 348	return ret;349}350EXPORT_SYMBOL_GPL(backing_file_mmap);351 352static int __init backing_aio_init(void)353{354	backing_aio_cachep = KMEM_CACHE(backing_aio, SLAB_HWCACHE_ALIGN);355	if (!backing_aio_cachep)356		return -ENOMEM;357 358	return 0;359}360fs_initcall(backing_aio_init);361