]>
Commit | Line | Data |
---|---|---|
bad7a0c8 | 1 | /* Tests for copy_file_range. |
d614a753 | 2 | Copyright (C) 2017-2020 Free Software Foundation, Inc. |
bad7a0c8 FW |
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 | |
5a82c748 | 17 | <https://www.gnu.org/licenses/>. */ |
bad7a0c8 FW |
18 | |
19 | #include <array_length.h> | |
20 | #include <errno.h> | |
21 | #include <fcntl.h> | |
22 | #include <inttypes.h> | |
bad7a0c8 FW |
23 | #include <stdbool.h> |
24 | #include <stdio.h> | |
25 | #include <stdlib.h> | |
26 | #include <string.h> | |
27 | #include <support/check.h> | |
bad7a0c8 FW |
28 | #include <support/support.h> |
29 | #include <support/temp_file.h> | |
30 | #include <support/test-driver.h> | |
31 | #include <support/xunistd.h> | |
bad7a0c8 FW |
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 | ||
bad7a0c8 FW |
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 | ||
5a659ccc | 53 | /* These are a collection of copy sizes used in tests. */ |
bad7a0c8 FW |
54 | enum { maximum_size = 99999 }; |
55 | static const int typical_sizes[] = | |
5a659ccc | 56 | { 0, 1, 2, 3, 1024, 2048, 4096, 8191, 8192, 8193, maximum_size }; |
bad7a0c8 FW |
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 | ||
bad7a0c8 FW |
65 | /* Perform a copy of a file. */ |
66 | static 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 | ||
bad7a0c8 FW |
141 | /* Test that a short input file results in a shortened copy. */ |
142 | static 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 }, | |
bad7a0c8 FW |
231 | { "short_copy", short_copy, .sizes = true }, |
232 | }; | |
233 | ||
234 | static int | |
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); | |
5a659ccc | 241 | outfd = create_temp_file ("tst-copy_file_range-out-", &outfile); |
aa42b3db | 242 | { |
5a659ccc FW |
243 | ssize_t ret = copy_file_range (infd, NULL, outfd, NULL, 0, 0); |
244 | if (ret != 0) | |
bad7a0c8 | 245 | { |
5a659ccc FW |
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"); | |
bad7a0c8 | 249 | } |
bad7a0c8 FW |
250 | } |
251 | xclose (infd); | |
5a659ccc | 252 | xclose (outfd); |
bad7a0c8 FW |
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); | |
bad7a0c8 FW |
294 | |
295 | return 0; | |
296 | } | |
297 | ||
298 | #include <support/test-driver.c> |