162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci#include <linux/slab.h> 362306a36Sopenharmony_ci#include <linux/stat.h> 462306a36Sopenharmony_ci#include <linux/sched/xacct.h> 562306a36Sopenharmony_ci#include <linux/fcntl.h> 662306a36Sopenharmony_ci#include <linux/file.h> 762306a36Sopenharmony_ci#include <linux/uio.h> 862306a36Sopenharmony_ci#include <linux/fsnotify.h> 962306a36Sopenharmony_ci#include <linux/security.h> 1062306a36Sopenharmony_ci#include <linux/export.h> 1162306a36Sopenharmony_ci#include <linux/syscalls.h> 1262306a36Sopenharmony_ci#include <linux/pagemap.h> 1362306a36Sopenharmony_ci#include <linux/splice.h> 1462306a36Sopenharmony_ci#include <linux/compat.h> 1562306a36Sopenharmony_ci#include <linux/mount.h> 1662306a36Sopenharmony_ci#include <linux/fs.h> 1762306a36Sopenharmony_ci#include <linux/dax.h> 1862306a36Sopenharmony_ci#include <linux/overflow.h> 1962306a36Sopenharmony_ci#include "internal.h" 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#include <linux/uaccess.h> 2262306a36Sopenharmony_ci#include <asm/unistd.h> 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci/* 2562306a36Sopenharmony_ci * Performs necessary checks before doing a clone. 2662306a36Sopenharmony_ci * 2762306a36Sopenharmony_ci * Can adjust amount of bytes to clone via @req_count argument. 2862306a36Sopenharmony_ci * Returns appropriate error code that caller should return or 2962306a36Sopenharmony_ci * zero in case the clone should be allowed. 3062306a36Sopenharmony_ci */ 3162306a36Sopenharmony_cistatic int generic_remap_checks(struct file *file_in, loff_t pos_in, 3262306a36Sopenharmony_ci struct file *file_out, loff_t pos_out, 3362306a36Sopenharmony_ci loff_t *req_count, unsigned int remap_flags) 3462306a36Sopenharmony_ci{ 3562306a36Sopenharmony_ci struct inode *inode_in = file_in->f_mapping->host; 3662306a36Sopenharmony_ci struct inode *inode_out = file_out->f_mapping->host; 3762306a36Sopenharmony_ci uint64_t count = *req_count; 3862306a36Sopenharmony_ci uint64_t bcount; 3962306a36Sopenharmony_ci loff_t size_in, size_out; 4062306a36Sopenharmony_ci loff_t bs = inode_out->i_sb->s_blocksize; 4162306a36Sopenharmony_ci int ret; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci /* The start of both ranges must be aligned to an fs block. */ 4462306a36Sopenharmony_ci if (!IS_ALIGNED(pos_in, bs) || !IS_ALIGNED(pos_out, bs)) 4562306a36Sopenharmony_ci return -EINVAL; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci /* Ensure offsets don't wrap. */ 4862306a36Sopenharmony_ci if (pos_in + count < pos_in || pos_out + count < pos_out) 4962306a36Sopenharmony_ci return -EINVAL; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci size_in = i_size_read(inode_in); 5262306a36Sopenharmony_ci size_out = i_size_read(inode_out); 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci /* Dedupe requires both ranges to be within EOF. */ 5562306a36Sopenharmony_ci if ((remap_flags & REMAP_FILE_DEDUP) && 5662306a36Sopenharmony_ci (pos_in >= size_in || pos_in + count > size_in || 5762306a36Sopenharmony_ci pos_out >= size_out || pos_out + count > size_out)) 5862306a36Sopenharmony_ci return -EINVAL; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci /* Ensure the infile range is within the infile. */ 6162306a36Sopenharmony_ci if (pos_in >= size_in) 6262306a36Sopenharmony_ci return -EINVAL; 6362306a36Sopenharmony_ci count = min(count, size_in - (uint64_t)pos_in); 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci ret = generic_write_check_limits(file_out, pos_out, &count); 6662306a36Sopenharmony_ci if (ret) 6762306a36Sopenharmony_ci return ret; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci /* 7062306a36Sopenharmony_ci * If the user wanted us to link to the infile's EOF, round up to the 7162306a36Sopenharmony_ci * next block boundary for this check. 7262306a36Sopenharmony_ci * 7362306a36Sopenharmony_ci * Otherwise, make sure the count is also block-aligned, having 7462306a36Sopenharmony_ci * already confirmed the starting offsets' block alignment. 7562306a36Sopenharmony_ci */ 7662306a36Sopenharmony_ci if (pos_in + count == size_in && 7762306a36Sopenharmony_ci (!(remap_flags & REMAP_FILE_DEDUP) || pos_out + count == size_out)) { 7862306a36Sopenharmony_ci bcount = ALIGN(size_in, bs) - pos_in; 7962306a36Sopenharmony_ci } else { 8062306a36Sopenharmony_ci if (!IS_ALIGNED(count, bs)) 8162306a36Sopenharmony_ci count = ALIGN_DOWN(count, bs); 8262306a36Sopenharmony_ci bcount = count; 8362306a36Sopenharmony_ci } 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci /* Don't allow overlapped cloning within the same file. */ 8662306a36Sopenharmony_ci if (inode_in == inode_out && 8762306a36Sopenharmony_ci pos_out + bcount > pos_in && 8862306a36Sopenharmony_ci pos_out < pos_in + bcount) 8962306a36Sopenharmony_ci return -EINVAL; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci /* 9262306a36Sopenharmony_ci * We shortened the request but the caller can't deal with that, so 9362306a36Sopenharmony_ci * bounce the request back to userspace. 9462306a36Sopenharmony_ci */ 9562306a36Sopenharmony_ci if (*req_count != count && !(remap_flags & REMAP_FILE_CAN_SHORTEN)) 9662306a36Sopenharmony_ci return -EINVAL; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci *req_count = count; 9962306a36Sopenharmony_ci return 0; 10062306a36Sopenharmony_ci} 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_cistatic int remap_verify_area(struct file *file, loff_t pos, loff_t len, 10362306a36Sopenharmony_ci bool write) 10462306a36Sopenharmony_ci{ 10562306a36Sopenharmony_ci loff_t tmp; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci if (unlikely(pos < 0 || len < 0)) 10862306a36Sopenharmony_ci return -EINVAL; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci if (unlikely(check_add_overflow(pos, len, &tmp))) 11162306a36Sopenharmony_ci return -EINVAL; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci return security_file_permission(file, write ? MAY_WRITE : MAY_READ); 11462306a36Sopenharmony_ci} 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci/* 11762306a36Sopenharmony_ci * Ensure that we don't remap a partial EOF block in the middle of something 11862306a36Sopenharmony_ci * else. Assume that the offsets have already been checked for block 11962306a36Sopenharmony_ci * alignment. 12062306a36Sopenharmony_ci * 12162306a36Sopenharmony_ci * For clone we only link a partial EOF block above or at the destination file's 12262306a36Sopenharmony_ci * EOF. For deduplication we accept a partial EOF block only if it ends at the 12362306a36Sopenharmony_ci * destination file's EOF (can not link it into the middle of a file). 12462306a36Sopenharmony_ci * 12562306a36Sopenharmony_ci * Shorten the request if possible. 12662306a36Sopenharmony_ci */ 12762306a36Sopenharmony_cistatic int generic_remap_check_len(struct inode *inode_in, 12862306a36Sopenharmony_ci struct inode *inode_out, 12962306a36Sopenharmony_ci loff_t pos_out, 13062306a36Sopenharmony_ci loff_t *len, 13162306a36Sopenharmony_ci unsigned int remap_flags) 13262306a36Sopenharmony_ci{ 13362306a36Sopenharmony_ci u64 blkmask = i_blocksize(inode_in) - 1; 13462306a36Sopenharmony_ci loff_t new_len = *len; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci if ((*len & blkmask) == 0) 13762306a36Sopenharmony_ci return 0; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci if (pos_out + *len < i_size_read(inode_out)) 14062306a36Sopenharmony_ci new_len &= ~blkmask; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci if (new_len == *len) 14362306a36Sopenharmony_ci return 0; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci if (remap_flags & REMAP_FILE_CAN_SHORTEN) { 14662306a36Sopenharmony_ci *len = new_len; 14762306a36Sopenharmony_ci return 0; 14862306a36Sopenharmony_ci } 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci return (remap_flags & REMAP_FILE_DEDUP) ? -EBADE : -EINVAL; 15162306a36Sopenharmony_ci} 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci/* Read a page's worth of file data into the page cache. */ 15462306a36Sopenharmony_cistatic struct folio *vfs_dedupe_get_folio(struct file *file, loff_t pos) 15562306a36Sopenharmony_ci{ 15662306a36Sopenharmony_ci return read_mapping_folio(file->f_mapping, pos >> PAGE_SHIFT, file); 15762306a36Sopenharmony_ci} 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci/* 16062306a36Sopenharmony_ci * Lock two folios, ensuring that we lock in offset order if the folios 16162306a36Sopenharmony_ci * are from the same file. 16262306a36Sopenharmony_ci */ 16362306a36Sopenharmony_cistatic void vfs_lock_two_folios(struct folio *folio1, struct folio *folio2) 16462306a36Sopenharmony_ci{ 16562306a36Sopenharmony_ci /* Always lock in order of increasing index. */ 16662306a36Sopenharmony_ci if (folio1->index > folio2->index) 16762306a36Sopenharmony_ci swap(folio1, folio2); 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci folio_lock(folio1); 17062306a36Sopenharmony_ci if (folio1 != folio2) 17162306a36Sopenharmony_ci folio_lock(folio2); 17262306a36Sopenharmony_ci} 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci/* Unlock two folios, being careful not to unlock the same folio twice. */ 17562306a36Sopenharmony_cistatic void vfs_unlock_two_folios(struct folio *folio1, struct folio *folio2) 17662306a36Sopenharmony_ci{ 17762306a36Sopenharmony_ci folio_unlock(folio1); 17862306a36Sopenharmony_ci if (folio1 != folio2) 17962306a36Sopenharmony_ci folio_unlock(folio2); 18062306a36Sopenharmony_ci} 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci/* 18362306a36Sopenharmony_ci * Compare extents of two files to see if they are the same. 18462306a36Sopenharmony_ci * Caller must have locked both inodes to prevent write races. 18562306a36Sopenharmony_ci */ 18662306a36Sopenharmony_cistatic int vfs_dedupe_file_range_compare(struct file *src, loff_t srcoff, 18762306a36Sopenharmony_ci struct file *dest, loff_t dstoff, 18862306a36Sopenharmony_ci loff_t len, bool *is_same) 18962306a36Sopenharmony_ci{ 19062306a36Sopenharmony_ci bool same = true; 19162306a36Sopenharmony_ci int error = -EINVAL; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci while (len) { 19462306a36Sopenharmony_ci struct folio *src_folio, *dst_folio; 19562306a36Sopenharmony_ci void *src_addr, *dst_addr; 19662306a36Sopenharmony_ci loff_t cmp_len = min(PAGE_SIZE - offset_in_page(srcoff), 19762306a36Sopenharmony_ci PAGE_SIZE - offset_in_page(dstoff)); 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci cmp_len = min(cmp_len, len); 20062306a36Sopenharmony_ci if (cmp_len <= 0) 20162306a36Sopenharmony_ci goto out_error; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci src_folio = vfs_dedupe_get_folio(src, srcoff); 20462306a36Sopenharmony_ci if (IS_ERR(src_folio)) { 20562306a36Sopenharmony_ci error = PTR_ERR(src_folio); 20662306a36Sopenharmony_ci goto out_error; 20762306a36Sopenharmony_ci } 20862306a36Sopenharmony_ci dst_folio = vfs_dedupe_get_folio(dest, dstoff); 20962306a36Sopenharmony_ci if (IS_ERR(dst_folio)) { 21062306a36Sopenharmony_ci error = PTR_ERR(dst_folio); 21162306a36Sopenharmony_ci folio_put(src_folio); 21262306a36Sopenharmony_ci goto out_error; 21362306a36Sopenharmony_ci } 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci vfs_lock_two_folios(src_folio, dst_folio); 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci /* 21862306a36Sopenharmony_ci * Now that we've locked both folios, make sure they're still 21962306a36Sopenharmony_ci * mapped to the file data we're interested in. If not, 22062306a36Sopenharmony_ci * someone is invalidating pages on us and we lose. 22162306a36Sopenharmony_ci */ 22262306a36Sopenharmony_ci if (!folio_test_uptodate(src_folio) || !folio_test_uptodate(dst_folio) || 22362306a36Sopenharmony_ci src_folio->mapping != src->f_mapping || 22462306a36Sopenharmony_ci dst_folio->mapping != dest->f_mapping) { 22562306a36Sopenharmony_ci same = false; 22662306a36Sopenharmony_ci goto unlock; 22762306a36Sopenharmony_ci } 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci src_addr = kmap_local_folio(src_folio, 23062306a36Sopenharmony_ci offset_in_folio(src_folio, srcoff)); 23162306a36Sopenharmony_ci dst_addr = kmap_local_folio(dst_folio, 23262306a36Sopenharmony_ci offset_in_folio(dst_folio, dstoff)); 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci flush_dcache_folio(src_folio); 23562306a36Sopenharmony_ci flush_dcache_folio(dst_folio); 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci if (memcmp(src_addr, dst_addr, cmp_len)) 23862306a36Sopenharmony_ci same = false; 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci kunmap_local(dst_addr); 24162306a36Sopenharmony_ci kunmap_local(src_addr); 24262306a36Sopenharmony_ciunlock: 24362306a36Sopenharmony_ci vfs_unlock_two_folios(src_folio, dst_folio); 24462306a36Sopenharmony_ci folio_put(dst_folio); 24562306a36Sopenharmony_ci folio_put(src_folio); 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci if (!same) 24862306a36Sopenharmony_ci break; 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci srcoff += cmp_len; 25162306a36Sopenharmony_ci dstoff += cmp_len; 25262306a36Sopenharmony_ci len -= cmp_len; 25362306a36Sopenharmony_ci } 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci *is_same = same; 25662306a36Sopenharmony_ci return 0; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ciout_error: 25962306a36Sopenharmony_ci return error; 26062306a36Sopenharmony_ci} 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci/* 26362306a36Sopenharmony_ci * Check that the two inodes are eligible for cloning, the ranges make 26462306a36Sopenharmony_ci * sense, and then flush all dirty data. Caller must ensure that the 26562306a36Sopenharmony_ci * inodes have been locked against any other modifications. 26662306a36Sopenharmony_ci * 26762306a36Sopenharmony_ci * If there's an error, then the usual negative error code is returned. 26862306a36Sopenharmony_ci * Otherwise returns 0 with *len set to the request length. 26962306a36Sopenharmony_ci */ 27062306a36Sopenharmony_ciint 27162306a36Sopenharmony_ci__generic_remap_file_range_prep(struct file *file_in, loff_t pos_in, 27262306a36Sopenharmony_ci struct file *file_out, loff_t pos_out, 27362306a36Sopenharmony_ci loff_t *len, unsigned int remap_flags, 27462306a36Sopenharmony_ci const struct iomap_ops *dax_read_ops) 27562306a36Sopenharmony_ci{ 27662306a36Sopenharmony_ci struct inode *inode_in = file_inode(file_in); 27762306a36Sopenharmony_ci struct inode *inode_out = file_inode(file_out); 27862306a36Sopenharmony_ci bool same_inode = (inode_in == inode_out); 27962306a36Sopenharmony_ci int ret; 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci /* Don't touch certain kinds of inodes */ 28262306a36Sopenharmony_ci if (IS_IMMUTABLE(inode_out)) 28362306a36Sopenharmony_ci return -EPERM; 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci if (IS_SWAPFILE(inode_in) || IS_SWAPFILE(inode_out)) 28662306a36Sopenharmony_ci return -ETXTBSY; 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci /* Don't reflink dirs, pipes, sockets... */ 28962306a36Sopenharmony_ci if (S_ISDIR(inode_in->i_mode) || S_ISDIR(inode_out->i_mode)) 29062306a36Sopenharmony_ci return -EISDIR; 29162306a36Sopenharmony_ci if (!S_ISREG(inode_in->i_mode) || !S_ISREG(inode_out->i_mode)) 29262306a36Sopenharmony_ci return -EINVAL; 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci /* Zero length dedupe exits immediately; reflink goes to EOF. */ 29562306a36Sopenharmony_ci if (*len == 0) { 29662306a36Sopenharmony_ci loff_t isize = i_size_read(inode_in); 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci if ((remap_flags & REMAP_FILE_DEDUP) || pos_in == isize) 29962306a36Sopenharmony_ci return 0; 30062306a36Sopenharmony_ci if (pos_in > isize) 30162306a36Sopenharmony_ci return -EINVAL; 30262306a36Sopenharmony_ci *len = isize - pos_in; 30362306a36Sopenharmony_ci if (*len == 0) 30462306a36Sopenharmony_ci return 0; 30562306a36Sopenharmony_ci } 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci /* Check that we don't violate system file offset limits. */ 30862306a36Sopenharmony_ci ret = generic_remap_checks(file_in, pos_in, file_out, pos_out, len, 30962306a36Sopenharmony_ci remap_flags); 31062306a36Sopenharmony_ci if (ret || *len == 0) 31162306a36Sopenharmony_ci return ret; 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci /* Wait for the completion of any pending IOs on both files */ 31462306a36Sopenharmony_ci inode_dio_wait(inode_in); 31562306a36Sopenharmony_ci if (!same_inode) 31662306a36Sopenharmony_ci inode_dio_wait(inode_out); 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci ret = filemap_write_and_wait_range(inode_in->i_mapping, 31962306a36Sopenharmony_ci pos_in, pos_in + *len - 1); 32062306a36Sopenharmony_ci if (ret) 32162306a36Sopenharmony_ci return ret; 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci ret = filemap_write_and_wait_range(inode_out->i_mapping, 32462306a36Sopenharmony_ci pos_out, pos_out + *len - 1); 32562306a36Sopenharmony_ci if (ret) 32662306a36Sopenharmony_ci return ret; 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci /* 32962306a36Sopenharmony_ci * Check that the extents are the same. 33062306a36Sopenharmony_ci */ 33162306a36Sopenharmony_ci if (remap_flags & REMAP_FILE_DEDUP) { 33262306a36Sopenharmony_ci bool is_same = false; 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci if (!IS_DAX(inode_in)) 33562306a36Sopenharmony_ci ret = vfs_dedupe_file_range_compare(file_in, pos_in, 33662306a36Sopenharmony_ci file_out, pos_out, *len, &is_same); 33762306a36Sopenharmony_ci else if (dax_read_ops) 33862306a36Sopenharmony_ci ret = dax_dedupe_file_range_compare(inode_in, pos_in, 33962306a36Sopenharmony_ci inode_out, pos_out, *len, &is_same, 34062306a36Sopenharmony_ci dax_read_ops); 34162306a36Sopenharmony_ci else 34262306a36Sopenharmony_ci return -EINVAL; 34362306a36Sopenharmony_ci if (ret) 34462306a36Sopenharmony_ci return ret; 34562306a36Sopenharmony_ci if (!is_same) 34662306a36Sopenharmony_ci return -EBADE; 34762306a36Sopenharmony_ci } 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci ret = generic_remap_check_len(inode_in, inode_out, pos_out, len, 35062306a36Sopenharmony_ci remap_flags); 35162306a36Sopenharmony_ci if (ret || *len == 0) 35262306a36Sopenharmony_ci return ret; 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci /* If can't alter the file contents, we're done. */ 35562306a36Sopenharmony_ci if (!(remap_flags & REMAP_FILE_DEDUP)) 35662306a36Sopenharmony_ci ret = file_modified(file_out); 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci return ret; 35962306a36Sopenharmony_ci} 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ciint generic_remap_file_range_prep(struct file *file_in, loff_t pos_in, 36262306a36Sopenharmony_ci struct file *file_out, loff_t pos_out, 36362306a36Sopenharmony_ci loff_t *len, unsigned int remap_flags) 36462306a36Sopenharmony_ci{ 36562306a36Sopenharmony_ci return __generic_remap_file_range_prep(file_in, pos_in, file_out, 36662306a36Sopenharmony_ci pos_out, len, remap_flags, NULL); 36762306a36Sopenharmony_ci} 36862306a36Sopenharmony_ciEXPORT_SYMBOL(generic_remap_file_range_prep); 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ciloff_t do_clone_file_range(struct file *file_in, loff_t pos_in, 37162306a36Sopenharmony_ci struct file *file_out, loff_t pos_out, 37262306a36Sopenharmony_ci loff_t len, unsigned int remap_flags) 37362306a36Sopenharmony_ci{ 37462306a36Sopenharmony_ci loff_t ret; 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci WARN_ON_ONCE(remap_flags & REMAP_FILE_DEDUP); 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci if (file_inode(file_in)->i_sb != file_inode(file_out)->i_sb) 37962306a36Sopenharmony_ci return -EXDEV; 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci ret = generic_file_rw_checks(file_in, file_out); 38262306a36Sopenharmony_ci if (ret < 0) 38362306a36Sopenharmony_ci return ret; 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci if (!file_in->f_op->remap_file_range) 38662306a36Sopenharmony_ci return -EOPNOTSUPP; 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci ret = remap_verify_area(file_in, pos_in, len, false); 38962306a36Sopenharmony_ci if (ret) 39062306a36Sopenharmony_ci return ret; 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci ret = remap_verify_area(file_out, pos_out, len, true); 39362306a36Sopenharmony_ci if (ret) 39462306a36Sopenharmony_ci return ret; 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci ret = file_in->f_op->remap_file_range(file_in, pos_in, 39762306a36Sopenharmony_ci file_out, pos_out, len, remap_flags); 39862306a36Sopenharmony_ci if (ret < 0) 39962306a36Sopenharmony_ci return ret; 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci fsnotify_access(file_in); 40262306a36Sopenharmony_ci fsnotify_modify(file_out); 40362306a36Sopenharmony_ci return ret; 40462306a36Sopenharmony_ci} 40562306a36Sopenharmony_ciEXPORT_SYMBOL(do_clone_file_range); 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ciloff_t vfs_clone_file_range(struct file *file_in, loff_t pos_in, 40862306a36Sopenharmony_ci struct file *file_out, loff_t pos_out, 40962306a36Sopenharmony_ci loff_t len, unsigned int remap_flags) 41062306a36Sopenharmony_ci{ 41162306a36Sopenharmony_ci loff_t ret; 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci file_start_write(file_out); 41462306a36Sopenharmony_ci ret = do_clone_file_range(file_in, pos_in, file_out, pos_out, len, 41562306a36Sopenharmony_ci remap_flags); 41662306a36Sopenharmony_ci file_end_write(file_out); 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci return ret; 41962306a36Sopenharmony_ci} 42062306a36Sopenharmony_ciEXPORT_SYMBOL(vfs_clone_file_range); 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci/* Check whether we are allowed to dedupe the destination file */ 42362306a36Sopenharmony_cistatic bool allow_file_dedupe(struct file *file) 42462306a36Sopenharmony_ci{ 42562306a36Sopenharmony_ci struct mnt_idmap *idmap = file_mnt_idmap(file); 42662306a36Sopenharmony_ci struct inode *inode = file_inode(file); 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci if (capable(CAP_SYS_ADMIN)) 42962306a36Sopenharmony_ci return true; 43062306a36Sopenharmony_ci if (file->f_mode & FMODE_WRITE) 43162306a36Sopenharmony_ci return true; 43262306a36Sopenharmony_ci if (vfsuid_eq_kuid(i_uid_into_vfsuid(idmap, inode), current_fsuid())) 43362306a36Sopenharmony_ci return true; 43462306a36Sopenharmony_ci if (!inode_permission(idmap, inode, MAY_WRITE)) 43562306a36Sopenharmony_ci return true; 43662306a36Sopenharmony_ci return false; 43762306a36Sopenharmony_ci} 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ciloff_t vfs_dedupe_file_range_one(struct file *src_file, loff_t src_pos, 44062306a36Sopenharmony_ci struct file *dst_file, loff_t dst_pos, 44162306a36Sopenharmony_ci loff_t len, unsigned int remap_flags) 44262306a36Sopenharmony_ci{ 44362306a36Sopenharmony_ci loff_t ret; 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci WARN_ON_ONCE(remap_flags & ~(REMAP_FILE_DEDUP | 44662306a36Sopenharmony_ci REMAP_FILE_CAN_SHORTEN)); 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci ret = mnt_want_write_file(dst_file); 44962306a36Sopenharmony_ci if (ret) 45062306a36Sopenharmony_ci return ret; 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci /* 45362306a36Sopenharmony_ci * This is redundant if called from vfs_dedupe_file_range(), but other 45462306a36Sopenharmony_ci * callers need it and it's not performance sesitive... 45562306a36Sopenharmony_ci */ 45662306a36Sopenharmony_ci ret = remap_verify_area(src_file, src_pos, len, false); 45762306a36Sopenharmony_ci if (ret) 45862306a36Sopenharmony_ci goto out_drop_write; 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci ret = remap_verify_area(dst_file, dst_pos, len, true); 46162306a36Sopenharmony_ci if (ret) 46262306a36Sopenharmony_ci goto out_drop_write; 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci ret = -EPERM; 46562306a36Sopenharmony_ci if (!allow_file_dedupe(dst_file)) 46662306a36Sopenharmony_ci goto out_drop_write; 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci ret = -EXDEV; 46962306a36Sopenharmony_ci if (file_inode(src_file)->i_sb != file_inode(dst_file)->i_sb) 47062306a36Sopenharmony_ci goto out_drop_write; 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci ret = -EISDIR; 47362306a36Sopenharmony_ci if (S_ISDIR(file_inode(dst_file)->i_mode)) 47462306a36Sopenharmony_ci goto out_drop_write; 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci ret = -EINVAL; 47762306a36Sopenharmony_ci if (!dst_file->f_op->remap_file_range) 47862306a36Sopenharmony_ci goto out_drop_write; 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci if (len == 0) { 48162306a36Sopenharmony_ci ret = 0; 48262306a36Sopenharmony_ci goto out_drop_write; 48362306a36Sopenharmony_ci } 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci ret = dst_file->f_op->remap_file_range(src_file, src_pos, dst_file, 48662306a36Sopenharmony_ci dst_pos, len, remap_flags | REMAP_FILE_DEDUP); 48762306a36Sopenharmony_ciout_drop_write: 48862306a36Sopenharmony_ci mnt_drop_write_file(dst_file); 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci return ret; 49162306a36Sopenharmony_ci} 49262306a36Sopenharmony_ciEXPORT_SYMBOL(vfs_dedupe_file_range_one); 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ciint vfs_dedupe_file_range(struct file *file, struct file_dedupe_range *same) 49562306a36Sopenharmony_ci{ 49662306a36Sopenharmony_ci struct file_dedupe_range_info *info; 49762306a36Sopenharmony_ci struct inode *src = file_inode(file); 49862306a36Sopenharmony_ci u64 off; 49962306a36Sopenharmony_ci u64 len; 50062306a36Sopenharmony_ci int i; 50162306a36Sopenharmony_ci int ret; 50262306a36Sopenharmony_ci u16 count = same->dest_count; 50362306a36Sopenharmony_ci loff_t deduped; 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci if (!(file->f_mode & FMODE_READ)) 50662306a36Sopenharmony_ci return -EINVAL; 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci if (same->reserved1 || same->reserved2) 50962306a36Sopenharmony_ci return -EINVAL; 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci off = same->src_offset; 51262306a36Sopenharmony_ci len = same->src_length; 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci if (S_ISDIR(src->i_mode)) 51562306a36Sopenharmony_ci return -EISDIR; 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci if (!S_ISREG(src->i_mode)) 51862306a36Sopenharmony_ci return -EINVAL; 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci if (!file->f_op->remap_file_range) 52162306a36Sopenharmony_ci return -EOPNOTSUPP; 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci ret = remap_verify_area(file, off, len, false); 52462306a36Sopenharmony_ci if (ret < 0) 52562306a36Sopenharmony_ci return ret; 52662306a36Sopenharmony_ci ret = 0; 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci if (off + len > i_size_read(src)) 52962306a36Sopenharmony_ci return -EINVAL; 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci /* Arbitrary 1G limit on a single dedupe request, can be raised. */ 53262306a36Sopenharmony_ci len = min_t(u64, len, 1 << 30); 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci /* pre-format output fields to sane values */ 53562306a36Sopenharmony_ci for (i = 0; i < count; i++) { 53662306a36Sopenharmony_ci same->info[i].bytes_deduped = 0ULL; 53762306a36Sopenharmony_ci same->info[i].status = FILE_DEDUPE_RANGE_SAME; 53862306a36Sopenharmony_ci } 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci for (i = 0, info = same->info; i < count; i++, info++) { 54162306a36Sopenharmony_ci struct fd dst_fd = fdget(info->dest_fd); 54262306a36Sopenharmony_ci struct file *dst_file = dst_fd.file; 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci if (!dst_file) { 54562306a36Sopenharmony_ci info->status = -EBADF; 54662306a36Sopenharmony_ci goto next_loop; 54762306a36Sopenharmony_ci } 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci if (info->reserved) { 55062306a36Sopenharmony_ci info->status = -EINVAL; 55162306a36Sopenharmony_ci goto next_fdput; 55262306a36Sopenharmony_ci } 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci deduped = vfs_dedupe_file_range_one(file, off, dst_file, 55562306a36Sopenharmony_ci info->dest_offset, len, 55662306a36Sopenharmony_ci REMAP_FILE_CAN_SHORTEN); 55762306a36Sopenharmony_ci if (deduped == -EBADE) 55862306a36Sopenharmony_ci info->status = FILE_DEDUPE_RANGE_DIFFERS; 55962306a36Sopenharmony_ci else if (deduped < 0) 56062306a36Sopenharmony_ci info->status = deduped; 56162306a36Sopenharmony_ci else 56262306a36Sopenharmony_ci info->bytes_deduped = len; 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_cinext_fdput: 56562306a36Sopenharmony_ci fdput(dst_fd); 56662306a36Sopenharmony_cinext_loop: 56762306a36Sopenharmony_ci if (fatal_signal_pending(current)) 56862306a36Sopenharmony_ci break; 56962306a36Sopenharmony_ci } 57062306a36Sopenharmony_ci return ret; 57162306a36Sopenharmony_ci} 57262306a36Sopenharmony_ciEXPORT_SYMBOL(vfs_dedupe_file_range); 573