]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/test/test-fs-util.c
Merge pull request #7530 from poettering/uid-gid-fixes
[thirdparty/systemd.git] / src / test / test-fs-util.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2 /***
3 This file is part of systemd.
4
5 Copyright 2010 Lennart Poettering
6
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
11
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
19 ***/
20
21 #include <unistd.h>
22
23 #include "alloc-util.h"
24 #include "fd-util.h"
25 #include "fileio.h"
26 #include "fs-util.h"
27 #include "macro.h"
28 #include "mkdir.h"
29 #include "path-util.h"
30 #include "rm-rf.h"
31 #include "string-util.h"
32 #include "strv.h"
33 #include "util.h"
34
35 static void test_chase_symlinks(void) {
36 _cleanup_free_ char *result = NULL;
37 char temp[] = "/tmp/test-chase.XXXXXX";
38 const char *top, *p, *pslash, *q, *qslash;
39 int r;
40
41 assert_se(mkdtemp(temp));
42
43 top = strjoina(temp, "/top");
44 assert_se(mkdir(top, 0700) >= 0);
45
46 p = strjoina(top, "/dot");
47 assert_se(symlink(".", p) >= 0);
48
49 p = strjoina(top, "/dotdot");
50 assert_se(symlink("..", p) >= 0);
51
52 p = strjoina(top, "/dotdota");
53 assert_se(symlink("../a", p) >= 0);
54
55 p = strjoina(temp, "/a");
56 assert_se(symlink("b", p) >= 0);
57
58 p = strjoina(temp, "/b");
59 assert_se(symlink("/usr", p) >= 0);
60
61 p = strjoina(temp, "/start");
62 assert_se(symlink("top/dot/dotdota", p) >= 0);
63
64 /* Paths that use symlinks underneath the "root" */
65
66 r = chase_symlinks(p, NULL, 0, &result);
67 assert_se(r > 0);
68 assert_se(path_equal(result, "/usr"));
69 result = mfree(result);
70
71 pslash = strjoina(p, "/");
72 r = chase_symlinks(pslash, NULL, 0, &result);
73 assert_se(r > 0);
74 assert_se(path_equal(result, "/usr/"));
75 result = mfree(result);
76
77 r = chase_symlinks(p, temp, 0, &result);
78 assert_se(r == -ENOENT);
79
80 r = chase_symlinks(pslash, temp, 0, &result);
81 assert_se(r == -ENOENT);
82
83 q = strjoina(temp, "/usr");
84
85 r = chase_symlinks(p, temp, CHASE_NONEXISTENT, &result);
86 assert_se(r == 0);
87 assert_se(path_equal(result, q));
88 result = mfree(result);
89
90 qslash = strjoina(q, "/");
91
92 r = chase_symlinks(pslash, temp, CHASE_NONEXISTENT, &result);
93 assert_se(r == 0);
94 assert_se(path_equal(result, qslash));
95 result = mfree(result);
96
97 assert_se(mkdir(q, 0700) >= 0);
98
99 r = chase_symlinks(p, temp, 0, &result);
100 assert_se(r > 0);
101 assert_se(path_equal(result, q));
102 result = mfree(result);
103
104 r = chase_symlinks(pslash, temp, 0, &result);
105 assert_se(r > 0);
106 assert_se(path_equal(result, qslash));
107 result = mfree(result);
108
109 p = strjoina(temp, "/slash");
110 assert_se(symlink("/", p) >= 0);
111
112 r = chase_symlinks(p, NULL, 0, &result);
113 assert_se(r > 0);
114 assert_se(path_equal(result, "/"));
115 result = mfree(result);
116
117 r = chase_symlinks(p, temp, 0, &result);
118 assert_se(r > 0);
119 assert_se(path_equal(result, temp));
120 result = mfree(result);
121
122 /* Paths that would "escape" outside of the "root" */
123
124 p = strjoina(temp, "/6dots");
125 assert_se(symlink("../../..", p) >= 0);
126
127 r = chase_symlinks(p, temp, 0, &result);
128 assert_se(r > 0 && path_equal(result, temp));
129 result = mfree(result);
130
131 p = strjoina(temp, "/6dotsusr");
132 assert_se(symlink("../../../usr", p) >= 0);
133
134 r = chase_symlinks(p, temp, 0, &result);
135 assert_se(r > 0 && path_equal(result, q));
136 result = mfree(result);
137
138 p = strjoina(temp, "/top/8dotsusr");
139 assert_se(symlink("../../../../usr", p) >= 0);
140
141 r = chase_symlinks(p, temp, 0, &result);
142 assert_se(r > 0 && path_equal(result, q));
143 result = mfree(result);
144
145 /* Paths that contain repeated slashes */
146
147 p = strjoina(temp, "/slashslash");
148 assert_se(symlink("///usr///", p) >= 0);
149
150 r = chase_symlinks(p, NULL, 0, &result);
151 assert_se(r > 0);
152 assert_se(path_equal(result, "/usr"));
153 result = mfree(result);
154
155 r = chase_symlinks(p, temp, 0, &result);
156 assert_se(r > 0);
157 assert_se(path_equal(result, q));
158 result = mfree(result);
159
160 /* Paths using . */
161
162 r = chase_symlinks("/etc/./.././", NULL, 0, &result);
163 assert_se(r > 0);
164 assert_se(path_equal(result, "/"));
165 result = mfree(result);
166
167 r = chase_symlinks("/etc/./.././", "/etc", 0, &result);
168 assert_se(r > 0 && path_equal(result, "/etc"));
169 result = mfree(result);
170
171 r = chase_symlinks("/../.././//../../etc", NULL, 0, &result);
172 assert_se(r > 0);
173 assert_se(streq(result, "/etc"));
174 result = mfree(result);
175
176 r = chase_symlinks("/../.././//../../test-chase.fsldajfl", NULL, CHASE_NONEXISTENT, &result);
177 assert_se(r == 0);
178 assert_se(streq(result, "/test-chase.fsldajfl"));
179 result = mfree(result);
180
181 r = chase_symlinks("/../.././//../../etc", "/", CHASE_PREFIX_ROOT, &result);
182 assert_se(r > 0);
183 assert_se(streq(result, "/etc"));
184 result = mfree(result);
185
186 r = chase_symlinks("/../.././//../../test-chase.fsldajfl", "/", CHASE_PREFIX_ROOT|CHASE_NONEXISTENT, &result);
187 assert_se(r == 0);
188 assert_se(streq(result, "/test-chase.fsldajfl"));
189 result = mfree(result);
190
191 r = chase_symlinks("/etc/machine-id/foo", NULL, 0, &result);
192 assert_se(r == -ENOTDIR);
193 result = mfree(result);
194
195 /* Path that loops back to self */
196
197 p = strjoina(temp, "/recursive-symlink");
198 assert_se(symlink("recursive-symlink", p) >= 0);
199 r = chase_symlinks(p, NULL, 0, &result);
200 assert_se(r == -ELOOP);
201
202 /* Path which doesn't exist */
203
204 p = strjoina(temp, "/idontexist");
205 r = chase_symlinks(p, NULL, 0, &result);
206 assert_se(r == -ENOENT);
207
208 r = chase_symlinks(p, NULL, CHASE_NONEXISTENT, &result);
209 assert_se(r == 0);
210 assert_se(path_equal(result, p));
211 result = mfree(result);
212
213 p = strjoina(temp, "/idontexist/meneither");
214 r = chase_symlinks(p, NULL, 0, &result);
215 assert_se(r == -ENOENT);
216
217 r = chase_symlinks(p, NULL, CHASE_NONEXISTENT, &result);
218 assert_se(r == 0);
219 assert_se(path_equal(result, p));
220 result = mfree(result);
221
222 /* Path which doesn't exist, but contains weird stuff */
223
224 p = strjoina(temp, "/idontexist/..");
225 r = chase_symlinks(p, NULL, 0, &result);
226 assert_se(r == -ENOENT);
227
228 r = chase_symlinks(p, NULL, CHASE_NONEXISTENT, &result);
229 assert_se(r == -ENOENT);
230
231 p = strjoina(temp, "/target");
232 q = strjoina(temp, "/top");
233 assert_se(symlink(q, p) >= 0);
234 p = strjoina(temp, "/target/idontexist");
235 r = chase_symlinks(p, NULL, 0, &result);
236 assert_se(r == -ENOENT);
237
238 assert_se(rm_rf(temp, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);
239 }
240
241 static void test_unlink_noerrno(void) {
242 char name[] = "/tmp/test-close_nointr.XXXXXX";
243 int fd;
244
245 fd = mkostemp_safe(name);
246 assert_se(fd >= 0);
247 assert_se(close_nointr(fd) >= 0);
248
249 {
250 PROTECT_ERRNO;
251 errno = -42;
252 assert_se(unlink_noerrno(name) >= 0);
253 assert_se(errno == -42);
254 assert_se(unlink_noerrno(name) < 0);
255 assert_se(errno == -42);
256 }
257 }
258
259 static void test_readlink_and_make_absolute(void) {
260 char tempdir[] = "/tmp/test-readlink_and_make_absolute";
261 char name[] = "/tmp/test-readlink_and_make_absolute/original";
262 char name2[] = "test-readlink_and_make_absolute/original";
263 char name_alias[] = "/tmp/test-readlink_and_make_absolute-alias";
264 char *r = NULL;
265 _cleanup_free_ char *pwd = NULL;
266
267 assert_se(mkdir_safe(tempdir, 0755, getuid(), getgid(), false) >= 0);
268 assert_se(touch(name) >= 0);
269
270 assert_se(symlink(name, name_alias) >= 0);
271 assert_se(readlink_and_make_absolute(name_alias, &r) >= 0);
272 assert_se(streq(r, name));
273 free(r);
274 assert_se(unlink(name_alias) >= 0);
275
276 assert_se(pwd = get_current_dir_name());
277
278 assert_se(chdir(tempdir) >= 0);
279 assert_se(symlink(name2, name_alias) >= 0);
280 assert_se(readlink_and_make_absolute(name_alias, &r) >= 0);
281 assert_se(streq(r, name));
282 free(r);
283 assert_se(unlink(name_alias) >= 0);
284
285 assert_se(chdir(pwd) >= 0);
286
287 assert_se(rm_rf(tempdir, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);
288 }
289
290 static void test_get_files_in_directory(void) {
291 _cleanup_strv_free_ char **l = NULL, **t = NULL;
292
293 assert_se(get_files_in_directory("/tmp", &l) >= 0);
294 assert_se(get_files_in_directory(".", &t) >= 0);
295 assert_se(get_files_in_directory(".", NULL) >= 0);
296 }
297
298 static void test_var_tmp(void) {
299 _cleanup_free_ char *tmpdir_backup = NULL, *temp_backup = NULL, *tmp_backup = NULL;
300 const char *tmp_dir = NULL, *t;
301
302 t = getenv("TMPDIR");
303 if (t) {
304 tmpdir_backup = strdup(t);
305 assert_se(tmpdir_backup);
306 }
307
308 t = getenv("TEMP");
309 if (t) {
310 temp_backup = strdup(t);
311 assert_se(temp_backup);
312 }
313
314 t = getenv("TMP");
315 if (t) {
316 tmp_backup = strdup(t);
317 assert_se(tmp_backup);
318 }
319
320 assert_se(unsetenv("TMPDIR") >= 0);
321 assert_se(unsetenv("TEMP") >= 0);
322 assert_se(unsetenv("TMP") >= 0);
323
324 assert_se(var_tmp_dir(&tmp_dir) >= 0);
325 assert_se(streq(tmp_dir, "/var/tmp"));
326
327 assert_se(setenv("TMPDIR", "/tmp", true) >= 0);
328 assert_se(streq(getenv("TMPDIR"), "/tmp"));
329
330 assert_se(var_tmp_dir(&tmp_dir) >= 0);
331 assert_se(streq(tmp_dir, "/tmp"));
332
333 assert_se(setenv("TMPDIR", "/88_does_not_exist_88", true) >= 0);
334 assert_se(streq(getenv("TMPDIR"), "/88_does_not_exist_88"));
335
336 assert_se(var_tmp_dir(&tmp_dir) >= 0);
337 assert_se(streq(tmp_dir, "/var/tmp"));
338
339 if (tmpdir_backup) {
340 assert_se(setenv("TMPDIR", tmpdir_backup, true) >= 0);
341 assert_se(streq(getenv("TMPDIR"), tmpdir_backup));
342 }
343
344 if (temp_backup) {
345 assert_se(setenv("TEMP", temp_backup, true) >= 0);
346 assert_se(streq(getenv("TEMP"), temp_backup));
347 }
348
349 if (tmp_backup) {
350 assert_se(setenv("TMP", tmp_backup, true) >= 0);
351 assert_se(streq(getenv("TMP"), tmp_backup));
352 }
353 }
354
355 static void test_dot_or_dot_dot(void) {
356 assert_se(!dot_or_dot_dot(NULL));
357 assert_se(!dot_or_dot_dot(""));
358 assert_se(!dot_or_dot_dot("xxx"));
359 assert_se(dot_or_dot_dot("."));
360 assert_se(dot_or_dot_dot(".."));
361 assert_se(!dot_or_dot_dot(".foo"));
362 assert_se(!dot_or_dot_dot("..foo"));
363 }
364
365 static void test_access_fd(void) {
366 _cleanup_(rmdir_and_freep) char *p = NULL;
367 _cleanup_close_ int fd = -1;
368
369 assert_se(mkdtemp_malloc("/tmp/access-fd.XXXXXX", &p) >= 0);
370
371 fd = open(p, O_RDONLY|O_DIRECTORY|O_CLOEXEC);
372 assert_se(fd >= 0);
373
374 assert_se(access_fd(fd, R_OK) >= 0);
375 assert_se(access_fd(fd, F_OK) >= 0);
376 assert_se(access_fd(fd, W_OK) >= 0);
377
378 assert_se(fchmod(fd, 0000) >= 0);
379
380 assert_se(access_fd(fd, F_OK) >= 0);
381
382 if (geteuid() == 0) {
383 assert_se(access_fd(fd, R_OK) >= 0);
384 assert_se(access_fd(fd, W_OK) >= 0);
385 } else {
386 assert_se(access_fd(fd, R_OK) == -EACCES);
387 assert_se(access_fd(fd, W_OK) == -EACCES);
388 }
389 }
390
391 int main(int argc, char *argv[]) {
392 test_unlink_noerrno();
393 test_get_files_in_directory();
394 test_readlink_and_make_absolute();
395 test_var_tmp();
396 test_chase_symlinks();
397 test_dot_or_dot_dot();
398 test_access_fd();
399
400 return 0;
401 }