diff options
Diffstat (limited to 'mm/filemap.c')
-rw-r--r-- | mm/filemap.c | 69 |
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 | } |
2975 | EXPORT_SYMBOL(generic_write_checks); | 2975 | EXPORT_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 | */ | ||
2984 | int 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 | |||
2977 | int pagecache_write_begin(struct file *file, struct address_space *mapping, | 3046 | int 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) |