diff options
Diffstat (limited to 'fs/overlayfs/file.c')
| -rw-r--r-- | fs/overlayfs/file.c | 162 | 
1 files changed, 148 insertions, 14 deletions
diff --git a/fs/overlayfs/file.c b/fs/overlayfs/file.c index e235a635d9ec..a5317216de73 100644 --- a/fs/overlayfs/file.c +++ b/fs/overlayfs/file.c @@ -9,8 +9,19 @@  #include <linux/xattr.h>  #include <linux/uio.h>  #include <linux/uaccess.h> +#include <linux/splice.h> +#include <linux/mm.h> +#include <linux/fs.h>  #include "overlayfs.h" +struct ovl_aio_req { +	struct kiocb iocb; +	struct kiocb *orig_iocb; +	struct fd fd; +}; + +static struct kmem_cache *ovl_aio_request_cachep; +  static char ovl_whatisit(struct inode *inode, struct inode *realinode)  {  	if (realinode != ovl_inode_upper(inode)) @@ -146,7 +157,7 @@ static loff_t ovl_llseek(struct file *file, loff_t offset, int whence)  	struct inode *inode = file_inode(file);  	struct fd real;  	const struct cred *old_cred; -	ssize_t ret; +	loff_t ret;  	/*  	 * The two special cases below do not need to involve real fs, @@ -171,7 +182,7 @@ static loff_t ovl_llseek(struct file *file, loff_t offset, int whence)  	 * limitations that are more strict than ->s_maxbytes for specific  	 * files, so we use the real file to perform seeks.  	 */ -	inode_lock(inode); +	ovl_inode_lock(inode);  	real.file->f_pos = file->f_pos;  	old_cred = ovl_override_creds(inode->i_sb); @@ -179,7 +190,7 @@ static loff_t ovl_llseek(struct file *file, loff_t offset, int whence)  	revert_creds(old_cred);  	file->f_pos = real.file->f_pos; -	inode_unlock(inode); +	ovl_inode_unlock(inode);  	fdput(real); @@ -225,6 +236,33 @@ static rwf_t ovl_iocb_to_rwf(struct kiocb *iocb)  	return flags;  } +static void ovl_aio_cleanup_handler(struct ovl_aio_req *aio_req) +{ +	struct kiocb *iocb = &aio_req->iocb; +	struct kiocb *orig_iocb = aio_req->orig_iocb; + +	if (iocb->ki_flags & IOCB_WRITE) { +		struct inode *inode = file_inode(orig_iocb->ki_filp); + +		file_end_write(iocb->ki_filp); +		ovl_copyattr(ovl_inode_real(inode), inode); +	} + +	orig_iocb->ki_pos = iocb->ki_pos; +	fdput(aio_req->fd); +	kmem_cache_free(ovl_aio_request_cachep, aio_req); +} + +static void ovl_aio_rw_complete(struct kiocb *iocb, long res, long res2) +{ +	struct ovl_aio_req *aio_req = container_of(iocb, +						   struct ovl_aio_req, iocb); +	struct kiocb *orig_iocb = aio_req->orig_iocb; + +	ovl_aio_cleanup_handler(aio_req); +	orig_iocb->ki_complete(orig_iocb, res, res2); +} +  static ssize_t ovl_read_iter(struct kiocb *iocb, struct iov_iter *iter)  {  	struct file *file = iocb->ki_filp; @@ -240,10 +278,28 @@ static ssize_t ovl_read_iter(struct kiocb *iocb, struct iov_iter *iter)  		return ret;  	old_cred = ovl_override_creds(file_inode(file)->i_sb); -	ret = vfs_iter_read(real.file, iter, &iocb->ki_pos, -			    ovl_iocb_to_rwf(iocb)); +	if (is_sync_kiocb(iocb)) { +		ret = vfs_iter_read(real.file, iter, &iocb->ki_pos, +				    ovl_iocb_to_rwf(iocb)); +	} else { +		struct ovl_aio_req *aio_req; + +		ret = -ENOMEM; +		aio_req = kmem_cache_zalloc(ovl_aio_request_cachep, GFP_KERNEL); +		if (!aio_req) +			goto out; + +		aio_req->fd = real; +		real.flags = 0; +		aio_req->orig_iocb = iocb; +		kiocb_clone(&aio_req->iocb, iocb, real.file); +		aio_req->iocb.ki_complete = ovl_aio_rw_complete; +		ret = vfs_iocb_iter_read(real.file, &aio_req->iocb, iter); +		if (ret != -EIOCBQUEUED) +			ovl_aio_cleanup_handler(aio_req); +	} +out:  	revert_creds(old_cred); -  	ovl_file_accessed(file);  	fdput(real); @@ -274,15 +330,33 @@ static ssize_t ovl_write_iter(struct kiocb *iocb, struct iov_iter *iter)  		goto out_unlock;  	old_cred = ovl_override_creds(file_inode(file)->i_sb); -	file_start_write(real.file); -	ret = vfs_iter_write(real.file, iter, &iocb->ki_pos, -			     ovl_iocb_to_rwf(iocb)); -	file_end_write(real.file); +	if (is_sync_kiocb(iocb)) { +		file_start_write(real.file); +		ret = vfs_iter_write(real.file, iter, &iocb->ki_pos, +				     ovl_iocb_to_rwf(iocb)); +		file_end_write(real.file); +		/* Update size */ +		ovl_copyattr(ovl_inode_real(inode), inode); +	} else { +		struct ovl_aio_req *aio_req; + +		ret = -ENOMEM; +		aio_req = kmem_cache_zalloc(ovl_aio_request_cachep, GFP_KERNEL); +		if (!aio_req) +			goto out; + +		file_start_write(real.file); +		aio_req->fd = real; +		real.flags = 0; +		aio_req->orig_iocb = iocb; +		kiocb_clone(&aio_req->iocb, iocb, real.file); +		aio_req->iocb.ki_complete = ovl_aio_rw_complete; +		ret = vfs_iocb_iter_write(real.file, &aio_req->iocb, iter); +		if (ret != -EIOCBQUEUED) +			ovl_aio_cleanup_handler(aio_req); +	} +out:  	revert_creds(old_cred); - -	/* Update size */ -	ovl_copyattr(ovl_inode_real(inode), inode); -  	fdput(real);  out_unlock: @@ -291,6 +365,48 @@ out_unlock:  	return ret;  } +static ssize_t ovl_splice_read(struct file *in, loff_t *ppos, +			 struct pipe_inode_info *pipe, size_t len, +			 unsigned int flags) +{ +	ssize_t ret; +	struct fd real; +	const struct cred *old_cred; + +	ret = ovl_real_fdget(in, &real); +	if (ret) +		return ret; + +	old_cred = ovl_override_creds(file_inode(in)->i_sb); +	ret = generic_file_splice_read(real.file, ppos, pipe, len, flags); +	revert_creds(old_cred); + +	ovl_file_accessed(in); +	fdput(real); +	return ret; +} + +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; +	const struct cred *old_cred; +	ssize_t ret; + +	ret = ovl_real_fdget(out, &real); +	if (ret) +		return ret; + +	old_cred = ovl_override_creds(file_inode(out)->i_sb); +	ret = iter_file_splice_write(pipe, real.file, ppos, len, flags); +	revert_creds(old_cred); + +	ovl_file_accessed(out); +	fdput(real); +	return ret; +} +  static int ovl_fsync(struct file *file, loff_t start, loff_t end, int datasync)  {  	struct fd real; @@ -647,7 +763,25 @@ const struct file_operations ovl_file_operations = {  	.fadvise	= ovl_fadvise,  	.unlocked_ioctl	= ovl_ioctl,  	.compat_ioctl	= ovl_compat_ioctl, +	.splice_read    = ovl_splice_read, +	.splice_write   = ovl_splice_write,  	.copy_file_range	= ovl_copy_file_range,  	.remap_file_range	= ovl_remap_file_range,  }; + +int __init ovl_aio_request_cache_init(void) +{ +	ovl_aio_request_cachep = kmem_cache_create("ovl_aio_req", +						   sizeof(struct ovl_aio_req), +						   0, SLAB_HWCACHE_ALIGN, NULL); +	if (!ovl_aio_request_cachep) +		return -ENOMEM; + +	return 0; +} + +void ovl_aio_request_cache_destroy(void) +{ +	kmem_cache_destroy(ovl_aio_request_cachep); +}  |