summaryrefslogtreecommitdiffstats
path: root/mm/filemap.c
diff options
context:
space:
mode:
Diffstat (limited to 'mm/filemap.c')
-rw-r--r--mm/filemap.c69
1 files changed, 69 insertions, 0 deletions
diff --git a/mm/filemap.c b/mm/filemap.c
index 52517f28e6f4..47e6bfd45a91 100644
--- a/mm/filemap.c
+++ b/mm/filemap.c
@@ -2974,6 +2974,75 @@ inline ssize_t generic_write_checks(struct kiocb *iocb, struct iov_iter *from)
2974} 2974}
2975EXPORT_SYMBOL(generic_write_checks); 2975EXPORT_SYMBOL(generic_write_checks);
2976 2976
2977/*
2978 * Performs necessary checks before doing a clone.
2979 *
2980 * Can adjust amount of bytes to clone.
2981 * Returns appropriate error code that caller should return or
2982 * zero in case the clone should be allowed.
2983 */
2984int generic_remap_checks(struct file *file_in, loff_t pos_in,
2985 struct file *file_out, loff_t pos_out,
2986 uint64_t *req_count, bool is_dedupe)
2987{
2988 struct inode *inode_in = file_in->f_mapping->host;
2989 struct inode *inode_out = file_out->f_mapping->host;
2990 uint64_t count = *req_count;
2991 uint64_t bcount;
2992 loff_t size_in, size_out;
2993 loff_t bs = inode_out->i_sb->s_blocksize;
2994
2995 /* The start of both ranges must be aligned to an fs block. */
2996 if (!IS_ALIGNED(pos_in, bs) || !IS_ALIGNED(pos_out, bs))
2997 return -EINVAL;
2998
2999 /* Ensure offsets don't wrap. */
3000 if (pos_in + count < pos_in || pos_out + count < pos_out)
3001 return -EINVAL;
3002
3003 size_in = i_size_read(inode_in);
3004 size_out = i_size_read(inode_out);
3005
3006 /* Dedupe requires both ranges to be within EOF. */
3007 if (is_dedupe &&
3008 (pos_in >= size_in || pos_in + count > size_in ||
3009 pos_out >= size_out || pos_out + count > size_out))
3010 return -EINVAL;
3011
3012 /* Ensure the infile range is within the infile. */
3013 if (pos_in >= size_in)
3014 return -EINVAL;
3015 count = min(count, size_in - (uint64_t)pos_in);
3016
3017 /*
3018 * If the user wanted us to link to the infile's EOF, round up to the
3019 * next block boundary for this check.
3020 *
3021 * Otherwise, make sure the count is also block-aligned, having
3022 * already confirmed the starting offsets' block alignment.
3023 */
3024 if (pos_in + count == size_in) {
3025 bcount = ALIGN(size_in, bs) - pos_in;
3026 } else {
3027 if (!IS_ALIGNED(count, bs))
3028 return -EINVAL;
3029
3030 bcount = count;
3031 }
3032
3033 /* Don't allow overlapped cloning within the same file. */
3034 if (inode_in == inode_out &&
3035 pos_out + bcount > pos_in &&
3036 pos_out < pos_in + bcount)
3037 return -EINVAL;
3038
3039 /* For now we don't support changing the length. */
3040 if (*req_count != count)
3041 return -EINVAL;
3042
3043 return 0;
3044}
3045
2977int pagecache_write_begin(struct file *file, struct address_space *mapping, 3046int pagecache_write_begin(struct file *file, struct address_space *mapping,
2978 loff_t pos, unsigned len, unsigned flags, 3047 loff_t pos, unsigned len, unsigned flags,
2979 struct page **pagep, void **fsdata) 3048 struct page **pagep, void **fsdata)