]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/test/test-copy.c
Add SPDX license identifiers to source files under the LGPL
[thirdparty/systemd.git] / src / test / test-copy.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2 /***
3 This file is part of systemd
4
5 Copyright 2014 Ronny Chevalier
6
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.
11
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.
16
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/>.
19 ***/
20
21 #include <unistd.h>
22
23 #include "alloc-util.h"
24 #include "copy.h"
25 #include "fd-util.h"
26 #include "fileio.h"
27 #include "fs-util.h"
28 #include "log.h"
29 #include "macro.h"
30 #include "mkdir.h"
31 #include "path-util.h"
32 #include "rm-rf.h"
33 #include "string-util.h"
34 #include "strv.h"
35 #include "user-util.h"
36 #include "util.h"
37
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";
42 size_t sz = 0;
43 int fd;
44
45 log_info("%s", __func__);
46
47 fd = mkostemp_safe(fn);
48 assert_se(fd >= 0);
49 close(fd);
50
51 fd = mkostemp_safe(fn_copy);
52 assert_se(fd >= 0);
53 close(fd);
54
55 assert_se(write_string_file(fn, "foo bar bar bar foo", WRITE_STRING_FILE_CREATE) == 0);
56
57 assert_se(copy_file(fn, fn_copy, 0, 0644, 0, COPY_REFLINK) == 0);
58
59 assert_se(read_full_file(fn_copy, &buf, &sz) == 0);
60 assert_se(streq(buf, "foo bar bar bar foo\n"));
61 assert_se(sz == 20);
62
63 unlink(fn);
64 unlink(fn_copy);
65 }
66
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";
72 char buf[64] = {0};
73
74 log_info("%s", __func__);
75
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);
80
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);
85
86 assert_se(read(out_fd, buf, sizeof(buf)) == sizeof(text) - 1);
87 assert_se(streq(buf, text));
88
89 unlink(in_fn);
90 unlink(out_fn);
91 }
92
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");
99 char **p, **link;
100 const char *unixsockp;
101 struct stat st;
102
103 log_info("%s", __func__);
104
105 (void) rm_rf(copy_dir, REMOVE_ROOT|REMOVE_PHYSICAL);
106 (void) rm_rf(original_dir, REMOVE_ROOT|REMOVE_PHYSICAL);
107
108 STRV_FOREACH(p, files) {
109 _cleanup_free_ char *f;
110
111 assert_se((f = strappend(original_dir, *p)));
112
113 assert_se(mkdir_parents(f, 0755) >= 0);
114 assert_se(write_string_file(f, "file", WRITE_STRING_FILE_CREATE) == 0);
115 }
116
117 STRV_FOREACH_PAIR(link, p, links) {
118 _cleanup_free_ char *f, *l;
119
120 assert_se((f = strappend(original_dir, *p)));
121 assert_se((l = strappend(original_dir, *link)));
122
123 assert_se(mkdir_parents(l, 0755) >= 0);
124 assert_se(symlink(f, l) == 0);
125 }
126
127 unixsockp = strjoina(original_dir, "unixsock");
128 assert_se(mknod(unixsockp, S_IFSOCK|0644, 0) >= 0);
129
130 assert_se(copy_tree(original_dir, copy_dir, UID_INVALID, GID_INVALID, COPY_REFLINK|COPY_MERGE) == 0);
131
132 STRV_FOREACH(p, files) {
133 _cleanup_free_ char *buf = NULL, *f;
134 size_t sz = 0;
135
136 assert_se((f = strappend(copy_dir, *p)));
137
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"));
141 }
142
143 STRV_FOREACH_PAIR(link, p, links) {
144 _cleanup_free_ char *target = NULL, *f, *l;
145
146 assert_se((f = strjoin(original_dir, *p)));
147 assert_se((l = strjoin(copy_dir, *link)));
148
149 assert_se(readlink_and_canonicalize(l, NULL, &target) == 0);
150 assert_se(path_equal(f, target));
151 }
152
153 unixsockp = strjoina(copy_dir, "unixsock");
154 assert_se(stat(unixsockp, &st) >= 0);
155 assert_se(S_ISSOCK(st.st_mode));
156
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);
159
160 (void) rm_rf(copy_dir, REMOVE_ROOT|REMOVE_PHYSICAL);
161 (void) rm_rf(original_dir, REMOVE_ROOT|REMOVE_PHYSICAL);
162 }
163
164 static void test_copy_bytes(void) {
165 _cleanup_close_pair_ int pipefd[2] = {-1, -1};
166 _cleanup_close_ int infd = -1;
167 int r, r2;
168 char buf[1024], buf2[1024];
169
170 infd = open("/usr/lib/os-release", O_RDONLY|O_CLOEXEC);
171 if (infd < 0)
172 infd = open("/etc/os-release", O_RDONLY|O_CLOEXEC);
173 assert_se(infd >= 0);
174
175 assert_se(pipe2(pipefd, O_CLOEXEC) == 0);
176
177 r = copy_bytes(infd, pipefd[1], (uint64_t) -1, 0);
178 assert_se(r == 0);
179
180 r = read(pipefd[0], buf, sizeof(buf));
181 assert_se(r >= 0);
182
183 assert_se(lseek(infd, 0, SEEK_SET) == 0);
184 r2 = read(infd, buf2, sizeof(buf2));
185 assert_se(r == r2);
186
187 assert_se(strneq(buf, buf2, r));
188
189 /* test copy_bytes with invalid descriptors */
190 r = copy_bytes(pipefd[0], pipefd[0], 1, 0);
191 assert_se(r == -EBADF);
192
193 r = copy_bytes(pipefd[1], pipefd[1], 1, 0);
194 assert_se(r == -EBADF);
195
196 r = copy_bytes(pipefd[1], infd, 1, 0);
197 assert_se(r == -EBADF);
198 }
199
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;
204 int r;
205 struct stat buf, buf2, buf3;
206
207 log_info("%s try_reflink=%s max_bytes=%" PRIu64, __func__, yes_no(try_reflink), max_bytes);
208
209 fd = open(src, O_RDONLY | O_CLOEXEC | O_NOCTTY);
210 assert_se(fd >= 0);
211
212 fd2 = mkostemp_safe(fn2);
213 assert_se(fd2 >= 0);
214
215 fd3 = mkostemp_safe(fn3);
216 assert_se(fd3 >= 0);
217
218 r = copy_bytes(fd, fd2, max_bytes, try_reflink ? COPY_REFLINK : 0);
219 if (max_bytes == (uint64_t) -1)
220 assert_se(r == 0);
221 else
222 assert_se(IN_SET(r, 0, 1));
223
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));
227
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);
231
232 assert_se(lseek(fd2, 0, SEEK_SET) == 0);
233
234 r = copy_bytes(fd2, fd3, max_bytes, try_reflink ? COPY_REFLINK : 0);
235 if (max_bytes == (uint64_t) -1)
236 assert_se(r == 0);
237 else
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. */
243 assert_se(r == 1);
244
245 assert_se(fstat(fd3, &buf3) == 0);
246
247 if (max_bytes == (uint64_t) -1)
248 assert_se(buf3.st_size == buf2.st_size);
249 else
250 assert_se((uint64_t) buf3.st_size == max_bytes);
251
252 unlink(fn2);
253 unlink(fn3);
254 }
255
256 int main(int argc, char *argv[]) {
257 test_copy_file();
258 test_copy_file_fd();
259 test_copy_tree();
260 test_copy_bytes();
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);
267
268 return 0;
269 }