LCOV - code coverage report
Current view: top level - test - test-copy.c (source / functions) Hit Total Coverage
Test: main_coverage.info Lines: 158 160 98.8 %
Date: 2019-08-22 15:41:25 Functions: 7 7 100.0 %

          Line data    Source code
       1             : /* SPDX-License-Identifier: LGPL-2.1+ */
       2             : 
       3             : #include <unistd.h>
       4             : 
       5             : #include "alloc-util.h"
       6             : #include "copy.h"
       7             : #include "fd-util.h"
       8             : #include "fileio.h"
       9             : #include "fs-util.h"
      10             : #include "log.h"
      11             : #include "macro.h"
      12             : #include "mkdir.h"
      13             : #include "path-util.h"
      14             : #include "rm-rf.h"
      15             : #include "string-util.h"
      16             : #include "strv.h"
      17             : #include "tests.h"
      18             : #include "tmpfile-util.h"
      19             : #include "user-util.h"
      20             : #include "util.h"
      21             : 
      22           1 : static void test_copy_file(void) {
      23           1 :         _cleanup_free_ char *buf = NULL;
      24           1 :         char fn[] = "/tmp/test-copy_file.XXXXXX";
      25           1 :         char fn_copy[] = "/tmp/test-copy_file.XXXXXX";
      26           1 :         size_t sz = 0;
      27             :         int fd;
      28             : 
      29           1 :         log_info("%s", __func__);
      30             : 
      31           1 :         fd = mkostemp_safe(fn);
      32           1 :         assert_se(fd >= 0);
      33           1 :         close(fd);
      34             : 
      35           1 :         fd = mkostemp_safe(fn_copy);
      36           1 :         assert_se(fd >= 0);
      37           1 :         close(fd);
      38             : 
      39           1 :         assert_se(write_string_file(fn, "foo bar bar bar foo", WRITE_STRING_FILE_CREATE) == 0);
      40             : 
      41           1 :         assert_se(copy_file(fn, fn_copy, 0, 0644, 0, 0, COPY_REFLINK) == 0);
      42             : 
      43           1 :         assert_se(read_full_file(fn_copy, &buf, &sz) == 0);
      44           1 :         assert_se(streq(buf, "foo bar bar bar foo\n"));
      45           1 :         assert_se(sz == 20);
      46             : 
      47           1 :         unlink(fn);
      48           1 :         unlink(fn_copy);
      49           1 : }
      50             : 
      51           1 : static void test_copy_file_fd(void) {
      52           1 :         char in_fn[] = "/tmp/test-copy-file-fd-XXXXXX";
      53           1 :         char out_fn[] = "/tmp/test-copy-file-fd-XXXXXX";
      54           1 :         _cleanup_close_ int in_fd = -1, out_fd = -1;
      55           1 :         const char *text = "boohoo\nfoo\n\tbar\n";
      56           1 :         char buf[64] = {};
      57             : 
      58           1 :         log_info("%s", __func__);
      59             : 
      60           1 :         in_fd = mkostemp_safe(in_fn);
      61           1 :         assert_se(in_fd >= 0);
      62           1 :         out_fd = mkostemp_safe(out_fn);
      63           1 :         assert_se(out_fd >= 0);
      64             : 
      65           1 :         assert_se(write_string_file(in_fn, text, WRITE_STRING_FILE_CREATE) == 0);
      66           1 :         assert_se(copy_file_fd("/a/file/which/does/not/exist/i/guess", out_fd, COPY_REFLINK) < 0);
      67           1 :         assert_se(copy_file_fd(in_fn, out_fd, COPY_REFLINK) >= 0);
      68           1 :         assert_se(lseek(out_fd, SEEK_SET, 0) == 0);
      69             : 
      70           1 :         assert_se(read(out_fd, buf, sizeof buf) == (ssize_t) strlen(text));
      71           1 :         assert_se(streq(buf, text));
      72             : 
      73           1 :         unlink(in_fn);
      74           1 :         unlink(out_fn);
      75           1 : }
      76             : 
      77           1 : static void test_copy_tree(void) {
      78           1 :         char original_dir[] = "/tmp/test-copy_tree/";
      79           1 :         char copy_dir[] = "/tmp/test-copy_tree-copy/";
      80           1 :         char **files = STRV_MAKE("file", "dir1/file", "dir1/dir2/file", "dir1/dir2/dir3/dir4/dir5/file");
      81           1 :         char **links = STRV_MAKE("link", "file",
      82             :                                  "link2", "dir1/file");
      83             :         char **p, **link;
      84             :         const char *unixsockp;
      85             :         struct stat st;
      86             : 
      87           1 :         log_info("%s", __func__);
      88             : 
      89           1 :         (void) rm_rf(copy_dir, REMOVE_ROOT|REMOVE_PHYSICAL);
      90           1 :         (void) rm_rf(original_dir, REMOVE_ROOT|REMOVE_PHYSICAL);
      91             : 
      92           5 :         STRV_FOREACH(p, files) {
      93           4 :                 _cleanup_free_ char *f;
      94             : 
      95           4 :                 assert_se(f = path_join(original_dir, *p));
      96             : 
      97           4 :                 assert_se(mkdir_parents(f, 0755) >= 0);
      98           4 :                 assert_se(write_string_file(f, "file", WRITE_STRING_FILE_CREATE) == 0);
      99             :         }
     100             : 
     101           3 :         STRV_FOREACH_PAIR(link, p, links) {
     102           2 :                 _cleanup_free_ char *f, *l;
     103             : 
     104           2 :                 assert_se(f = path_join(original_dir, *p));
     105           2 :                 assert_se(l = path_join(original_dir, *link));
     106             : 
     107           2 :                 assert_se(mkdir_parents(l, 0755) >= 0);
     108           2 :                 assert_se(symlink(f, l) == 0);
     109             :         }
     110             : 
     111           5 :         unixsockp = strjoina(original_dir, "unixsock");
     112           1 :         assert_se(mknod(unixsockp, S_IFSOCK|0644, 0) >= 0);
     113             : 
     114           1 :         assert_se(copy_tree(original_dir, copy_dir, UID_INVALID, GID_INVALID, COPY_REFLINK|COPY_MERGE) == 0);
     115             : 
     116           5 :         STRV_FOREACH(p, files) {
     117           4 :                 _cleanup_free_ char *buf, *f;
     118             :                 size_t sz;
     119             : 
     120           4 :                 assert_se(f = path_join(copy_dir, *p));
     121             : 
     122           4 :                 assert_se(access(f, F_OK) == 0);
     123           4 :                 assert_se(read_full_file(f, &buf, &sz) == 0);
     124           4 :                 assert_se(streq(buf, "file\n"));
     125             :         }
     126             : 
     127           3 :         STRV_FOREACH_PAIR(link, p, links) {
     128           2 :                 _cleanup_free_ char *target, *f, *l;
     129             : 
     130           2 :                 assert_se(f = strjoin(original_dir, *p));
     131           2 :                 assert_se(l = strjoin(copy_dir, *link));
     132             : 
     133           2 :                 assert_se(chase_symlinks(l, NULL, 0, &target) == 1);
     134           2 :                 assert_se(path_equal(f, target));
     135             :         }
     136             : 
     137           5 :         unixsockp = strjoina(copy_dir, "unixsock");
     138           1 :         assert_se(stat(unixsockp, &st) >= 0);
     139           1 :         assert_se(S_ISSOCK(st.st_mode));
     140             : 
     141           1 :         assert_se(copy_tree(original_dir, copy_dir, UID_INVALID, GID_INVALID, COPY_REFLINK) < 0);
     142           1 :         assert_se(copy_tree("/tmp/inexistent/foo/bar/fsdoi", copy_dir, UID_INVALID, GID_INVALID, COPY_REFLINK) < 0);
     143             : 
     144           1 :         (void) rm_rf(copy_dir, REMOVE_ROOT|REMOVE_PHYSICAL);
     145           1 :         (void) rm_rf(original_dir, REMOVE_ROOT|REMOVE_PHYSICAL);
     146           1 : }
     147             : 
     148           1 : static void test_copy_bytes(void) {
     149           1 :         _cleanup_close_pair_ int pipefd[2] = {-1, -1};
     150           1 :         _cleanup_close_ int infd = -1;
     151             :         int r, r2;
     152             :         char buf[1024], buf2[1024];
     153             : 
     154           1 :         infd = open("/usr/lib/os-release", O_RDONLY|O_CLOEXEC);
     155           1 :         if (infd < 0)
     156           0 :                 infd = open("/etc/os-release", O_RDONLY|O_CLOEXEC);
     157           1 :         assert_se(infd >= 0);
     158             : 
     159           1 :         assert_se(pipe2(pipefd, O_CLOEXEC) == 0);
     160             : 
     161           1 :         r = copy_bytes(infd, pipefd[1], (uint64_t) -1, 0);
     162           1 :         assert_se(r == 0);
     163             : 
     164           1 :         r = read(pipefd[0], buf, sizeof(buf));
     165           1 :         assert_se(r >= 0);
     166             : 
     167           1 :         assert_se(lseek(infd, 0, SEEK_SET) == 0);
     168           1 :         r2 = read(infd, buf2, sizeof(buf2));
     169           1 :         assert_se(r == r2);
     170             : 
     171           1 :         assert_se(strneq(buf, buf2, r));
     172             : 
     173             :         /* test copy_bytes with invalid descriptors */
     174           1 :         r = copy_bytes(pipefd[0], pipefd[0], 1, 0);
     175           1 :         assert_se(r == -EBADF);
     176             : 
     177           1 :         r = copy_bytes(pipefd[1], pipefd[1], 1, 0);
     178           1 :         assert_se(r == -EBADF);
     179             : 
     180           1 :         r = copy_bytes(pipefd[1], infd, 1, 0);
     181           1 :         assert_se(r == -EBADF);
     182           1 : }
     183             : 
     184           6 : static void test_copy_bytes_regular_file(const char *src, bool try_reflink, uint64_t max_bytes) {
     185           6 :         char fn2[] = "/tmp/test-copy-file-XXXXXX";
     186           6 :         char fn3[] = "/tmp/test-copy-file-XXXXXX";
     187           6 :         _cleanup_close_ int fd = -1, fd2 = -1, fd3 = -1;
     188             :         int r;
     189             :         struct stat buf, buf2, buf3;
     190             : 
     191           6 :         log_info("%s try_reflink=%s max_bytes=%" PRIu64, __func__, yes_no(try_reflink), max_bytes);
     192             : 
     193           6 :         fd = open(src, O_RDONLY | O_CLOEXEC | O_NOCTTY);
     194           6 :         assert_se(fd >= 0);
     195             : 
     196           6 :         fd2 = mkostemp_safe(fn2);
     197           6 :         assert_se(fd2 >= 0);
     198             : 
     199           6 :         fd3 = mkostemp_safe(fn3);
     200           6 :         assert_se(fd3 >= 0);
     201             : 
     202           6 :         r = copy_bytes(fd, fd2, max_bytes, try_reflink ? COPY_REFLINK : 0);
     203           6 :         if (max_bytes == (uint64_t) -1)
     204           2 :                 assert_se(r == 0);
     205             :         else
     206           4 :                 assert_se(IN_SET(r, 0, 1));
     207             : 
     208           6 :         assert_se(fstat(fd, &buf) == 0);
     209           6 :         assert_se(fstat(fd2, &buf2) == 0);
     210           6 :         assert_se((uint64_t) buf2.st_size == MIN((uint64_t) buf.st_size, max_bytes));
     211             : 
     212           6 :         if (max_bytes < (uint64_t) -1)
     213             :                 /* Make sure the file is now higher than max_bytes */
     214           4 :                 assert_se(ftruncate(fd2, max_bytes + 1) == 0);
     215             : 
     216           6 :         assert_se(lseek(fd2, 0, SEEK_SET) == 0);
     217             : 
     218           6 :         r = copy_bytes(fd2, fd3, max_bytes, try_reflink ? COPY_REFLINK : 0);
     219           6 :         if (max_bytes == (uint64_t) -1)
     220           2 :                 assert_se(r == 0);
     221             :         else
     222             :                 /* We cannot distinguish between the input being exactly max_bytes
     223             :                  * or longer than max_bytes (without trying to read one more byte,
     224             :                  * or calling stat, or FION_READ, etc, and we don't want to do any
     225             :                  * of that). So we expect "truncation" since we know that file we
     226             :                  * are copying is exactly max_bytes bytes. */
     227           4 :                 assert_se(r == 1);
     228             : 
     229           6 :         assert_se(fstat(fd3, &buf3) == 0);
     230             : 
     231           6 :         if (max_bytes == (uint64_t) -1)
     232           2 :                 assert_se(buf3.st_size == buf2.st_size);
     233             :         else
     234           4 :                 assert_se((uint64_t) buf3.st_size == max_bytes);
     235             : 
     236           6 :         unlink(fn2);
     237           6 :         unlink(fn3);
     238           6 : }
     239             : 
     240           1 : static void test_copy_atomic(void) {
     241           1 :         _cleanup_(rm_rf_physical_and_freep) char *p = NULL;
     242             :         const char *q;
     243             :         int r;
     244             : 
     245           1 :         assert_se(mkdtemp_malloc(NULL, &p) >= 0);
     246             : 
     247           5 :         q = strjoina(p, "/fstab");
     248             : 
     249           1 :         r = copy_file_atomic("/etc/fstab", q, 0644, 0, 0, COPY_REFLINK);
     250           1 :         if (r == -ENOENT)
     251           0 :                 return;
     252             : 
     253           1 :         assert_se(copy_file_atomic("/etc/fstab", q, 0644, 0, 0, COPY_REFLINK) == -EEXIST);
     254             : 
     255           1 :         assert_se(copy_file_atomic("/etc/fstab", q, 0644, 0, 0, COPY_REPLACE) >= 0);
     256             : }
     257             : 
     258           1 : int main(int argc, char *argv[]) {
     259           1 :         test_setup_logging(LOG_DEBUG);
     260             : 
     261           1 :         test_copy_file();
     262           1 :         test_copy_file_fd();
     263           1 :         test_copy_tree();
     264           1 :         test_copy_bytes();
     265           1 :         test_copy_bytes_regular_file(argv[0], false, (uint64_t) -1);
     266           1 :         test_copy_bytes_regular_file(argv[0], true, (uint64_t) -1);
     267           1 :         test_copy_bytes_regular_file(argv[0], false, 1000); /* smaller than copy buffer size */
     268           1 :         test_copy_bytes_regular_file(argv[0], true, 1000);
     269           1 :         test_copy_bytes_regular_file(argv[0], false, 32000); /* larger than copy buffer size */
     270           1 :         test_copy_bytes_regular_file(argv[0], true, 32000);
     271           1 :         test_copy_atomic();
     272             : 
     273           1 :         return 0;
     274             : }

Generated by: LCOV version 1.14