1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
6 #include "alloc-util.h"
7 #include "chase-symlinks.h"
12 #include "hexdecoct.h"
16 #include "path-util.h"
18 #include "string-util.h"
21 #include "tmpfile-util.h"
22 #include "user-util.h"
23 #include "xattr-util.h"
26 _cleanup_free_
char *buf
= NULL
;
27 char fn
[] = "/tmp/test-copy_file.XXXXXX";
28 char fn_copy
[] = "/tmp/test-copy_file.XXXXXX";
32 fd
= mkostemp_safe(fn
);
36 fd
= mkostemp_safe(fn_copy
);
40 assert_se(write_string_file(fn
, "foo bar bar bar foo", WRITE_STRING_FILE_CREATE
) == 0);
42 assert_se(copy_file(fn
, fn_copy
, 0, 0644, 0, 0, COPY_REFLINK
) == 0);
44 assert_se(read_full_file(fn_copy
, &buf
, &sz
) == 0);
45 assert_se(streq(buf
, "foo bar bar bar foo\n"));
52 static bool read_file_at_and_streq(int dir_fd
, const char *path
, const char *expected
) {
53 _cleanup_free_
char *buf
= NULL
;
55 assert_se(read_full_file_at(dir_fd
, path
, &buf
, NULL
) == 0);
56 return streq(buf
, expected
);
59 TEST(copy_tree_replace_file
) {
60 _cleanup_free_
char *src
= NULL
, *dst
= NULL
;
62 assert_se(tempfn_random("/tmp/test-copy_file.XXXXXX", NULL
, &src
) >= 0);
63 assert_se(tempfn_random("/tmp/test-copy_file.XXXXXX", NULL
, &dst
) >= 0);
65 assert_se(write_string_file(src
, "bar bar", WRITE_STRING_FILE_CREATE
) == 0);
66 assert_se(write_string_file(dst
, "foo foo foo", WRITE_STRING_FILE_CREATE
) == 0);
68 /* The file exists- now overwrite original contents, and test the COPY_REPLACE flag. */
70 assert_se(copy_tree(src
, dst
, UID_INVALID
, GID_INVALID
, COPY_REFLINK
, NULL
) == -EEXIST
);
72 assert_se(read_file_at_and_streq(AT_FDCWD
, dst
, "foo foo foo\n"));
74 assert_se(copy_tree(src
, dst
, UID_INVALID
, GID_INVALID
, COPY_REFLINK
|COPY_REPLACE
, NULL
) == 0);
76 assert_se(read_file_at_and_streq(AT_FDCWD
, dst
, "bar bar\n"));
79 TEST(copy_tree_replace_dirs
) {
80 _cleanup_(rm_rf_physical_and_freep
) char *srcp
= NULL
, *dstp
= NULL
;
81 _cleanup_close_
int src
= -1, dst
= -1;
83 /* Create the random source/destination directories */
84 assert_se((src
= mkdtemp_open(NULL
, 0, &srcp
)) >= 0);
85 assert_se((dst
= mkdtemp_open(NULL
, 0, &dstp
)) >= 0);
87 /* Populate some data to differentiate the files. */
88 assert_se(write_string_file_at(src
, "foo", "src file 1", WRITE_STRING_FILE_CREATE
) >= 0);
89 assert_se(write_string_file_at(src
, "bar", "src file 2", WRITE_STRING_FILE_CREATE
) == 0);
91 assert_se(write_string_file_at(dst
, "foo", "dest file 1", WRITE_STRING_FILE_CREATE
) == 0);
92 assert_se(write_string_file_at(dst
, "bar", "dest file 2", WRITE_STRING_FILE_CREATE
) == 0);
94 /* Copying without COPY_REPLACE should fail because the destination file already exists. */
95 assert_se(copy_tree_at(src
, ".", dst
, ".", UID_INVALID
, GID_INVALID
, COPY_REFLINK
, NULL
) == -EEXIST
);
97 assert_se(read_file_at_and_streq(src
, "foo", "src file 1\n"));
98 assert_se(read_file_at_and_streq(src
, "bar", "src file 2\n"));
99 assert_se(read_file_at_and_streq(dst
, "foo", "dest file 1\n"));
100 assert_se(read_file_at_and_streq(dst
, "bar", "dest file 2\n"));
102 assert_se(copy_tree_at(src
, ".", dst
, ".", UID_INVALID
, GID_INVALID
, COPY_REFLINK
|COPY_REPLACE
|COPY_MERGE
, NULL
) == 0);
104 assert_se(read_file_at_and_streq(src
, "foo", "src file 1\n"));
105 assert_se(read_file_at_and_streq(src
, "bar", "src file 2\n"));
106 assert_se(read_file_at_and_streq(dst
, "foo", "src file 1\n"));
107 assert_se(read_file_at_and_streq(dst
, "bar", "src file 2\n"));
111 char in_fn
[] = "/tmp/test-copy-file-fd-XXXXXX";
112 char out_fn
[] = "/tmp/test-copy-file-fd-XXXXXX";
113 _cleanup_close_
int in_fd
= -EBADF
, out_fd
= -EBADF
;
114 const char *text
= "boohoo\nfoo\n\tbar\n";
117 in_fd
= mkostemp_safe(in_fn
);
118 assert_se(in_fd
>= 0);
119 out_fd
= mkostemp_safe(out_fn
);
120 assert_se(out_fd
>= 0);
122 assert_se(write_string_file(in_fn
, text
, WRITE_STRING_FILE_CREATE
) == 0);
123 assert_se(copy_file_fd("/a/file/which/does/not/exist/i/guess", out_fd
, COPY_REFLINK
) < 0);
124 assert_se(copy_file_fd(in_fn
, out_fd
, COPY_REFLINK
) >= 0);
125 assert_se(lseek(out_fd
, SEEK_SET
, 0) == 0);
127 assert_se(read(out_fd
, buf
, sizeof buf
) == (ssize_t
) strlen(text
));
128 assert_se(streq(buf
, text
));
135 _cleanup_set_free_ Set
*denylist
= NULL
;
136 _cleanup_free_
char *cp
= NULL
;
137 char original_dir
[] = "/tmp/test-copy_tree/";
138 char copy_dir
[] = "/tmp/test-copy_tree-copy/";
139 char **files
= STRV_MAKE("file", "dir1/file", "dir1/dir2/file", "dir1/dir2/dir3/dir4/dir5/file");
140 char **symlinks
= STRV_MAKE("link", "file",
141 "link2", "dir1/file");
142 char **hardlinks
= STRV_MAKE("hlink", "file",
143 "hlink2", "dir1/file");
144 const char *unixsockp
, *ignorep
;
146 int xattr_worked
= -1; /* xattr support is optional in temporary directories, hence use it if we can,
147 * but don't fail if we can't */
149 (void) rm_rf(copy_dir
, REMOVE_ROOT
|REMOVE_PHYSICAL
);
150 (void) rm_rf(original_dir
, REMOVE_ROOT
|REMOVE_PHYSICAL
);
152 STRV_FOREACH(p
, files
) {
153 _cleanup_free_
char *f
, *c
;
156 assert_se(f
= path_join(original_dir
, *p
));
158 assert_se(write_string_file(f
, "file", WRITE_STRING_FILE_CREATE
|WRITE_STRING_FILE_MKDIR_0755
) == 0);
160 assert_se(base64mem(*p
, strlen(*p
), &c
) >= 0);
162 k
= setxattr(f
, "user.testxattr", c
, strlen(c
), 0);
163 assert_se(xattr_worked
< 0 || ((k
>= 0) == !!xattr_worked
));
164 xattr_worked
= k
>= 0;
167 STRV_FOREACH_PAIR(ll
, p
, symlinks
) {
168 _cleanup_free_
char *f
, *l
;
170 assert_se(f
= path_join(original_dir
, *p
));
171 assert_se(l
= path_join(original_dir
, *ll
));
173 assert_se(mkdir_parents(l
, 0755) >= 0);
174 assert_se(symlink(f
, l
) == 0);
177 STRV_FOREACH_PAIR(ll
, p
, hardlinks
) {
178 _cleanup_free_
char *f
, *l
;
180 assert_se(f
= path_join(original_dir
, *p
));
181 assert_se(l
= path_join(original_dir
, *ll
));
183 assert_se(mkdir_parents(l
, 0755) >= 0);
184 assert_se(link(f
, l
) == 0);
187 unixsockp
= strjoina(original_dir
, "unixsock");
188 assert_se(mknod(unixsockp
, S_IFSOCK
|0644, 0) >= 0);
190 ignorep
= strjoina(original_dir
, "ignore/file");
191 assert_se(write_string_file(ignorep
, "ignore", WRITE_STRING_FILE_CREATE
|WRITE_STRING_FILE_MKDIR_0755
) == 0);
192 assert_se(RET_NERRNO(stat(ignorep
, &st
)) >= 0);
193 assert_se(cp
= memdup(&st
, sizeof(st
)));
194 assert_se(set_ensure_put(&denylist
, &inode_hash_ops
, cp
) >= 0);
197 assert_se(copy_tree(original_dir
, copy_dir
, UID_INVALID
, GID_INVALID
, COPY_REFLINK
|COPY_MERGE
|COPY_HARDLINKS
, denylist
) == 0);
199 STRV_FOREACH(p
, files
) {
200 _cleanup_free_
char *buf
, *f
, *c
= NULL
;
204 assert_se(f
= path_join(copy_dir
, *p
));
206 assert_se(access(f
, F_OK
) == 0);
207 assert_se(read_full_file(f
, &buf
, &sz
) == 0);
208 assert_se(streq(buf
, "file\n"));
210 k
= lgetxattr_malloc(f
, "user.testxattr", &c
);
211 assert_se(xattr_worked
< 0 || ((k
>= 0) == !!xattr_worked
));
214 _cleanup_free_
char *d
= NULL
;
216 assert_se(base64mem(*p
, strlen(*p
), &d
) >= 0);
217 assert_se(streq(d
, c
));
221 STRV_FOREACH_PAIR(ll
, p
, symlinks
) {
222 _cleanup_free_
char *target
, *f
, *l
;
224 assert_se(f
= strjoin(original_dir
, *p
));
225 assert_se(l
= strjoin(copy_dir
, *ll
));
227 assert_se(chase_symlinks(l
, NULL
, 0, &target
, NULL
) == 1);
228 assert_se(path_equal(f
, target
));
231 STRV_FOREACH_PAIR(ll
, p
, hardlinks
) {
232 _cleanup_free_
char *f
, *l
;
235 assert_se(f
= strjoin(copy_dir
, *p
));
236 assert_se(l
= strjoin(copy_dir
, *ll
));
238 assert_se(lstat(f
, &a
) >= 0);
239 assert_se(lstat(l
, &b
) >= 0);
241 assert_se(a
.st_ino
== b
.st_ino
);
242 assert_se(a
.st_dev
== b
.st_dev
);
245 unixsockp
= strjoina(copy_dir
, "unixsock");
246 assert_se(stat(unixsockp
, &st
) >= 0);
247 assert_se(S_ISSOCK(st
.st_mode
));
249 assert_se(copy_tree(original_dir
, copy_dir
, UID_INVALID
, GID_INVALID
, COPY_REFLINK
, denylist
) < 0);
250 assert_se(copy_tree("/tmp/inexistent/foo/bar/fsdoi", copy_dir
, UID_INVALID
, GID_INVALID
, COPY_REFLINK
, denylist
) < 0);
252 ignorep
= strjoina(copy_dir
, "ignore/file");
253 assert_se(RET_NERRNO(access(ignorep
, F_OK
)) == -ENOENT
);
255 (void) rm_rf(copy_dir
, REMOVE_ROOT
|REMOVE_PHYSICAL
);
256 (void) rm_rf(original_dir
, REMOVE_ROOT
|REMOVE_PHYSICAL
);
260 _cleanup_close_pair_
int pipefd
[2] = PIPE_EBADF
;
261 _cleanup_close_
int infd
= -EBADF
;
263 char buf
[1024], buf2
[1024];
265 infd
= open("/usr/lib/os-release", O_RDONLY
|O_CLOEXEC
);
267 infd
= open("/etc/os-release", O_RDONLY
|O_CLOEXEC
);
268 assert_se(infd
>= 0);
270 assert_se(pipe2(pipefd
, O_CLOEXEC
) == 0);
272 r
= copy_bytes(infd
, pipefd
[1], UINT64_MAX
, 0);
275 r
= read(pipefd
[0], buf
, sizeof(buf
));
278 assert_se(lseek(infd
, 0, SEEK_SET
) == 0);
279 r2
= read(infd
, buf2
, sizeof(buf2
));
282 assert_se(strneq(buf
, buf2
, r
));
284 /* test copy_bytes with invalid descriptors */
285 r
= copy_bytes(pipefd
[0], pipefd
[0], 1, 0);
286 assert_se(r
== -EBADF
);
288 r
= copy_bytes(pipefd
[1], pipefd
[1], 1, 0);
289 assert_se(r
== -EBADF
);
291 r
= copy_bytes(pipefd
[1], infd
, 1, 0);
292 assert_se(r
== -EBADF
);
295 static void test_copy_bytes_regular_file_one(const char *src
, bool try_reflink
, uint64_t max_bytes
) {
296 char fn2
[] = "/tmp/test-copy-file-XXXXXX";
297 char fn3
[] = "/tmp/test-copy-file-XXXXXX";
298 _cleanup_close_
int fd
= -EBADF
, fd2
= -EBADF
, fd3
= -EBADF
;
300 struct stat buf
, buf2
, buf3
;
302 log_info("%s try_reflink=%s max_bytes=%" PRIu64
, __func__
, yes_no(try_reflink
), max_bytes
);
304 fd
= open(src
, O_RDONLY
| O_CLOEXEC
| O_NOCTTY
);
307 fd2
= mkostemp_safe(fn2
);
310 fd3
= mkostemp_safe(fn3
);
313 r
= copy_bytes(fd
, fd2
, max_bytes
, try_reflink
? COPY_REFLINK
: 0);
314 if (max_bytes
== UINT64_MAX
)
317 assert_se(IN_SET(r
, 0, 1));
319 assert_se(fstat(fd
, &buf
) == 0);
320 assert_se(fstat(fd2
, &buf2
) == 0);
321 assert_se((uint64_t) buf2
.st_size
== MIN((uint64_t) buf
.st_size
, max_bytes
));
323 if (max_bytes
< UINT64_MAX
)
324 /* Make sure the file is now higher than max_bytes */
325 assert_se(ftruncate(fd2
, max_bytes
+ 1) == 0);
327 assert_se(lseek(fd2
, 0, SEEK_SET
) == 0);
329 r
= copy_bytes(fd2
, fd3
, max_bytes
, try_reflink
? COPY_REFLINK
: 0);
330 if (max_bytes
== UINT64_MAX
)
333 /* We cannot distinguish between the input being exactly max_bytes
334 * or longer than max_bytes (without trying to read one more byte,
335 * or calling stat, or FION_READ, etc, and we don't want to do any
336 * of that). So we expect "truncation" since we know that file we
337 * are copying is exactly max_bytes bytes. */
340 assert_se(fstat(fd3
, &buf3
) == 0);
342 if (max_bytes
== UINT64_MAX
)
343 assert_se(buf3
.st_size
== buf2
.st_size
);
345 assert_se((uint64_t) buf3
.st_size
== max_bytes
);
351 TEST(copy_bytes_regular_file
) {
352 test_copy_bytes_regular_file_one(saved_argv
[0], false, UINT64_MAX
);
353 test_copy_bytes_regular_file_one(saved_argv
[0], true, UINT64_MAX
);
354 test_copy_bytes_regular_file_one(saved_argv
[0], false, 1000); /* smaller than copy buffer size */
355 test_copy_bytes_regular_file_one(saved_argv
[0], true, 1000);
356 test_copy_bytes_regular_file_one(saved_argv
[0], false, 32000); /* larger than copy buffer size */
357 test_copy_bytes_regular_file_one(saved_argv
[0], true, 32000);
361 _cleanup_(rm_rf_physical_and_freep
) char *p
= NULL
;
365 assert_se(mkdtemp_malloc(NULL
, &p
) >= 0);
367 q
= strjoina(p
, "/fstab");
369 r
= copy_file_atomic("/etc/fstab", q
, 0644, 0, 0, COPY_REFLINK
);
370 if (r
== -ENOENT
|| ERRNO_IS_PRIVILEGE(r
))
373 assert_se(copy_file_atomic("/etc/fstab", q
, 0644, 0, 0, COPY_REFLINK
) == -EEXIST
);
375 assert_se(copy_file_atomic("/etc/fstab", q
, 0644, 0, 0, COPY_REPLACE
) >= 0);
379 _cleanup_(rm_rf_physical_and_freep
) char *p
= NULL
;
380 _cleanup_free_
char *f
= NULL
, *a
= NULL
, *b
= NULL
;
382 /* Check if copying data from /proc/ works correctly, i.e. let's see if https://lwn.net/Articles/846403/ is a problem for us */
384 assert_se(mkdtemp_malloc(NULL
, &p
) >= 0);
385 assert_se(f
= path_join(p
, "version"));
386 assert_se(copy_file("/proc/version", f
, 0, MODE_INVALID
, 0, 0, 0) >= 0);
388 assert_se(read_one_line_file("/proc/version", &a
) >= 0);
389 assert_se(read_one_line_file(f
, &b
) >= 0);
390 assert_se(streq(a
, b
));
391 assert_se(!isempty(a
));
394 TEST_RET(copy_holes
) {
395 char fn
[] = "/var/tmp/test-copy-hole-fd-XXXXXX";
396 char fn_copy
[] = "/var/tmp/test-copy-hole-fd-XXXXXX";
402 fd
= mkostemp_safe(fn
);
405 fd_copy
= mkostemp_safe(fn_copy
);
406 assert_se(fd_copy
>= 0);
408 r
= RET_NERRNO(fallocate(fd
, FALLOC_FL_PUNCH_HOLE
| FALLOC_FL_KEEP_SIZE
, 0, 1));
409 if (ERRNO_IS_NOT_SUPPORTED(r
))
410 return log_tests_skipped("Filesystem doesn't support hole punching");
413 assert_se(fstat(fd
, &stat
) >= 0);
414 blksz
= stat
.st_blksize
;
415 buf
= alloca_safe(blksz
);
416 memset(buf
, 1, blksz
);
418 /* We need to make sure to create hole in multiples of the block size, otherwise filesystems (btrfs)
419 * might silently truncate/extend the holes. */
421 assert_se(lseek(fd
, blksz
, SEEK_CUR
) >= 0);
422 assert_se(write(fd
, buf
, blksz
) >= 0);
423 assert_se(lseek(fd
, 0, SEEK_END
) == 2 * blksz
);
424 /* Only ftruncate() can create holes at the end of a file. */
425 assert_se(ftruncate(fd
, 3 * blksz
) >= 0);
426 assert_se(lseek(fd
, 0, SEEK_SET
) >= 0);
428 assert_se(copy_bytes(fd
, fd_copy
, UINT64_MAX
, COPY_HOLES
) >= 0);
430 /* Test that the hole starts at the beginning of the file. */
431 assert_se(lseek(fd_copy
, 0, SEEK_HOLE
) == 0);
432 /* Test that the hole has the expected size. */
433 assert_se(lseek(fd_copy
, 0, SEEK_DATA
) == blksz
);
434 assert_se(lseek(fd_copy
, blksz
, SEEK_HOLE
) == 2 * blksz
);
435 assert_se(lseek(fd_copy
, 2 * blksz
, SEEK_DATA
) < 0 && errno
== ENXIO
);
437 /* Test that the copied file has the correct size. */
438 assert_se(fstat(fd_copy
, &stat
) >= 0);
439 assert_se(stat
.st_size
== 3 * blksz
);
450 DEFINE_TEST_MAIN(LOG_DEBUG
);