]>
git.ipfire.org Git - thirdparty/systemd.git/blob - src/test/test-copy.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd
5 Copyright 2014 Ronny Chevalier
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
23 #include "alloc-util.h"
31 #include "path-util.h"
33 #include "string-util.h"
35 #include "user-util.h"
38 static void test_copy_file(void) {
39 _cleanup_free_
char *buf
= NULL
;
40 char fn
[] = "/tmp/test-copy_file.XXXXXX";
41 char fn_copy
[] = "/tmp/test-copy_file.XXXXXX";
45 log_info("%s", __func__
);
47 fd
= mkostemp_safe(fn
);
51 fd
= mkostemp_safe(fn_copy
);
55 assert_se(write_string_file(fn
, "foo bar bar bar foo", WRITE_STRING_FILE_CREATE
) == 0);
57 assert_se(copy_file(fn
, fn_copy
, 0, 0644, 0, COPY_REFLINK
) == 0);
59 assert_se(read_full_file(fn_copy
, &buf
, &sz
) == 0);
60 assert_se(streq(buf
, "foo bar bar bar foo\n"));
67 static void test_copy_file_fd(void) {
68 char in_fn
[] = "/tmp/test-copy-file-fd-XXXXXX";
69 char out_fn
[] = "/tmp/test-copy-file-fd-XXXXXX";
70 _cleanup_close_
int in_fd
= -1, out_fd
= -1;
71 char text
[] = "boohoo\nfoo\n\tbar\n";
74 log_info("%s", __func__
);
76 in_fd
= mkostemp_safe(in_fn
);
77 assert_se(in_fd
>= 0);
78 out_fd
= mkostemp_safe(out_fn
);
79 assert_se(out_fd
>= 0);
81 assert_se(write_string_file(in_fn
, text
, WRITE_STRING_FILE_CREATE
) == 0);
82 assert_se(copy_file_fd("/a/file/which/does/not/exist/i/guess", out_fd
, COPY_REFLINK
) < 0);
83 assert_se(copy_file_fd(in_fn
, out_fd
, COPY_REFLINK
) >= 0);
84 assert_se(lseek(out_fd
, SEEK_SET
, 0) == 0);
86 assert_se(read(out_fd
, buf
, sizeof(buf
)) == sizeof(text
) - 1);
87 assert_se(streq(buf
, text
));
93 static void test_copy_tree(void) {
94 char original_dir
[] = "/tmp/test-copy_tree/";
95 char copy_dir
[] = "/tmp/test-copy_tree-copy/";
96 char **files
= STRV_MAKE("file", "dir1/file", "dir1/dir2/file", "dir1/dir2/dir3/dir4/dir5/file");
97 char **links
= STRV_MAKE("link", "file",
98 "link2", "dir1/file");
100 const char *unixsockp
;
103 log_info("%s", __func__
);
105 (void) rm_rf(copy_dir
, REMOVE_ROOT
|REMOVE_PHYSICAL
);
106 (void) rm_rf(original_dir
, REMOVE_ROOT
|REMOVE_PHYSICAL
);
108 STRV_FOREACH(p
, files
) {
109 _cleanup_free_
char *f
;
111 assert_se((f
= strappend(original_dir
, *p
)));
113 assert_se(mkdir_parents(f
, 0755) >= 0);
114 assert_se(write_string_file(f
, "file", WRITE_STRING_FILE_CREATE
) == 0);
117 STRV_FOREACH_PAIR(link
, p
, links
) {
118 _cleanup_free_
char *f
, *l
;
120 assert_se((f
= strappend(original_dir
, *p
)));
121 assert_se((l
= strappend(original_dir
, *link
)));
123 assert_se(mkdir_parents(l
, 0755) >= 0);
124 assert_se(symlink(f
, l
) == 0);
127 unixsockp
= strjoina(original_dir
, "unixsock");
128 assert_se(mknod(unixsockp
, S_IFSOCK
|0644, 0) >= 0);
130 assert_se(copy_tree(original_dir
, copy_dir
, UID_INVALID
, GID_INVALID
, COPY_REFLINK
|COPY_MERGE
) == 0);
132 STRV_FOREACH(p
, files
) {
133 _cleanup_free_
char *buf
= NULL
, *f
;
136 assert_se((f
= strappend(copy_dir
, *p
)));
138 assert_se(access(f
, F_OK
) == 0);
139 assert_se(read_full_file(f
, &buf
, &sz
) == 0);
140 assert_se(streq(buf
, "file\n"));
143 STRV_FOREACH_PAIR(link
, p
, links
) {
144 _cleanup_free_
char *target
= NULL
, *f
, *l
;
146 assert_se((f
= strjoin(original_dir
, *p
)));
147 assert_se((l
= strjoin(copy_dir
, *link
)));
149 assert_se(readlink_and_canonicalize(l
, NULL
, &target
) == 0);
150 assert_se(path_equal(f
, target
));
153 unixsockp
= strjoina(copy_dir
, "unixsock");
154 assert_se(stat(unixsockp
, &st
) >= 0);
155 assert_se(S_ISSOCK(st
.st_mode
));
157 assert_se(copy_tree(original_dir
, copy_dir
, UID_INVALID
, GID_INVALID
, COPY_REFLINK
) < 0);
158 assert_se(copy_tree("/tmp/inexistent/foo/bar/fsdoi", copy_dir
, UID_INVALID
, GID_INVALID
, COPY_REFLINK
) < 0);
160 (void) rm_rf(copy_dir
, REMOVE_ROOT
|REMOVE_PHYSICAL
);
161 (void) rm_rf(original_dir
, REMOVE_ROOT
|REMOVE_PHYSICAL
);
164 static void test_copy_bytes(void) {
165 _cleanup_close_pair_
int pipefd
[2] = {-1, -1};
166 _cleanup_close_
int infd
= -1;
168 char buf
[1024], buf2
[1024];
170 infd
= open("/usr/lib/os-release", O_RDONLY
|O_CLOEXEC
);
172 infd
= open("/etc/os-release", O_RDONLY
|O_CLOEXEC
);
173 assert_se(infd
>= 0);
175 assert_se(pipe2(pipefd
, O_CLOEXEC
) == 0);
177 r
= copy_bytes(infd
, pipefd
[1], (uint64_t) -1, 0);
180 r
= read(pipefd
[0], buf
, sizeof(buf
));
183 assert_se(lseek(infd
, 0, SEEK_SET
) == 0);
184 r2
= read(infd
, buf2
, sizeof(buf2
));
187 assert_se(strneq(buf
, buf2
, r
));
189 /* test copy_bytes with invalid descriptors */
190 r
= copy_bytes(pipefd
[0], pipefd
[0], 1, 0);
191 assert_se(r
== -EBADF
);
193 r
= copy_bytes(pipefd
[1], pipefd
[1], 1, 0);
194 assert_se(r
== -EBADF
);
196 r
= copy_bytes(pipefd
[1], infd
, 1, 0);
197 assert_se(r
== -EBADF
);
200 static void test_copy_bytes_regular_file(const char *src
, bool try_reflink
, uint64_t max_bytes
) {
201 char fn2
[] = "/tmp/test-copy-file-XXXXXX";
202 char fn3
[] = "/tmp/test-copy-file-XXXXXX";
203 _cleanup_close_
int fd
= -1, fd2
= -1, fd3
= -1;
205 struct stat buf
, buf2
, buf3
;
207 log_info("%s try_reflink=%s max_bytes=%" PRIu64
, __func__
, yes_no(try_reflink
), max_bytes
);
209 fd
= open(src
, O_RDONLY
| O_CLOEXEC
| O_NOCTTY
);
212 fd2
= mkostemp_safe(fn2
);
215 fd3
= mkostemp_safe(fn3
);
218 r
= copy_bytes(fd
, fd2
, max_bytes
, try_reflink
? COPY_REFLINK
: 0);
219 if (max_bytes
== (uint64_t) -1)
222 assert_se(IN_SET(r
, 0, 1));
224 assert_se(fstat(fd
, &buf
) == 0);
225 assert_se(fstat(fd2
, &buf2
) == 0);
226 assert_se((uint64_t) buf2
.st_size
== MIN((uint64_t) buf
.st_size
, max_bytes
));
228 if (max_bytes
< (uint64_t) -1)
229 /* Make sure the file is now higher than max_bytes */
230 assert_se(ftruncate(fd2
, max_bytes
+ 1) == 0);
232 assert_se(lseek(fd2
, 0, SEEK_SET
) == 0);
234 r
= copy_bytes(fd2
, fd3
, max_bytes
, try_reflink
? COPY_REFLINK
: 0);
235 if (max_bytes
== (uint64_t) -1)
238 /* We cannot distinguish between the input being exactly max_bytes
239 * or longer than max_bytes (without trying to read one more byte,
240 * or calling stat, or FION_READ, etc, and we don't want to do any
241 * of that). So we expect "truncation" since we know that file we
242 * are copying is exactly max_bytes bytes. */
245 assert_se(fstat(fd3
, &buf3
) == 0);
247 if (max_bytes
== (uint64_t) -1)
248 assert_se(buf3
.st_size
== buf2
.st_size
);
250 assert_se((uint64_t) buf3
.st_size
== max_bytes
);
256 int main(int argc
, char *argv
[]) {
261 test_copy_bytes_regular_file(argv
[0], false, (uint64_t) -1);
262 test_copy_bytes_regular_file(argv
[0], true, (uint64_t) -1);
263 test_copy_bytes_regular_file(argv
[0], false, 1000); /* smaller than copy buffer size */
264 test_copy_bytes_regular_file(argv
[0], true, 1000);
265 test_copy_bytes_regular_file(argv
[0], false, 32000); /* larger than copy buffer size */
266 test_copy_bytes_regular_file(argv
[0], true, 32000);