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. */
35static int do_inoff;
36static int do_outoff;
37
38/* Name and descriptors of the input files. Files are truncated and
39 reopened (with O_RDWR) between tests. */
40static char *infile;
41static int infd;
42static char *outfile;
43static 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. */
48static off64_t inoff;
49static off64_t *pinoff;
50static off64_t outoff;
51static off64_t *poutoff;
52
53/* These are a collection of copy sizes used in tests. */
54enum { maximum_size = 99999 };
55static 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. */
60static unsigned char random_data[maximum_size];
61
62/* The size chosen by the test harness. */
63static int current_size;
64
65/* Perform a copy of a file. */
66static void
67simple_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 (fd: infd, offset: 1, SEEK_SET);
76 inoff = 2;
77 length = current_size - 3;
78 in_skipped = 2;
79 }
80 else
81 {
82 xlseek (fd: infd, offset: 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 (fd: outfd, offset: 4, SEEK_SET);
90 outoff = 5;
91 out_skipped = 5;
92 }
93 else
94 {
95 xlseek (fd: outfd, offset: 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 (fd: 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 (fd: outfd, offset: 0, SEEK_SET);
132 char *bytes = xmalloc (n: 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 (ptr: bytes);
139}
140
141/* Test that a short input file results in a shortened copy. */
142static void
143short_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 (format: "info: shift=%d\n", shift);
155 xftruncate (fd: infd, length: 0);
156 xlseek (fd: infd, offset: 0, SEEK_SET);
157 xwrite (infd, random_data, current_size - !shift);
158
159 if (do_inoff)
160 {
161 inoff = shift;
162 xlseek (fd: infd, offset: 2, SEEK_SET);
163 }
164 else
165 {
166 inoff = 3;
167 xlseek (fd: infd, offset: shift, SEEK_SET);
168 }
169 ftruncate (fd: outfd, length: 0);
170 xlseek (fd: outfd, offset: 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 (n: 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 (ptr: 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. */
220struct 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. */
228static struct test_case tests[] =
229 {
230 { "simple_file_copy", simple_file_copy, .sizes = true },
231 { "short_copy", short_copy, .sizes = true },
232 };
233
234static int
235do_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 (base: "tst-copy_file_range-in-", filename: &infile);
241 outfd = create_temp_file (base: "tst-copy_file_range-out-", filename: &outfile);
242 {
243 ssize_t ret = copy_file_range (infd: infd, NULL, outfd: outfd, NULL, length: 0, flags: 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 (format: "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 (path: infile, O_RDWR | O_LARGEFILE, 0);
277 xftruncate (fd: infd, length: 0);
278 outfd = xopen (path: outfile, O_RDWR | O_LARGEFILE, 0);
279 xftruncate (fd: outfd, length: 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 (ptr: infile);
293 free (ptr: outfile);
294
295 return 0;
296}
297
298#include <support/test-driver.c>
299

source code of glibc/io/tst-copy_file_range.c