1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
6 #include "alloc-util.h"
8 #include "dirent-util.h"
14 #include "path-util.h"
15 #include "process-util.h"
16 #include "random-util.h"
18 #include "stat-util.h"
19 #include "stdio-util.h"
20 #include "string-util.h"
22 #include "sync-util.h"
24 #include "tmpfile-util.h"
25 #include "umask-util.h"
26 #include "user-util.h"
29 static const char *arg_test_dir
= NULL
;
31 TEST(readlink_and_make_absolute
) {
32 const char *tempdir
, *name
, *name2
, *name_alias
;
33 _cleanup_free_
char *r1
= NULL
, *r2
= NULL
, *pwd
= NULL
;
35 tempdir
= strjoina(arg_test_dir
?: "/tmp", "/test-readlink_and_make_absolute");
36 name
= strjoina(tempdir
, "/original");
37 name2
= "test-readlink_and_make_absolute/original";
38 name_alias
= strjoina(arg_test_dir
?: "/tmp", "/test-readlink_and_make_absolute-alias");
40 assert_se(mkdir_safe(tempdir
, 0755, getuid(), getgid(), MKDIR_WARN_MODE
) >= 0);
41 assert_se(touch(name
) >= 0);
43 if (symlink(name
, name_alias
) < 0) {
44 assert_se(IN_SET(errno
, EINVAL
, ENOSYS
, ENOTTY
, EPERM
));
45 log_tests_skipped_errno(errno
, "symlink() not possible");
47 assert_se(readlink_and_make_absolute(name_alias
, &r1
) >= 0);
48 assert_se(streq(r1
, name
));
49 assert_se(unlink(name_alias
) >= 0);
51 assert_se(safe_getcwd(&pwd
) >= 0);
53 assert_se(chdir(tempdir
) >= 0);
54 assert_se(symlink(name2
, name_alias
) >= 0);
55 assert_se(readlink_and_make_absolute(name_alias
, &r2
) >= 0);
56 assert_se(streq(r2
, name
));
57 assert_se(unlink(name_alias
) >= 0);
59 assert_se(chdir(pwd
) >= 0);
62 assert_se(rm_rf(tempdir
, REMOVE_ROOT
|REMOVE_PHYSICAL
) >= 0);
65 TEST(get_files_in_directory
) {
66 _cleanup_strv_free_
char **l
= NULL
, **t
= NULL
;
68 assert_se(get_files_in_directory(arg_test_dir
?: "/tmp", &l
) >= 0);
69 assert_se(get_files_in_directory(".", &t
) >= 0);
70 assert_se(get_files_in_directory(".", NULL
) >= 0);
74 _cleanup_free_
char *tmpdir_backup
= NULL
, *temp_backup
= NULL
, *tmp_backup
= NULL
;
75 const char *tmp_dir
= NULL
, *t
;
79 tmpdir_backup
= strdup(t
);
80 assert_se(tmpdir_backup
);
85 temp_backup
= strdup(t
);
86 assert_se(temp_backup
);
91 tmp_backup
= strdup(t
);
92 assert_se(tmp_backup
);
95 assert_se(unsetenv("TMPDIR") >= 0);
96 assert_se(unsetenv("TEMP") >= 0);
97 assert_se(unsetenv("TMP") >= 0);
99 assert_se(var_tmp_dir(&tmp_dir
) >= 0);
100 assert_se(streq(tmp_dir
, "/var/tmp"));
102 assert_se(setenv("TMPDIR", "/tmp", true) >= 0);
103 assert_se(streq(getenv("TMPDIR"), "/tmp"));
105 assert_se(var_tmp_dir(&tmp_dir
) >= 0);
106 assert_se(streq(tmp_dir
, "/tmp"));
108 assert_se(setenv("TMPDIR", "/88_does_not_exist_88", true) >= 0);
109 assert_se(streq(getenv("TMPDIR"), "/88_does_not_exist_88"));
111 assert_se(var_tmp_dir(&tmp_dir
) >= 0);
112 assert_se(streq(tmp_dir
, "/var/tmp"));
115 assert_se(setenv("TMPDIR", tmpdir_backup
, true) >= 0);
116 assert_se(streq(getenv("TMPDIR"), tmpdir_backup
));
120 assert_se(setenv("TEMP", temp_backup
, true) >= 0);
121 assert_se(streq(getenv("TEMP"), temp_backup
));
125 assert_se(setenv("TMP", tmp_backup
, true) >= 0);
126 assert_se(streq(getenv("TMP"), tmp_backup
));
130 TEST(dot_or_dot_dot
) {
131 assert_se(!dot_or_dot_dot(NULL
));
132 assert_se(!dot_or_dot_dot(""));
133 assert_se(!dot_or_dot_dot("xxx"));
134 assert_se(dot_or_dot_dot("."));
135 assert_se(dot_or_dot_dot(".."));
136 assert_se(!dot_or_dot_dot(".foo"));
137 assert_se(!dot_or_dot_dot("..foo"));
141 _cleanup_(rmdir_and_freep
) char *p
= NULL
;
142 _cleanup_close_
int fd
= -EBADF
;
145 a
= strjoina(arg_test_dir
?: "/tmp", "/access-fd.XXXXXX");
146 assert_se(mkdtemp_malloc(a
, &p
) >= 0);
148 fd
= open(p
, O_RDONLY
|O_DIRECTORY
|O_CLOEXEC
);
151 assert_se(access_fd(fd
, R_OK
) >= 0);
152 assert_se(access_fd(fd
, F_OK
) >= 0);
153 assert_se(access_fd(fd
, W_OK
) >= 0);
155 assert_se(fchmod(fd
, 0000) >= 0);
157 assert_se(access_fd(fd
, F_OK
) >= 0);
159 if (geteuid() == 0) {
160 assert_se(access_fd(fd
, R_OK
) >= 0);
161 assert_se(access_fd(fd
, W_OK
) >= 0);
163 assert_se(access_fd(fd
, R_OK
) == -EACCES
);
164 assert_se(access_fd(fd
, W_OK
) == -EACCES
);
169 uid_t test_uid
, test_gid
;
170 _cleanup_(rm_rf_physical_and_freep
) char *p
= NULL
;
176 test_uid
= geteuid() == 0 ? 65534 : getuid();
177 test_gid
= geteuid() == 0 ? 65534 : getgid();
179 test_mtime
= usec_sub_unsigned(now(CLOCK_REALTIME
), USEC_PER_WEEK
);
181 a
= strjoina(arg_test_dir
?: "/dev/shm", "/touch-file-XXXXXX");
182 assert_se(mkdtemp_malloc(a
, &p
) >= 0);
184 a
= strjoina(p
, "/regular");
185 r
= touch_file(a
, false, test_mtime
, test_uid
, test_gid
, 0640);
187 assert_se(IN_SET(r
, -EINVAL
, -ENOSYS
, -ENOTTY
, -EPERM
));
188 log_tests_skipped_errno(errno
, "touch_file() not possible");
192 assert_se(lstat(a
, &st
) >= 0);
193 assert_se(st
.st_uid
== test_uid
);
194 assert_se(st
.st_gid
== test_gid
);
195 assert_se(S_ISREG(st
.st_mode
));
196 assert_se((st
.st_mode
& 0777) == 0640);
197 assert_se(timespec_load(&st
.st_mtim
) == test_mtime
);
199 a
= strjoina(p
, "/dir");
200 assert_se(mkdir(a
, 0775) >= 0);
201 assert_se(touch_file(a
, false, test_mtime
, test_uid
, test_gid
, 0640) >= 0);
202 assert_se(lstat(a
, &st
) >= 0);
203 assert_se(st
.st_uid
== test_uid
);
204 assert_se(st
.st_gid
== test_gid
);
205 assert_se(S_ISDIR(st
.st_mode
));
206 assert_se((st
.st_mode
& 0777) == 0640);
207 assert_se(timespec_load(&st
.st_mtim
) == test_mtime
);
209 a
= strjoina(p
, "/fifo");
210 assert_se(mkfifo(a
, 0775) >= 0);
211 assert_se(touch_file(a
, false, test_mtime
, test_uid
, test_gid
, 0640) >= 0);
212 assert_se(lstat(a
, &st
) >= 0);
213 assert_se(st
.st_uid
== test_uid
);
214 assert_se(st
.st_gid
== test_gid
);
215 assert_se(S_ISFIFO(st
.st_mode
));
216 assert_se((st
.st_mode
& 0777) == 0640);
217 assert_se(timespec_load(&st
.st_mtim
) == test_mtime
);
219 a
= strjoina(p
, "/sock");
220 assert_se(mknod(a
, 0775 | S_IFSOCK
, 0) >= 0);
221 assert_se(touch_file(a
, false, test_mtime
, test_uid
, test_gid
, 0640) >= 0);
222 assert_se(lstat(a
, &st
) >= 0);
223 assert_se(st
.st_uid
== test_uid
);
224 assert_se(st
.st_gid
== test_gid
);
225 assert_se(S_ISSOCK(st
.st_mode
));
226 assert_se((st
.st_mode
& 0777) == 0640);
227 assert_se(timespec_load(&st
.st_mtim
) == test_mtime
);
229 if (geteuid() == 0) {
230 a
= strjoina(p
, "/bdev");
231 r
= mknod(a
, 0775 | S_IFBLK
, makedev(0, 0));
232 if (r
< 0 && errno
== EPERM
&& detect_container() > 0) {
233 log_notice("Running in unprivileged container? Skipping remaining tests in %s", __func__
);
237 assert_se(touch_file(a
, false, test_mtime
, test_uid
, test_gid
, 0640) >= 0);
238 assert_se(lstat(a
, &st
) >= 0);
239 assert_se(st
.st_uid
== test_uid
);
240 assert_se(st
.st_gid
== test_gid
);
241 assert_se(S_ISBLK(st
.st_mode
));
242 assert_se((st
.st_mode
& 0777) == 0640);
243 assert_se(timespec_load(&st
.st_mtim
) == test_mtime
);
245 a
= strjoina(p
, "/cdev");
246 assert_se(mknod(a
, 0775 | S_IFCHR
, makedev(0, 0)) >= 0);
247 assert_se(touch_file(a
, false, test_mtime
, test_uid
, test_gid
, 0640) >= 0);
248 assert_se(lstat(a
, &st
) >= 0);
249 assert_se(st
.st_uid
== test_uid
);
250 assert_se(st
.st_gid
== test_gid
);
251 assert_se(S_ISCHR(st
.st_mode
));
252 assert_se((st
.st_mode
& 0777) == 0640);
253 assert_se(timespec_load(&st
.st_mtim
) == test_mtime
);
256 a
= strjoina(p
, "/lnk");
257 assert_se(symlink("target", a
) >= 0);
258 assert_se(touch_file(a
, false, test_mtime
, test_uid
, test_gid
, 0640) >= 0);
259 assert_se(lstat(a
, &st
) >= 0);
260 assert_se(st
.st_uid
== test_uid
);
261 assert_se(st
.st_gid
== test_gid
);
262 assert_se(S_ISLNK(st
.st_mode
));
263 assert_se(timespec_load(&st
.st_mtim
) == test_mtime
);
266 TEST(unlinkat_deallocate
) {
267 _cleanup_free_
char *p
= NULL
;
268 _cleanup_close_
int fd
= -EBADF
;
271 assert_se(tempfn_random_child(arg_test_dir
, "unlink-deallocation", &p
) >= 0);
273 fd
= open(p
, O_WRONLY
|O_CLOEXEC
|O_CREAT
|O_EXCL
, 0600);
276 assert_se(write(fd
, "hallo\n", 6) == 6);
278 assert_se(fstat(fd
, &st
) >= 0);
279 assert_se(st
.st_size
== 6);
280 assert_se(st
.st_blocks
> 0);
281 assert_se(st
.st_nlink
== 1);
283 assert_se(unlinkat_deallocate(AT_FDCWD
, p
, UNLINK_ERASE
) >= 0);
285 assert_se(fstat(fd
, &st
) >= 0);
286 assert_se(IN_SET(st
.st_size
, 0, 6)); /* depending on whether hole punching worked the size will be 6
287 (it worked) or 0 (we had to resort to truncation) */
288 assert_se(st
.st_blocks
== 0);
289 assert_se(st
.st_nlink
== 0);
292 TEST(fsync_directory_of_file
) {
293 _cleanup_close_
int fd
= -EBADF
;
295 fd
= open_tmpfile_unlinkable(arg_test_dir
, O_RDWR
);
298 assert_se(fsync_directory_of_file(fd
) >= 0);
301 TEST(rename_noreplace
) {
302 static const char* const table
[] = {
311 _cleanup_(rm_rf_physical_and_freep
) char *z
= NULL
;
312 const char *j
= NULL
;
315 j
= strjoina(arg_test_dir
, "/testXXXXXX");
316 assert_se(mkdtemp_malloc(j
, &z
) >= 0);
318 j
= strjoina(z
, table
[0]);
319 assert_se(touch(j
) >= 0);
321 j
= strjoina(z
, table
[1]);
322 assert_se(mkdir(j
, 0777) >= 0);
324 j
= strjoina(z
, table
[2]);
325 (void) mkfifo(j
, 0777);
327 j
= strjoina(z
, table
[3]);
328 (void) mknod(j
, S_IFSOCK
| 0777, 0);
330 j
= strjoina(z
, table
[4]);
331 (void) symlink("foobar", j
);
333 STRV_FOREACH(a
, table
) {
334 _cleanup_free_
char *x
= NULL
, *y
= NULL
;
339 if (access(x
, F_OK
) < 0) {
340 assert_se(errno
== ENOENT
);
344 STRV_FOREACH(b
, table
) {
345 _cleanup_free_
char *w
= NULL
;
350 if (access(w
, F_OK
) < 0) {
351 assert_se(errno
== ENOENT
);
355 assert_se(rename_noreplace(AT_FDCWD
, x
, AT_FDCWD
, w
) == -EEXIST
);
358 y
= strjoin(z
, "/somethingelse");
361 assert_se(rename_noreplace(AT_FDCWD
, x
, AT_FDCWD
, y
) >= 0);
362 assert_se(rename_noreplace(AT_FDCWD
, y
, AT_FDCWD
, x
) >= 0);
366 TEST(chmod_and_chown
) {
367 _cleanup_(rm_rf_physical_and_freep
) char *d
= NULL
;
374 BLOCK_WITH_UMASK(0000);
376 assert_se(mkdtemp_malloc(NULL
, &d
) >= 0);
378 p
= strjoina(d
, "/reg");
379 assert_se(mknod(p
, S_IFREG
| 0123, 0) >= 0);
381 assert_se(chmod_and_chown(p
, S_IFREG
| 0321, 1, 2) >= 0);
382 assert_se(chmod_and_chown(p
, S_IFDIR
| 0555, 3, 4) == -EINVAL
);
384 assert_se(lstat(p
, &st
) >= 0);
385 assert_se(S_ISREG(st
.st_mode
));
386 assert_se((st
.st_mode
& 07777) == 0321);
388 p
= strjoina(d
, "/dir");
389 assert_se(mkdir(p
, 0123) >= 0);
391 assert_se(chmod_and_chown(p
, S_IFDIR
| 0321, 1, 2) >= 0);
392 assert_se(chmod_and_chown(p
, S_IFREG
| 0555, 3, 4) == -EINVAL
);
394 assert_se(lstat(p
, &st
) >= 0);
395 assert_se(S_ISDIR(st
.st_mode
));
396 assert_se((st
.st_mode
& 07777) == 0321);
398 p
= strjoina(d
, "/lnk");
399 assert_se(symlink("idontexist", p
) >= 0);
401 assert_se(chmod_and_chown(p
, S_IFLNK
| 0321, 1, 2) >= 0);
402 assert_se(chmod_and_chown(p
, S_IFREG
| 0555, 3, 4) == -EINVAL
);
403 assert_se(chmod_and_chown(p
, S_IFDIR
| 0555, 3, 4) == -EINVAL
);
405 assert_se(lstat(p
, &st
) >= 0);
406 assert_se(S_ISLNK(st
.st_mode
));
409 static void create_binary_file(const char *p
, const void *data
, size_t l
) {
410 _cleanup_close_
int fd
= -EBADF
;
412 fd
= open(p
, O_CREAT
|O_WRONLY
|O_EXCL
|O_CLOEXEC
, 0600);
414 assert_se(write(fd
, data
, l
) == (ssize_t
) l
);
417 TEST(conservative_rename
) {
418 _cleanup_(unlink_and_freep
) char *p
= NULL
;
419 _cleanup_free_
char *q
= NULL
;
420 size_t l
= 16*1024 + random_u64() % (32 * 1024); /* some randomly sized buffer 16k…48k */
423 random_bytes(buffer
, l
);
425 assert_se(tempfn_random_child(NULL
, NULL
, &p
) >= 0);
426 create_binary_file(p
, buffer
, l
);
428 assert_se(tempfn_random_child(NULL
, NULL
, &q
) >= 0);
430 /* Check that the hardlinked "copy" is detected */
431 assert_se(link(p
, q
) >= 0);
432 assert_se(conservative_renameat(AT_FDCWD
, q
, AT_FDCWD
, p
) == 0);
433 assert_se(access(q
, F_OK
) < 0 && errno
== ENOENT
);
435 /* Check that a manual copy is detected */
436 assert_se(copy_file(p
, q
, 0, MODE_INVALID
, COPY_REFLINK
) >= 0);
437 assert_se(conservative_renameat(AT_FDCWD
, q
, AT_FDCWD
, p
) == 0);
438 assert_se(access(q
, F_OK
) < 0 && errno
== ENOENT
);
440 /* Check that a manual new writeout is also detected */
441 create_binary_file(q
, buffer
, l
);
442 assert_se(conservative_renameat(AT_FDCWD
, q
, AT_FDCWD
, p
) == 0);
443 assert_se(access(q
, F_OK
) < 0 && errno
== ENOENT
);
445 /* Check that a minimally changed version is detected */
446 buffer
[47] = ~buffer
[47];
447 create_binary_file(q
, buffer
, l
);
448 assert_se(conservative_renameat(AT_FDCWD
, q
, AT_FDCWD
, p
) > 0);
449 assert_se(access(q
, F_OK
) < 0 && errno
== ENOENT
);
451 /* Check that this really is new updated version */
452 create_binary_file(q
, buffer
, l
);
453 assert_se(conservative_renameat(AT_FDCWD
, q
, AT_FDCWD
, p
) == 0);
454 assert_se(access(q
, F_OK
) < 0 && errno
== ENOENT
);
456 /* Make sure we detect extended files */
458 create_binary_file(q
, buffer
, l
);
459 assert_se(conservative_renameat(AT_FDCWD
, q
, AT_FDCWD
, p
) > 0);
460 assert_se(access(q
, F_OK
) < 0 && errno
== ENOENT
);
462 /* Make sure we detect truncated files */
464 create_binary_file(q
, buffer
, l
);
465 assert_se(conservative_renameat(AT_FDCWD
, q
, AT_FDCWD
, p
) > 0);
466 assert_se(access(q
, F_OK
) < 0 && errno
== ENOENT
);
469 static void test_rmdir_parents_one(
474 const char *test_exist
,
475 const char *test_nonexist_subdir
) {
479 log_debug("/* %s(%s, %s) */", __func__
, path
, stop
);
481 p
= strjoina(prefix
, path
);
482 s
= strjoina(prefix
, stop
);
485 assert_se(mkdir_parents(p
, 0700) >= 0);
487 assert_se(rmdir_parents(p
, s
) == expected
);
492 e
= strjoina(prefix
, test_exist
);
493 f
= strjoina(e
, test_nonexist_subdir
);
495 assert_se(access(e
, F_OK
) >= 0);
496 assert_se(access(f
, F_OK
) < 0);
500 TEST(rmdir_parents
) {
503 temp
= strjoina(arg_test_dir
?: "/tmp", "/test-rmdir.XXXXXX");
504 assert_se(mkdtemp(temp
));
506 test_rmdir_parents_one(temp
, "/aaa/../hoge/foo", "/hoge/foo", -EINVAL
, NULL
, NULL
);
507 test_rmdir_parents_one(temp
, "/aaa/bbb/ccc", "/hoge/../aaa", -EINVAL
, NULL
, NULL
);
509 test_rmdir_parents_one(temp
, "/aaa/bbb/ccc/ddd/eee", "/aaa/bbb/ccc/ddd", 0, "/aaa/bbb/ccc/ddd", "/eee");
510 test_rmdir_parents_one(temp
, "/aaa/bbb/ccc/ddd/eee", "/aaa/bbb/ccc", 0, "/aaa/bbb/ccc", "/ddd");
511 test_rmdir_parents_one(temp
, "/aaa/bbb/ccc/ddd/eee", "/aaa/bbb", 0, "/aaa/bbb", "/ccc");
512 test_rmdir_parents_one(temp
, "/aaa/bbb/ccc/ddd/eee", "/aaa", 0, "/aaa", "/bbb");
513 test_rmdir_parents_one(temp
, "/aaa/bbb/ccc/ddd/eee", "/", 0, "/", "/aaa");
515 test_rmdir_parents_one(temp
, "/aaa/bbb/ccc/ddd/eee", "/aaa/hoge/foo", 0, "/aaa", "/bbb");
516 test_rmdir_parents_one(temp
, "/aaa////bbb/.//ccc//ddd/eee///./.", "///././aaa/.", 0, "/aaa", "/bbb");
518 assert_se(rm_rf(temp
, REMOVE_ROOT
|REMOVE_PHYSICAL
) >= 0);
521 static void test_parse_cifs_service_one(const char *f
, const char *h
, const char *s
, const char *d
, int ret
) {
522 _cleanup_free_
char *a
= NULL
, *b
= NULL
, *c
= NULL
;
524 assert_se(parse_cifs_service(f
, &a
, &b
, &c
) == ret
);
525 assert_se(streq_ptr(a
, h
));
526 assert_se(streq_ptr(b
, s
));
527 assert_se(streq_ptr(c
, d
));
530 TEST(parse_cifs_service
) {
531 test_parse_cifs_service_one("//foo/bar/baz", "foo", "bar", "baz", 0);
532 test_parse_cifs_service_one("\\\\foo\\bar\\baz", "foo", "bar", "baz", 0);
533 test_parse_cifs_service_one("//foo/bar", "foo", "bar", NULL
, 0);
534 test_parse_cifs_service_one("\\\\foo\\bar", "foo", "bar", NULL
, 0);
535 test_parse_cifs_service_one("//foo/bar/baz/uuu", "foo", "bar", "baz/uuu", 0);
536 test_parse_cifs_service_one("\\\\foo\\bar\\baz\\uuu", "foo", "bar", "baz/uuu", 0);
538 test_parse_cifs_service_one(NULL
, NULL
, NULL
, NULL
, -EINVAL
);
539 test_parse_cifs_service_one("", NULL
, NULL
, NULL
, -EINVAL
);
540 test_parse_cifs_service_one("abc", NULL
, NULL
, NULL
, -EINVAL
);
541 test_parse_cifs_service_one("abc/cde/efg", NULL
, NULL
, NULL
, -EINVAL
);
542 test_parse_cifs_service_one("//foo/bar/baz/..", NULL
, NULL
, NULL
, -EINVAL
);
543 test_parse_cifs_service_one("//foo///", NULL
, NULL
, NULL
, -EINVAL
);
544 test_parse_cifs_service_one("//foo/.", NULL
, NULL
, NULL
, -EINVAL
);
545 test_parse_cifs_service_one("//foo/a/.", NULL
, NULL
, NULL
, -EINVAL
);
546 test_parse_cifs_service_one("//./a", NULL
, NULL
, NULL
, -EINVAL
);
549 TEST(open_mkdir_at
) {
550 _cleanup_close_
int fd
= -EBADF
, subdir_fd
= -EBADF
, subsubdir_fd
= -EBADF
;
551 _cleanup_(rm_rf_physical_and_freep
) char *t
= NULL
;
552 struct stat sta
, stb
;
554 assert_se(open_mkdir_at(AT_FDCWD
, "/", O_EXCL
|O_CLOEXEC
, 0) == -EEXIST
);
555 assert_se(open_mkdir_at(AT_FDCWD
, ".", O_EXCL
|O_CLOEXEC
, 0) == -EEXIST
);
557 fd
= open_mkdir_at(AT_FDCWD
, "/", O_CLOEXEC
, 0);
559 assert_se(stat("/", &sta
) >= 0);
560 assert_se(fstat(fd
, &stb
) >= 0);
561 assert_se(stat_inode_same(&sta
, &stb
));
564 fd
= open_mkdir_at(AT_FDCWD
, ".", O_CLOEXEC
, 0);
565 assert_se(stat(".", &sta
) >= 0);
566 assert_se(fstat(fd
, &stb
) >= 0);
567 assert_se(stat_inode_same(&sta
, &stb
));
570 assert_se(open_mkdir_at(AT_FDCWD
, "/proc", O_EXCL
|O_CLOEXEC
, 0) == -EEXIST
);
572 fd
= open_mkdir_at(AT_FDCWD
, "/proc", O_CLOEXEC
, 0);
576 assert_se(open_mkdir_at(AT_FDCWD
, "/bin/sh", O_EXCL
|O_CLOEXEC
, 0) == -EEXIST
);
577 assert_se(open_mkdir_at(AT_FDCWD
, "/bin/sh", O_CLOEXEC
, 0) == -EEXIST
);
579 assert_se(mkdtemp_malloc(NULL
, &t
) >= 0);
581 assert_se(open_mkdir_at(AT_FDCWD
, t
, O_EXCL
|O_CLOEXEC
, 0) == -EEXIST
);
582 assert_se(open_mkdir_at(AT_FDCWD
, t
, O_PATH
|O_EXCL
|O_CLOEXEC
, 0) == -EEXIST
);
584 fd
= open_mkdir_at(AT_FDCWD
, t
, O_CLOEXEC
, 0000);
588 fd
= open_mkdir_at(AT_FDCWD
, t
, O_PATH
|O_CLOEXEC
, 0000);
591 subdir_fd
= open_mkdir_at(fd
, "xxx", O_PATH
|O_EXCL
|O_CLOEXEC
, 0700);
592 assert_se(subdir_fd
>= 0);
594 assert_se(open_mkdir_at(fd
, "xxx", O_PATH
|O_EXCL
|O_CLOEXEC
, 0) == -EEXIST
);
596 subsubdir_fd
= open_mkdir_at(subdir_fd
, "yyy", O_EXCL
|O_CLOEXEC
, 0700);
597 assert_se(subsubdir_fd
>= 0);
598 subsubdir_fd
= safe_close(subsubdir_fd
);
600 assert_se(open_mkdir_at(subdir_fd
, "yyy", O_EXCL
|O_CLOEXEC
, 0) == -EEXIST
);
602 assert_se(open_mkdir_at(fd
, "xxx/yyy", O_EXCL
|O_CLOEXEC
, 0) == -EEXIST
);
604 subsubdir_fd
= open_mkdir_at(fd
, "xxx/yyy", O_CLOEXEC
, 0700);
605 assert_se(subsubdir_fd
>= 0);
608 TEST(openat_report_new
) {
609 _cleanup_free_
char *j
= NULL
;
610 _cleanup_(rm_rf_physical_and_freep
) char *d
= NULL
;
611 _cleanup_close_
int fd
= -EBADF
;
614 assert_se(mkdtemp_malloc(NULL
, &d
) >= 0);
616 j
= path_join(d
, "test");
619 fd
= openat_report_new(AT_FDCWD
, j
, O_RDWR
|O_CREAT
, 0666, &b
);
624 fd
= openat_report_new(AT_FDCWD
, j
, O_RDWR
|O_CREAT
, 0666, &b
);
629 fd
= openat_report_new(AT_FDCWD
, j
, O_RDWR
|O_CREAT
, 0666, &b
);
634 assert_se(unlink(j
) >= 0);
636 fd
= openat_report_new(AT_FDCWD
, j
, O_RDWR
|O_CREAT
, 0666, &b
);
641 fd
= openat_report_new(AT_FDCWD
, j
, O_RDWR
|O_CREAT
, 0666, &b
);
646 assert_se(unlink(j
) >= 0);
648 fd
= openat_report_new(AT_FDCWD
, j
, O_RDWR
|O_CREAT
, 0666, NULL
);
652 fd
= openat_report_new(AT_FDCWD
, j
, O_RDWR
|O_CREAT
, 0666, &b
);
657 fd
= openat_report_new(AT_FDCWD
, j
, O_RDWR
, 0666, &b
);
662 fd
= openat_report_new(AT_FDCWD
, j
, O_RDWR
|O_CREAT
|O_EXCL
, 0666, &b
);
663 assert_se(fd
== -EEXIST
);
665 assert_se(unlink(j
) >= 0);
667 fd
= openat_report_new(AT_FDCWD
, j
, O_RDWR
, 0666, &b
);
668 assert_se(fd
== -ENOENT
);
670 fd
= openat_report_new(AT_FDCWD
, j
, O_RDWR
|O_CREAT
|O_EXCL
, 0666, &b
);
677 _cleanup_(rm_rf_physical_and_freep
) char *t
= NULL
;
678 _cleanup_close_
int tfd
= -EBADF
, fd
= -EBADF
, fd2
= -EBADF
;
680 assert_se((tfd
= mkdtemp_open(NULL
, 0, &t
)) >= 0);
682 /* Test that xopenat_full() creates directories if O_DIRECTORY is specified. */
684 assert_se((fd
= xopenat_full(tfd
, "abc", O_DIRECTORY
|O_CREAT
|O_EXCL
|O_CLOEXEC
, 0, 0755)) >= 0);
685 assert_se((fd_verify_directory(fd
) >= 0));
688 assert_se(xopenat_full(tfd
, "abc", O_DIRECTORY
|O_CREAT
|O_EXCL
|O_CLOEXEC
, 0, 0755) == -EEXIST
);
690 assert_se((fd
= xopenat_full(tfd
, "abc", O_DIRECTORY
|O_CREAT
|O_CLOEXEC
, 0, 0755)) >= 0);
691 assert_se((fd_verify_directory(fd
) >= 0));
694 /* Test that xopenat_full() creates regular files if O_DIRECTORY is not specified. */
696 assert_se((fd
= xopenat_full(tfd
, "def", O_CREAT
|O_EXCL
|O_CLOEXEC
, 0, 0644)) >= 0);
697 assert_se(fd_verify_regular(fd
) >= 0);
700 /* Test that we can reopen an existing fd with xopenat_full() by specifying an empty path. */
702 assert_se((fd
= xopenat_full(tfd
, "def", O_PATH
|O_CLOEXEC
, 0, 0)) >= 0);
703 assert_se((fd2
= xopenat_full(fd
, "", O_RDWR
|O_CLOEXEC
, 0, 0644)) >= 0);
706 TEST(xopenat_lock_full
) {
707 _cleanup_(rm_rf_physical_and_freep
) char *t
= NULL
;
708 _cleanup_close_
int tfd
= -EBADF
, fd
= -EBADF
;
711 assert_se((tfd
= mkdtemp_open(NULL
, 0, &t
)) >= 0);
713 /* Test that we can acquire an exclusive lock on a directory in one process, remove the directory,
714 * and close the file descriptor and still properly create the directory and acquire the lock in
715 * another process. */
717 fd
= xopenat_lock_full(tfd
, "abc", O_CREAT
|O_DIRECTORY
|O_CLOEXEC
, 0, 0755, LOCK_BSD
, LOCK_EX
);
719 assert_se(faccessat(tfd
, "abc", F_OK
, 0) >= 0);
720 assert_se(fd_verify_directory(fd
) >= 0);
721 assert_se(xopenat_lock_full(tfd
, "abc", O_DIRECTORY
|O_CLOEXEC
, 0, 0755, LOCK_BSD
, LOCK_EX
|LOCK_NB
) == -EAGAIN
);
729 fd
= xopenat_lock_full(tfd
, "abc", O_CREAT
|O_DIRECTORY
|O_CLOEXEC
, 0, 0755, LOCK_BSD
, LOCK_EX
);
731 assert_se(faccessat(tfd
, "abc", F_OK
, 0) >= 0);
732 assert_se(fd_verify_directory(fd
) >= 0);
733 assert_se(xopenat_lock_full(tfd
, "abc", O_DIRECTORY
|O_CLOEXEC
, 0, 0755, LOCK_BSD
, LOCK_EX
|LOCK_NB
) == -EAGAIN
);
738 /* We need to give the child process some time to get past the xopenat() call in xopenat_lock_full()
739 * and block in the call to lock_generic() waiting for the lock to become free. We can't modify
740 * xopenat_lock_full() to signal an eventfd to let us know when that has happened, so we just sleep
741 * for a little and assume that's enough time for the child process to get along far enough. It
742 * doesn't matter if it doesn't get far enough, in that case we just won't trigger the fallback logic
743 * in xopenat_lock_full(), but the test will still succeed. */
744 assert_se(usleep_safe(20 * USEC_PER_MSEC
) >= 0);
746 assert_se(unlinkat(tfd
, "abc", AT_REMOVEDIR
) >= 0);
749 assert_se(wait_for_terminate(pid
, &si
) >= 0);
750 assert_se(si
.si_code
== CLD_EXITED
);
752 assert_se(xopenat_lock_full(tfd
, "abc", 0, 0, 0755, LOCK_POSIX
, LOCK_EX
) == -EBADF
);
753 assert_se(xopenat_lock_full(tfd
, "def", O_DIRECTORY
, 0, 0755, LOCK_POSIX
, LOCK_EX
) == -EBADF
);
756 TEST(linkat_replace
) {
757 _cleanup_(rm_rf_physical_and_freep
) char *t
= NULL
;
758 _cleanup_close_
int tfd
= -EBADF
;
760 assert_se((tfd
= mkdtemp_open(NULL
, 0, &t
)) >= 0);
762 _cleanup_close_
int fd1
= openat(tfd
, "foo", O_CREAT
|O_RDWR
|O_CLOEXEC
, 0600);
765 assert_se(linkat_replace(tfd
, "foo", tfd
, "bar") >= 0);
766 assert_se(linkat_replace(tfd
, "foo", tfd
, "bar") >= 0);
768 _cleanup_close_
int fd1_check
= openat(tfd
, "bar", O_RDWR
|O_CLOEXEC
);
769 assert_se(fd1_check
>= 0);
771 assert_se(inode_same_at(fd1
, NULL
, fd1_check
, NULL
, AT_EMPTY_PATH
) > 0);
773 _cleanup_close_
int fd2
= openat(tfd
, "baz", O_CREAT
|O_RDWR
|O_CLOEXEC
, 0600);
776 assert_se(inode_same_at(fd1
, NULL
, fd2
, NULL
, AT_EMPTY_PATH
) == 0);
778 assert_se(linkat_replace(tfd
, "foo", tfd
, "baz") >= 0);
780 _cleanup_close_
int fd2_check
= openat(tfd
, "baz", O_RDWR
|O_CLOEXEC
);
782 assert_se(inode_same_at(fd2
, NULL
, fd2_check
, NULL
, AT_EMPTY_PATH
) == 0);
783 assert_se(inode_same_at(fd1
, NULL
, fd2_check
, NULL
, AT_EMPTY_PATH
) > 0);
786 static int intro(void) {
787 arg_test_dir
= saved_argv
[1];
791 TEST(readlinkat_malloc
) {
792 _cleanup_(rm_rf_physical_and_freep
) char *t
= NULL
;
793 _cleanup_close_
int tfd
= -EBADF
, fd
= -EBADF
;
794 _cleanup_free_
char *p
= NULL
, *q
= NULL
;
795 const char *expect
= "hgoehogefoobar";
797 tfd
= mkdtemp_open(NULL
, O_PATH
, &t
);
800 assert_se(symlinkat(expect
, tfd
, "linkname") >= 0);
802 assert_se(readlinkat_malloc(tfd
, "linkname", &p
) >= 0);
803 assert_se(streq(p
, expect
));
806 fd
= openat(tfd
, "linkname", O_PATH
| O_NOFOLLOW
| O_CLOEXEC
);
808 assert_se(readlinkat_malloc(fd
, NULL
, &p
) >= 0);
809 assert_se(streq(p
, expect
));
811 assert_se(readlinkat_malloc(fd
, "", &p
) >= 0);
812 assert_se(streq(p
, expect
));
816 assert_se(q
= path_join(t
, "linkname"));
817 assert_se(readlinkat_malloc(AT_FDCWD
, q
, &p
) >= 0);
818 assert_se(streq(p
, expect
));
820 assert_se(readlinkat_malloc(INT_MAX
, q
, &p
) >= 0);
821 assert_se(streq(p
, expect
));
826 DEFINE_TEST_MAIN_WITH_INTRO(LOG_INFO
, intro
);