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