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