]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/test/test-fs-util.c
udev: use path_make_relative()
[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 "fd-util.h"
8 #include "fileio.h"
9 #include "fs-util.h"
10 #include "id128-util.h"
11 #include "macro.h"
12 #include "mkdir.h"
13 #include "path-util.h"
14 #include "rm-rf.h"
15 #include "stdio-util.h"
16 #include "string-util.h"
17 #include "strv.h"
18 #include "user-util.h"
19 #include "util.h"
20
21 static void test_chase_symlinks(void) {
22 _cleanup_free_ char *result = NULL;
23 char temp[] = "/tmp/test-chase.XXXXXX";
24 const char *top, *p, *pslash, *q, *qslash;
25 struct stat st;
26 int r, pfd;
27
28 assert_se(mkdtemp(temp));
29
30 top = strjoina(temp, "/top");
31 assert_se(mkdir(top, 0700) >= 0);
32
33 p = strjoina(top, "/dot");
34 assert_se(symlink(".", p) >= 0);
35
36 p = strjoina(top, "/dotdot");
37 assert_se(symlink("..", p) >= 0);
38
39 p = strjoina(top, "/dotdota");
40 assert_se(symlink("../a", p) >= 0);
41
42 p = strjoina(temp, "/a");
43 assert_se(symlink("b", p) >= 0);
44
45 p = strjoina(temp, "/b");
46 assert_se(symlink("/usr", p) >= 0);
47
48 p = strjoina(temp, "/start");
49 assert_se(symlink("top/dot/dotdota", p) >= 0);
50
51 /* Paths that use symlinks underneath the "root" */
52
53 r = chase_symlinks(p, NULL, 0, &result);
54 assert_se(r > 0);
55 assert_se(path_equal(result, "/usr"));
56 result = mfree(result);
57
58 pslash = strjoina(p, "/");
59 r = chase_symlinks(pslash, NULL, 0, &result);
60 assert_se(r > 0);
61 assert_se(path_equal(result, "/usr/"));
62 result = mfree(result);
63
64 r = chase_symlinks(p, temp, 0, &result);
65 assert_se(r == -ENOENT);
66
67 r = chase_symlinks(pslash, temp, 0, &result);
68 assert_se(r == -ENOENT);
69
70 q = strjoina(temp, "/usr");
71
72 r = chase_symlinks(p, temp, CHASE_NONEXISTENT, &result);
73 assert_se(r == 0);
74 assert_se(path_equal(result, q));
75 result = mfree(result);
76
77 qslash = strjoina(q, "/");
78
79 r = chase_symlinks(pslash, temp, CHASE_NONEXISTENT, &result);
80 assert_se(r == 0);
81 assert_se(path_equal(result, qslash));
82 result = mfree(result);
83
84 assert_se(mkdir(q, 0700) >= 0);
85
86 r = chase_symlinks(p, temp, 0, &result);
87 assert_se(r > 0);
88 assert_se(path_equal(result, q));
89 result = mfree(result);
90
91 r = chase_symlinks(pslash, temp, 0, &result);
92 assert_se(r > 0);
93 assert_se(path_equal(result, qslash));
94 result = mfree(result);
95
96 p = strjoina(temp, "/slash");
97 assert_se(symlink("/", p) >= 0);
98
99 r = chase_symlinks(p, NULL, 0, &result);
100 assert_se(r > 0);
101 assert_se(path_equal(result, "/"));
102 result = mfree(result);
103
104 r = chase_symlinks(p, temp, 0, &result);
105 assert_se(r > 0);
106 assert_se(path_equal(result, temp));
107 result = mfree(result);
108
109 /* Paths that would "escape" outside of the "root" */
110
111 p = strjoina(temp, "/6dots");
112 assert_se(symlink("../../..", p) >= 0);
113
114 r = chase_symlinks(p, temp, 0, &result);
115 assert_se(r > 0 && path_equal(result, temp));
116 result = mfree(result);
117
118 p = strjoina(temp, "/6dotsusr");
119 assert_se(symlink("../../../usr", p) >= 0);
120
121 r = chase_symlinks(p, temp, 0, &result);
122 assert_se(r > 0 && path_equal(result, q));
123 result = mfree(result);
124
125 p = strjoina(temp, "/top/8dotsusr");
126 assert_se(symlink("../../../../usr", p) >= 0);
127
128 r = chase_symlinks(p, temp, 0, &result);
129 assert_se(r > 0 && path_equal(result, q));
130 result = mfree(result);
131
132 /* Paths that contain repeated slashes */
133
134 p = strjoina(temp, "/slashslash");
135 assert_se(symlink("///usr///", p) >= 0);
136
137 r = chase_symlinks(p, NULL, 0, &result);
138 assert_se(r > 0);
139 assert_se(path_equal(result, "/usr"));
140 result = mfree(result);
141
142 r = chase_symlinks(p, temp, 0, &result);
143 assert_se(r > 0);
144 assert_se(path_equal(result, q));
145 result = mfree(result);
146
147 /* Paths using . */
148
149 r = chase_symlinks("/etc/./.././", NULL, 0, &result);
150 assert_se(r > 0);
151 assert_se(path_equal(result, "/"));
152 result = mfree(result);
153
154 r = chase_symlinks("/etc/./.././", "/etc", 0, &result);
155 assert_se(r > 0 && path_equal(result, "/etc"));
156 result = mfree(result);
157
158 r = chase_symlinks("/../.././//../../etc", NULL, 0, &result);
159 assert_se(r > 0);
160 assert_se(streq(result, "/etc"));
161 result = mfree(result);
162
163 r = chase_symlinks("/../.././//../../test-chase.fsldajfl", NULL, CHASE_NONEXISTENT, &result);
164 assert_se(r == 0);
165 assert_se(streq(result, "/test-chase.fsldajfl"));
166 result = mfree(result);
167
168 r = chase_symlinks("/../.././//../../etc", "/", CHASE_PREFIX_ROOT, &result);
169 assert_se(r > 0);
170 assert_se(streq(result, "/etc"));
171 result = mfree(result);
172
173 r = chase_symlinks("/../.././//../../test-chase.fsldajfl", "/", CHASE_PREFIX_ROOT|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/machine-id/foo", NULL, 0, &result);
179 assert_se(r == -ENOTDIR);
180 result = mfree(result);
181
182 /* Path that loops back to self */
183
184 p = strjoina(temp, "/recursive-symlink");
185 assert_se(symlink("recursive-symlink", p) >= 0);
186 r = chase_symlinks(p, NULL, 0, &result);
187 assert_se(r == -ELOOP);
188
189 /* Path which doesn't exist */
190
191 p = strjoina(temp, "/idontexist");
192 r = chase_symlinks(p, NULL, 0, &result);
193 assert_se(r == -ENOENT);
194
195 r = chase_symlinks(p, NULL, CHASE_NONEXISTENT, &result);
196 assert_se(r == 0);
197 assert_se(path_equal(result, p));
198 result = mfree(result);
199
200 p = strjoina(temp, "/idontexist/meneither");
201 r = chase_symlinks(p, NULL, 0, &result);
202 assert_se(r == -ENOENT);
203
204 r = chase_symlinks(p, NULL, CHASE_NONEXISTENT, &result);
205 assert_se(r == 0);
206 assert_se(path_equal(result, p));
207 result = mfree(result);
208
209 /* Path which doesn't exist, but contains weird stuff */
210
211 p = strjoina(temp, "/idontexist/..");
212 r = chase_symlinks(p, NULL, 0, &result);
213 assert_se(r == -ENOENT);
214
215 r = chase_symlinks(p, NULL, CHASE_NONEXISTENT, &result);
216 assert_se(r == -ENOENT);
217
218 p = strjoina(temp, "/target");
219 q = strjoina(temp, "/top");
220 assert_se(symlink(q, p) >= 0);
221 p = strjoina(temp, "/target/idontexist");
222 r = chase_symlinks(p, NULL, 0, &result);
223 assert_se(r == -ENOENT);
224
225 if (geteuid() == 0) {
226 p = strjoina(temp, "/priv1");
227 assert_se(mkdir(p, 0755) >= 0);
228
229 q = strjoina(p, "/priv2");
230 assert_se(mkdir(q, 0755) >= 0);
231
232 assert_se(chase_symlinks(q, NULL, CHASE_SAFE, NULL) >= 0);
233
234 assert_se(chown(q, UID_NOBODY, GID_NOBODY) >= 0);
235 assert_se(chase_symlinks(q, NULL, CHASE_SAFE, NULL) >= 0);
236
237 assert_se(chown(p, UID_NOBODY, GID_NOBODY) >= 0);
238 assert_se(chase_symlinks(q, NULL, CHASE_SAFE, NULL) >= 0);
239
240 assert_se(chown(q, 0, 0) >= 0);
241 assert_se(chase_symlinks(q, NULL, CHASE_SAFE, NULL) == -EPERM);
242
243 assert_se(rmdir(q) >= 0);
244 assert_se(symlink("/etc/passwd", q) >= 0);
245 assert_se(chase_symlinks(q, NULL, CHASE_SAFE, NULL) == -EPERM);
246
247 assert_se(chown(p, 0, 0) >= 0);
248 assert_se(chase_symlinks(q, NULL, CHASE_SAFE, NULL) >= 0);
249 }
250
251 p = strjoina(temp, "/machine-id-test");
252 assert_se(symlink("/usr/../etc/./machine-id", p) >= 0);
253
254 pfd = chase_symlinks(p, NULL, CHASE_OPEN, NULL);
255 if (pfd != -ENOENT) {
256 _cleanup_close_ int fd = -1;
257 sd_id128_t a, b;
258
259 assert_se(pfd >= 0);
260
261 fd = fd_reopen(pfd, O_RDONLY|O_CLOEXEC);
262 assert_se(fd >= 0);
263 safe_close(pfd);
264
265 assert_se(id128_read_fd(fd, ID128_PLAIN, &a) >= 0);
266 assert_se(sd_id128_get_machine(&b) >= 0);
267 assert_se(sd_id128_equal(a, b));
268 }
269
270 /* Test CHASE_NOFOLLOW */
271
272 p = strjoina(temp, "/target");
273 q = strjoina(temp, "/symlink");
274 assert_se(symlink(p, q) >= 0);
275 pfd = chase_symlinks(q, NULL, CHASE_OPEN|CHASE_NOFOLLOW, &result);
276 assert_se(pfd > 0);
277 assert_se(path_equal(result, q));
278 assert_se(fstat(pfd, &st) >= 0);
279 assert_se(S_ISLNK(st.st_mode));
280 result = mfree(result);
281
282 /* s1 -> s2 -> nonexistent */
283 q = strjoina(temp, "/s1");
284 assert_se(symlink("s2", q) >= 0);
285 p = strjoina(temp, "/s2");
286 assert_se(symlink("nonexistent", p) >= 0);
287 pfd = chase_symlinks(q, NULL, CHASE_OPEN|CHASE_NOFOLLOW, &result);
288 assert_se(pfd > 0);
289 assert_se(path_equal(result, q));
290 assert_se(fstat(pfd, &st) >= 0);
291 assert_se(S_ISLNK(st.st_mode));
292 result = mfree(result);
293
294 /* Test CHASE_ONE */
295
296 p = strjoina(temp, "/start");
297 r = chase_symlinks(p, NULL, CHASE_STEP, &result);
298 assert_se(r == 0);
299 p = strjoina(temp, "/top/dot/dotdota");
300 assert_se(streq(p, result));
301 result = mfree(result);
302
303 r = chase_symlinks(p, NULL, CHASE_STEP, &result);
304 assert_se(r == 0);
305 p = strjoina(temp, "/top/./dotdota");
306 assert_se(streq(p, result));
307 result = mfree(result);
308
309 r = chase_symlinks(p, NULL, CHASE_STEP, &result);
310 assert_se(r == 0);
311 p = strjoina(temp, "/top/../a");
312 assert_se(streq(p, result));
313 result = mfree(result);
314
315 r = chase_symlinks(p, NULL, CHASE_STEP, &result);
316 assert_se(r == 0);
317 p = strjoina(temp, "/a");
318 assert_se(streq(p, result));
319 result = mfree(result);
320
321 r = chase_symlinks(p, NULL, CHASE_STEP, &result);
322 assert_se(r == 0);
323 p = strjoina(temp, "/b");
324 assert_se(streq(p, result));
325 result = mfree(result);
326
327 r = chase_symlinks(p, NULL, CHASE_STEP, &result);
328 assert_se(r == 0);
329 assert_se(streq("/usr", result));
330 result = mfree(result);
331
332 r = chase_symlinks("/usr", NULL, CHASE_STEP, &result);
333 assert_se(r > 0);
334 assert_se(streq("/usr", result));
335 result = mfree(result);
336
337 assert_se(rm_rf(temp, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);
338 }
339
340 static void test_unlink_noerrno(void) {
341 char name[] = "/tmp/test-close_nointr.XXXXXX";
342 int fd;
343
344 fd = mkostemp_safe(name);
345 assert_se(fd >= 0);
346 assert_se(close_nointr(fd) >= 0);
347
348 {
349 PROTECT_ERRNO;
350 errno = -42;
351 assert_se(unlink_noerrno(name) >= 0);
352 assert_se(errno == -42);
353 assert_se(unlink_noerrno(name) < 0);
354 assert_se(errno == -42);
355 }
356 }
357
358 static void test_readlink_and_make_absolute(void) {
359 char tempdir[] = "/tmp/test-readlink_and_make_absolute";
360 char name[] = "/tmp/test-readlink_and_make_absolute/original";
361 char name2[] = "test-readlink_and_make_absolute/original";
362 char name_alias[] = "/tmp/test-readlink_and_make_absolute-alias";
363 char *r = NULL;
364 _cleanup_free_ char *pwd = NULL;
365
366 assert_se(mkdir_safe(tempdir, 0755, getuid(), getgid(), MKDIR_WARN_MODE) >= 0);
367 assert_se(touch(name) >= 0);
368
369 assert_se(symlink(name, name_alias) >= 0);
370 assert_se(readlink_and_make_absolute(name_alias, &r) >= 0);
371 assert_se(streq(r, name));
372 free(r);
373 assert_se(unlink(name_alias) >= 0);
374
375 assert_se(safe_getcwd(&pwd) >= 0);
376
377 assert_se(chdir(tempdir) >= 0);
378 assert_se(symlink(name2, name_alias) >= 0);
379 assert_se(readlink_and_make_absolute(name_alias, &r) >= 0);
380 assert_se(streq(r, name));
381 free(r);
382 assert_se(unlink(name_alias) >= 0);
383
384 assert_se(chdir(pwd) >= 0);
385
386 assert_se(rm_rf(tempdir, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);
387 }
388
389 static void test_get_files_in_directory(void) {
390 _cleanup_strv_free_ char **l = NULL, **t = NULL;
391
392 assert_se(get_files_in_directory("/tmp", &l) >= 0);
393 assert_se(get_files_in_directory(".", &t) >= 0);
394 assert_se(get_files_in_directory(".", NULL) >= 0);
395 }
396
397 static void test_var_tmp(void) {
398 _cleanup_free_ char *tmpdir_backup = NULL, *temp_backup = NULL, *tmp_backup = NULL;
399 const char *tmp_dir = NULL, *t;
400
401 t = getenv("TMPDIR");
402 if (t) {
403 tmpdir_backup = strdup(t);
404 assert_se(tmpdir_backup);
405 }
406
407 t = getenv("TEMP");
408 if (t) {
409 temp_backup = strdup(t);
410 assert_se(temp_backup);
411 }
412
413 t = getenv("TMP");
414 if (t) {
415 tmp_backup = strdup(t);
416 assert_se(tmp_backup);
417 }
418
419 assert_se(unsetenv("TMPDIR") >= 0);
420 assert_se(unsetenv("TEMP") >= 0);
421 assert_se(unsetenv("TMP") >= 0);
422
423 assert_se(var_tmp_dir(&tmp_dir) >= 0);
424 assert_se(streq(tmp_dir, "/var/tmp"));
425
426 assert_se(setenv("TMPDIR", "/tmp", true) >= 0);
427 assert_se(streq(getenv("TMPDIR"), "/tmp"));
428
429 assert_se(var_tmp_dir(&tmp_dir) >= 0);
430 assert_se(streq(tmp_dir, "/tmp"));
431
432 assert_se(setenv("TMPDIR", "/88_does_not_exist_88", true) >= 0);
433 assert_se(streq(getenv("TMPDIR"), "/88_does_not_exist_88"));
434
435 assert_se(var_tmp_dir(&tmp_dir) >= 0);
436 assert_se(streq(tmp_dir, "/var/tmp"));
437
438 if (tmpdir_backup) {
439 assert_se(setenv("TMPDIR", tmpdir_backup, true) >= 0);
440 assert_se(streq(getenv("TMPDIR"), tmpdir_backup));
441 }
442
443 if (temp_backup) {
444 assert_se(setenv("TEMP", temp_backup, true) >= 0);
445 assert_se(streq(getenv("TEMP"), temp_backup));
446 }
447
448 if (tmp_backup) {
449 assert_se(setenv("TMP", tmp_backup, true) >= 0);
450 assert_se(streq(getenv("TMP"), tmp_backup));
451 }
452 }
453
454 static void test_dot_or_dot_dot(void) {
455 assert_se(!dot_or_dot_dot(NULL));
456 assert_se(!dot_or_dot_dot(""));
457 assert_se(!dot_or_dot_dot("xxx"));
458 assert_se(dot_or_dot_dot("."));
459 assert_se(dot_or_dot_dot(".."));
460 assert_se(!dot_or_dot_dot(".foo"));
461 assert_se(!dot_or_dot_dot("..foo"));
462 }
463
464 static void test_access_fd(void) {
465 _cleanup_(rmdir_and_freep) char *p = NULL;
466 _cleanup_close_ int fd = -1;
467
468 assert_se(mkdtemp_malloc("/tmp/access-fd.XXXXXX", &p) >= 0);
469
470 fd = open(p, O_RDONLY|O_DIRECTORY|O_CLOEXEC);
471 assert_se(fd >= 0);
472
473 assert_se(access_fd(fd, R_OK) >= 0);
474 assert_se(access_fd(fd, F_OK) >= 0);
475 assert_se(access_fd(fd, W_OK) >= 0);
476
477 assert_se(fchmod(fd, 0000) >= 0);
478
479 assert_se(access_fd(fd, F_OK) >= 0);
480
481 if (geteuid() == 0) {
482 assert_se(access_fd(fd, R_OK) >= 0);
483 assert_se(access_fd(fd, W_OK) >= 0);
484 } else {
485 assert_se(access_fd(fd, R_OK) == -EACCES);
486 assert_se(access_fd(fd, W_OK) == -EACCES);
487 }
488 }
489
490 static void test_touch_file(void) {
491 uid_t test_uid, test_gid;
492 _cleanup_(rm_rf_physical_and_freep) char *p = NULL;
493 struct stat st;
494 const char *a;
495 usec_t test_mtime;
496
497 test_uid = geteuid() == 0 ? 65534 : getuid();
498 test_gid = geteuid() == 0 ? 65534 : getgid();
499
500 test_mtime = usec_sub_unsigned(now(CLOCK_REALTIME), USEC_PER_WEEK);
501
502 assert_se(mkdtemp_malloc("/dev/shm/touch-file-XXXXXX", &p) >= 0);
503
504 a = strjoina(p, "/regular");
505 assert_se(touch_file(a, false, test_mtime, test_uid, test_gid, 0640) >= 0);
506 assert_se(lstat(a, &st) >= 0);
507 assert_se(st.st_uid == test_uid);
508 assert_se(st.st_gid == test_gid);
509 assert_se(S_ISREG(st.st_mode));
510 assert_se((st.st_mode & 0777) == 0640);
511 assert_se(timespec_load(&st.st_mtim) == test_mtime);
512
513 a = strjoina(p, "/dir");
514 assert_se(mkdir(a, 0775) >= 0);
515 assert_se(touch_file(a, false, test_mtime, test_uid, test_gid, 0640) >= 0);
516 assert_se(lstat(a, &st) >= 0);
517 assert_se(st.st_uid == test_uid);
518 assert_se(st.st_gid == test_gid);
519 assert_se(S_ISDIR(st.st_mode));
520 assert_se((st.st_mode & 0777) == 0640);
521 assert_se(timespec_load(&st.st_mtim) == test_mtime);
522
523 a = strjoina(p, "/fifo");
524 assert_se(mkfifo(a, 0775) >= 0);
525 assert_se(touch_file(a, false, test_mtime, test_uid, test_gid, 0640) >= 0);
526 assert_se(lstat(a, &st) >= 0);
527 assert_se(st.st_uid == test_uid);
528 assert_se(st.st_gid == test_gid);
529 assert_se(S_ISFIFO(st.st_mode));
530 assert_se((st.st_mode & 0777) == 0640);
531 assert_se(timespec_load(&st.st_mtim) == test_mtime);
532
533 a = strjoina(p, "/sock");
534 assert_se(mknod(a, 0775 | S_IFSOCK, 0) >= 0);
535 assert_se(touch_file(a, false, test_mtime, test_uid, test_gid, 0640) >= 0);
536 assert_se(lstat(a, &st) >= 0);
537 assert_se(st.st_uid == test_uid);
538 assert_se(st.st_gid == test_gid);
539 assert_se(S_ISSOCK(st.st_mode));
540 assert_se((st.st_mode & 0777) == 0640);
541 assert_se(timespec_load(&st.st_mtim) == test_mtime);
542
543 if (geteuid() == 0) {
544 a = strjoina(p, "/cdev");
545 assert_se(mknod(a, 0775 | S_IFCHR, makedev(0, 0)) >= 0);
546 assert_se(touch_file(a, false, test_mtime, test_uid, test_gid, 0640) >= 0);
547 assert_se(lstat(a, &st) >= 0);
548 assert_se(st.st_uid == test_uid);
549 assert_se(st.st_gid == test_gid);
550 assert_se(S_ISCHR(st.st_mode));
551 assert_se((st.st_mode & 0777) == 0640);
552 assert_se(timespec_load(&st.st_mtim) == test_mtime);
553
554 a = strjoina(p, "/bdev");
555 assert_se(mknod(a, 0775 | S_IFBLK, makedev(0, 0)) >= 0);
556 assert_se(touch_file(a, false, test_mtime, test_uid, test_gid, 0640) >= 0);
557 assert_se(lstat(a, &st) >= 0);
558 assert_se(st.st_uid == test_uid);
559 assert_se(st.st_gid == test_gid);
560 assert_se(S_ISBLK(st.st_mode));
561 assert_se((st.st_mode & 0777) == 0640);
562 assert_se(timespec_load(&st.st_mtim) == test_mtime);
563 }
564
565 a = strjoina(p, "/lnk");
566 assert_se(symlink("target", a) >= 0);
567 assert_se(touch_file(a, false, test_mtime, test_uid, test_gid, 0640) >= 0);
568 assert_se(lstat(a, &st) >= 0);
569 assert_se(st.st_uid == test_uid);
570 assert_se(st.st_gid == test_gid);
571 assert_se(S_ISLNK(st.st_mode));
572 assert_se((st.st_mode & 0777) == 0640);
573 assert_se(timespec_load(&st.st_mtim) == test_mtime);
574 }
575
576 static void test_unlinkat_deallocate(void) {
577 _cleanup_free_ char *p = NULL;
578 _cleanup_close_ int fd = -1;
579 struct stat st;
580
581 assert_se(tempfn_random_child(NULL, "unlink-deallocation", &p) >= 0);
582
583 fd = open(p, O_WRONLY|O_CLOEXEC|O_CREAT|O_EXCL, 0600);
584 assert_se(fd >= 0);
585
586 assert_se(write(fd, "hallo\n", 6) == 6);
587
588 assert_se(fstat(fd, &st) >= 0);
589 assert_se(st.st_size == 6);
590 assert_se(st.st_blocks > 0);
591 assert_se(st.st_nlink == 1);
592
593 assert_se(unlinkat_deallocate(AT_FDCWD, p, 0) >= 0);
594
595 assert_se(fstat(fd, &st) >= 0);
596 assert_se(IN_SET(st.st_size, 0, 6)); /* depending on whether hole punching worked the size will be 6 (it worked) or 0 (we had to resort to truncation) */
597 assert_se(st.st_blocks == 0);
598 assert_se(st.st_nlink == 0);
599 }
600
601 static void test_fsync_directory_of_file(void) {
602 _cleanup_close_ int fd = -1;
603
604 fd = open_tmpfile_unlinkable(NULL, O_RDWR);
605 assert_se(fd >= 0);
606
607 assert_se(fsync_directory_of_file(fd) >= 0);
608 }
609
610 int main(int argc, char *argv[]) {
611 test_unlink_noerrno();
612 test_get_files_in_directory();
613 test_readlink_and_make_absolute();
614 test_var_tmp();
615 test_chase_symlinks();
616 test_dot_or_dot_dot();
617 test_access_fd();
618 test_touch_file();
619 test_unlinkat_deallocate();
620 test_fsync_directory_of_file();
621
622 return 0;
623 }