xref: /freebsd-src/sys/contrib/openzfs/tests/zfs-tests/cmd/clonefile.c (revision 78ae60b447ebf420dd5cebfec30480866fd5cef4)
1315ee00fSMartin Matuska /*
2315ee00fSMartin Matuska  * SPDX-License-Identifier: MIT
3315ee00fSMartin Matuska  *
4315ee00fSMartin Matuska  * Copyright (c) 2023, Rob Norris <robn@despairlabs.com>
5315ee00fSMartin Matuska  *
6315ee00fSMartin Matuska  * Permission is hereby granted, free of charge, to any person obtaining a copy
7315ee00fSMartin Matuska  * of this software and associated documentation files (the "Software"), to
8315ee00fSMartin Matuska  * deal in the Software without restriction, including without limitation the
9315ee00fSMartin Matuska  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10315ee00fSMartin Matuska  * sell copies of the Software, and to permit persons to whom the Software is
11315ee00fSMartin Matuska  * furnished to do so, subject to the following conditions:
12315ee00fSMartin Matuska  *
13315ee00fSMartin Matuska  * The above copyright notice and this permission notice shall be included in
14315ee00fSMartin Matuska  * all copies or substantial portions of the Software.
15315ee00fSMartin Matuska  *
16315ee00fSMartin Matuska  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17315ee00fSMartin Matuska  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18315ee00fSMartin Matuska  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19315ee00fSMartin Matuska  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20315ee00fSMartin Matuska  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21315ee00fSMartin Matuska  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
22315ee00fSMartin Matuska  * IN THE SOFTWARE.
23315ee00fSMartin Matuska  */
24315ee00fSMartin Matuska 
25315ee00fSMartin Matuska /*
26315ee00fSMartin Matuska  * This program is to test the availability and behaviour of copy_file_range,
27315ee00fSMartin Matuska  * FICLONE, FICLONERANGE and FIDEDUPERANGE in the Linux kernel. It should
28315ee00fSMartin Matuska  * compile and run even if these features aren't exposed through the libc.
29315ee00fSMartin Matuska  */
30315ee00fSMartin Matuska 
31315ee00fSMartin Matuska #include <sys/ioctl.h>
32315ee00fSMartin Matuska #include <sys/types.h>
33315ee00fSMartin Matuska #include <sys/stat.h>
34315ee00fSMartin Matuska #include <fcntl.h>
35315ee00fSMartin Matuska #include <stdint.h>
36315ee00fSMartin Matuska #include <unistd.h>
37315ee00fSMartin Matuska #include <sys/syscall.h>
38315ee00fSMartin Matuska #include <stdlib.h>
39315ee00fSMartin Matuska #include <limits.h>
40315ee00fSMartin Matuska #include <stdio.h>
41315ee00fSMartin Matuska #include <string.h>
42315ee00fSMartin Matuska #include <errno.h>
43315ee00fSMartin Matuska 
44315ee00fSMartin Matuska #ifndef __NR_copy_file_range
45315ee00fSMartin Matuska #if defined(__x86_64__)
46315ee00fSMartin Matuska #define	__NR_copy_file_range (326)
47315ee00fSMartin Matuska #elif defined(__i386__)
48315ee00fSMartin Matuska #define	__NR_copy_file_range (377)
49315ee00fSMartin Matuska #elif defined(__s390__)
50315ee00fSMartin Matuska #define	__NR_copy_file_range (375)
51315ee00fSMartin Matuska #elif defined(__arm__)
52315ee00fSMartin Matuska #define	__NR_copy_file_range (391)
53315ee00fSMartin Matuska #elif defined(__aarch64__)
54315ee00fSMartin Matuska #define	__NR_copy_file_range (285)
55315ee00fSMartin Matuska #elif defined(__powerpc__)
56315ee00fSMartin Matuska #define	__NR_copy_file_range (379)
57315ee00fSMartin Matuska #else
58315ee00fSMartin Matuska #error "no definition of __NR_copy_file_range for this platform"
59315ee00fSMartin Matuska #endif
60315ee00fSMartin Matuska #endif /* __NR_copy_file_range */
61315ee00fSMartin Matuska 
62b356da80SMartin Matuska #ifdef __FreeBSD__
63b356da80SMartin Matuska #define	loff_t	off_t
64b356da80SMartin Matuska #endif
65b356da80SMartin Matuska 
66315ee00fSMartin Matuska ssize_t
67315ee00fSMartin Matuska copy_file_range(int, loff_t *, int, loff_t *, size_t, unsigned int)
68315ee00fSMartin Matuska     __attribute__((weak));
69315ee00fSMartin Matuska 
70315ee00fSMartin Matuska static inline ssize_t
cf_copy_file_range(int sfd,loff_t * soff,int dfd,loff_t * doff,size_t len,unsigned int flags)71315ee00fSMartin Matuska cf_copy_file_range(int sfd, loff_t *soff, int dfd, loff_t *doff,
72315ee00fSMartin Matuska     size_t len, unsigned int flags)
73315ee00fSMartin Matuska {
74315ee00fSMartin Matuska 	if (copy_file_range)
75315ee00fSMartin Matuska 		return (copy_file_range(sfd, soff, dfd, doff, len, flags));
76315ee00fSMartin Matuska 	return (
77315ee00fSMartin Matuska 	    syscall(__NR_copy_file_range, sfd, soff, dfd, doff, len, flags));
78315ee00fSMartin Matuska }
79315ee00fSMartin Matuska 
80315ee00fSMartin Matuska /* Define missing FICLONE */
81315ee00fSMartin Matuska #ifdef FICLONE
82315ee00fSMartin Matuska #define	CF_FICLONE	FICLONE
83315ee00fSMartin Matuska #else
84315ee00fSMartin Matuska #define	CF_FICLONE	_IOW(0x94, 9, int)
85315ee00fSMartin Matuska #endif
86315ee00fSMartin Matuska 
87315ee00fSMartin Matuska /* Define missing FICLONERANGE and support structs */
88315ee00fSMartin Matuska #ifdef FICLONERANGE
89315ee00fSMartin Matuska #define	CF_FICLONERANGE	FICLONERANGE
90315ee00fSMartin Matuska typedef struct file_clone_range cf_file_clone_range_t;
91315ee00fSMartin Matuska #else
92315ee00fSMartin Matuska typedef struct {
93315ee00fSMartin Matuska 	int64_t		src_fd;
94315ee00fSMartin Matuska 	uint64_t	src_offset;
95315ee00fSMartin Matuska 	uint64_t	src_length;
96315ee00fSMartin Matuska 	uint64_t	dest_offset;
97315ee00fSMartin Matuska } cf_file_clone_range_t;
98315ee00fSMartin Matuska #define	CF_FICLONERANGE	_IOW(0x94, 13, cf_file_clone_range_t)
99315ee00fSMartin Matuska #endif
100315ee00fSMartin Matuska 
101315ee00fSMartin Matuska /* Define missing FIDEDUPERANGE and support structs */
102315ee00fSMartin Matuska #ifdef FIDEDUPERANGE
103315ee00fSMartin Matuska #define	CF_FIDEDUPERANGE		FIDEDUPERANGE
104315ee00fSMartin Matuska #define	CF_FILE_DEDUPE_RANGE_SAME	FILE_DEDUPE_RANGE_SAME
105315ee00fSMartin Matuska #define	CF_FILE_DEDUPE_RANGE_DIFFERS	FILE_DEDUPE_RANGE_DIFFERS
106315ee00fSMartin Matuska typedef struct file_dedupe_range_info	cf_file_dedupe_range_info_t;
107315ee00fSMartin Matuska typedef struct file_dedupe_range	cf_file_dedupe_range_t;
108315ee00fSMartin Matuska #else
109315ee00fSMartin Matuska typedef struct {
110315ee00fSMartin Matuska 	int64_t dest_fd;
111315ee00fSMartin Matuska 	uint64_t dest_offset;
112315ee00fSMartin Matuska 	uint64_t bytes_deduped;
113315ee00fSMartin Matuska 	int32_t status;
114315ee00fSMartin Matuska 	uint32_t reserved;
115315ee00fSMartin Matuska } cf_file_dedupe_range_info_t;
116315ee00fSMartin Matuska typedef struct {
117315ee00fSMartin Matuska 	uint64_t src_offset;
118315ee00fSMartin Matuska 	uint64_t src_length;
119315ee00fSMartin Matuska 	uint16_t dest_count;
120315ee00fSMartin Matuska 	uint16_t reserved1;
121315ee00fSMartin Matuska 	uint32_t reserved2;
122315ee00fSMartin Matuska 	cf_file_dedupe_range_info_t info[0];
123315ee00fSMartin Matuska } cf_file_dedupe_range_t;
124315ee00fSMartin Matuska #define	CF_FIDEDUPERANGE		_IOWR(0x94, 54, cf_file_dedupe_range_t)
125315ee00fSMartin Matuska #define	CF_FILE_DEDUPE_RANGE_SAME	(0)
126315ee00fSMartin Matuska #define	CF_FILE_DEDUPE_RANGE_DIFFERS	(1)
127315ee00fSMartin Matuska #endif
128315ee00fSMartin Matuska 
129315ee00fSMartin Matuska typedef enum {
130315ee00fSMartin Matuska 	CF_MODE_NONE,
131315ee00fSMartin Matuska 	CF_MODE_CLONE,
132315ee00fSMartin Matuska 	CF_MODE_CLONERANGE,
133315ee00fSMartin Matuska 	CF_MODE_COPYFILERANGE,
134315ee00fSMartin Matuska 	CF_MODE_DEDUPERANGE,
135315ee00fSMartin Matuska } cf_mode_t;
136315ee00fSMartin Matuska 
137315ee00fSMartin Matuska static int
usage(void)138315ee00fSMartin Matuska usage(void)
139315ee00fSMartin Matuska {
140315ee00fSMartin Matuska 	printf(
141315ee00fSMartin Matuska 	    "usage:\n"
142315ee00fSMartin Matuska 	    "  FICLONE:\n"
143315ee00fSMartin Matuska 	    "    clonefile -c <src> <dst>\n"
144315ee00fSMartin Matuska 	    "  FICLONERANGE:\n"
145315ee00fSMartin Matuska 	    "    clonefile -r <src> <dst> <soff> <doff> <len>\n"
146315ee00fSMartin Matuska 	    "  copy_file_range:\n"
147b356da80SMartin Matuska 	    "    clonefile -f <src> <dst> [<soff> <doff> <len | \"all\">]\n"
148315ee00fSMartin Matuska 	    "  FIDEDUPERANGE:\n"
149315ee00fSMartin Matuska 	    "    clonefile -d <src> <dst> <soff> <doff> <len>\n");
150315ee00fSMartin Matuska 	return (1);
151315ee00fSMartin Matuska }
152315ee00fSMartin Matuska 
153315ee00fSMartin Matuska int do_clone(int sfd, int dfd);
154315ee00fSMartin Matuska int do_clonerange(int sfd, int dfd, loff_t soff, loff_t doff, size_t len);
155315ee00fSMartin Matuska int do_copyfilerange(int sfd, int dfd, loff_t soff, loff_t doff, size_t len);
156315ee00fSMartin Matuska int do_deduperange(int sfd, int dfd, loff_t soff, loff_t doff, size_t len);
157315ee00fSMartin Matuska 
158315ee00fSMartin Matuska int quiet = 0;
159315ee00fSMartin Matuska 
160315ee00fSMartin Matuska int
main(int argc,char ** argv)161315ee00fSMartin Matuska main(int argc, char **argv)
162315ee00fSMartin Matuska {
163315ee00fSMartin Matuska 	cf_mode_t mode = CF_MODE_NONE;
164315ee00fSMartin Matuska 
165*78ae60b4SMartin Matuska 	int c;
166315ee00fSMartin Matuska 	while ((c = getopt(argc, argv, "crfdq")) != -1) {
167315ee00fSMartin Matuska 		switch (c) {
168315ee00fSMartin Matuska 			case 'c':
169315ee00fSMartin Matuska 				mode = CF_MODE_CLONE;
170315ee00fSMartin Matuska 				break;
171315ee00fSMartin Matuska 			case 'r':
172315ee00fSMartin Matuska 				mode = CF_MODE_CLONERANGE;
173315ee00fSMartin Matuska 				break;
174315ee00fSMartin Matuska 			case 'f':
175315ee00fSMartin Matuska 				mode = CF_MODE_COPYFILERANGE;
176315ee00fSMartin Matuska 				break;
177315ee00fSMartin Matuska 			case 'd':
178315ee00fSMartin Matuska 				mode = CF_MODE_DEDUPERANGE;
179315ee00fSMartin Matuska 				break;
180315ee00fSMartin Matuska 			case 'q':
181315ee00fSMartin Matuska 				quiet = 1;
182315ee00fSMartin Matuska 				break;
183315ee00fSMartin Matuska 		}
184315ee00fSMartin Matuska 	}
185315ee00fSMartin Matuska 
186b356da80SMartin Matuska 	switch (mode) {
187b356da80SMartin Matuska 		case CF_MODE_NONE:
188315ee00fSMartin Matuska 			return (usage());
189b356da80SMartin Matuska 		case CF_MODE_CLONE:
190b356da80SMartin Matuska 			if ((argc-optind) != 2)
191b356da80SMartin Matuska 				return (usage());
192b356da80SMartin Matuska 			break;
193b356da80SMartin Matuska 		case CF_MODE_CLONERANGE:
194b356da80SMartin Matuska 		case CF_MODE_DEDUPERANGE:
195b356da80SMartin Matuska 			if ((argc-optind) != 5)
196b356da80SMartin Matuska 				return (usage());
197b356da80SMartin Matuska 			break;
198b356da80SMartin Matuska 		case CF_MODE_COPYFILERANGE:
199b356da80SMartin Matuska 			if ((argc-optind) != 2 && (argc-optind) != 5)
200b356da80SMartin Matuska 				return (usage());
201b356da80SMartin Matuska 			break;
202b356da80SMartin Matuska 		default:
203b356da80SMartin Matuska 			abort();
204b356da80SMartin Matuska 	}
205315ee00fSMartin Matuska 
206315ee00fSMartin Matuska 	loff_t soff = 0, doff = 0;
207b356da80SMartin Matuska 	size_t len = SSIZE_MAX;
208b356da80SMartin Matuska 	if ((argc-optind) == 5) {
209315ee00fSMartin Matuska 		soff = strtoull(argv[optind+2], NULL, 10);
210315ee00fSMartin Matuska 		if (soff == ULLONG_MAX) {
211315ee00fSMartin Matuska 			fprintf(stderr, "invalid source offset");
212315ee00fSMartin Matuska 			return (1);
213315ee00fSMartin Matuska 		}
214315ee00fSMartin Matuska 		doff = strtoull(argv[optind+3], NULL, 10);
215315ee00fSMartin Matuska 		if (doff == ULLONG_MAX) {
216315ee00fSMartin Matuska 			fprintf(stderr, "invalid dest offset");
217315ee00fSMartin Matuska 			return (1);
218315ee00fSMartin Matuska 		}
219b356da80SMartin Matuska 		if (mode == CF_MODE_COPYFILERANGE &&
220b356da80SMartin Matuska 		    strcmp(argv[optind+4], "all") == 0) {
221b356da80SMartin Matuska 			len = SSIZE_MAX;
222b356da80SMartin Matuska 		} else {
223315ee00fSMartin Matuska 			len = strtoull(argv[optind+4], NULL, 10);
224315ee00fSMartin Matuska 			if (len == ULLONG_MAX) {
225315ee00fSMartin Matuska 				fprintf(stderr, "invalid length");
226315ee00fSMartin Matuska 				return (1);
227315ee00fSMartin Matuska 			}
228315ee00fSMartin Matuska 		}
229b356da80SMartin Matuska 	}
230315ee00fSMartin Matuska 
231315ee00fSMartin Matuska 	int sfd = open(argv[optind], O_RDONLY);
232315ee00fSMartin Matuska 	if (sfd < 0) {
233315ee00fSMartin Matuska 		fprintf(stderr, "open: %s: %s\n",
234315ee00fSMartin Matuska 		    argv[optind], strerror(errno));
235315ee00fSMartin Matuska 		return (1);
236315ee00fSMartin Matuska 	}
237315ee00fSMartin Matuska 
238315ee00fSMartin Matuska 	int dfd = open(argv[optind+1], O_WRONLY|O_CREAT,
239315ee00fSMartin Matuska 	    S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
240315ee00fSMartin Matuska 	if (dfd < 0) {
241315ee00fSMartin Matuska 		fprintf(stderr, "open: %s: %s\n",
242315ee00fSMartin Matuska 		    argv[optind+1], strerror(errno));
243315ee00fSMartin Matuska 		close(sfd);
244315ee00fSMartin Matuska 		return (1);
245315ee00fSMartin Matuska 	}
246315ee00fSMartin Matuska 
247315ee00fSMartin Matuska 	int err;
248315ee00fSMartin Matuska 	switch (mode) {
249315ee00fSMartin Matuska 		case CF_MODE_CLONE:
250315ee00fSMartin Matuska 			err = do_clone(sfd, dfd);
251315ee00fSMartin Matuska 			break;
252315ee00fSMartin Matuska 		case CF_MODE_CLONERANGE:
253315ee00fSMartin Matuska 			err = do_clonerange(sfd, dfd, soff, doff, len);
254315ee00fSMartin Matuska 			break;
255315ee00fSMartin Matuska 		case CF_MODE_COPYFILERANGE:
256315ee00fSMartin Matuska 			err = do_copyfilerange(sfd, dfd, soff, doff, len);
257315ee00fSMartin Matuska 			break;
258315ee00fSMartin Matuska 		case CF_MODE_DEDUPERANGE:
259315ee00fSMartin Matuska 			err = do_deduperange(sfd, dfd, soff, doff, len);
260315ee00fSMartin Matuska 			break;
261315ee00fSMartin Matuska 		default:
262315ee00fSMartin Matuska 			abort();
263315ee00fSMartin Matuska 	}
264315ee00fSMartin Matuska 
265b356da80SMartin Matuska 	if (!quiet) {
266315ee00fSMartin Matuska 		off_t spos = lseek(sfd, 0, SEEK_CUR);
267315ee00fSMartin Matuska 		off_t slen = lseek(sfd, 0, SEEK_END);
268315ee00fSMartin Matuska 		off_t dpos = lseek(dfd, 0, SEEK_CUR);
269315ee00fSMartin Matuska 		off_t dlen = lseek(dfd, 0, SEEK_END);
270315ee00fSMartin Matuska 
271b356da80SMartin Matuska 		fprintf(stderr, "file offsets: src=%lu/%lu; dst=%lu/%lu\n",
272b356da80SMartin Matuska 		    spos, slen, dpos, dlen);
273b356da80SMartin Matuska 	}
274315ee00fSMartin Matuska 
275315ee00fSMartin Matuska 	close(dfd);
276315ee00fSMartin Matuska 	close(sfd);
277315ee00fSMartin Matuska 
278315ee00fSMartin Matuska 	return (err == 0 ? 0 : 1);
279315ee00fSMartin Matuska }
280315ee00fSMartin Matuska 
281315ee00fSMartin Matuska int
do_clone(int sfd,int dfd)282315ee00fSMartin Matuska do_clone(int sfd, int dfd)
283315ee00fSMartin Matuska {
284b356da80SMartin Matuska 	if (!quiet)
285315ee00fSMartin Matuska 		fprintf(stderr, "using FICLONE\n");
286315ee00fSMartin Matuska 	int err = ioctl(dfd, CF_FICLONE, sfd);
287315ee00fSMartin Matuska 	if (err < 0) {
288315ee00fSMartin Matuska 		fprintf(stderr, "ioctl(FICLONE): %s\n", strerror(errno));
289315ee00fSMartin Matuska 		return (err);
290315ee00fSMartin Matuska 	}
291315ee00fSMartin Matuska 	return (0);
292315ee00fSMartin Matuska }
293315ee00fSMartin Matuska 
294315ee00fSMartin Matuska int
do_clonerange(int sfd,int dfd,loff_t soff,loff_t doff,size_t len)295315ee00fSMartin Matuska do_clonerange(int sfd, int dfd, loff_t soff, loff_t doff, size_t len)
296315ee00fSMartin Matuska {
297b356da80SMartin Matuska 	if (!quiet)
298315ee00fSMartin Matuska 		fprintf(stderr, "using FICLONERANGE\n");
299315ee00fSMartin Matuska 	cf_file_clone_range_t fcr = {
300315ee00fSMartin Matuska 		.src_fd = sfd,
301315ee00fSMartin Matuska 		.src_offset = soff,
302315ee00fSMartin Matuska 		.src_length = len,
303315ee00fSMartin Matuska 		.dest_offset = doff,
304315ee00fSMartin Matuska 	};
305315ee00fSMartin Matuska 	int err = ioctl(dfd, CF_FICLONERANGE, &fcr);
306315ee00fSMartin Matuska 	if (err < 0) {
307315ee00fSMartin Matuska 		fprintf(stderr, "ioctl(FICLONERANGE): %s\n", strerror(errno));
308315ee00fSMartin Matuska 		return (err);
309315ee00fSMartin Matuska 	}
310315ee00fSMartin Matuska 	return (0);
311315ee00fSMartin Matuska }
312315ee00fSMartin Matuska 
313315ee00fSMartin Matuska int
do_copyfilerange(int sfd,int dfd,loff_t soff,loff_t doff,size_t len)314315ee00fSMartin Matuska do_copyfilerange(int sfd, int dfd, loff_t soff, loff_t doff, size_t len)
315315ee00fSMartin Matuska {
316b356da80SMartin Matuska 	if (!quiet)
317315ee00fSMartin Matuska 		fprintf(stderr, "using copy_file_range\n");
318315ee00fSMartin Matuska 	ssize_t copied = cf_copy_file_range(sfd, &soff, dfd, &doff, len, 0);
319315ee00fSMartin Matuska 	if (copied < 0) {
320315ee00fSMartin Matuska 		fprintf(stderr, "copy_file_range: %s\n", strerror(errno));
321315ee00fSMartin Matuska 		return (1);
322315ee00fSMartin Matuska 	}
323b356da80SMartin Matuska 	if (len == SSIZE_MAX) {
324b356da80SMartin Matuska 		struct stat sb;
325b356da80SMartin Matuska 
326b356da80SMartin Matuska 		if (fstat(sfd, &sb) < 0) {
327b356da80SMartin Matuska 			fprintf(stderr, "fstat(sfd): %s\n", strerror(errno));
328b356da80SMartin Matuska 			return (1);
329b356da80SMartin Matuska 		}
330b356da80SMartin Matuska 		len = sb.st_size;
331b356da80SMartin Matuska 	}
332315ee00fSMartin Matuska 	if (copied != len) {
333315ee00fSMartin Matuska 		fprintf(stderr, "copy_file_range: copied less than requested: "
334315ee00fSMartin Matuska 		    "requested=%lu; copied=%lu\n", len, copied);
335315ee00fSMartin Matuska 		return (1);
336315ee00fSMartin Matuska 	}
337315ee00fSMartin Matuska 	return (0);
338315ee00fSMartin Matuska }
339315ee00fSMartin Matuska 
340315ee00fSMartin Matuska int
do_deduperange(int sfd,int dfd,loff_t soff,loff_t doff,size_t len)341315ee00fSMartin Matuska do_deduperange(int sfd, int dfd, loff_t soff, loff_t doff, size_t len)
342315ee00fSMartin Matuska {
343b356da80SMartin Matuska 	if (!quiet)
344315ee00fSMartin Matuska 		fprintf(stderr, "using FIDEDUPERANGE\n");
345315ee00fSMartin Matuska 
346315ee00fSMartin Matuska 	char buf[sizeof (cf_file_dedupe_range_t)+
347315ee00fSMartin Matuska 	    sizeof (cf_file_dedupe_range_info_t)] = {0};
348315ee00fSMartin Matuska 	cf_file_dedupe_range_t *fdr = (cf_file_dedupe_range_t *)&buf[0];
349315ee00fSMartin Matuska 	cf_file_dedupe_range_info_t *fdri =
350315ee00fSMartin Matuska 	    (cf_file_dedupe_range_info_t *)
351315ee00fSMartin Matuska 	    &buf[sizeof (cf_file_dedupe_range_t)];
352315ee00fSMartin Matuska 
353315ee00fSMartin Matuska 	fdr->src_offset = soff;
354315ee00fSMartin Matuska 	fdr->src_length = len;
355315ee00fSMartin Matuska 	fdr->dest_count = 1;
356315ee00fSMartin Matuska 
357315ee00fSMartin Matuska 	fdri->dest_fd = dfd;
358315ee00fSMartin Matuska 	fdri->dest_offset = doff;
359315ee00fSMartin Matuska 
360315ee00fSMartin Matuska 	int err = ioctl(sfd, CF_FIDEDUPERANGE, fdr);
361315ee00fSMartin Matuska 	if (err != 0)
362315ee00fSMartin Matuska 		fprintf(stderr, "ioctl(FIDEDUPERANGE): %s\n", strerror(errno));
363315ee00fSMartin Matuska 
364315ee00fSMartin Matuska 	if (fdri->status < 0) {
365315ee00fSMartin Matuska 		fprintf(stderr, "dedup failed: %s\n", strerror(-fdri->status));
366315ee00fSMartin Matuska 		err = -1;
367315ee00fSMartin Matuska 	} else if (fdri->status == CF_FILE_DEDUPE_RANGE_DIFFERS) {
368315ee00fSMartin Matuska 		fprintf(stderr, "dedup failed: range differs\n");
369315ee00fSMartin Matuska 		err = -1;
370315ee00fSMartin Matuska 	}
371315ee00fSMartin Matuska 
372315ee00fSMartin Matuska 	return (err);
373315ee00fSMartin Matuska }
374