@@ -147,18 +147,35 @@ zpl_remap_file_range(struct file *src_file, loff_t src_off,
147
147
* doesn't do that, so we just turn the flag off.
148
148
*/
149
149
flags &= ~REMAP_FILE_CAN_SHORTEN ;
150
+ if (flags & REMAP_FILE_DEDUP ) {
151
+ /* Zero length means to clone everything to the end of the file
152
+ */
153
+ if (len == 0 )
154
+ len = i_size_read (file_inode (src_file )) - src_off ;
155
+ // Both nodes must be range locked
156
+ zfs_locked_range_t * src_zlr = zfs_rangelock_enter (
157
+ & ITOZ (file_inode (src_file ))-> z_rangelock , src_off , len ,
158
+ RL_READER );
159
+ zfs_locked_range_t * dst_zlr = zfs_rangelock_enter (
160
+ & ITOZ (file_inode (dst_file ))-> z_rangelock , dst_off , len ,
161
+ RL_WRITER );
162
+ bool same = false;
163
+ int ret = zpl_dedupe_file_compare (src_file , src_off , dst_file ,
164
+ dst_off , len , & same );
165
+ if (ret )
166
+ return ret ;
167
+ if (!same )
168
+ return - EBADE ;
169
+ /* TODO(locked version) */
170
+ ret = __zpl_clone_file_range (src_file , src_off , dst_file ,
171
+ dst_off , len );
172
+ zfs_rangelock_exit (src_zlr );
173
+ zfs_rangelock_exit (dst_zlr );
174
+ }
150
175
151
- if (flags & REMAP_FILE_DEDUP )
152
- /* No support for dedup yet */
153
- return (- EOPNOTSUPP );
154
-
155
- /* Zero length means to clone everything to the end of the file */
156
- if (len == 0 )
157
- len = i_size_read (file_inode (src_file )) - src_off ;
158
-
159
- return (__zpl_clone_file_range (src_file , src_off ,
160
- dst_file , dst_off , len ));
161
- }
176
+ return (__zpl_clone_file_range (src_file , src_off , dst_file ,
177
+ dst_off , len ));
178
+ }
162
179
#endif /* HAVE_VFS_REMAP_FILE_RANGE */
163
180
164
181
#if defined(HAVE_VFS_CLONE_FILE_RANGE ) || \
@@ -202,9 +219,10 @@ zpl_ioctl_ficlone(struct file *dst_file, void *arg)
202
219
if (src_file == NULL )
203
220
return (- EBADF );
204
221
205
- if (dst_file -> f_op != src_file -> f_op )
222
+ if (dst_file -> f_op != src_file -> f_op ) {
223
+ fput (src_file );
206
224
return (- EXDEV );
207
-
225
+ }
208
226
size_t len = i_size_read (file_inode (src_file ));
209
227
210
228
ssize_t ret =
@@ -237,8 +255,10 @@ zpl_ioctl_ficlonerange(struct file *dst_file, void __user *arg)
237
255
if (src_file == NULL )
238
256
return (- EBADF );
239
257
240
- if (dst_file -> f_op != src_file -> f_op )
258
+ if (dst_file -> f_op != src_file -> f_op ) {
259
+ fput (src_file );
241
260
return (- EXDEV );
261
+ }
242
262
243
263
size_t len = fcr .fcr_src_length ;
244
264
if (len == 0 )
@@ -263,10 +283,132 @@ zpl_ioctl_ficlonerange(struct file *dst_file, void __user *arg)
263
283
264
284
/* Entry point for FIDEDUPERANGE, before Linux 4.5. */
265
285
long
266
- zpl_ioctl_fideduperange (struct file * filp , void * arg )
267
- {
268
- ( void ) arg ;
286
+ zpl_ioctl_fideduperange (struct file * src_file , void * arg ) {
287
+ zfs_ioc_compat_dedupe_range_t dup ;
288
+ int i ;
269
289
270
- /* No support for dedup yet */
271
- return (- ENOTTY );
290
+ if (copy_from_user (& dup , arg , sizeof (dup )))
291
+ return (- EFAULT );
292
+
293
+ u16 count = dup .fdr_dest_count ;
294
+ struct inode * src_inode = file_inode (src_file );
295
+
296
+ /* Nothing to duplicate to */
297
+ if (count == 0 )
298
+ return - EINVAL ;
299
+
300
+ /* Check the src file */
301
+ if (!(src_file -> f_mode & FMODE_READ ))
302
+ return - EINVAL ;
303
+
304
+ if (S_ISDIR (src_inode -> i_mode ))
305
+ return - EISDIR ;
306
+
307
+ if (!S_ISREG (src_inode -> i_mode ))
308
+ return - EINVAL ;
309
+
310
+ if (dup .fdr_src_offset + dup .fdr_src_length > i_size_read (src_inode ))
311
+ return - EINVAL ;
312
+
313
+ /* Check the dup structure */
314
+ if (dup .fdr_reserved1 || dup .fdr_reserved2 )
315
+ return - EINVAL ;
316
+
317
+ /* Set output values to safe results */
318
+ for (i = 0 ; i < count ; i ++ ) {
319
+ dup .fdr_info [i ].fdri_bytes_deduped = 0ULL ;
320
+ dup .fdr_info [i ].fdri_status = FILE_DEDUPE_RANGE_SAME ;
321
+ }
322
+
323
+ for (i = 0 ; i < count ; i ++ ) {
324
+ struct fd dst_fd = fdget (dup .fdr_info [i ].fdri_dest_fd );
325
+ struct file * dst_file = dst_fd .file ;
326
+
327
+ if (!dst_file ) {
328
+ dup .fdr_info [i ].fdri_status = - EBADF ;
329
+ continue ;
330
+ }
331
+ if (dup .fdr_info [i ].fdri_reserved ) {
332
+ dup .fdr_info [i ].fdri_status = - EINVAL ;
333
+ goto do_fdput ;
334
+ }
335
+ loff_t deduped =
336
+ zpl_remap_file_range (src_file , dup .fdr_src_offset , dst_file ,
337
+ dup .fdr_info [i ].fdri_dest_offset ,
338
+ dup .fdr_src_length , REMAP_FILE_DEDUP );
339
+ if (deduped == - EBADE ) {
340
+ dup .fdr_info [i ].fdri_status = FILE_DEDUPE_RANGE_DIFFERS ;
341
+ } else if (deduped < 0 ) {
342
+ dup .fdr_info [i ].fdri_status = deduped ;
343
+ } else {
344
+ dup .fdr_info [i ].fdri_bytes_deduped = dup .fdr_src_length ;
345
+ }
346
+ do_fdput :
347
+ fdput (dst_fd );
348
+ }
349
+ return 0 ;
350
+ }
351
+
352
+ int
353
+ zpl_dedupe_file_compare (struct file * src_file , loff_t src_off ,
354
+ struct file * dst_file , loff_t dst_off , uint64_t len ,
355
+ bool * is_same ) {
356
+ bool same = true;
357
+ int err = 0 ;
358
+ znode_t * src_znode = ITOZ (file_inode (src_file ));
359
+ znode_t * dst_znode = ITOZ (file_inode (dst_file ));
360
+ fstrans_cookie_t cookie ;
361
+ cred_t * cr = CRED ();
362
+ void * src_buf = kmem_zalloc (PAGE_SIZE , KM_SLEEP );
363
+ void * dst_buf = kmem_zalloc (PAGE_SIZE , KM_SLEEP );
364
+
365
+
366
+ while (len ) {
367
+ zfs_uio_t uio ;
368
+
369
+ uint64_t cmp_len = min (((uint64_t )PAGE_SIZE ), len );
370
+
371
+ if (cmp_len == 0 )
372
+ break ;
373
+ struct iovec iov ;
374
+ iov .iov_base = src_buf ;
375
+ iov .iov_len = cmp_len ;
376
+
377
+ zfs_uio_iovec_init (& uio , & iov , 1 , 0 , UIO_SYSSPACE , cmp_len , 0 );
378
+ crhold (cr );
379
+ cookie = spl_fstrans_mark ();
380
+ err = - zfs_read (src_znode , & uio , src_file -> f_flags , cr );
381
+ spl_fstrans_unmark (cookie );
382
+ crfree (cr );
383
+
384
+ if (err )
385
+ goto done ;
386
+ iov .iov_base = dst_buf ;
387
+ iov .iov_len = cmp_len ;
388
+ crhold (cr );
389
+ cookie = spl_fstrans_mark ();
390
+ err = - zfs_read (dst_znode , & uio , dst_file -> f_flags , cr );
391
+ spl_fstrans_unmark (cookie );
392
+ crfree (cr );
393
+
394
+ if (err )
395
+ goto done ;
396
+
397
+ if (memcmp (src_buf , dst_buf , cmp_len ))
398
+ same = false;
399
+
400
+ if (!same )
401
+ break ;
402
+
403
+ src_off += cmp_len ;
404
+ dst_off += cmp_len ;
405
+ len -= cmp_len ;
406
+ }
407
+
408
+ * is_same = same ;
409
+
410
+ done :
411
+ kmem_free (src_buf , PAGE_SIZE );
412
+ kmem_free (dst_buf , PAGE_SIZE );
413
+ return err ;
272
414
}
0 commit comments