Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/contrib/openzfs/tests/zfs-tests/cmd/clonefile.c
48529 views
1
/*
2
* SPDX-License-Identifier: MIT
3
*
4
* Copyright (c) 2023, Rob Norris <[email protected]>
5
*
6
* Permission is hereby granted, free of charge, to any person obtaining a copy
7
* of this software and associated documentation files (the "Software"), to
8
* deal in the Software without restriction, including without limitation the
9
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10
* sell copies of the Software, and to permit persons to whom the Software is
11
* furnished to do so, subject to the following conditions:
12
*
13
* The above copyright notice and this permission notice shall be included in
14
* all copies or substantial portions of the Software.
15
*
16
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
22
* IN THE SOFTWARE.
23
*/
24
25
/*
26
* This program is to test the availability and behaviour of copy_file_range,
27
* FICLONE, FICLONERANGE and FIDEDUPERANGE in the Linux kernel. It should
28
* compile and run even if these features aren't exposed through the libc.
29
*/
30
31
#include <sys/ioctl.h>
32
#include <sys/types.h>
33
#include <sys/stat.h>
34
#include <fcntl.h>
35
#include <stdint.h>
36
#include <unistd.h>
37
#include <sys/syscall.h>
38
#include <stdlib.h>
39
#include <limits.h>
40
#include <stdio.h>
41
#include <string.h>
42
#include <errno.h>
43
44
#ifndef __NR_copy_file_range
45
#if defined(__x86_64__)
46
#define __NR_copy_file_range (326)
47
#elif defined(__i386__)
48
#define __NR_copy_file_range (377)
49
#elif defined(__s390__)
50
#define __NR_copy_file_range (375)
51
#elif defined(__arm__)
52
#define __NR_copy_file_range (391)
53
#elif defined(__aarch64__)
54
#define __NR_copy_file_range (285)
55
#elif defined(__powerpc__)
56
#define __NR_copy_file_range (379)
57
#else
58
#error "no definition of __NR_copy_file_range for this platform"
59
#endif
60
#endif /* __NR_copy_file_range */
61
62
#ifdef __FreeBSD__
63
#define loff_t off_t
64
#endif
65
66
ssize_t
67
copy_file_range(int, loff_t *, int, loff_t *, size_t, unsigned int)
68
__attribute__((weak));
69
70
static inline ssize_t
71
cf_copy_file_range(int sfd, loff_t *soff, int dfd, loff_t *doff,
72
size_t len, unsigned int flags)
73
{
74
if (copy_file_range)
75
return (copy_file_range(sfd, soff, dfd, doff, len, flags));
76
return (
77
syscall(__NR_copy_file_range, sfd, soff, dfd, doff, len, flags));
78
}
79
80
/* Define missing FICLONE */
81
#ifdef FICLONE
82
#define CF_FICLONE FICLONE
83
#else
84
#define CF_FICLONE _IOW(0x94, 9, int)
85
#endif
86
87
/* Define missing FICLONERANGE and support structs */
88
#ifdef FICLONERANGE
89
#define CF_FICLONERANGE FICLONERANGE
90
typedef struct file_clone_range cf_file_clone_range_t;
91
#else
92
typedef struct {
93
int64_t src_fd;
94
uint64_t src_offset;
95
uint64_t src_length;
96
uint64_t dest_offset;
97
} cf_file_clone_range_t;
98
#define CF_FICLONERANGE _IOW(0x94, 13, cf_file_clone_range_t)
99
#endif
100
101
/* Define missing FIDEDUPERANGE and support structs */
102
#ifdef FIDEDUPERANGE
103
#define CF_FIDEDUPERANGE FIDEDUPERANGE
104
#define CF_FILE_DEDUPE_RANGE_SAME FILE_DEDUPE_RANGE_SAME
105
#define CF_FILE_DEDUPE_RANGE_DIFFERS FILE_DEDUPE_RANGE_DIFFERS
106
typedef struct file_dedupe_range_info cf_file_dedupe_range_info_t;
107
typedef struct file_dedupe_range cf_file_dedupe_range_t;
108
#else
109
typedef struct {
110
int64_t dest_fd;
111
uint64_t dest_offset;
112
uint64_t bytes_deduped;
113
int32_t status;
114
uint32_t reserved;
115
} cf_file_dedupe_range_info_t;
116
typedef struct {
117
uint64_t src_offset;
118
uint64_t src_length;
119
uint16_t dest_count;
120
uint16_t reserved1;
121
uint32_t reserved2;
122
cf_file_dedupe_range_info_t info[0];
123
} cf_file_dedupe_range_t;
124
#define CF_FIDEDUPERANGE _IOWR(0x94, 54, cf_file_dedupe_range_t)
125
#define CF_FILE_DEDUPE_RANGE_SAME (0)
126
#define CF_FILE_DEDUPE_RANGE_DIFFERS (1)
127
#endif
128
129
typedef enum {
130
CF_MODE_NONE,
131
CF_MODE_CLONE,
132
CF_MODE_CLONERANGE,
133
CF_MODE_COPYFILERANGE,
134
CF_MODE_DEDUPERANGE,
135
} cf_mode_t;
136
137
static int
138
usage(void)
139
{
140
printf(
141
"usage:\n"
142
" FICLONE:\n"
143
" clonefile -c <src> <dst>\n"
144
" FICLONERANGE:\n"
145
" clonefile -r <src> <dst> <soff> <doff> <len>\n"
146
" copy_file_range:\n"
147
" clonefile -f <src> <dst> [<soff> <doff> <len | \"all\">]\n"
148
" FIDEDUPERANGE:\n"
149
" clonefile -d <src> <dst> <soff> <doff> <len>\n");
150
return (1);
151
}
152
153
int do_clone(int sfd, int dfd);
154
int do_clonerange(int sfd, int dfd, loff_t soff, loff_t doff, size_t len);
155
int do_copyfilerange(int sfd, int dfd, loff_t soff, loff_t doff, size_t len);
156
int do_deduperange(int sfd, int dfd, loff_t soff, loff_t doff, size_t len);
157
158
int quiet = 0;
159
160
int
161
main(int argc, char **argv)
162
{
163
cf_mode_t mode = CF_MODE_NONE;
164
165
int c;
166
while ((c = getopt(argc, argv, "crfdq")) != -1) {
167
switch (c) {
168
case 'c':
169
mode = CF_MODE_CLONE;
170
break;
171
case 'r':
172
mode = CF_MODE_CLONERANGE;
173
break;
174
case 'f':
175
mode = CF_MODE_COPYFILERANGE;
176
break;
177
case 'd':
178
mode = CF_MODE_DEDUPERANGE;
179
break;
180
case 'q':
181
quiet = 1;
182
break;
183
}
184
}
185
186
switch (mode) {
187
case CF_MODE_NONE:
188
return (usage());
189
case CF_MODE_CLONE:
190
if ((argc-optind) != 2)
191
return (usage());
192
break;
193
case CF_MODE_CLONERANGE:
194
case CF_MODE_DEDUPERANGE:
195
if ((argc-optind) != 5)
196
return (usage());
197
break;
198
case CF_MODE_COPYFILERANGE:
199
if ((argc-optind) != 2 && (argc-optind) != 5)
200
return (usage());
201
break;
202
default:
203
abort();
204
}
205
206
loff_t soff = 0, doff = 0;
207
size_t len = SSIZE_MAX;
208
unsigned long long len2;
209
if ((argc-optind) == 5) {
210
soff = strtoull(argv[optind+2], NULL, 10);
211
if (soff == ULLONG_MAX) {
212
fprintf(stderr, "invalid source offset");
213
return (1);
214
}
215
doff = strtoull(argv[optind+3], NULL, 10);
216
if (doff == ULLONG_MAX) {
217
fprintf(stderr, "invalid dest offset");
218
return (1);
219
}
220
if (mode == CF_MODE_COPYFILERANGE &&
221
strcmp(argv[optind+4], "all") == 0) {
222
len = SSIZE_MAX;
223
} else {
224
len2 = strtoull(argv[optind+4], NULL, 10);
225
if (len2 == ULLONG_MAX) {
226
fprintf(stderr, "invalid length");
227
return (1);
228
}
229
if (len2 < SSIZE_MAX)
230
len = (size_t)len2;
231
}
232
}
233
234
int sfd = open(argv[optind], O_RDONLY);
235
if (sfd < 0) {
236
fprintf(stderr, "open: %s: %s\n",
237
argv[optind], strerror(errno));
238
return (1);
239
}
240
241
int dfd = open(argv[optind+1], O_WRONLY|O_CREAT,
242
S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
243
if (dfd < 0) {
244
fprintf(stderr, "open: %s: %s\n",
245
argv[optind+1], strerror(errno));
246
close(sfd);
247
return (1);
248
}
249
250
int err;
251
switch (mode) {
252
case CF_MODE_CLONE:
253
err = do_clone(sfd, dfd);
254
break;
255
case CF_MODE_CLONERANGE:
256
err = do_clonerange(sfd, dfd, soff, doff, len);
257
break;
258
case CF_MODE_COPYFILERANGE:
259
err = do_copyfilerange(sfd, dfd, soff, doff, len);
260
break;
261
case CF_MODE_DEDUPERANGE:
262
err = do_deduperange(sfd, dfd, soff, doff, len);
263
break;
264
default:
265
abort();
266
}
267
268
if (!quiet) {
269
off_t spos = lseek(sfd, 0, SEEK_CUR);
270
off_t slen = lseek(sfd, 0, SEEK_END);
271
off_t dpos = lseek(dfd, 0, SEEK_CUR);
272
off_t dlen = lseek(dfd, 0, SEEK_END);
273
274
fprintf(stderr, "file offsets: src=%jd/%jd; dst=%jd/%jd\n",
275
spos, slen, dpos, dlen);
276
}
277
278
close(dfd);
279
close(sfd);
280
281
return (err == 0 ? 0 : 1);
282
}
283
284
int
285
do_clone(int sfd, int dfd)
286
{
287
if (!quiet)
288
fprintf(stderr, "using FICLONE\n");
289
int err = ioctl(dfd, CF_FICLONE, sfd);
290
if (err < 0) {
291
fprintf(stderr, "ioctl(FICLONE): %s\n", strerror(errno));
292
return (err);
293
}
294
return (0);
295
}
296
297
int
298
do_clonerange(int sfd, int dfd, loff_t soff, loff_t doff, size_t len)
299
{
300
if (!quiet)
301
fprintf(stderr, "using FICLONERANGE\n");
302
cf_file_clone_range_t fcr = {
303
.src_fd = sfd,
304
.src_offset = soff,
305
.src_length = len,
306
.dest_offset = doff,
307
};
308
int err = ioctl(dfd, CF_FICLONERANGE, &fcr);
309
if (err < 0) {
310
fprintf(stderr, "ioctl(FICLONERANGE): %s\n", strerror(errno));
311
return (err);
312
}
313
return (0);
314
}
315
316
int
317
do_copyfilerange(int sfd, int dfd, loff_t soff, loff_t doff, size_t len)
318
{
319
if (!quiet)
320
fprintf(stderr, "using copy_file_range\n");
321
ssize_t copied = cf_copy_file_range(sfd, &soff, dfd, &doff, len, 0);
322
if (copied < 0) {
323
fprintf(stderr, "copy_file_range: %s\n", strerror(errno));
324
return (1);
325
}
326
if (len == SSIZE_MAX) {
327
struct stat sb;
328
329
if (fstat(sfd, &sb) < 0) {
330
fprintf(stderr, "fstat(sfd): %s\n", strerror(errno));
331
return (1);
332
}
333
len = sb.st_size;
334
}
335
if (copied != len) {
336
fprintf(stderr, "copy_file_range: copied less than requested: "
337
"requested=%zu; copied=%zd\n", len, copied);
338
return (1);
339
}
340
return (0);
341
}
342
343
int
344
do_deduperange(int sfd, int dfd, loff_t soff, loff_t doff, size_t len)
345
{
346
if (!quiet)
347
fprintf(stderr, "using FIDEDUPERANGE\n");
348
349
char buf[sizeof (cf_file_dedupe_range_t)+
350
sizeof (cf_file_dedupe_range_info_t)] = {0};
351
cf_file_dedupe_range_t *fdr = (cf_file_dedupe_range_t *)&buf[0];
352
cf_file_dedupe_range_info_t *fdri =
353
(cf_file_dedupe_range_info_t *)
354
&buf[sizeof (cf_file_dedupe_range_t)];
355
356
fdr->src_offset = soff;
357
fdr->src_length = len;
358
fdr->dest_count = 1;
359
360
fdri->dest_fd = dfd;
361
fdri->dest_offset = doff;
362
363
int err = ioctl(sfd, CF_FIDEDUPERANGE, fdr);
364
if (err != 0)
365
fprintf(stderr, "ioctl(FIDEDUPERANGE): %s\n", strerror(errno));
366
367
if (fdri->status < 0) {
368
fprintf(stderr, "dedup failed: %s\n", strerror(-fdri->status));
369
err = -1;
370
} else if (fdri->status == CF_FILE_DEDUPE_RANGE_DIFFERS) {
371
fprintf(stderr, "dedup failed: range differs\n");
372
err = -1;
373
}
374
375
return (err);
376
}
377
378