NEWS: Fix spelling mistake: "%ob" -> "%Ob".
[glibc.git] / io / tst-copy_file_range.c
blobf1cab1252d39ba1dadffe18dc7b59611e1dec930
1 /* Tests for copy_file_range.
2 Copyright (C) 2017-2018 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
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.
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.
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 <http://www.gnu.org/licenses/>. */
19 #include <array_length.h>
20 #include <errno.h>
21 #include <fcntl.h>
22 #include <inttypes.h>
23 #include <libgen.h>
24 #include <poll.h>
25 #include <sched.h>
26 #include <stdbool.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <support/check.h>
31 #include <support/namespace.h>
32 #include <support/support.h>
33 #include <support/temp_file.h>
34 #include <support/test-driver.h>
35 #include <support/xunistd.h>
36 #include <sys/mount.h>
38 /* Boolean flags which indicate whether to use pointers with explicit
39 output flags. */
40 static int do_inoff;
41 static int do_outoff;
43 /* Name and descriptors of the input files. Files are truncated and
44 reopened (with O_RDWR) between tests. */
45 static char *infile;
46 static int infd;
47 static char *outfile;
48 static int outfd;
50 /* Like the above, but on a different file system. xdevfile can be
51 NULL if no suitable file system has been found. */
52 static char *xdevfile;
54 /* Input and output offsets. Set according to do_inoff and do_outoff
55 before the test. The offsets themselves are always set to
56 zero. */
57 static off64_t inoff;
58 static off64_t *pinoff;
59 static off64_t outoff;
60 static off64_t *poutoff;
62 /* These are a collection of copy sizes used in tests. The selection
63 takes into account that the fallback implementation uses an
64 internal buffer of 8192 bytes. */
65 enum { maximum_size = 99999 };
66 static const int typical_sizes[] =
67 { 0, 1, 2, 3, 1024, 2048, 4096, 8191, 8192, 8193, 16383, 16384, 16385,
68 maximum_size };
70 /* The random contents of this array can be used as a pattern to check
71 for correct write operations. */
72 static unsigned char random_data[maximum_size];
74 /* The size chosen by the test harness. */
75 static int current_size;
77 /* Maximum writable file offset. Updated by find_maximum_offset
78 below. */
79 static off64_t maximum_offset;
81 /* Error code when crossing the offset. */
82 static int maximum_offset_errno;
84 /* If true: Writes which cross the limit will fail. If false: Writes
85 which cross the limit will result in a partial write. */
86 static bool maximum_offset_hard_limit;
88 /* Fills maximum_offset etc. above. Truncates outfd as a side
89 effect. */
90 static void
91 find_maximum_offset (void)
93 xftruncate (outfd, 0);
94 if (maximum_offset != 0)
95 return;
97 uint64_t upper = -1;
98 upper >>= 1; /* Maximum of off64_t. */
99 TEST_VERIFY ((off64_t) upper > 0);
100 TEST_VERIFY ((off64_t) (upper + 1) < 0);
101 if (lseek64 (outfd, upper, SEEK_SET) >= 0)
103 if (write (outfd, "", 1) == 1)
104 FAIL_EXIT1 ("created a file larger than the off64_t range");
107 uint64_t lower = 1024 * 1024; /* A reasonable minimum file size. */
108 /* Loop invariant: writing at lower succeeds, writing at upper fails. */
109 while (lower + 1 < upper)
111 uint64_t middle = (lower + upper) / 2;
112 if (test_verbose > 0)
113 printf ("info: %s: remaining test range %" PRIu64 " .. %" PRIu64
114 ", probe at %" PRIu64 "\n", __func__, lower, upper, middle);
115 xftruncate (outfd, 0);
116 if (lseek64 (outfd, middle, SEEK_SET) >= 0
117 && write (outfd, "", 1) == 1)
118 lower = middle;
119 else
120 upper = middle;
122 TEST_VERIFY (lower + 1 == upper);
123 maximum_offset = lower;
124 printf ("info: maximum writable file offset: %" PRIu64 " (%" PRIx64 ")\n",
125 lower, lower);
127 /* Check that writing at the valid offset actually works. */
128 xftruncate (outfd, 0);
129 xlseek (outfd, lower, SEEK_SET);
130 TEST_COMPARE (write (outfd, "", 1), 1);
132 /* Cross the boundary with a two-byte write. This can either result
133 in a short write, or a failure. */
134 xlseek (outfd, lower, SEEK_SET);
135 ssize_t ret = write (outfd, " ", 2);
136 if (ret < 0)
138 maximum_offset_errno = errno;
139 maximum_offset_hard_limit = true;
141 else
142 maximum_offset_hard_limit = false;
144 /* Check that writing at the next offset actually fails. This also
145 obtains the expected errno value. */
146 xftruncate (outfd, 0);
147 const char *action;
148 if (lseek64 (outfd, lower + 1, SEEK_SET) != 0)
150 if (write (outfd, "", 1) != -1)
151 FAIL_EXIT1 ("write to impossible offset %" PRIu64 " succeeded",
152 lower + 1);
153 action = "writing";
154 int errno_copy = errno;
155 if (maximum_offset_hard_limit)
156 TEST_COMPARE (errno_copy, maximum_offset_errno);
157 else
158 maximum_offset_errno = errno_copy;
160 else
162 action = "seeking";
163 maximum_offset_errno = errno;
165 printf ("info: %s out of range fails with %m (%d)\n",
166 action, maximum_offset_errno);
168 xftruncate (outfd, 0);
169 xlseek (outfd, 0, SEEK_SET);
172 /* Perform a copy of a file. */
173 static void
174 simple_file_copy (void)
176 xwrite (infd, random_data, current_size);
178 int length;
179 int in_skipped; /* Expected skipped bytes in input. */
180 if (do_inoff)
182 xlseek (infd, 1, SEEK_SET);
183 inoff = 2;
184 length = current_size - 3;
185 in_skipped = 2;
187 else
189 xlseek (infd, 3, SEEK_SET);
190 length = current_size - 5;
191 in_skipped = 3;
193 int out_skipped; /* Expected skipped bytes before the written data. */
194 if (do_outoff)
196 xlseek (outfd, 4, SEEK_SET);
197 outoff = 5;
198 out_skipped = 5;
200 else
202 xlseek (outfd, 6, SEEK_SET);
203 length = current_size - 6;
204 out_skipped = 6;
206 if (length < 0)
207 length = 0;
209 TEST_COMPARE (copy_file_range (infd, pinoff, outfd, poutoff,
210 length, 0), length);
211 if (do_inoff)
213 TEST_COMPARE (inoff, 2 + length);
214 TEST_COMPARE (xlseek (infd, 0, SEEK_CUR), 1);
216 else
217 TEST_COMPARE (xlseek (infd, 0, SEEK_CUR), 3 + length);
218 if (do_outoff)
220 TEST_COMPARE (outoff, 5 + length);
221 TEST_COMPARE (xlseek (outfd, 0, SEEK_CUR), 4);
223 else
224 TEST_COMPARE (xlseek (outfd, 0, SEEK_CUR), 6 + length);
226 struct stat64 st;
227 xfstat (outfd, &st);
228 if (length > 0)
229 TEST_COMPARE (st.st_size, out_skipped + length);
230 else
232 /* If we did not write anything, we also did not add any
233 padding. */
234 TEST_COMPARE (st.st_size, 0);
235 return;
238 xlseek (outfd, 0, SEEK_SET);
239 char *bytes = xmalloc (st.st_size);
240 TEST_COMPARE (read (outfd, bytes, st.st_size), st.st_size);
241 for (int i = 0; i < out_skipped; ++i)
242 TEST_COMPARE (bytes[i], 0);
243 TEST_VERIFY (memcmp (bytes + out_skipped, random_data + in_skipped,
244 length) == 0);
245 free (bytes);
248 /* Test that reading from a pipe willfails. */
249 static void
250 pipe_as_source (void)
252 int pipefds[2];
253 xpipe (pipefds);
255 for (int length = 0; length < 2; ++length)
257 if (test_verbose > 0)
258 printf ("info: %s: length=%d\n", __func__, length);
260 /* Make sure that there is something to copy in the pipe. */
261 xwrite (pipefds[1], "@", 1);
263 TEST_COMPARE (copy_file_range (pipefds[0], pinoff, outfd, poutoff,
264 length, 0), -1);
265 /* Linux 4.10 and later return EINVAL. Older kernels return
266 EXDEV. */
267 TEST_VERIFY (errno == EINVAL || errno == EXDEV);
268 TEST_COMPARE (inoff, 0);
269 TEST_COMPARE (outoff, 0);
270 TEST_COMPARE (xlseek (outfd, 0, SEEK_CUR), 0);
272 /* Make sure that nothing was read. */
273 char buf = 'A';
274 TEST_COMPARE (read (pipefds[0], &buf, 1), 1);
275 TEST_COMPARE (buf, '@');
278 xclose (pipefds[0]);
279 xclose (pipefds[1]);
282 /* Test that writing to a pipe fails. */
283 static void
284 pipe_as_destination (void)
286 /* Make sure that there is something to read in the input file. */
287 xwrite (infd, "abc", 3);
288 xlseek (infd, 0, SEEK_SET);
290 int pipefds[2];
291 xpipe (pipefds);
293 for (int length = 0; length < 2; ++length)
295 if (test_verbose > 0)
296 printf ("info: %s: length=%d\n", __func__, length);
298 TEST_COMPARE (copy_file_range (infd, pinoff, pipefds[1], poutoff,
299 length, 0), -1);
300 /* Linux 4.10 and later return EINVAL. Older kernels return
301 EXDEV. */
302 TEST_VERIFY (errno == EINVAL || errno == EXDEV);
303 TEST_COMPARE (inoff, 0);
304 TEST_COMPARE (outoff, 0);
305 TEST_COMPARE (xlseek (infd, 0, SEEK_CUR), 0);
307 /* Make sure that nothing was written. */
308 struct pollfd pollfd = { .fd = pipefds[0], .events = POLLIN, };
309 TEST_COMPARE (poll (&pollfd, 1, 0), 0);
312 xclose (pipefds[0]);
313 xclose (pipefds[1]);
316 /* Test a write failure after (potentially) writing some bytes.
317 Failure occurs near the start of the buffer. */
318 static void
319 delayed_write_failure_beginning (void)
321 /* We need to write something to provoke the error. */
322 if (current_size == 0)
323 return;
324 xwrite (infd, random_data, sizeof (random_data));
325 xlseek (infd, 0, SEEK_SET);
327 /* Write failure near the start. The actual error code varies among
328 file systems. */
329 find_maximum_offset ();
330 off64_t where = maximum_offset;
332 if (current_size == 1)
333 ++where;
334 outoff = where;
335 if (do_outoff)
336 xlseek (outfd, 1, SEEK_SET);
337 else
338 xlseek (outfd, where, SEEK_SET);
339 if (maximum_offset_hard_limit || where > maximum_offset)
341 TEST_COMPARE (copy_file_range (infd, pinoff, outfd, poutoff,
342 sizeof (random_data), 0), -1);
343 TEST_COMPARE (errno, maximum_offset_errno);
344 TEST_COMPARE (xlseek (infd, 0, SEEK_CUR), 0);
345 TEST_COMPARE (inoff, 0);
346 if (do_outoff)
347 TEST_COMPARE (xlseek (outfd, 0, SEEK_CUR), 1);
348 else
349 TEST_COMPARE (xlseek (outfd, 0, SEEK_CUR), where);
350 TEST_COMPARE (outoff, where);
351 struct stat64 st;
352 xfstat (outfd, &st);
353 TEST_COMPARE (st.st_size, 0);
355 else
357 /* The offset is not a hard limit. This means we write one
358 byte. */
359 TEST_COMPARE (copy_file_range (infd, pinoff, outfd, poutoff,
360 sizeof (random_data), 0), 1);
361 if (do_inoff)
363 TEST_COMPARE (inoff, 1);
364 TEST_COMPARE (xlseek (infd, 0, SEEK_CUR), 0);
366 else
368 TEST_COMPARE (xlseek (infd, 0, SEEK_CUR), 1);
369 TEST_COMPARE (inoff, 0);
371 if (do_outoff)
373 TEST_COMPARE (xlseek (outfd, 0, SEEK_CUR), 1);
374 TEST_COMPARE (outoff, where + 1);
376 else
378 TEST_COMPARE (xlseek (outfd, 0, SEEK_CUR), where + 1);
379 TEST_COMPARE (outoff, where);
381 struct stat64 st;
382 xfstat (outfd, &st);
383 TEST_COMPARE (st.st_size, where + 1);
387 /* Test a write failure after (potentially) writing some bytes.
388 Failure occurs near the end of the buffer. */
389 static void
390 delayed_write_failure_end (void)
392 if (current_size <= 1)
393 /* This would be same as the first test because there is not
394 enough data to write to make a difference. */
395 return;
396 xwrite (infd, random_data, sizeof (random_data));
397 xlseek (infd, 0, SEEK_SET);
399 find_maximum_offset ();
400 off64_t where = maximum_offset - current_size + 1;
401 if (current_size == sizeof (random_data))
402 /* Otherwise we do not reach the non-writable byte. */
403 ++where;
404 outoff = where;
405 if (do_outoff)
406 xlseek (outfd, 1, SEEK_SET);
407 else
408 xlseek (outfd, where, SEEK_SET);
409 ssize_t ret = copy_file_range (infd, pinoff, outfd, poutoff,
410 sizeof (random_data), 0);
411 if (ret < 0)
413 TEST_COMPARE (ret, -1);
414 TEST_COMPARE (errno, maximum_offset_errno);
415 struct stat64 st;
416 xfstat (outfd, &st);
417 TEST_COMPARE (st.st_size, 0);
419 else
421 /* The first copy succeeded. This happens in the emulation
422 because the internal buffer of limited size does not
423 necessarily cross the off64_t boundary on the first write
424 operation. */
425 if (test_verbose > 0)
426 printf ("info: copy_file_range (%zu) returned %zd\n",
427 sizeof (random_data), ret);
428 TEST_VERIFY (ret > 0);
429 TEST_VERIFY (ret < maximum_size);
430 struct stat64 st;
431 xfstat (outfd, &st);
432 TEST_COMPARE (st.st_size, where + ret);
433 if (do_inoff)
435 TEST_COMPARE (inoff, ret);
436 TEST_COMPARE (xlseek (infd, 0, SEEK_CUR), 0);
438 else
439 TEST_COMPARE (xlseek (infd, 0, SEEK_CUR), ret);
441 char *buffer = xmalloc (ret);
442 TEST_COMPARE (pread64 (outfd, buffer, ret, where), ret);
443 TEST_VERIFY (memcmp (buffer, random_data, ret) == 0);
444 free (buffer);
446 /* The second copy fails. */
447 TEST_COMPARE (copy_file_range (infd, pinoff, outfd, poutoff,
448 sizeof (random_data), 0), -1);
449 TEST_COMPARE (errno, maximum_offset_errno);
453 /* Test a write failure across devices. */
454 static void
455 cross_device_failure (void)
457 if (xdevfile == NULL)
458 /* Subtest not supported due to missing cross-device file. */
459 return;
461 /* We need something to write. */
462 xwrite (infd, random_data, sizeof (random_data));
463 xlseek (infd, 0, SEEK_SET);
465 int xdevfd = xopen (xdevfile, O_RDWR | O_LARGEFILE, 0);
466 TEST_COMPARE (copy_file_range (infd, pinoff, xdevfd, poutoff,
467 current_size, 0), -1);
468 TEST_COMPARE (errno, EXDEV);
469 TEST_COMPARE (xlseek (infd, 0, SEEK_CUR), 0);
470 struct stat64 st;
471 xfstat (xdevfd, &st);
472 TEST_COMPARE (st.st_size, 0);
474 xclose (xdevfd);
477 /* Try to exercise ENOSPC behavior with a tempfs file system (so that
478 we do not have to fill up a regular file system to get the error).
479 This function runs in a subprocess, so that we do not change the
480 mount namespace of the actual test process. */
481 static void
482 enospc_failure_1 (void *closure)
484 #ifdef CLONE_NEWNS
485 support_become_root ();
487 /* Make sure that we do not alter the file system mounts of the
488 parents. */
489 if (! support_enter_mount_namespace ())
491 printf ("warning: ENOSPC test skipped\n");
492 return;
495 char *mountpoint = closure;
496 if (mount ("none", mountpoint, "tmpfs", MS_NODEV | MS_NOEXEC,
497 "size=500k") != 0)
499 printf ("warning: could not mount tmpfs at %s: %m\n", mountpoint);
500 return;
503 /* The source file must reside on the same file system. */
504 char *intmpfsfile = xasprintf ("%s/%s", mountpoint, "in");
505 int intmpfsfd = xopen (intmpfsfile, O_RDWR | O_CREAT | O_LARGEFILE, 0600);
506 xwrite (intmpfsfd, random_data, sizeof (random_data));
507 xlseek (intmpfsfd, 1, SEEK_SET);
508 inoff = 1;
510 char *outtmpfsfile = xasprintf ("%s/%s", mountpoint, "out");
511 int outtmpfsfd = xopen (outtmpfsfile, O_RDWR | O_CREAT | O_LARGEFILE, 0600);
513 /* Fill the file with data until ENOSPC is reached. */
514 while (true)
516 ssize_t ret = write (outtmpfsfd, random_data, sizeof (random_data));
517 if (ret < 0 && errno != ENOSPC)
518 FAIL_EXIT1 ("write to %s: %m", outtmpfsfile);
519 if (ret < sizeof (random_data))
520 break;
522 TEST_COMPARE (write (outtmpfsfd, "", 1), -1);
523 TEST_COMPARE (errno, ENOSPC);
524 off64_t maxsize = xlseek (outtmpfsfd, 0, SEEK_CUR);
525 TEST_VERIFY_EXIT (maxsize > sizeof (random_data));
527 /* Constructed the expected file contents. */
528 char *expected = xmalloc (maxsize);
529 TEST_COMPARE (pread64 (outtmpfsfd, expected, maxsize, 0), maxsize);
530 /* Go back a little, so some bytes can be written. */
531 enum { offset = 20000 };
532 TEST_VERIFY_EXIT (offset < maxsize);
533 TEST_VERIFY_EXIT (offset < sizeof (random_data));
534 memcpy (expected + maxsize - offset, random_data + 1, offset);
536 if (do_outoff)
538 outoff = maxsize - offset;
539 xlseek (outtmpfsfd, 2, SEEK_SET);
541 else
542 xlseek (outtmpfsfd, -offset, SEEK_CUR);
544 /* First call is expected to succeed because we made room for some
545 bytes. */
546 TEST_COMPARE (copy_file_range (intmpfsfd, pinoff, outtmpfsfd, poutoff,
547 maximum_size, 0), offset);
548 if (do_inoff)
550 TEST_COMPARE (inoff, 1 + offset);
551 TEST_COMPARE (xlseek (intmpfsfd, 0, SEEK_CUR), 1);
553 else
554 TEST_COMPARE (xlseek (intmpfsfd, 0, SEEK_CUR), 1 + offset);
555 if (do_outoff)
557 TEST_COMPARE (outoff, maxsize);
558 TEST_COMPARE (xlseek (outtmpfsfd, 0, SEEK_CUR), 2);
560 else
561 TEST_COMPARE (xlseek (outtmpfsfd, 0, SEEK_CUR), maxsize);
562 struct stat64 st;
563 xfstat (outtmpfsfd, &st);
564 TEST_COMPARE (st.st_size, maxsize);
565 char *actual = xmalloc (st.st_size);
566 TEST_COMPARE (pread64 (outtmpfsfd, actual, st.st_size, 0), st.st_size);
567 TEST_VERIFY (memcmp (expected, actual, maxsize) == 0);
569 /* Second call should fail with ENOSPC. */
570 TEST_COMPARE (copy_file_range (intmpfsfd, pinoff, outtmpfsfd, poutoff,
571 maximum_size, 0), -1);
572 TEST_COMPARE (errno, ENOSPC);
574 /* Offsets should be unchanged. */
575 if (do_inoff)
577 TEST_COMPARE (inoff, 1 + offset);
578 TEST_COMPARE (xlseek (intmpfsfd, 0, SEEK_CUR), 1);
580 else
581 TEST_COMPARE (xlseek (intmpfsfd, 0, SEEK_CUR), 1 + offset);
582 if (do_outoff)
584 TEST_COMPARE (outoff, maxsize);
585 TEST_COMPARE (xlseek (outtmpfsfd, 0, SEEK_CUR), 2);
587 else
588 TEST_COMPARE (xlseek (outtmpfsfd, 0, SEEK_CUR), maxsize);
589 TEST_COMPARE (xlseek (outtmpfsfd, 0, SEEK_END), maxsize);
590 TEST_COMPARE (pread64 (outtmpfsfd, actual, maxsize, 0), maxsize);
591 TEST_VERIFY (memcmp (expected, actual, maxsize) == 0);
593 free (actual);
594 free (expected);
596 xclose (intmpfsfd);
597 xclose (outtmpfsfd);
598 free (intmpfsfile);
599 free (outtmpfsfile);
601 #else /* !CLONE_NEWNS */
602 puts ("warning: ENOSPC test skipped (no mount namespaces)");
603 #endif
606 /* Call enospc_failure_1 in a subprocess. */
607 static void
608 enospc_failure (void)
610 char *mountpoint
611 = support_create_temp_directory ("tst-copy_file_range-enospc-");
612 support_isolate_in_subprocess (enospc_failure_1, mountpoint);
613 free (mountpoint);
616 /* The target file descriptor must have O_APPEND enabled. */
617 static void
618 oappend_failure (void)
620 /* Add data, to make sure we do not fail because there is
621 insufficient input data. */
622 xwrite (infd, random_data, current_size);
623 xlseek (infd, 0, SEEK_SET);
625 xclose (outfd);
626 outfd = xopen (outfile, O_RDWR | O_APPEND, 0);
627 TEST_COMPARE (copy_file_range (infd, pinoff, outfd, poutoff,
628 current_size, 0), -1);
629 TEST_COMPARE (errno, EBADF);
632 /* Test that a short input file results in a shortened copy. */
633 static void
634 short_copy (void)
636 if (current_size == 0)
637 /* Nothing to shorten. */
638 return;
640 /* Two subtests, one with offset 0 and current_size - 1 bytes, and
641 another one with current_size bytes, but offset 1. */
642 for (int shift = 0; shift < 2; ++shift)
644 if (test_verbose > 0)
645 printf ("info: shift=%d\n", shift);
646 xftruncate (infd, 0);
647 xlseek (infd, 0, SEEK_SET);
648 xwrite (infd, random_data, current_size - !shift);
650 if (do_inoff)
652 inoff = shift;
653 xlseek (infd, 2, SEEK_SET);
655 else
657 inoff = 3;
658 xlseek (infd, shift, SEEK_SET);
660 ftruncate (outfd, 0);
661 xlseek (outfd, 0, SEEK_SET);
662 outoff = 0;
664 /* First call copies current_size - 1 bytes. */
665 TEST_COMPARE (copy_file_range (infd, pinoff, outfd, poutoff,
666 current_size, 0), current_size - 1);
667 char *buffer = xmalloc (current_size);
668 TEST_COMPARE (pread64 (outfd, buffer, current_size, 0),
669 current_size - 1);
670 TEST_VERIFY (memcmp (buffer, random_data + shift, current_size - 1)
671 == 0);
672 free (buffer);
674 if (do_inoff)
676 TEST_COMPARE (inoff, current_size - 1 + shift);
677 TEST_COMPARE (xlseek (infd, 0, SEEK_CUR), 2);
679 else
680 TEST_COMPARE (xlseek (infd, 0, SEEK_CUR), current_size - 1 + shift);
681 if (do_outoff)
683 TEST_COMPARE (outoff, current_size - 1);
684 TEST_COMPARE (xlseek (outfd, 0, SEEK_CUR), 0);
686 else
687 TEST_COMPARE (xlseek (outfd, 0, SEEK_CUR), current_size - 1);
689 /* First call copies zero bytes. */
690 TEST_COMPARE (copy_file_range (infd, pinoff, outfd, poutoff,
691 current_size, 0), 0);
692 /* And the offsets are unchanged. */
693 if (do_inoff)
695 TEST_COMPARE (inoff, current_size - 1 + shift);
696 TEST_COMPARE (xlseek (infd, 0, SEEK_CUR), 2);
698 else
699 TEST_COMPARE (xlseek (infd, 0, SEEK_CUR), current_size - 1 + shift);
700 if (do_outoff)
702 TEST_COMPARE (outoff, current_size - 1);
703 TEST_COMPARE (xlseek (outfd, 0, SEEK_CUR), 0);
705 else
706 TEST_COMPARE (xlseek (outfd, 0, SEEK_CUR), current_size - 1);
710 /* A named test function. */
711 struct test_case
713 const char *name;
714 void (*func) (void);
715 bool sizes; /* If true, call the test with different current_size values. */
718 /* The available test cases. */
719 static struct test_case tests[] =
721 { "simple_file_copy", simple_file_copy, .sizes = true },
722 { "pipe_as_source", pipe_as_source, },
723 { "pipe_as_destination", pipe_as_destination, },
724 { "delayed_write_failure_beginning", delayed_write_failure_beginning,
725 .sizes = true },
726 { "delayed_write_failure_end", delayed_write_failure_end, .sizes = true },
727 { "cross_device_failure", cross_device_failure, .sizes = true },
728 { "enospc_failure", enospc_failure, },
729 { "oappend_failure", oappend_failure, .sizes = true },
730 { "short_copy", short_copy, .sizes = true },
733 static int
734 do_test (void)
736 for (unsigned char *p = random_data; p < array_end (random_data); ++p)
737 *p = rand () >> 24;
739 infd = create_temp_file ("tst-copy_file_range-in-", &infile);
740 xclose (create_temp_file ("tst-copy_file_range-out-", &outfile));
742 /* Try to find a different directory from the default input/output
743 file. */
745 struct stat64 instat;
746 xfstat (infd, &instat);
747 static const char *const candidates[] =
748 { NULL, "/var/tmp", "/dev/shm" };
749 for (const char *const *c = candidates; c < array_end (candidates); ++c)
751 const char *path = *c;
752 char *to_free = NULL;
753 if (path == NULL)
755 to_free = xreadlink ("/proc/self/exe");
756 path = dirname (to_free);
759 struct stat64 cstat;
760 xstat (path, &cstat);
761 if (cstat.st_dev == instat.st_dev)
763 free (to_free);
764 continue;
767 printf ("info: using alternate temporary files directory: %s\n", path);
768 xdevfile = xasprintf ("%s/tst-copy_file_range-xdev-XXXXXX", path);
769 free (to_free);
770 break;
772 if (xdevfile != NULL)
774 int xdevfd = mkstemp (xdevfile);
775 if (xdevfd < 0)
776 FAIL_EXIT1 ("mkstemp (\"%s\"): %m", xdevfile);
777 struct stat64 xdevst;
778 xfstat (xdevfd, &xdevst);
779 TEST_VERIFY (xdevst.st_dev != instat.st_dev);
780 add_temp_file (xdevfile);
781 xclose (xdevfd);
783 else
784 puts ("warning: no alternate directory on different file system found");
786 xclose (infd);
788 for (do_inoff = 0; do_inoff < 2; ++do_inoff)
789 for (do_outoff = 0; do_outoff < 2; ++do_outoff)
790 for (struct test_case *test = tests; test < array_end (tests); ++test)
791 for (const int *size = typical_sizes;
792 size < array_end (typical_sizes); ++size)
794 current_size = *size;
795 if (test_verbose > 0)
796 printf ("info: %s do_inoff=%d do_outoff=%d current_size=%d\n",
797 test->name, do_inoff, do_outoff, current_size);
799 inoff = 0;
800 if (do_inoff)
801 pinoff = &inoff;
802 else
803 pinoff = NULL;
804 outoff = 0;
805 if (do_outoff)
806 poutoff = &outoff;
807 else
808 poutoff = NULL;
810 infd = xopen (infile, O_RDWR | O_LARGEFILE, 0);
811 xftruncate (infd, 0);
812 outfd = xopen (outfile, O_RDWR | O_LARGEFILE, 0);
813 xftruncate (outfd, 0);
815 test->func ();
817 xclose (infd);
818 xclose (outfd);
820 if (!test->sizes)
821 /* Skip the other sizes unless they have been
822 requested. */
823 break;
826 free (infile);
827 free (outfile);
828 free (xdevfile);
830 return 0;
833 #include <support/test-driver.c>