|
@@ -1752,6 +1752,97 @@ static int generic_remap_check_len(struct inode *inode_in,
|
|
|
return (remap_flags & REMAP_FILE_DEDUP) ? -EBADE : -EINVAL;
|
|
|
}
|
|
|
|
|
|
+/*
|
|
|
+ * Read a page's worth of file data into the page cache. Return the page
|
|
|
+ * locked.
|
|
|
+ */
|
|
|
+static struct page *vfs_dedupe_get_page(struct inode *inode, loff_t offset)
|
|
|
+{
|
|
|
+ struct page *page;
|
|
|
+
|
|
|
+ page = read_mapping_page(inode->i_mapping, offset >> PAGE_SHIFT, NULL);
|
|
|
+ if (IS_ERR(page))
|
|
|
+ return page;
|
|
|
+ if (!PageUptodate(page)) {
|
|
|
+ put_page(page);
|
|
|
+ return ERR_PTR(-EIO);
|
|
|
+ }
|
|
|
+ lock_page(page);
|
|
|
+ return page;
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * Compare extents of two files to see if they are the same.
|
|
|
+ * Caller must have locked both inodes to prevent write races.
|
|
|
+ */
|
|
|
+static int vfs_dedupe_file_range_compare(struct inode *src, loff_t srcoff,
|
|
|
+ struct inode *dest, loff_t destoff,
|
|
|
+ loff_t len, bool *is_same)
|
|
|
+{
|
|
|
+ loff_t src_poff;
|
|
|
+ loff_t dest_poff;
|
|
|
+ void *src_addr;
|
|
|
+ void *dest_addr;
|
|
|
+ struct page *src_page;
|
|
|
+ struct page *dest_page;
|
|
|
+ loff_t cmp_len;
|
|
|
+ bool same;
|
|
|
+ int error;
|
|
|
+
|
|
|
+ error = -EINVAL;
|
|
|
+ same = true;
|
|
|
+ while (len) {
|
|
|
+ src_poff = srcoff & (PAGE_SIZE - 1);
|
|
|
+ dest_poff = destoff & (PAGE_SIZE - 1);
|
|
|
+ cmp_len = min(PAGE_SIZE - src_poff,
|
|
|
+ PAGE_SIZE - dest_poff);
|
|
|
+ cmp_len = min(cmp_len, len);
|
|
|
+ if (cmp_len <= 0)
|
|
|
+ goto out_error;
|
|
|
+
|
|
|
+ src_page = vfs_dedupe_get_page(src, srcoff);
|
|
|
+ if (IS_ERR(src_page)) {
|
|
|
+ error = PTR_ERR(src_page);
|
|
|
+ goto out_error;
|
|
|
+ }
|
|
|
+ dest_page = vfs_dedupe_get_page(dest, destoff);
|
|
|
+ if (IS_ERR(dest_page)) {
|
|
|
+ error = PTR_ERR(dest_page);
|
|
|
+ unlock_page(src_page);
|
|
|
+ put_page(src_page);
|
|
|
+ goto out_error;
|
|
|
+ }
|
|
|
+ src_addr = kmap_atomic(src_page);
|
|
|
+ dest_addr = kmap_atomic(dest_page);
|
|
|
+
|
|
|
+ flush_dcache_page(src_page);
|
|
|
+ flush_dcache_page(dest_page);
|
|
|
+
|
|
|
+ if (memcmp(src_addr + src_poff, dest_addr + dest_poff, cmp_len))
|
|
|
+ same = false;
|
|
|
+
|
|
|
+ kunmap_atomic(dest_addr);
|
|
|
+ kunmap_atomic(src_addr);
|
|
|
+ unlock_page(dest_page);
|
|
|
+ unlock_page(src_page);
|
|
|
+ put_page(dest_page);
|
|
|
+ put_page(src_page);
|
|
|
+
|
|
|
+ if (!same)
|
|
|
+ break;
|
|
|
+
|
|
|
+ srcoff += cmp_len;
|
|
|
+ destoff += cmp_len;
|
|
|
+ len -= cmp_len;
|
|
|
+ }
|
|
|
+
|
|
|
+ *is_same = same;
|
|
|
+ return 0;
|
|
|
+
|
|
|
+out_error:
|
|
|
+ return error;
|
|
|
+}
|
|
|
+
|
|
|
/*
|
|
|
* Check that the two inodes are eligible for cloning, the ranges make
|
|
|
* sense, and then flush all dirty data. Caller must ensure that the
|
|
@@ -1923,102 +2014,6 @@ loff_t vfs_clone_file_range(struct file *file_in, loff_t pos_in,
|
|
|
}
|
|
|
EXPORT_SYMBOL(vfs_clone_file_range);
|
|
|
|
|
|
-/*
|
|
|
- * Read a page's worth of file data into the page cache. Return the page
|
|
|
- * locked.
|
|
|
- */
|
|
|
-static struct page *vfs_dedupe_get_page(struct inode *inode, loff_t offset)
|
|
|
-{
|
|
|
- struct address_space *mapping;
|
|
|
- struct page *page;
|
|
|
- pgoff_t n;
|
|
|
-
|
|
|
- n = offset >> PAGE_SHIFT;
|
|
|
- mapping = inode->i_mapping;
|
|
|
- page = read_mapping_page(mapping, n, NULL);
|
|
|
- if (IS_ERR(page))
|
|
|
- return page;
|
|
|
- if (!PageUptodate(page)) {
|
|
|
- put_page(page);
|
|
|
- return ERR_PTR(-EIO);
|
|
|
- }
|
|
|
- lock_page(page);
|
|
|
- return page;
|
|
|
-}
|
|
|
-
|
|
|
-/*
|
|
|
- * Compare extents of two files to see if they are the same.
|
|
|
- * Caller must have locked both inodes to prevent write races.
|
|
|
- */
|
|
|
-int vfs_dedupe_file_range_compare(struct inode *src, loff_t srcoff,
|
|
|
- struct inode *dest, loff_t destoff,
|
|
|
- loff_t len, bool *is_same)
|
|
|
-{
|
|
|
- loff_t src_poff;
|
|
|
- loff_t dest_poff;
|
|
|
- void *src_addr;
|
|
|
- void *dest_addr;
|
|
|
- struct page *src_page;
|
|
|
- struct page *dest_page;
|
|
|
- loff_t cmp_len;
|
|
|
- bool same;
|
|
|
- int error;
|
|
|
-
|
|
|
- error = -EINVAL;
|
|
|
- same = true;
|
|
|
- while (len) {
|
|
|
- src_poff = srcoff & (PAGE_SIZE - 1);
|
|
|
- dest_poff = destoff & (PAGE_SIZE - 1);
|
|
|
- cmp_len = min(PAGE_SIZE - src_poff,
|
|
|
- PAGE_SIZE - dest_poff);
|
|
|
- cmp_len = min(cmp_len, len);
|
|
|
- if (cmp_len <= 0)
|
|
|
- goto out_error;
|
|
|
-
|
|
|
- src_page = vfs_dedupe_get_page(src, srcoff);
|
|
|
- if (IS_ERR(src_page)) {
|
|
|
- error = PTR_ERR(src_page);
|
|
|
- goto out_error;
|
|
|
- }
|
|
|
- dest_page = vfs_dedupe_get_page(dest, destoff);
|
|
|
- if (IS_ERR(dest_page)) {
|
|
|
- error = PTR_ERR(dest_page);
|
|
|
- unlock_page(src_page);
|
|
|
- put_page(src_page);
|
|
|
- goto out_error;
|
|
|
- }
|
|
|
- src_addr = kmap_atomic(src_page);
|
|
|
- dest_addr = kmap_atomic(dest_page);
|
|
|
-
|
|
|
- flush_dcache_page(src_page);
|
|
|
- flush_dcache_page(dest_page);
|
|
|
-
|
|
|
- if (memcmp(src_addr + src_poff, dest_addr + dest_poff, cmp_len))
|
|
|
- same = false;
|
|
|
-
|
|
|
- kunmap_atomic(dest_addr);
|
|
|
- kunmap_atomic(src_addr);
|
|
|
- unlock_page(dest_page);
|
|
|
- unlock_page(src_page);
|
|
|
- put_page(dest_page);
|
|
|
- put_page(src_page);
|
|
|
-
|
|
|
- if (!same)
|
|
|
- break;
|
|
|
-
|
|
|
- srcoff += cmp_len;
|
|
|
- destoff += cmp_len;
|
|
|
- len -= cmp_len;
|
|
|
- }
|
|
|
-
|
|
|
- *is_same = same;
|
|
|
- return 0;
|
|
|
-
|
|
|
-out_error:
|
|
|
- return error;
|
|
|
-}
|
|
|
-EXPORT_SYMBOL(vfs_dedupe_file_range_compare);
|
|
|
-
|
|
|
loff_t vfs_dedupe_file_range_one(struct file *src_file, loff_t src_pos,
|
|
|
struct file *dst_file, loff_t dst_pos,
|
|
|
loff_t len, unsigned int remap_flags)
|