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