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
109066 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
#if defined(_GNU_SOURCE) && defined(__linux__)
63
_Static_assert(sizeof (loff_t) == sizeof (off_t),
64
"loff_t and off_t must be the same size");
65
#endif
66
67
ssize_t
68
copy_file_range(int, off_t *, int, off_t *, size_t, unsigned int)
69
__attribute__((weak));
70
71
static inline ssize_t
72
cf_copy_file_range(int sfd, off_t *soff, int dfd, off_t *doff,
73
size_t len, unsigned int flags)
74
{
75
if (copy_file_range)
76
return (copy_file_range(sfd, soff, dfd, doff, len, flags));
77
return (
78
syscall(__NR_copy_file_range, sfd, soff, dfd, doff, len, flags));
79
}
80
81
/* Define missing FICLONE */
82
#ifdef FICLONE
83
#define CF_FICLONE FICLONE
84
#else
85
#define CF_FICLONE _IOW(0x94, 9, int)
86
#endif
87
88
/* Define missing FICLONERANGE and support structs */
89
#ifdef FICLONERANGE
90
#define CF_FICLONERANGE FICLONERANGE
91
typedef struct file_clone_range cf_file_clone_range_t;
92
#else
93
typedef struct {
94
int64_t src_fd;
95
uint64_t src_offset;
96
uint64_t src_length;
97
uint64_t dest_offset;
98
} cf_file_clone_range_t;
99
#define CF_FICLONERANGE _IOW(0x94, 13, cf_file_clone_range_t)
100
#endif
101
102
/* Define missing FIDEDUPERANGE and support structs */
103
#ifdef FIDEDUPERANGE
104
#define CF_FIDEDUPERANGE FIDEDUPERANGE
105
#define CF_FILE_DEDUPE_RANGE_SAME FILE_DEDUPE_RANGE_SAME
106
#define CF_FILE_DEDUPE_RANGE_DIFFERS FILE_DEDUPE_RANGE_DIFFERS
107
typedef struct file_dedupe_range_info cf_file_dedupe_range_info_t;
108
typedef struct file_dedupe_range cf_file_dedupe_range_t;
109
#else
110
typedef struct {
111
int64_t dest_fd;
112
uint64_t dest_offset;
113
uint64_t bytes_deduped;
114
int32_t status;
115
uint32_t reserved;
116
} cf_file_dedupe_range_info_t;
117
typedef struct {
118
uint64_t src_offset;
119
uint64_t src_length;
120
uint16_t dest_count;
121
uint16_t reserved1;
122
uint32_t reserved2;
123
cf_file_dedupe_range_info_t info[0];
124
} cf_file_dedupe_range_t;
125
#define CF_FIDEDUPERANGE _IOWR(0x94, 54, cf_file_dedupe_range_t)
126
#define CF_FILE_DEDUPE_RANGE_SAME (0)
127
#define CF_FILE_DEDUPE_RANGE_DIFFERS (1)
128
#endif
129
130
typedef enum {
131
CF_MODE_NONE,
132
CF_MODE_CLONE,
133
CF_MODE_CLONERANGE,
134
CF_MODE_COPYFILERANGE,
135
CF_MODE_DEDUPERANGE,
136
} cf_mode_t;
137
138
static int
139
usage(void)
140
{
141
printf(
142
"usage:\n"
143
" FICLONE:\n"
144
" clonefile -c <src> <dst>\n"
145
" FICLONERANGE:\n"
146
" clonefile -r <src> <dst> <soff> <doff> <len>\n"
147
" copy_file_range:\n"
148
" clonefile -f <src> <dst> [<soff> <doff> <len | \"all\">]\n"
149
" FIDEDUPERANGE:\n"
150
" clonefile -d <src> <dst> <soff> <doff> <len>\n");
151
return (1);
152
}
153
154
int do_clone(int sfd, int dfd);
155
int do_clonerange(int sfd, int dfd, off_t soff, off_t doff, size_t len);
156
int do_copyfilerange(int sfd, int dfd, off_t soff, off_t doff, size_t len);
157
int do_deduperange(int sfd, int dfd, off_t soff, off_t doff, size_t len);
158
159
int quiet = 0;
160
161
int
162
main(int argc, char **argv)
163
{
164
cf_mode_t mode = CF_MODE_NONE;
165
166
int c;
167
while ((c = getopt(argc, argv, "crfdq")) != -1) {
168
switch (c) {
169
case 'c':
170
mode = CF_MODE_CLONE;
171
break;
172
case 'r':
173
mode = CF_MODE_CLONERANGE;
174
break;
175
case 'f':
176
mode = CF_MODE_COPYFILERANGE;
177
break;
178
case 'd':
179
mode = CF_MODE_DEDUPERANGE;
180
break;
181
case 'q':
182
quiet = 1;
183
break;
184
}
185
}
186
187
switch (mode) {
188
case CF_MODE_NONE:
189
return (usage());
190
case CF_MODE_CLONE:
191
if ((argc-optind) != 2)
192
return (usage());
193
break;
194
case CF_MODE_CLONERANGE:
195
case CF_MODE_DEDUPERANGE:
196
if ((argc-optind) != 5)
197
return (usage());
198
break;
199
case CF_MODE_COPYFILERANGE:
200
if ((argc-optind) != 2 && (argc-optind) != 5)
201
return (usage());
202
break;
203
default:
204
abort();
205
}
206
207
off_t soff = 0, doff = 0;
208
size_t len = SSIZE_MAX;
209
unsigned long long len2;
210
if ((argc-optind) == 5) {
211
soff = strtoull(argv[optind+2], NULL, 10);
212
if (soff == ULLONG_MAX) {
213
fprintf(stderr, "invalid source offset");
214
return (1);
215
}
216
doff = strtoull(argv[optind+3], NULL, 10);
217
if (doff == ULLONG_MAX) {
218
fprintf(stderr, "invalid dest offset");
219
return (1);
220
}
221
if (mode == CF_MODE_COPYFILERANGE &&
222
strcmp(argv[optind+4], "all") == 0) {
223
len = SSIZE_MAX;
224
} else {
225
len2 = strtoull(argv[optind+4], NULL, 10);
226
if (len2 == ULLONG_MAX) {
227
fprintf(stderr, "invalid length");
228
return (1);
229
}
230
if (len2 < SSIZE_MAX)
231
len = (size_t)len2;
232
}
233
}
234
235
int sfd = open(argv[optind], O_RDONLY);
236
if (sfd < 0) {
237
fprintf(stderr, "open: %s: %s\n",
238
argv[optind], strerror(errno));
239
return (1);
240
}
241
242
int dfd = open(argv[optind+1], O_WRONLY|O_CREAT,
243
S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
244
if (dfd < 0) {
245
fprintf(stderr, "open: %s: %s\n",
246
argv[optind+1], strerror(errno));
247
close(sfd);
248
return (1);
249
}
250
251
int err;
252
switch (mode) {
253
case CF_MODE_CLONE:
254
err = do_clone(sfd, dfd);
255
break;
256
case CF_MODE_CLONERANGE:
257
err = do_clonerange(sfd, dfd, soff, doff, len);
258
break;
259
case CF_MODE_COPYFILERANGE:
260
err = do_copyfilerange(sfd, dfd, soff, doff, len);
261
break;
262
case CF_MODE_DEDUPERANGE:
263
err = do_deduperange(sfd, dfd, soff, doff, len);
264
break;
265
default:
266
abort();
267
}
268
269
if (!quiet) {
270
off_t spos = lseek(sfd, 0, SEEK_CUR);
271
off_t slen = lseek(sfd, 0, SEEK_END);
272
off_t dpos = lseek(dfd, 0, SEEK_CUR);
273
off_t dlen = lseek(dfd, 0, SEEK_END);
274
275
fprintf(stderr, "file offsets: src=%jd/%jd; dst=%jd/%jd\n",
276
spos, slen, dpos, dlen);
277
}
278
279
close(dfd);
280
close(sfd);
281
282
return (err == 0 ? 0 : 1);
283
}
284
285
int
286
do_clone(int sfd, int dfd)
287
{
288
if (!quiet)
289
fprintf(stderr, "using FICLONE\n");
290
int err = ioctl(dfd, CF_FICLONE, sfd);
291
if (err < 0) {
292
fprintf(stderr, "ioctl(FICLONE): %s\n", strerror(errno));
293
return (err);
294
}
295
return (0);
296
}
297
298
int
299
do_clonerange(int sfd, int dfd, off_t soff, off_t doff, size_t len)
300
{
301
if (!quiet)
302
fprintf(stderr, "using FICLONERANGE\n");
303
cf_file_clone_range_t fcr = {
304
.src_fd = sfd,
305
.src_offset = soff,
306
.src_length = len,
307
.dest_offset = doff,
308
};
309
int err = ioctl(dfd, CF_FICLONERANGE, &fcr);
310
if (err < 0) {
311
fprintf(stderr, "ioctl(FICLONERANGE): %s\n", strerror(errno));
312
return (err);
313
}
314
return (0);
315
}
316
317
int
318
do_copyfilerange(int sfd, int dfd, off_t soff, off_t doff, size_t len)
319
{
320
if (!quiet)
321
fprintf(stderr, "using copy_file_range\n");
322
ssize_t copied = cf_copy_file_range(sfd, &soff, dfd, &doff, len, 0);
323
if (copied < 0) {
324
fprintf(stderr, "copy_file_range: %s\n", strerror(errno));
325
return (1);
326
}
327
if (len == SSIZE_MAX) {
328
struct stat sb;
329
330
if (fstat(sfd, &sb) < 0) {
331
fprintf(stderr, "fstat(sfd): %s\n", strerror(errno));
332
return (1);
333
}
334
len = sb.st_size;
335
}
336
if (copied != len) {
337
fprintf(stderr, "copy_file_range: copied less than requested: "
338
"requested=%zu; copied=%zd\n", len, copied);
339
return (1);
340
}
341
return (0);
342
}
343
344
int
345
do_deduperange(int sfd, int dfd, off_t soff, off_t doff, size_t len)
346
{
347
if (!quiet)
348
fprintf(stderr, "using FIDEDUPERANGE\n");
349
350
char buf[sizeof (cf_file_dedupe_range_t)+
351
sizeof (cf_file_dedupe_range_info_t)] = {0};
352
cf_file_dedupe_range_t *fdr = (cf_file_dedupe_range_t *)&buf[0];
353
cf_file_dedupe_range_info_t *fdri =
354
(cf_file_dedupe_range_info_t *)
355
&buf[sizeof (cf_file_dedupe_range_t)];
356
357
fdr->src_offset = soff;
358
fdr->src_length = len;
359
fdr->dest_count = 1;
360
361
fdri->dest_fd = dfd;
362
fdri->dest_offset = doff;
363
364
int err = ioctl(sfd, CF_FIDEDUPERANGE, fdr);
365
if (err != 0)
366
fprintf(stderr, "ioctl(FIDEDUPERANGE): %s\n", strerror(errno));
367
368
if (fdri->status < 0) {
369
fprintf(stderr, "dedup failed: %s\n", strerror(-fdri->status));
370
err = -1;
371
} else if (fdri->status == CF_FILE_DEDUPE_RANGE_DIFFERS) {
372
fprintf(stderr, "dedup failed: range differs\n");
373
err = -1;
374
}
375
376
return (err);
377
}
378
379