]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/test/test-fs-util.c
util-lib: use trailing slash in chase_symlinks, fd_is_mount_point, path_is_mount_point
[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/machine-id/foo", NULL, 0, &result);
172 assert_se(r == -ENOTDIR);
173 result = mfree(result);
174
175 /* Path that loops back to self */
176
177 p = strjoina(temp, "/recursive-symlink");
178 assert_se(symlink("recursive-symlink", p) >= 0);
179 r = chase_symlinks(p, NULL, 0, &result);
180 assert_se(r == -ELOOP);
181
182 /* Path which doesn't exist */
183
184 p = strjoina(temp, "/idontexist");
185 r = chase_symlinks(p, NULL, 0, &result);
186 assert_se(r == -ENOENT);
187
188 r = chase_symlinks(p, NULL, CHASE_NONEXISTENT, &result);
189 assert_se(r == 0);
190 assert_se(path_equal(result, p));
191 result = mfree(result);
192
193 p = strjoina(temp, "/idontexist/meneither");
194 r = chase_symlinks(p, NULL, 0, &result);
195 assert_se(r == -ENOENT);
196
197 r = chase_symlinks(p, NULL, CHASE_NONEXISTENT, &result);
198 assert_se(r == 0);
199 assert_se(path_equal(result, p));
200 result = mfree(result);
201
202 /* Path which doesn't exist, but contains weird stuff */
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 == -ENOENT);
210
211 p = strjoina(temp, "/target");
212 q = strjoina(temp, "/top");
213 assert_se(symlink(q, p) >= 0);
214 p = strjoina(temp, "/target/idontexist");
215 r = chase_symlinks(p, NULL, 0, &result);
216 assert_se(r == -ENOENT);
217
218 assert_se(rm_rf(temp, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);
219 }
220
221 static void test_unlink_noerrno(void) {
222 char name[] = "/tmp/test-close_nointr.XXXXXX";
223 int fd;
224
225 fd = mkostemp_safe(name);
226 assert_se(fd >= 0);
227 assert_se(close_nointr(fd) >= 0);
228
229 {
230 PROTECT_ERRNO;
231 errno = -42;
232 assert_se(unlink_noerrno(name) >= 0);
233 assert_se(errno == -42);
234 assert_se(unlink_noerrno(name) < 0);
235 assert_se(errno == -42);
236 }
237 }
238
239 static void test_readlink_and_make_absolute(void) {
240 char tempdir[] = "/tmp/test-readlink_and_make_absolute";
241 char name[] = "/tmp/test-readlink_and_make_absolute/original";
242 char name2[] = "test-readlink_and_make_absolute/original";
243 char name_alias[] = "/tmp/test-readlink_and_make_absolute-alias";
244 char *r = NULL;
245
246 assert_se(mkdir_safe(tempdir, 0755, getuid(), getgid(), false) >= 0);
247 assert_se(touch(name) >= 0);
248
249 assert_se(symlink(name, name_alias) >= 0);
250 assert_se(readlink_and_make_absolute(name_alias, &r) >= 0);
251 assert_se(streq(r, name));
252 free(r);
253 assert_se(unlink(name_alias) >= 0);
254
255 assert_se(chdir(tempdir) >= 0);
256 assert_se(symlink(name2, name_alias) >= 0);
257 assert_se(readlink_and_make_absolute(name_alias, &r) >= 0);
258 assert_se(streq(r, name));
259 free(r);
260 assert_se(unlink(name_alias) >= 0);
261
262 assert_se(rm_rf(tempdir, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);
263 }
264
265 static void test_get_files_in_directory(void) {
266 _cleanup_strv_free_ char **l = NULL, **t = NULL;
267
268 assert_se(get_files_in_directory("/tmp", &l) >= 0);
269 assert_se(get_files_in_directory(".", &t) >= 0);
270 assert_se(get_files_in_directory(".", NULL) >= 0);
271 }
272
273 static void test_var_tmp(void) {
274 _cleanup_free_ char *tmpdir_backup = NULL, *temp_backup = NULL, *tmp_backup = NULL;
275 const char *tmp_dir = NULL, *t;
276
277 t = getenv("TMPDIR");
278 if (t) {
279 tmpdir_backup = strdup(t);
280 assert_se(tmpdir_backup);
281 }
282
283 t = getenv("TEMP");
284 if (t) {
285 temp_backup = strdup(t);
286 assert_se(temp_backup);
287 }
288
289 t = getenv("TMP");
290 if (t) {
291 tmp_backup = strdup(t);
292 assert_se(tmp_backup);
293 }
294
295 assert_se(unsetenv("TMPDIR") >= 0);
296 assert_se(unsetenv("TEMP") >= 0);
297 assert_se(unsetenv("TMP") >= 0);
298
299 assert_se(var_tmp_dir(&tmp_dir) >= 0);
300 assert_se(streq(tmp_dir, "/var/tmp"));
301
302 assert_se(setenv("TMPDIR", "/tmp", true) >= 0);
303 assert_se(streq(getenv("TMPDIR"), "/tmp"));
304
305 assert_se(var_tmp_dir(&tmp_dir) >= 0);
306 assert_se(streq(tmp_dir, "/tmp"));
307
308 assert_se(setenv("TMPDIR", "/88_does_not_exist_88", true) >= 0);
309 assert_se(streq(getenv("TMPDIR"), "/88_does_not_exist_88"));
310
311 assert_se(var_tmp_dir(&tmp_dir) >= 0);
312 assert_se(streq(tmp_dir, "/var/tmp"));
313
314 if (tmpdir_backup) {
315 assert_se(setenv("TMPDIR", tmpdir_backup, true) >= 0);
316 assert_se(streq(getenv("TMPDIR"), tmpdir_backup));
317 }
318
319 if (temp_backup) {
320 assert_se(setenv("TEMP", temp_backup, true) >= 0);
321 assert_se(streq(getenv("TEMP"), temp_backup));
322 }
323
324 if (tmp_backup) {
325 assert_se(setenv("TMP", tmp_backup, true) >= 0);
326 assert_se(streq(getenv("TMP"), tmp_backup));
327 }
328 }
329
330 static void test_dot_or_dot_dot(void) {
331 assert_se(!dot_or_dot_dot(NULL));
332 assert_se(!dot_or_dot_dot(""));
333 assert_se(!dot_or_dot_dot("xxx"));
334 assert_se(dot_or_dot_dot("."));
335 assert_se(dot_or_dot_dot(".."));
336 assert_se(!dot_or_dot_dot(".foo"));
337 assert_se(!dot_or_dot_dot("..foo"));
338 }
339
340 static void test_access_fd(void) {
341 _cleanup_(rmdir_and_freep) char *p = NULL;
342 _cleanup_close_ int fd = -1;
343
344 assert_se(mkdtemp_malloc("/tmp/access-fd.XXXXXX", &p) >= 0);
345
346 fd = open(p, O_RDONLY|O_DIRECTORY|O_CLOEXEC);
347 assert_se(fd >= 0);
348
349 assert_se(access_fd(fd, R_OK) >= 0);
350 assert_se(access_fd(fd, F_OK) >= 0);
351 assert_se(access_fd(fd, W_OK) >= 0);
352
353 assert_se(fchmod(fd, 0000) >= 0);
354
355 assert_se(access_fd(fd, F_OK) >= 0);
356
357 if (geteuid() == 0) {
358 assert_se(access_fd(fd, R_OK) >= 0);
359 assert_se(access_fd(fd, W_OK) >= 0);
360 } else {
361 assert_se(access_fd(fd, R_OK) == -EACCES);
362 assert_se(access_fd(fd, W_OK) == -EACCES);
363 }
364 }
365
366 int main(int argc, char *argv[]) {
367 test_unlink_noerrno();
368 test_get_files_in_directory();
369 test_readlink_and_make_absolute();
370 test_var_tmp();
371 test_chase_symlinks();
372 test_dot_or_dot_dot();
373 test_access_fd();
374
375 return 0;
376 }