]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/test/test-fs-util.c
test-bpf: skip test when run inside containers
[thirdparty/systemd.git] / src / test / test-fs-util.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
c270684a
RC
2
3#include <unistd.h>
4
5#include "alloc-util.h"
c270684a
RC
6#include "fd-util.h"
7#include "fs-util.h"
1ed34d75 8#include "id128-util.h"
c270684a
RC
9#include "macro.h"
10#include "mkdir.h"
d944dc95 11#include "path-util.h"
c270684a 12#include "rm-rf.h"
1ed34d75 13#include "stdio-util.h"
c270684a
RC
14#include "string-util.h"
15#include "strv.h"
27964854 16#include "tests.h"
e4de7287 17#include "tmpfile-util.h"
f14f1806 18#include "user-util.h"
c270684a 19#include "util.h"
9590065f 20#include "virt.h"
c270684a 21
27964854
ZJS
22static const char *arg_test_dir = NULL;
23
d944dc95 24static void test_chase_symlinks(void) {
c9825701 25 _cleanup_free_ char *result = NULL;
27964854 26 char *temp;
b12d25a8 27 const char *top, *p, *pslash, *q, *qslash;
1f56e4ce 28 struct stat st;
1ed34d75 29 int r, pfd;
d944dc95 30
27964854
ZJS
31 log_info("/* %s */", __func__);
32
33 temp = strjoina(arg_test_dir ?: "/tmp", "/test-chase.XXXXXX");
d944dc95
LP
34 assert_se(mkdtemp(temp));
35
36 top = strjoina(temp, "/top");
37 assert_se(mkdir(top, 0700) >= 0);
38
39 p = strjoina(top, "/dot");
27964854
ZJS
40 if (symlink(".", p) < 0) {
41 assert_se(IN_SET(errno, EINVAL, ENOSYS, ENOTTY, EPERM));
42 log_tests_skipped_errno(errno, "symlink() not possible");
43 goto cleanup;
44 };
d944dc95
LP
45
46 p = strjoina(top, "/dotdot");
47 assert_se(symlink("..", p) >= 0);
48
49 p = strjoina(top, "/dotdota");
50 assert_se(symlink("../a", p) >= 0);
51
52 p = strjoina(temp, "/a");
53 assert_se(symlink("b", p) >= 0);
54
55 p = strjoina(temp, "/b");
56 assert_se(symlink("/usr", p) >= 0);
57
58 p = strjoina(temp, "/start");
59 assert_se(symlink("top/dot/dotdota", p) >= 0);
60
df878e68
ZJS
61 /* Paths that use symlinks underneath the "root" */
62
c4f4fce7 63 r = chase_symlinks(p, NULL, 0, &result);
a9fb0867 64 assert_se(r > 0);
d944dc95 65 assert_se(path_equal(result, "/usr"));
b12d25a8 66 result = mfree(result);
d944dc95 67
b12d25a8
ZJS
68 pslash = strjoina(p, "/");
69 r = chase_symlinks(pslash, NULL, 0, &result);
70 assert_se(r > 0);
71 assert_se(path_equal(result, "/usr/"));
d944dc95 72 result = mfree(result);
b12d25a8 73
c4f4fce7 74 r = chase_symlinks(p, temp, 0, &result);
d944dc95
LP
75 assert_se(r == -ENOENT);
76
b12d25a8
ZJS
77 r = chase_symlinks(pslash, temp, 0, &result);
78 assert_se(r == -ENOENT);
79
d944dc95 80 q = strjoina(temp, "/usr");
a9fb0867 81
cb638b5e 82 r = chase_symlinks(p, temp, CHASE_NONEXISTENT, &result);
a9fb0867
LP
83 assert_se(r == 0);
84 assert_se(path_equal(result, q));
b12d25a8 85 result = mfree(result);
a9fb0867 86
b12d25a8 87 qslash = strjoina(q, "/");
d944dc95 88
b12d25a8
ZJS
89 r = chase_symlinks(pslash, temp, CHASE_NONEXISTENT, &result);
90 assert_se(r == 0);
91 assert_se(path_equal(result, qslash));
f4b85a0f 92 result = mfree(result);
b12d25a8
ZJS
93
94 assert_se(mkdir(q, 0700) >= 0);
95
c4f4fce7 96 r = chase_symlinks(p, temp, 0, &result);
a9fb0867 97 assert_se(r > 0);
d944dc95 98 assert_se(path_equal(result, q));
b12d25a8
ZJS
99 result = mfree(result);
100
101 r = chase_symlinks(pslash, temp, 0, &result);
102 assert_se(r > 0);
103 assert_se(path_equal(result, qslash));
104 result = mfree(result);
d944dc95
LP
105
106 p = strjoina(temp, "/slash");
107 assert_se(symlink("/", p) >= 0);
108
c4f4fce7 109 r = chase_symlinks(p, NULL, 0, &result);
a9fb0867 110 assert_se(r > 0);
d944dc95 111 assert_se(path_equal(result, "/"));
d944dc95 112 result = mfree(result);
b12d25a8 113
c4f4fce7 114 r = chase_symlinks(p, temp, 0, &result);
a9fb0867 115 assert_se(r > 0);
d944dc95 116 assert_se(path_equal(result, temp));
b12d25a8 117 result = mfree(result);
d944dc95 118
df878e68
ZJS
119 /* Paths that would "escape" outside of the "root" */
120
121 p = strjoina(temp, "/6dots");
122 assert_se(symlink("../../..", p) >= 0);
123
c4f4fce7 124 r = chase_symlinks(p, temp, 0, &result);
a9fb0867 125 assert_se(r > 0 && path_equal(result, temp));
b12d25a8 126 result = mfree(result);
df878e68
ZJS
127
128 p = strjoina(temp, "/6dotsusr");
129 assert_se(symlink("../../../usr", p) >= 0);
130
c4f4fce7 131 r = chase_symlinks(p, temp, 0, &result);
a9fb0867 132 assert_se(r > 0 && path_equal(result, q));
b12d25a8 133 result = mfree(result);
df878e68
ZJS
134
135 p = strjoina(temp, "/top/8dotsusr");
136 assert_se(symlink("../../../../usr", p) >= 0);
137
c4f4fce7 138 r = chase_symlinks(p, temp, 0, &result);
a9fb0867 139 assert_se(r > 0 && path_equal(result, q));
b12d25a8 140 result = mfree(result);
df878e68
ZJS
141
142 /* Paths that contain repeated slashes */
143
d944dc95
LP
144 p = strjoina(temp, "/slashslash");
145 assert_se(symlink("///usr///", p) >= 0);
146
c4f4fce7 147 r = chase_symlinks(p, NULL, 0, &result);
a9fb0867 148 assert_se(r > 0);
d944dc95 149 assert_se(path_equal(result, "/usr"));
d944dc95 150 result = mfree(result);
b12d25a8 151
c4f4fce7 152 r = chase_symlinks(p, temp, 0, &result);
a9fb0867 153 assert_se(r > 0);
d944dc95 154 assert_se(path_equal(result, q));
b12d25a8 155 result = mfree(result);
d944dc95 156
2a2fe6ed
DM
157 /* Paths underneath the "root" with different UIDs while using CHASE_SAFE */
158
159 if (geteuid() == 0) {
160 p = strjoina(temp, "/user");
161 assert_se(mkdir(p, 0755) >= 0);
162 assert_se(chown(p, UID_NOBODY, GID_NOBODY) >= 0);
163
164 q = strjoina(temp, "/user/root");
165 assert_se(mkdir(q, 0755) >= 0);
166
167 p = strjoina(q, "/link");
168 assert_se(symlink("/", p) >= 0);
169
170 /* Fail when user-owned directories contain root-owned subdirectories. */
171 r = chase_symlinks(p, temp, CHASE_SAFE, &result);
172 assert_se(r == -ENOLINK);
173 result = mfree(result);
174
175 /* Allow this when the user-owned directories are all in the "root". */
176 r = chase_symlinks(p, q, CHASE_SAFE, &result);
177 assert_se(r > 0);
178 result = mfree(result);
179 }
180
df878e68
ZJS
181 /* Paths using . */
182
c4f4fce7 183 r = chase_symlinks("/etc/./.././", NULL, 0, &result);
a9fb0867 184 assert_se(r > 0);
d944dc95 185 assert_se(path_equal(result, "/"));
d944dc95 186 result = mfree(result);
b12d25a8 187
c4f4fce7 188 r = chase_symlinks("/etc/./.././", "/etc", 0, &result);
a9fb0867 189 assert_se(r > 0 && path_equal(result, "/etc"));
d944dc95 190 result = mfree(result);
b12d25a8 191
95f35ccc
YW
192 r = chase_symlinks("/../.././//../../etc", NULL, 0, &result);
193 assert_se(r > 0);
194 assert_se(streq(result, "/etc"));
195 result = mfree(result);
196
197 r = chase_symlinks("/../.././//../../test-chase.fsldajfl", NULL, CHASE_NONEXISTENT, &result);
198 assert_se(r == 0);
199 assert_se(streq(result, "/test-chase.fsldajfl"));
200 result = mfree(result);
201
202 r = chase_symlinks("/../.././//../../etc", "/", CHASE_PREFIX_ROOT, &result);
203 assert_se(r > 0);
204 assert_se(streq(result, "/etc"));
205 result = mfree(result);
206
207 r = chase_symlinks("/../.././//../../test-chase.fsldajfl", "/", CHASE_PREFIX_ROOT|CHASE_NONEXISTENT, &result);
208 assert_se(r == 0);
209 assert_se(streq(result, "/test-chase.fsldajfl"));
210 result = mfree(result);
211
c4f4fce7 212 r = chase_symlinks("/etc/machine-id/foo", NULL, 0, &result);
d944dc95 213 assert_se(r == -ENOTDIR);
b12d25a8 214 result = mfree(result);
d944dc95 215
df878e68
ZJS
216 /* Path that loops back to self */
217
d944dc95
LP
218 p = strjoina(temp, "/recursive-symlink");
219 assert_se(symlink("recursive-symlink", p) >= 0);
c4f4fce7 220 r = chase_symlinks(p, NULL, 0, &result);
d944dc95
LP
221 assert_se(r == -ELOOP);
222
a9fb0867
LP
223 /* Path which doesn't exist */
224
225 p = strjoina(temp, "/idontexist");
226 r = chase_symlinks(p, NULL, 0, &result);
227 assert_se(r == -ENOENT);
228
cb638b5e 229 r = chase_symlinks(p, NULL, CHASE_NONEXISTENT, &result);
a9fb0867
LP
230 assert_se(r == 0);
231 assert_se(path_equal(result, p));
232 result = mfree(result);
233
234 p = strjoina(temp, "/idontexist/meneither");
235 r = chase_symlinks(p, NULL, 0, &result);
236 assert_se(r == -ENOENT);
237
cb638b5e 238 r = chase_symlinks(p, NULL, CHASE_NONEXISTENT, &result);
a9fb0867
LP
239 assert_se(r == 0);
240 assert_se(path_equal(result, p));
241 result = mfree(result);
242
243 /* Path which doesn't exist, but contains weird stuff */
244
245 p = strjoina(temp, "/idontexist/..");
246 r = chase_symlinks(p, NULL, 0, &result);
247 assert_se(r == -ENOENT);
248
cb638b5e 249 r = chase_symlinks(p, NULL, CHASE_NONEXISTENT, &result);
a9fb0867
LP
250 assert_se(r == -ENOENT);
251
877777d7
CCW
252 p = strjoina(temp, "/target");
253 q = strjoina(temp, "/top");
254 assert_se(symlink(q, p) >= 0);
255 p = strjoina(temp, "/target/idontexist");
256 r = chase_symlinks(p, NULL, 0, &result);
257 assert_se(r == -ENOENT);
258
f14f1806
LP
259 if (geteuid() == 0) {
260 p = strjoina(temp, "/priv1");
261 assert_se(mkdir(p, 0755) >= 0);
262
263 q = strjoina(p, "/priv2");
264 assert_se(mkdir(q, 0755) >= 0);
265
266 assert_se(chase_symlinks(q, NULL, CHASE_SAFE, NULL) >= 0);
267
268 assert_se(chown(q, UID_NOBODY, GID_NOBODY) >= 0);
269 assert_se(chase_symlinks(q, NULL, CHASE_SAFE, NULL) >= 0);
270
271 assert_se(chown(p, UID_NOBODY, GID_NOBODY) >= 0);
272 assert_se(chase_symlinks(q, NULL, CHASE_SAFE, NULL) >= 0);
273
274 assert_se(chown(q, 0, 0) >= 0);
36c97dec 275 assert_se(chase_symlinks(q, NULL, CHASE_SAFE, NULL) == -ENOLINK);
f14f1806
LP
276
277 assert_se(rmdir(q) >= 0);
278 assert_se(symlink("/etc/passwd", q) >= 0);
36c97dec 279 assert_se(chase_symlinks(q, NULL, CHASE_SAFE, NULL) == -ENOLINK);
f14f1806
LP
280
281 assert_se(chown(p, 0, 0) >= 0);
282 assert_se(chase_symlinks(q, NULL, CHASE_SAFE, NULL) >= 0);
283 }
284
1ed34d75
LP
285 p = strjoina(temp, "/machine-id-test");
286 assert_se(symlink("/usr/../etc/./machine-id", p) >= 0);
287
288 pfd = chase_symlinks(p, NULL, CHASE_OPEN, NULL);
289 if (pfd != -ENOENT) {
1ed34d75
LP
290 _cleanup_close_ int fd = -1;
291 sd_id128_t a, b;
292
293 assert_se(pfd >= 0);
294
f2324783 295 fd = fd_reopen(pfd, O_RDONLY|O_CLOEXEC);
1ed34d75 296 assert_se(fd >= 0);
1ed34d75
LP
297 safe_close(pfd);
298
299 assert_se(id128_read_fd(fd, ID128_PLAIN, &a) >= 0);
300 assert_se(sd_id128_get_machine(&b) >= 0);
301 assert_se(sd_id128_equal(a, b));
302 }
303
1f56e4ce
FB
304 /* Test CHASE_NOFOLLOW */
305
306 p = strjoina(temp, "/target");
307 q = strjoina(temp, "/symlink");
308 assert_se(symlink(p, q) >= 0);
309 pfd = chase_symlinks(q, NULL, CHASE_OPEN|CHASE_NOFOLLOW, &result);
310 assert_se(pfd > 0);
311 assert_se(path_equal(result, q));
312 assert_se(fstat(pfd, &st) >= 0);
313 assert_se(S_ISLNK(st.st_mode));
314 result = mfree(result);
315
316 /* s1 -> s2 -> nonexistent */
317 q = strjoina(temp, "/s1");
318 assert_se(symlink("s2", q) >= 0);
319 p = strjoina(temp, "/s2");
320 assert_se(symlink("nonexistent", p) >= 0);
321 pfd = chase_symlinks(q, NULL, CHASE_OPEN|CHASE_NOFOLLOW, &result);
322 assert_se(pfd > 0);
323 assert_se(path_equal(result, q));
324 assert_se(fstat(pfd, &st) >= 0);
325 assert_se(S_ISLNK(st.st_mode));
326 result = mfree(result);
327
49eb3659
LP
328 /* Test CHASE_ONE */
329
330 p = strjoina(temp, "/start");
331 r = chase_symlinks(p, NULL, CHASE_STEP, &result);
332 assert_se(r == 0);
333 p = strjoina(temp, "/top/dot/dotdota");
334 assert_se(streq(p, result));
335 result = mfree(result);
336
337 r = chase_symlinks(p, NULL, CHASE_STEP, &result);
338 assert_se(r == 0);
339 p = strjoina(temp, "/top/./dotdota");
340 assert_se(streq(p, result));
341 result = mfree(result);
342
343 r = chase_symlinks(p, NULL, CHASE_STEP, &result);
344 assert_se(r == 0);
345 p = strjoina(temp, "/top/../a");
346 assert_se(streq(p, result));
347 result = mfree(result);
348
349 r = chase_symlinks(p, NULL, CHASE_STEP, &result);
350 assert_se(r == 0);
351 p = strjoina(temp, "/a");
352 assert_se(streq(p, result));
353 result = mfree(result);
354
355 r = chase_symlinks(p, NULL, CHASE_STEP, &result);
356 assert_se(r == 0);
357 p = strjoina(temp, "/b");
358 assert_se(streq(p, result));
359 result = mfree(result);
360
361 r = chase_symlinks(p, NULL, CHASE_STEP, &result);
362 assert_se(r == 0);
363 assert_se(streq("/usr", result));
364 result = mfree(result);
365
366 r = chase_symlinks("/usr", NULL, CHASE_STEP, &result);
367 assert_se(r > 0);
368 assert_se(streq("/usr", result));
369 result = mfree(result);
370
27964854 371 cleanup:
d944dc95
LP
372 assert_se(rm_rf(temp, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);
373}
374
c270684a 375static void test_unlink_noerrno(void) {
27964854 376 char *name;
c270684a
RC
377 int fd;
378
27964854
ZJS
379 log_info("/* %s */", __func__);
380
381 name = strjoina(arg_test_dir ?: "/tmp", "/test-close_nointr.XXXXXX");
646853bd 382 fd = mkostemp_safe(name);
c270684a
RC
383 assert_se(fd >= 0);
384 assert_se(close_nointr(fd) >= 0);
385
386 {
387 PROTECT_ERRNO;
840f606d 388 errno = 42;
c270684a 389 assert_se(unlink_noerrno(name) >= 0);
840f606d 390 assert_se(errno == 42);
c270684a 391 assert_se(unlink_noerrno(name) < 0);
840f606d 392 assert_se(errno == 42);
c270684a
RC
393 }
394}
395
396static void test_readlink_and_make_absolute(void) {
27964854
ZJS
397 const char *tempdir, *name, *name2, *name_alias;
398 _cleanup_free_ char *r1 = NULL, *r2 = NULL, *pwd = NULL;
399
400 log_info("/* %s */", __func__);
401
402 tempdir = strjoina(arg_test_dir ?: "/tmp", "/test-readlink_and_make_absolute");
403 name = strjoina(tempdir, "/original");
404 name2 = "test-readlink_and_make_absolute/original";
405 name_alias = strjoina(arg_test_dir ?: "/tmp", "/test-readlink_and_make_absolute-alias");
c270684a 406
37c1d5e9 407 assert_se(mkdir_safe(tempdir, 0755, getuid(), getgid(), MKDIR_WARN_MODE) >= 0);
c270684a
RC
408 assert_se(touch(name) >= 0);
409
27964854
ZJS
410 if (symlink(name, name_alias) < 0) {
411 assert_se(IN_SET(errno, EINVAL, ENOSYS, ENOTTY, EPERM));
412 log_tests_skipped_errno(errno, "symlink() not possible");
413 } else {
414 assert_se(readlink_and_make_absolute(name_alias, &r1) >= 0);
415 assert_se(streq(r1, name));
416 assert_se(unlink(name_alias) >= 0);
c270684a 417
27964854 418 assert_se(safe_getcwd(&pwd) >= 0);
cd76d4c2 419
27964854
ZJS
420 assert_se(chdir(tempdir) >= 0);
421 assert_se(symlink(name2, name_alias) >= 0);
422 assert_se(readlink_and_make_absolute(name_alias, &r2) >= 0);
423 assert_se(streq(r2, name));
424 assert_se(unlink(name_alias) >= 0);
c270684a 425
27964854
ZJS
426 assert_se(chdir(pwd) >= 0);
427 }
cd76d4c2 428
c270684a
RC
429 assert_se(rm_rf(tempdir, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);
430}
431
432static void test_get_files_in_directory(void) {
433 _cleanup_strv_free_ char **l = NULL, **t = NULL;
434
27964854 435 assert_se(get_files_in_directory(arg_test_dir ?: "/tmp", &l) >= 0);
c270684a
RC
436 assert_se(get_files_in_directory(".", &t) >= 0);
437 assert_se(get_files_in_directory(".", NULL) >= 0);
438}
439
34a8f081 440static void test_var_tmp(void) {
4245eb50 441 _cleanup_free_ char *tmpdir_backup = NULL, *temp_backup = NULL, *tmp_backup = NULL;
992e8f22 442 const char *tmp_dir = NULL, *t;
34a8f081 443
27964854
ZJS
444 log_info("/* %s */", __func__);
445
992e8f22
LP
446 t = getenv("TMPDIR");
447 if (t) {
448 tmpdir_backup = strdup(t);
449 assert_se(tmpdir_backup);
450 }
34a8f081 451
4245eb50
MAP
452 t = getenv("TEMP");
453 if (t) {
454 temp_backup = strdup(t);
455 assert_se(temp_backup);
456 }
457
458 t = getenv("TMP");
459 if (t) {
460 tmp_backup = strdup(t);
461 assert_se(tmp_backup);
462 }
463
85e55d14
YW
464 assert_se(unsetenv("TMPDIR") >= 0);
465 assert_se(unsetenv("TEMP") >= 0);
466 assert_se(unsetenv("TMP") >= 0);
34a8f081 467
992e8f22
LP
468 assert_se(var_tmp_dir(&tmp_dir) >= 0);
469 assert_se(streq(tmp_dir, "/var/tmp"));
34a8f081 470
992e8f22
LP
471 assert_se(setenv("TMPDIR", "/tmp", true) >= 0);
472 assert_se(streq(getenv("TMPDIR"), "/tmp"));
34a8f081 473
992e8f22
LP
474 assert_se(var_tmp_dir(&tmp_dir) >= 0);
475 assert_se(streq(tmp_dir, "/tmp"));
34a8f081 476
992e8f22
LP
477 assert_se(setenv("TMPDIR", "/88_does_not_exist_88", true) >= 0);
478 assert_se(streq(getenv("TMPDIR"), "/88_does_not_exist_88"));
34a8f081 479
992e8f22
LP
480 assert_se(var_tmp_dir(&tmp_dir) >= 0);
481 assert_se(streq(tmp_dir, "/var/tmp"));
34a8f081 482
992e8f22
LP
483 if (tmpdir_backup) {
484 assert_se(setenv("TMPDIR", tmpdir_backup, true) >= 0);
485 assert_se(streq(getenv("TMPDIR"), tmpdir_backup));
34a8f081 486 }
4245eb50
MAP
487
488 if (temp_backup) {
489 assert_se(setenv("TEMP", temp_backup, true) >= 0);
490 assert_se(streq(getenv("TEMP"), temp_backup));
491 }
492
493 if (tmp_backup) {
494 assert_se(setenv("TMP", tmp_backup, true) >= 0);
495 assert_se(streq(getenv("TMP"), tmp_backup));
496 }
34a8f081
OW
497}
498
49bfc877 499static void test_dot_or_dot_dot(void) {
27964854
ZJS
500 log_info("/* %s */", __func__);
501
49bfc877
LP
502 assert_se(!dot_or_dot_dot(NULL));
503 assert_se(!dot_or_dot_dot(""));
504 assert_se(!dot_or_dot_dot("xxx"));
505 assert_se(dot_or_dot_dot("."));
506 assert_se(dot_or_dot_dot(".."));
507 assert_se(!dot_or_dot_dot(".foo"));
508 assert_se(!dot_or_dot_dot("..foo"));
509}
510
57a4359e
LP
511static void test_access_fd(void) {
512 _cleanup_(rmdir_and_freep) char *p = NULL;
513 _cleanup_close_ int fd = -1;
27964854 514 const char *a;
57a4359e 515
27964854
ZJS
516 log_info("/* %s */", __func__);
517
518 a = strjoina(arg_test_dir ?: "/tmp", "/access-fd.XXXXXX");
519 assert_se(mkdtemp_malloc(a, &p) >= 0);
57a4359e
LP
520
521 fd = open(p, O_RDONLY|O_DIRECTORY|O_CLOEXEC);
522 assert_se(fd >= 0);
523
524 assert_se(access_fd(fd, R_OK) >= 0);
525 assert_se(access_fd(fd, F_OK) >= 0);
526 assert_se(access_fd(fd, W_OK) >= 0);
527
528 assert_se(fchmod(fd, 0000) >= 0);
529
530 assert_se(access_fd(fd, F_OK) >= 0);
531
532 if (geteuid() == 0) {
533 assert_se(access_fd(fd, R_OK) >= 0);
534 assert_se(access_fd(fd, W_OK) >= 0);
535 } else {
536 assert_se(access_fd(fd, R_OK) == -EACCES);
537 assert_se(access_fd(fd, W_OK) == -EACCES);
538 }
539}
540
9e3fa6e8
LP
541static void test_touch_file(void) {
542 uid_t test_uid, test_gid;
543 _cleanup_(rm_rf_physical_and_freep) char *p = NULL;
544 struct stat st;
545 const char *a;
546 usec_t test_mtime;
9590065f 547 int r;
9e3fa6e8 548
27964854
ZJS
549 log_info("/* %s */", __func__);
550
9e3fa6e8
LP
551 test_uid = geteuid() == 0 ? 65534 : getuid();
552 test_gid = geteuid() == 0 ? 65534 : getgid();
553
554 test_mtime = usec_sub_unsigned(now(CLOCK_REALTIME), USEC_PER_WEEK);
555
27964854
ZJS
556 a = strjoina(arg_test_dir ?: "/dev/shm", "/touch-file-XXXXXX");
557 assert_se(mkdtemp_malloc(a, &p) >= 0);
9e3fa6e8
LP
558
559 a = strjoina(p, "/regular");
27964854
ZJS
560 r = touch_file(a, false, test_mtime, test_uid, test_gid, 0640);
561 if (r < 0) {
562 assert_se(IN_SET(r, -EINVAL, -ENOSYS, -ENOTTY, -EPERM));
563 log_tests_skipped_errno(errno, "touch_file() not possible");
564 return;
565 }
566
9e3fa6e8
LP
567 assert_se(lstat(a, &st) >= 0);
568 assert_se(st.st_uid == test_uid);
569 assert_se(st.st_gid == test_gid);
570 assert_se(S_ISREG(st.st_mode));
571 assert_se((st.st_mode & 0777) == 0640);
572 assert_se(timespec_load(&st.st_mtim) == test_mtime);
573
574 a = strjoina(p, "/dir");
575 assert_se(mkdir(a, 0775) >= 0);
576 assert_se(touch_file(a, false, test_mtime, test_uid, test_gid, 0640) >= 0);
577 assert_se(lstat(a, &st) >= 0);
578 assert_se(st.st_uid == test_uid);
579 assert_se(st.st_gid == test_gid);
580 assert_se(S_ISDIR(st.st_mode));
581 assert_se((st.st_mode & 0777) == 0640);
582 assert_se(timespec_load(&st.st_mtim) == test_mtime);
583
584 a = strjoina(p, "/fifo");
585 assert_se(mkfifo(a, 0775) >= 0);
586 assert_se(touch_file(a, false, test_mtime, test_uid, test_gid, 0640) >= 0);
587 assert_se(lstat(a, &st) >= 0);
588 assert_se(st.st_uid == test_uid);
589 assert_se(st.st_gid == test_gid);
590 assert_se(S_ISFIFO(st.st_mode));
591 assert_se((st.st_mode & 0777) == 0640);
592 assert_se(timespec_load(&st.st_mtim) == test_mtime);
593
594 a = strjoina(p, "/sock");
595 assert_se(mknod(a, 0775 | S_IFSOCK, 0) >= 0);
596 assert_se(touch_file(a, false, test_mtime, test_uid, test_gid, 0640) >= 0);
597 assert_se(lstat(a, &st) >= 0);
598 assert_se(st.st_uid == test_uid);
599 assert_se(st.st_gid == test_gid);
600 assert_se(S_ISSOCK(st.st_mode));
601 assert_se((st.st_mode & 0777) == 0640);
602 assert_se(timespec_load(&st.st_mtim) == test_mtime);
603
604 if (geteuid() == 0) {
605 a = strjoina(p, "/cdev");
9590065f
YW
606 r = mknod(a, 0775 | S_IFCHR, makedev(0, 0));
607 if (r < 0 && errno == EPERM && detect_container() > 0) {
608 log_notice("Running in unprivileged container? Skipping remaining tests in %s", __func__);
609 return;
610 }
611 assert_se(r >= 0);
9e3fa6e8
LP
612 assert_se(touch_file(a, false, test_mtime, test_uid, test_gid, 0640) >= 0);
613 assert_se(lstat(a, &st) >= 0);
614 assert_se(st.st_uid == test_uid);
615 assert_se(st.st_gid == test_gid);
616 assert_se(S_ISCHR(st.st_mode));
617 assert_se((st.st_mode & 0777) == 0640);
618 assert_se(timespec_load(&st.st_mtim) == test_mtime);
619
620 a = strjoina(p, "/bdev");
621 assert_se(mknod(a, 0775 | S_IFBLK, makedev(0, 0)) >= 0);
622 assert_se(touch_file(a, false, test_mtime, test_uid, test_gid, 0640) >= 0);
623 assert_se(lstat(a, &st) >= 0);
624 assert_se(st.st_uid == test_uid);
625 assert_se(st.st_gid == test_gid);
626 assert_se(S_ISBLK(st.st_mode));
627 assert_se((st.st_mode & 0777) == 0640);
628 assert_se(timespec_load(&st.st_mtim) == test_mtime);
629 }
630
631 a = strjoina(p, "/lnk");
632 assert_se(symlink("target", a) >= 0);
633 assert_se(touch_file(a, false, test_mtime, test_uid, test_gid, 0640) >= 0);
634 assert_se(lstat(a, &st) >= 0);
635 assert_se(st.st_uid == test_uid);
636 assert_se(st.st_gid == test_gid);
637 assert_se(S_ISLNK(st.st_mode));
638 assert_se((st.st_mode & 0777) == 0640);
639 assert_se(timespec_load(&st.st_mtim) == test_mtime);
640}
641
43767d9d
LP
642static void test_unlinkat_deallocate(void) {
643 _cleanup_free_ char *p = NULL;
644 _cleanup_close_ int fd = -1;
645 struct stat st;
646
27964854
ZJS
647 log_info("/* %s */", __func__);
648
649 assert_se(tempfn_random_child(arg_test_dir, "unlink-deallocation", &p) >= 0);
43767d9d
LP
650
651 fd = open(p, O_WRONLY|O_CLOEXEC|O_CREAT|O_EXCL, 0600);
652 assert_se(fd >= 0);
653
654 assert_se(write(fd, "hallo\n", 6) == 6);
655
656 assert_se(fstat(fd, &st) >= 0);
657 assert_se(st.st_size == 6);
658 assert_se(st.st_blocks > 0);
659 assert_se(st.st_nlink == 1);
660
661 assert_se(unlinkat_deallocate(AT_FDCWD, p, 0) >= 0);
662
663 assert_se(fstat(fd, &st) >= 0);
27964854
ZJS
664 assert_se(IN_SET(st.st_size, 0, 6)); /* depending on whether hole punching worked the size will be 6
665 (it worked) or 0 (we had to resort to truncation) */
43767d9d
LP
666 assert_se(st.st_blocks == 0);
667 assert_se(st.st_nlink == 0);
668}
669
11b29a96
LP
670static void test_fsync_directory_of_file(void) {
671 _cleanup_close_ int fd = -1;
672
27964854
ZJS
673 log_info("/* %s */", __func__);
674
675 fd = open_tmpfile_unlinkable(arg_test_dir, O_RDWR);
11b29a96
LP
676 assert_se(fd >= 0);
677
678 assert_se(fsync_directory_of_file(fd) >= 0);
679}
680
4a5d7761 681static void test_rename_noreplace(void) {
7158b4b3 682 static const char* const table[] = {
4a5d7761
LP
683 "/reg",
684 "/dir",
685 "/fifo",
686 "/socket",
687 "/symlink",
688 NULL
689 };
690
691 _cleanup_(rm_rf_physical_and_freep) char *z = NULL;
27964854 692 const char *j = NULL;
4a5d7761
LP
693 char **a, **b;
694
27964854
ZJS
695 log_info("/* %s */", __func__);
696
697 if (arg_test_dir)
698 j = strjoina(arg_test_dir, "/testXXXXXX");
7158b4b3 699 assert_se(mkdtemp_malloc(j, &z) >= 0);
4a5d7761
LP
700
701 j = strjoina(z, table[0]);
702 assert_se(touch(j) >= 0);
703
704 j = strjoina(z, table[1]);
705 assert_se(mkdir(j, 0777) >= 0);
706
707 j = strjoina(z, table[2]);
708 (void) mkfifo(j, 0777);
709
710 j = strjoina(z, table[3]);
711 (void) mknod(j, S_IFSOCK | 0777, 0);
712
713 j = strjoina(z, table[4]);
714 (void) symlink("foobar", j);
715
716 STRV_FOREACH(a, (char**) table) {
717 _cleanup_free_ char *x = NULL, *y = NULL;
718
719 x = strjoin(z, *a);
720 assert_se(x);
721
722 if (access(x, F_OK) < 0) {
723 assert_se(errno == ENOENT);
724 continue;
725 }
726
727 STRV_FOREACH(b, (char**) table) {
b81b9d40 728 _cleanup_free_ char *w = NULL;
4a5d7761 729
b81b9d40
YW
730 w = strjoin(w, *b);
731 assert_se(w);
732
733 if (access(w, F_OK) < 0) {
4a5d7761
LP
734 assert_se(errno == ENOENT);
735 continue;
736 }
737
b81b9d40 738 assert_se(rename_noreplace(AT_FDCWD, w, AT_FDCWD, y) == -EEXIST);
4a5d7761
LP
739 }
740
741 y = strjoin(z, "/somethingelse");
742 assert_se(y);
743
744 assert_se(rename_noreplace(AT_FDCWD, x, AT_FDCWD, y) >= 0);
745 assert_se(rename_noreplace(AT_FDCWD, y, AT_FDCWD, x) >= 0);
746 }
747}
748
c270684a 749int main(int argc, char *argv[]) {
27964854
ZJS
750 test_setup_logging(LOG_INFO);
751
752 arg_test_dir = argv[1];
753
c270684a 754 test_unlink_noerrno();
c270684a 755 test_get_files_in_directory();
496c486f 756 test_readlink_and_make_absolute();
34a8f081 757 test_var_tmp();
d944dc95 758 test_chase_symlinks();
49bfc877 759 test_dot_or_dot_dot();
57a4359e 760 test_access_fd();
9e3fa6e8 761 test_touch_file();
43767d9d 762 test_unlinkat_deallocate();
11b29a96 763 test_fsync_directory_of_file();
4a5d7761 764 test_rename_noreplace();
c270684a
RC
765
766 return 0;
767}