Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/tests/sys/kern/copy_file_range.c
39536 views
1
/*
2
* Copyright (c) 2025 Mark Johnston <[email protected]>
3
*
4
* SPDX-License-Identifier: BSD-2-Clause
5
*/
6
7
#include <sys/mman.h>
8
#include <sys/stat.h>
9
10
#include <errno.h>
11
#include <fcntl.h>
12
#include <limits.h>
13
#include <stdint.h>
14
#include <stdio.h>
15
#include <stdlib.h>
16
#include <unistd.h>
17
18
#include <atf-c.h>
19
#include <sha256.h>
20
21
/*
22
* Create a file with random data and size between 1B and 32MB. Return a file
23
* descriptor for the file.
24
*/
25
static int
26
genfile(void)
27
{
28
char buf[256], file[NAME_MAX];
29
size_t sz;
30
int fd;
31
32
sz = (random() % (32 * 1024 * 1024ul)) + 1;
33
34
snprintf(file, sizeof(file), "testfile.XXXXXX");
35
fd = mkstemp(file);
36
ATF_REQUIRE(fd != -1);
37
38
while (sz > 0) {
39
ssize_t n;
40
int error;
41
42
error = getentropy(buf, sizeof(buf));
43
ATF_REQUIRE(error == 0);
44
n = write(fd, buf, sizeof(buf) < sz ? sizeof(buf) : sz);
45
ATF_REQUIRE(n > 0);
46
47
sz -= n;
48
}
49
50
ATF_REQUIRE(lseek(fd, 0, SEEK_SET) == 0);
51
return (fd);
52
}
53
54
/*
55
* Return true if the file data in the two file descriptors is the same,
56
* false otherwise.
57
*/
58
static bool
59
cmpfile(int fd1, int fd2)
60
{
61
struct stat st1, st2;
62
void *addr1, *addr2;
63
size_t sz;
64
int res;
65
66
ATF_REQUIRE(fstat(fd1, &st1) == 0);
67
ATF_REQUIRE(fstat(fd2, &st2) == 0);
68
if (st1.st_size != st2.st_size)
69
return (false);
70
71
sz = st1.st_size;
72
addr1 = mmap(NULL, sz, PROT_READ, MAP_PRIVATE, fd1, 0);
73
ATF_REQUIRE(addr1 != MAP_FAILED);
74
addr2 = mmap(NULL, sz, PROT_READ, MAP_PRIVATE, fd2, 0);
75
ATF_REQUIRE(addr2 != MAP_FAILED);
76
77
res = memcmp(addr1, addr2, sz);
78
79
ATF_REQUIRE(munmap(addr1, sz) == 0);
80
ATF_REQUIRE(munmap(addr2, sz) == 0);
81
82
return (res == 0);
83
}
84
85
/*
86
* Exercise a few error paths in the copy_file_range() syscall.
87
*/
88
ATF_TC_WITHOUT_HEAD(copy_file_range_invalid);
89
ATF_TC_BODY(copy_file_range_invalid, tc)
90
{
91
off_t off1, off2;
92
int fd1, fd2;
93
94
fd1 = genfile();
95
fd2 = genfile();
96
97
/* Can't copy a file to itself without explicit offsets. */
98
ATF_REQUIRE_ERRNO(EINVAL,
99
copy_file_range(fd1, NULL, fd1, NULL, SSIZE_MAX, 0) == -1);
100
101
/* When copying a file to itself, ranges cannot overlap. */
102
off1 = off2 = 0;
103
ATF_REQUIRE_ERRNO(EINVAL,
104
copy_file_range(fd1, &off1, fd1, &off2, 1, 0) == -1);
105
106
/* Negative offsets are not allowed. */
107
off1 = -1;
108
off2 = 0;
109
ATF_REQUIRE_ERRNO(EINVAL,
110
copy_file_range(fd1, &off1, fd2, &off2, 42, 0) == -1);
111
ATF_REQUIRE_ERRNO(EINVAL,
112
copy_file_range(fd2, &off2, fd1, &off1, 42, 0) == -1);
113
}
114
115
/*
116
* Make sure that copy_file_range() updates the file offsets passed to it.
117
*/
118
ATF_TC_WITHOUT_HEAD(copy_file_range_offset);
119
ATF_TC_BODY(copy_file_range_offset, tc)
120
{
121
struct stat sb;
122
off_t off1, off2;
123
ssize_t n;
124
int fd1, fd2;
125
126
off1 = off2 = 0;
127
128
fd1 = genfile();
129
fd2 = open("copy", O_RDWR | O_CREAT, 0644);
130
ATF_REQUIRE(fd2 != -1);
131
132
ATF_REQUIRE(fstat(fd1, &sb) == 0);
133
134
ATF_REQUIRE(lseek(fd1, 0, SEEK_CUR) == 0);
135
ATF_REQUIRE(lseek(fd2, 0, SEEK_CUR) == 0);
136
137
do {
138
off_t ooff1, ooff2;
139
140
ooff1 = off1;
141
ooff2 = off2;
142
n = copy_file_range(fd1, &off1, fd2, &off2, sb.st_size, 0);
143
ATF_REQUIRE(n >= 0);
144
ATF_REQUIRE_EQ(off1, ooff1 + n);
145
ATF_REQUIRE_EQ(off2, ooff2 + n);
146
} while (n != 0);
147
148
/* Offsets should have been adjusted by copy_file_range(). */
149
ATF_REQUIRE_EQ(off1, sb.st_size);
150
ATF_REQUIRE_EQ(off2, sb.st_size);
151
/* Seek offsets should have been left alone. */
152
ATF_REQUIRE(lseek(fd1, 0, SEEK_CUR) == 0);
153
ATF_REQUIRE(lseek(fd2, 0, SEEK_CUR) == 0);
154
/* Make sure the file contents are the same. */
155
ATF_REQUIRE_MSG(cmpfile(fd1, fd2), "file contents differ");
156
157
ATF_REQUIRE(close(fd1) == 0);
158
ATF_REQUIRE(close(fd2) == 0);
159
}
160
161
/*
162
* Make sure that copying to a larger file doesn't cause it to be truncated.
163
*/
164
ATF_TC_WITHOUT_HEAD(copy_file_range_truncate);
165
ATF_TC_BODY(copy_file_range_truncate, tc)
166
{
167
struct stat sb, sb1, sb2;
168
char digest1[65], digest2[65];
169
off_t off;
170
ssize_t n;
171
int fd1, fd2;
172
173
fd1 = genfile();
174
fd2 = genfile();
175
176
ATF_REQUIRE(fstat(fd1, &sb1) == 0);
177
ATF_REQUIRE(fstat(fd2, &sb2) == 0);
178
179
/* fd1 refers to the smaller file. */
180
if (sb1.st_size > sb2.st_size) {
181
int tmp;
182
183
tmp = fd1;
184
fd1 = fd2;
185
fd2 = tmp;
186
ATF_REQUIRE(fstat(fd1, &sb1) == 0);
187
ATF_REQUIRE(fstat(fd2, &sb2) == 0);
188
}
189
190
/*
191
* Compute a hash of the bytes in the larger file which lie beyond the
192
* length of the smaller file.
193
*/
194
SHA256_FdChunk(fd2, digest1, sb1.st_size, sb2.st_size - sb1.st_size);
195
ATF_REQUIRE(lseek(fd2, 0, SEEK_SET) == 0);
196
197
do {
198
n = copy_file_range(fd1, NULL, fd2, NULL, SSIZE_MAX, 0);
199
ATF_REQUIRE(n >= 0);
200
} while (n != 0);
201
202
/* Validate file offsets after the copy. */
203
off = lseek(fd1, 0, SEEK_CUR);
204
ATF_REQUIRE(off == sb1.st_size);
205
off = lseek(fd2, 0, SEEK_CUR);
206
ATF_REQUIRE(off == sb1.st_size);
207
208
/* The larger file's size should remain the same. */
209
ATF_REQUIRE(fstat(fd2, &sb) == 0);
210
ATF_REQUIRE(sb.st_size == sb2.st_size);
211
212
/* The bytes beyond the end of the copy should be unchanged. */
213
SHA256_FdChunk(fd2, digest2, sb1.st_size, sb2.st_size - sb1.st_size);
214
ATF_REQUIRE_MSG(strcmp(digest1, digest2) == 0,
215
"trailing file contents differ after copy_file_range()");
216
217
/*
218
* Verify that the copy actually replicated bytes from the smaller file.
219
*/
220
ATF_REQUIRE(ftruncate(fd2, sb1.st_size) == 0);
221
ATF_REQUIRE(cmpfile(fd1, fd2));
222
}
223
224
ATF_TP_ADD_TCS(tp)
225
{
226
ATF_TP_ADD_TC(tp, copy_file_range_invalid);
227
ATF_TP_ADD_TC(tp, copy_file_range_offset);
228
ATF_TP_ADD_TC(tp, copy_file_range_truncate);
229
230
return (atf_no_error());
231
}
232
233