1 /* Tests for copy_file_range.
2 Copyright (C) 2017-2022 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
4
5 The GNU C Library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
9
10 The GNU C Library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public
16 License along with the GNU C Library; if not, see
17 <https://www.gnu.org/licenses/>. */
18
19 #include <array_length.h>
20 #include <errno.h>
21 #include <fcntl.h>
22 #include <inttypes.h>
23 #include <stdbool.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <support/check.h>
28 #include <support/support.h>
29 #include <support/temp_file.h>
30 #include <support/test-driver.h>
31 #include <support/xunistd.h>
32
33 /* Boolean flags which indicate whether to use pointers with explicit
34 output flags. */
35 static int do_inoff;
36 static int do_outoff;
37
38 /* Name and descriptors of the input files. Files are truncated and
39 reopened (with O_RDWR) between tests. */
40 static char *infile;
41 static int infd;
42 static char *outfile;
43 static int outfd;
44
45 /* Input and output offsets. Set according to do_inoff and do_outoff
46 before the test. The offsets themselves are always set to
47 zero. */
48 static off64_t inoff;
49 static off64_t *pinoff;
50 static off64_t outoff;
51 static off64_t *poutoff;
52
53 /* These are a collection of copy sizes used in tests. */
54 enum { maximum_size = 99999 };
55 static const int typical_sizes[] =
56 { 0, 1, 2, 3, 1024, 2048, 4096, 8191, 8192, 8193, maximum_size };
57
58 /* The random contents of this array can be used as a pattern to check
59 for correct write operations. */
60 static unsigned char random_data[maximum_size];
61
62 /* The size chosen by the test harness. */
63 static int current_size;
64
65 /* Perform a copy of a file. */
66 static void
simple_file_copy(void)67 simple_file_copy (void)
68 {
69 xwrite (infd, random_data, current_size);
70
71 int length;
72 int in_skipped; /* Expected skipped bytes in input. */
73 if (do_inoff)
74 {
75 xlseek (infd, 1, SEEK_SET);
76 inoff = 2;
77 length = current_size - 3;
78 in_skipped = 2;
79 }
80 else
81 {
82 xlseek (infd, 3, SEEK_SET);
83 length = current_size - 5;
84 in_skipped = 3;
85 }
86 int out_skipped; /* Expected skipped bytes before the written data. */
87 if (do_outoff)
88 {
89 xlseek (outfd, 4, SEEK_SET);
90 outoff = 5;
91 out_skipped = 5;
92 }
93 else
94 {
95 xlseek (outfd, 6, SEEK_SET);
96 length = current_size - 6;
97 out_skipped = 6;
98 }
99 if (length < 0)
100 length = 0;
101
102 TEST_COMPARE (copy_file_range (infd, pinoff, outfd, poutoff,
103 length, 0), length);
104 if (do_inoff)
105 {
106 TEST_COMPARE (inoff, 2 + length);
107 TEST_COMPARE (xlseek (infd, 0, SEEK_CUR), 1);
108 }
109 else
110 TEST_COMPARE (xlseek (infd, 0, SEEK_CUR), 3 + length);
111 if (do_outoff)
112 {
113 TEST_COMPARE (outoff, 5 + length);
114 TEST_COMPARE (xlseek (outfd, 0, SEEK_CUR), 4);
115 }
116 else
117 TEST_COMPARE (xlseek (outfd, 0, SEEK_CUR), 6 + length);
118
119 struct stat64 st;
120 xfstat (outfd, &st);
121 if (length > 0)
122 TEST_COMPARE (st.st_size, out_skipped + length);
123 else
124 {
125 /* If we did not write anything, we also did not add any
126 padding. */
127 TEST_COMPARE (st.st_size, 0);
128 return;
129 }
130
131 xlseek (outfd, 0, SEEK_SET);
132 char *bytes = xmalloc (st.st_size);
133 TEST_COMPARE (read (outfd, bytes, st.st_size), st.st_size);
134 for (int i = 0; i < out_skipped; ++i)
135 TEST_COMPARE (bytes[i], 0);
136 TEST_VERIFY (memcmp (bytes + out_skipped, random_data + in_skipped,
137 length) == 0);
138 free (bytes);
139 }
140
141 /* Test that a short input file results in a shortened copy. */
142 static void
short_copy(void)143 short_copy (void)
144 {
145 if (current_size == 0)
146 /* Nothing to shorten. */
147 return;
148
149 /* Two subtests, one with offset 0 and current_size - 1 bytes, and
150 another one with current_size bytes, but offset 1. */
151 for (int shift = 0; shift < 2; ++shift)
152 {
153 if (test_verbose > 0)
154 printf ("info: shift=%d\n", shift);
155 xftruncate (infd, 0);
156 xlseek (infd, 0, SEEK_SET);
157 xwrite (infd, random_data, current_size - !shift);
158
159 if (do_inoff)
160 {
161 inoff = shift;
162 xlseek (infd, 2, SEEK_SET);
163 }
164 else
165 {
166 inoff = 3;
167 xlseek (infd, shift, SEEK_SET);
168 }
169 ftruncate (outfd, 0);
170 xlseek (outfd, 0, SEEK_SET);
171 outoff = 0;
172
173 /* First call copies current_size - 1 bytes. */
174 TEST_COMPARE (copy_file_range (infd, pinoff, outfd, poutoff,
175 current_size, 0), current_size - 1);
176 char *buffer = xmalloc (current_size);
177 TEST_COMPARE (pread64 (outfd, buffer, current_size, 0),
178 current_size - 1);
179 TEST_VERIFY (memcmp (buffer, random_data + shift, current_size - 1)
180 == 0);
181 free (buffer);
182
183 if (do_inoff)
184 {
185 TEST_COMPARE (inoff, current_size - 1 + shift);
186 TEST_COMPARE (xlseek (infd, 0, SEEK_CUR), 2);
187 }
188 else
189 TEST_COMPARE (xlseek (infd, 0, SEEK_CUR), current_size - 1 + shift);
190 if (do_outoff)
191 {
192 TEST_COMPARE (outoff, current_size - 1);
193 TEST_COMPARE (xlseek (outfd, 0, SEEK_CUR), 0);
194 }
195 else
196 TEST_COMPARE (xlseek (outfd, 0, SEEK_CUR), current_size - 1);
197
198 /* First call copies zero bytes. */
199 TEST_COMPARE (copy_file_range (infd, pinoff, outfd, poutoff,
200 current_size, 0), 0);
201 /* And the offsets are unchanged. */
202 if (do_inoff)
203 {
204 TEST_COMPARE (inoff, current_size - 1 + shift);
205 TEST_COMPARE (xlseek (infd, 0, SEEK_CUR), 2);
206 }
207 else
208 TEST_COMPARE (xlseek (infd, 0, SEEK_CUR), current_size - 1 + shift);
209 if (do_outoff)
210 {
211 TEST_COMPARE (outoff, current_size - 1);
212 TEST_COMPARE (xlseek (outfd, 0, SEEK_CUR), 0);
213 }
214 else
215 TEST_COMPARE (xlseek (outfd, 0, SEEK_CUR), current_size - 1);
216 }
217 }
218
219 /* A named test function. */
220 struct test_case
221 {
222 const char *name;
223 void (*func) (void);
224 bool sizes; /* If true, call the test with different current_size values. */
225 };
226
227 /* The available test cases. */
228 static struct test_case tests[] =
229 {
230 { "simple_file_copy", simple_file_copy, .sizes = true },
231 { "short_copy", short_copy, .sizes = true },
232 };
233
234 static int
do_test(void)235 do_test (void)
236 {
237 for (unsigned char *p = random_data; p < array_end (random_data); ++p)
238 *p = rand () >> 24;
239
240 infd = create_temp_file ("tst-copy_file_range-in-", &infile);
241 outfd = create_temp_file ("tst-copy_file_range-out-", &outfile);
242 {
243 ssize_t ret = copy_file_range (infd, NULL, outfd, NULL, 0, 0);
244 if (ret != 0)
245 {
246 if (errno == ENOSYS)
247 FAIL_UNSUPPORTED ("copy_file_range is not support on this system");
248 FAIL_EXIT1 ("copy_file_range probing call: %m");
249 }
250 }
251 xclose (infd);
252 xclose (outfd);
253
254 for (do_inoff = 0; do_inoff < 2; ++do_inoff)
255 for (do_outoff = 0; do_outoff < 2; ++do_outoff)
256 for (struct test_case *test = tests; test < array_end (tests); ++test)
257 for (const int *size = typical_sizes;
258 size < array_end (typical_sizes); ++size)
259 {
260 current_size = *size;
261 if (test_verbose > 0)
262 printf ("info: %s do_inoff=%d do_outoff=%d current_size=%d\n",
263 test->name, do_inoff, do_outoff, current_size);
264
265 inoff = 0;
266 if (do_inoff)
267 pinoff = &inoff;
268 else
269 pinoff = NULL;
270 outoff = 0;
271 if (do_outoff)
272 poutoff = &outoff;
273 else
274 poutoff = NULL;
275
276 infd = xopen (infile, O_RDWR | O_LARGEFILE, 0);
277 xftruncate (infd, 0);
278 outfd = xopen (outfile, O_RDWR | O_LARGEFILE, 0);
279 xftruncate (outfd, 0);
280
281 test->func ();
282
283 xclose (infd);
284 xclose (outfd);
285
286 if (!test->sizes)
287 /* Skip the other sizes unless they have been
288 requested. */
289 break;
290 }
291
292 free (infile);
293 free (outfile);
294
295 return 0;
296 }
297
298 #include <support/test-driver.c>
299