1
2
3
4
5
6
7#define _GNU_SOURCE
8#include <fcntl.h>
9#include <sched.h>
10#include <sys/stat.h>
11#include <sys/types.h>
12#include <sys/mount.h>
13#include <stdlib.h>
14#include <stdbool.h>
15#include <string.h>
16
17#include "../kselftest.h"
18#include "helpers.h"
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47int setup_testdir(void)
48{
49 int dfd, tmpfd;
50 char dirname[] = "/tmp/ksft-openat2-testdir.XXXXXX";
51
52
53 E_unshare(CLONE_NEWNS);
54 E_mount("", "/tmp", "", MS_PRIVATE, "");
55
56
57 if (!mkdtemp(dirname))
58 ksft_exit_fail_msg("setup_testdir: failed to create tmpdir\n");
59 dfd = open(dirname, O_PATH | O_DIRECTORY);
60 if (dfd < 0)
61 ksft_exit_fail_msg("setup_testdir: failed to open tmpdir\n");
62
63
64 E_mkdirat(dfd, "root", 0755);
65 tmpfd = openat(dfd, "root", O_PATH | O_DIRECTORY);
66 if (tmpfd < 0)
67 ksft_exit_fail_msg("setup_testdir: failed to open tmpdir\n");
68 close(dfd);
69 dfd = tmpfd;
70
71 E_symlinkat("/proc/self/exe", dfd, "procexe");
72 E_symlinkat("/proc/self/root", dfd, "procroot");
73 E_mkdirat(dfd, "root", 0755);
74
75
76 E_mkdirat(dfd, "mnt", 0755);
77 E_fchdir(dfd);
78 E_mount("tmpfs", "./mnt", "tmpfs", MS_NOSUID | MS_NODEV, "");
79 E_symlinkat("../mnt/", dfd, "mnt/self");
80 E_symlinkat("/mnt/", dfd, "mnt/absself");
81
82 E_mkdirat(dfd, "etc", 0755);
83 E_touchat(dfd, "etc/passwd");
84
85 E_symlinkat("/newfile3", dfd, "creatlink");
86 E_symlinkat("etc/", dfd, "reletc");
87 E_symlinkat("etc/passwd", dfd, "relsym");
88 E_symlinkat("/etc/", dfd, "absetc");
89 E_symlinkat("/etc/passwd", dfd, "abssym");
90 E_symlinkat("/cheeky", dfd, "abscheeky");
91
92 E_mkdirat(dfd, "cheeky", 0755);
93
94 E_symlinkat("/", dfd, "cheeky/absself");
95 E_symlinkat("../../root/", dfd, "cheeky/self");
96 E_symlinkat("/../../root/", dfd, "cheeky/garbageself");
97
98 E_symlinkat("../cheeky/../etc/../etc/passwd", dfd, "cheeky/passwd");
99 E_symlinkat("/../cheeky/../etc/../etc/passwd", dfd, "cheeky/abspasswd");
100
101 E_symlinkat("../../../../../../../../../../../../../../etc/passwd",
102 dfd, "cheeky/dotdotlink");
103 E_symlinkat("/../../../../../../../../../../../../../../etc/passwd",
104 dfd, "cheeky/garbagelink");
105
106 return dfd;
107}
108
109struct basic_test {
110 const char *name;
111 const char *dir;
112 const char *path;
113 struct open_how how;
114 bool pass;
115 union {
116 int err;
117 const char *path;
118 } out;
119};
120
121#define NUM_OPENAT2_OPATH_TESTS 88
122
123void test_openat2_opath_tests(void)
124{
125 int rootfd, hardcoded_fd;
126 char *procselfexe, *hardcoded_fdpath;
127
128 E_asprintf(&procselfexe, "/proc/%d/exe", getpid());
129 rootfd = setup_testdir();
130
131 hardcoded_fd = open("/dev/null", O_RDONLY);
132 E_assert(hardcoded_fd >= 0, "open fd to hardcode");
133 E_asprintf(&hardcoded_fdpath, "self/fd/%d", hardcoded_fd);
134
135 struct basic_test tests[] = {
136
137
138 { .name = "[beneath] jump to /",
139 .path = "/", .how.resolve = RESOLVE_BENEATH,
140 .out.err = -EXDEV, .pass = false },
141 { .name = "[beneath] absolute link to $root",
142 .path = "cheeky/absself", .how.resolve = RESOLVE_BENEATH,
143 .out.err = -EXDEV, .pass = false },
144 { .name = "[beneath] chained absolute links to $root",
145 .path = "abscheeky/absself", .how.resolve = RESOLVE_BENEATH,
146 .out.err = -EXDEV, .pass = false },
147 { .name = "[beneath] jump outside $root",
148 .path = "..", .how.resolve = RESOLVE_BENEATH,
149 .out.err = -EXDEV, .pass = false },
150 { .name = "[beneath] temporary jump outside $root",
151 .path = "../root/", .how.resolve = RESOLVE_BENEATH,
152 .out.err = -EXDEV, .pass = false },
153 { .name = "[beneath] symlink temporary jump outside $root",
154 .path = "cheeky/self", .how.resolve = RESOLVE_BENEATH,
155 .out.err = -EXDEV, .pass = false },
156 { .name = "[beneath] chained symlink temporary jump outside $root",
157 .path = "abscheeky/self", .how.resolve = RESOLVE_BENEATH,
158 .out.err = -EXDEV, .pass = false },
159 { .name = "[beneath] garbage links to $root",
160 .path = "cheeky/garbageself", .how.resolve = RESOLVE_BENEATH,
161 .out.err = -EXDEV, .pass = false },
162 { .name = "[beneath] chained garbage links to $root",
163 .path = "abscheeky/garbageself", .how.resolve = RESOLVE_BENEATH,
164 .out.err = -EXDEV, .pass = false },
165
166 { .name = "[beneath] ordinary path to 'root'",
167 .path = "root", .how.resolve = RESOLVE_BENEATH,
168 .out.path = "root", .pass = true },
169 { .name = "[beneath] ordinary path to 'etc'",
170 .path = "etc", .how.resolve = RESOLVE_BENEATH,
171 .out.path = "etc", .pass = true },
172 { .name = "[beneath] ordinary path to 'etc/passwd'",
173 .path = "etc/passwd", .how.resolve = RESOLVE_BENEATH,
174 .out.path = "etc/passwd", .pass = true },
175 { .name = "[beneath] relative symlink inside $root",
176 .path = "relsym", .how.resolve = RESOLVE_BENEATH,
177 .out.path = "etc/passwd", .pass = true },
178 { .name = "[beneath] chained-'..' relative symlink inside $root",
179 .path = "cheeky/passwd", .how.resolve = RESOLVE_BENEATH,
180 .out.path = "etc/passwd", .pass = true },
181 { .name = "[beneath] absolute symlink component outside $root",
182 .path = "abscheeky/passwd", .how.resolve = RESOLVE_BENEATH,
183 .out.err = -EXDEV, .pass = false },
184 { .name = "[beneath] absolute symlink target outside $root",
185 .path = "abssym", .how.resolve = RESOLVE_BENEATH,
186 .out.err = -EXDEV, .pass = false },
187 { .name = "[beneath] absolute path outside $root",
188 .path = "/etc/passwd", .how.resolve = RESOLVE_BENEATH,
189 .out.err = -EXDEV, .pass = false },
190 { .name = "[beneath] cheeky absolute path outside $root",
191 .path = "cheeky/abspasswd", .how.resolve = RESOLVE_BENEATH,
192 .out.err = -EXDEV, .pass = false },
193 { .name = "[beneath] chained cheeky absolute path outside $root",
194 .path = "abscheeky/abspasswd", .how.resolve = RESOLVE_BENEATH,
195 .out.err = -EXDEV, .pass = false },
196
197 { .name = "[beneath] tricky '..'-chained symlink outside $root",
198 .path = "cheeky/dotdotlink", .how.resolve = RESOLVE_BENEATH,
199 .out.err = -EXDEV, .pass = false },
200 { .name = "[beneath] tricky absolute + '..'-chained symlink outside $root",
201 .path = "abscheeky/dotdotlink", .how.resolve = RESOLVE_BENEATH,
202 .out.err = -EXDEV, .pass = false },
203 { .name = "[beneath] tricky garbage link outside $root",
204 .path = "cheeky/garbagelink", .how.resolve = RESOLVE_BENEATH,
205 .out.err = -EXDEV, .pass = false },
206 { .name = "[beneath] tricky absolute + garbage link outside $root",
207 .path = "abscheeky/garbagelink", .how.resolve = RESOLVE_BENEATH,
208 .out.err = -EXDEV, .pass = false },
209
210
211
212 { .name = "[in_root] jump to /",
213 .path = "/", .how.resolve = RESOLVE_IN_ROOT,
214 .out.path = NULL, .pass = true },
215 { .name = "[in_root] absolute symlink to /root",
216 .path = "cheeky/absself", .how.resolve = RESOLVE_IN_ROOT,
217 .out.path = NULL, .pass = true },
218 { .name = "[in_root] chained absolute symlinks to /root",
219 .path = "abscheeky/absself", .how.resolve = RESOLVE_IN_ROOT,
220 .out.path = NULL, .pass = true },
221 { .name = "[in_root] '..' at root",
222 .path = "..", .how.resolve = RESOLVE_IN_ROOT,
223 .out.path = NULL, .pass = true },
224 { .name = "[in_root] '../root' at root",
225 .path = "../root/", .how.resolve = RESOLVE_IN_ROOT,
226 .out.path = "root", .pass = true },
227 { .name = "[in_root] relative symlink containing '..' above root",
228 .path = "cheeky/self", .how.resolve = RESOLVE_IN_ROOT,
229 .out.path = "root", .pass = true },
230 { .name = "[in_root] garbage link to /root",
231 .path = "cheeky/garbageself", .how.resolve = RESOLVE_IN_ROOT,
232 .out.path = "root", .pass = true },
233 { .name = "[in_root] chained garbage links to /root",
234 .path = "abscheeky/garbageself", .how.resolve = RESOLVE_IN_ROOT,
235 .out.path = "root", .pass = true },
236 { .name = "[in_root] relative path to 'root'",
237 .path = "root", .how.resolve = RESOLVE_IN_ROOT,
238 .out.path = "root", .pass = true },
239 { .name = "[in_root] relative path to 'etc'",
240 .path = "etc", .how.resolve = RESOLVE_IN_ROOT,
241 .out.path = "etc", .pass = true },
242 { .name = "[in_root] relative path to 'etc/passwd'",
243 .path = "etc/passwd", .how.resolve = RESOLVE_IN_ROOT,
244 .out.path = "etc/passwd", .pass = true },
245 { .name = "[in_root] relative symlink to 'etc/passwd'",
246 .path = "relsym", .how.resolve = RESOLVE_IN_ROOT,
247 .out.path = "etc/passwd", .pass = true },
248 { .name = "[in_root] chained-'..' relative symlink to 'etc/passwd'",
249 .path = "cheeky/passwd", .how.resolve = RESOLVE_IN_ROOT,
250 .out.path = "etc/passwd", .pass = true },
251 { .name = "[in_root] chained-'..' absolute + relative symlink to 'etc/passwd'",
252 .path = "abscheeky/passwd", .how.resolve = RESOLVE_IN_ROOT,
253 .out.path = "etc/passwd", .pass = true },
254 { .name = "[in_root] absolute symlink to 'etc/passwd'",
255 .path = "abssym", .how.resolve = RESOLVE_IN_ROOT,
256 .out.path = "etc/passwd", .pass = true },
257 { .name = "[in_root] absolute path 'etc/passwd'",
258 .path = "/etc/passwd", .how.resolve = RESOLVE_IN_ROOT,
259 .out.path = "etc/passwd", .pass = true },
260 { .name = "[in_root] cheeky absolute path 'etc/passwd'",
261 .path = "cheeky/abspasswd", .how.resolve = RESOLVE_IN_ROOT,
262 .out.path = "etc/passwd", .pass = true },
263 { .name = "[in_root] chained cheeky absolute path 'etc/passwd'",
264 .path = "abscheeky/abspasswd", .how.resolve = RESOLVE_IN_ROOT,
265 .out.path = "etc/passwd", .pass = true },
266 { .name = "[in_root] tricky '..'-chained symlink outside $root",
267 .path = "cheeky/dotdotlink", .how.resolve = RESOLVE_IN_ROOT,
268 .out.path = "etc/passwd", .pass = true },
269 { .name = "[in_root] tricky absolute + '..'-chained symlink outside $root",
270 .path = "abscheeky/dotdotlink", .how.resolve = RESOLVE_IN_ROOT,
271 .out.path = "etc/passwd", .pass = true },
272 { .name = "[in_root] tricky absolute path + absolute + '..'-chained symlink outside $root",
273 .path = "/../../../../abscheeky/dotdotlink", .how.resolve = RESOLVE_IN_ROOT,
274 .out.path = "etc/passwd", .pass = true },
275 { .name = "[in_root] tricky garbage link outside $root",
276 .path = "cheeky/garbagelink", .how.resolve = RESOLVE_IN_ROOT,
277 .out.path = "etc/passwd", .pass = true },
278 { .name = "[in_root] tricky absolute + garbage link outside $root",
279 .path = "abscheeky/garbagelink", .how.resolve = RESOLVE_IN_ROOT,
280 .out.path = "etc/passwd", .pass = true },
281 { .name = "[in_root] tricky absolute path + absolute + garbage link outside $root",
282 .path = "/../../../../abscheeky/garbagelink", .how.resolve = RESOLVE_IN_ROOT,
283 .out.path = "etc/passwd", .pass = true },
284
285 { .name = "[in_root] O_CREAT of relative path inside $root",
286 .path = "newfile1", .how.flags = O_CREAT,
287 .how.mode = 0700,
288 .how.resolve = RESOLVE_IN_ROOT,
289 .out.path = "newfile1", .pass = true },
290 { .name = "[in_root] O_CREAT of absolute path",
291 .path = "/newfile2", .how.flags = O_CREAT,
292 .how.mode = 0700,
293 .how.resolve = RESOLVE_IN_ROOT,
294 .out.path = "newfile2", .pass = true },
295 { .name = "[in_root] O_CREAT of tricky symlink outside root",
296 .path = "/creatlink", .how.flags = O_CREAT,
297 .how.mode = 0700,
298 .how.resolve = RESOLVE_IN_ROOT,
299 .out.path = "newfile3", .pass = true },
300
301
302
303 { .name = "[no_xdev] cross into $mnt",
304 .path = "mnt", .how.resolve = RESOLVE_NO_XDEV,
305 .out.err = -EXDEV, .pass = false },
306 { .name = "[no_xdev] cross into $mnt/",
307 .path = "mnt/", .how.resolve = RESOLVE_NO_XDEV,
308 .out.err = -EXDEV, .pass = false },
309 { .name = "[no_xdev] cross into $mnt/.",
310 .path = "mnt/.", .how.resolve = RESOLVE_NO_XDEV,
311 .out.err = -EXDEV, .pass = false },
312
313 { .name = "[no_xdev] goto mountpoint root",
314 .dir = "mnt", .path = ".", .how.resolve = RESOLVE_NO_XDEV,
315 .out.path = "mnt", .pass = true },
316 { .name = "[no_xdev] cross up through '..'",
317 .dir = "mnt", .path = "..", .how.resolve = RESOLVE_NO_XDEV,
318 .out.err = -EXDEV, .pass = false },
319 { .name = "[no_xdev] temporary cross up through '..'",
320 .dir = "mnt", .path = "../mnt", .how.resolve = RESOLVE_NO_XDEV,
321 .out.err = -EXDEV, .pass = false },
322 { .name = "[no_xdev] temporary relative symlink cross up",
323 .dir = "mnt", .path = "self", .how.resolve = RESOLVE_NO_XDEV,
324 .out.err = -EXDEV, .pass = false },
325 { .name = "[no_xdev] temporary absolute symlink cross up",
326 .dir = "mnt", .path = "absself", .how.resolve = RESOLVE_NO_XDEV,
327 .out.err = -EXDEV, .pass = false },
328
329 { .name = "[no_xdev] jump to / directly",
330 .dir = "mnt", .path = "/", .how.resolve = RESOLVE_NO_XDEV,
331 .out.path = "/", .pass = true },
332 { .name = "[no_xdev] jump to / (from /) directly",
333 .dir = "/", .path = "/", .how.resolve = RESOLVE_NO_XDEV,
334 .out.path = "/", .pass = true },
335 { .name = "[no_xdev] jump to / then proc",
336 .path = "/proc/1", .how.resolve = RESOLVE_NO_XDEV,
337 .out.err = -EXDEV, .pass = false },
338 { .name = "[no_xdev] jump to / then tmp",
339 .path = "/tmp", .how.resolve = RESOLVE_NO_XDEV,
340 .out.err = -EXDEV, .pass = false },
341
342 { .name = "[no_xdev] cross through magic-link to self/root",
343 .dir = "/proc", .path = "self/root", .how.resolve = RESOLVE_NO_XDEV,
344 .out.err = -EXDEV, .pass = false },
345 { .name = "[no_xdev] cross through magic-link to self/cwd",
346 .dir = "/proc", .path = "self/cwd", .how.resolve = RESOLVE_NO_XDEV,
347 .out.err = -EXDEV, .pass = false },
348
349 { .name = "[no_xdev] jump through magic-link to same procfs",
350 .dir = "/proc", .path = hardcoded_fdpath, .how.resolve = RESOLVE_NO_XDEV,
351 .out.path = "/proc", .pass = true, },
352
353
354
355 { .name = "[no_magiclinks] ordinary relative symlink",
356 .path = "relsym", .how.resolve = RESOLVE_NO_MAGICLINKS,
357 .out.path = "etc/passwd", .pass = true },
358
359 { .name = "[no_magiclinks] symlink to magic-link",
360 .path = "procexe", .how.resolve = RESOLVE_NO_MAGICLINKS,
361 .out.err = -ELOOP, .pass = false },
362 { .name = "[no_magiclinks] normal path to magic-link",
363 .path = "/proc/self/exe", .how.resolve = RESOLVE_NO_MAGICLINKS,
364 .out.err = -ELOOP, .pass = false },
365 { .name = "[no_magiclinks] normal path to magic-link with O_NOFOLLOW",
366 .path = "/proc/self/exe", .how.flags = O_NOFOLLOW,
367 .how.resolve = RESOLVE_NO_MAGICLINKS,
368 .out.path = procselfexe, .pass = true },
369 { .name = "[no_magiclinks] symlink to magic-link path component",
370 .path = "procroot/etc", .how.resolve = RESOLVE_NO_MAGICLINKS,
371 .out.err = -ELOOP, .pass = false },
372 { .name = "[no_magiclinks] magic-link path component",
373 .path = "/proc/self/root/etc", .how.resolve = RESOLVE_NO_MAGICLINKS,
374 .out.err = -ELOOP, .pass = false },
375 { .name = "[no_magiclinks] magic-link path component with O_NOFOLLOW",
376 .path = "/proc/self/root/etc", .how.flags = O_NOFOLLOW,
377 .how.resolve = RESOLVE_NO_MAGICLINKS,
378 .out.err = -ELOOP, .pass = false },
379
380
381
382 { .name = "[no_symlinks] ordinary path to '.'",
383 .path = ".", .how.resolve = RESOLVE_NO_SYMLINKS,
384 .out.path = NULL, .pass = true },
385 { .name = "[no_symlinks] ordinary path to 'root'",
386 .path = "root", .how.resolve = RESOLVE_NO_SYMLINKS,
387 .out.path = "root", .pass = true },
388 { .name = "[no_symlinks] ordinary path to 'etc'",
389 .path = "etc", .how.resolve = RESOLVE_NO_SYMLINKS,
390 .out.path = "etc", .pass = true },
391 { .name = "[no_symlinks] ordinary path to 'etc/passwd'",
392 .path = "etc/passwd", .how.resolve = RESOLVE_NO_SYMLINKS,
393 .out.path = "etc/passwd", .pass = true },
394
395 { .name = "[no_symlinks] relative symlink target",
396 .path = "relsym", .how.resolve = RESOLVE_NO_SYMLINKS,
397 .out.err = -ELOOP, .pass = false },
398 { .name = "[no_symlinks] relative symlink component",
399 .path = "reletc/passwd", .how.resolve = RESOLVE_NO_SYMLINKS,
400 .out.err = -ELOOP, .pass = false },
401 { .name = "[no_symlinks] absolute symlink target",
402 .path = "abssym", .how.resolve = RESOLVE_NO_SYMLINKS,
403 .out.err = -ELOOP, .pass = false },
404 { .name = "[no_symlinks] absolute symlink component",
405 .path = "absetc/passwd", .how.resolve = RESOLVE_NO_SYMLINKS,
406 .out.err = -ELOOP, .pass = false },
407 { .name = "[no_symlinks] cheeky garbage link",
408 .path = "cheeky/garbagelink", .how.resolve = RESOLVE_NO_SYMLINKS,
409 .out.err = -ELOOP, .pass = false },
410 { .name = "[no_symlinks] cheeky absolute + garbage link",
411 .path = "abscheeky/garbagelink", .how.resolve = RESOLVE_NO_SYMLINKS,
412 .out.err = -ELOOP, .pass = false },
413 { .name = "[no_symlinks] cheeky absolute + absolute symlink",
414 .path = "abscheeky/absself", .how.resolve = RESOLVE_NO_SYMLINKS,
415 .out.err = -ELOOP, .pass = false },
416
417 { .name = "[no_symlinks] relative symlink with O_NOFOLLOW",
418 .path = "relsym", .how.flags = O_NOFOLLOW,
419 .how.resolve = RESOLVE_NO_SYMLINKS,
420 .out.path = "relsym", .pass = true },
421 { .name = "[no_symlinks] absolute symlink with O_NOFOLLOW",
422 .path = "abssym", .how.flags = O_NOFOLLOW,
423 .how.resolve = RESOLVE_NO_SYMLINKS,
424 .out.path = "abssym", .pass = true },
425 { .name = "[no_symlinks] trailing symlink with O_NOFOLLOW",
426 .path = "cheeky/garbagelink", .how.flags = O_NOFOLLOW,
427 .how.resolve = RESOLVE_NO_SYMLINKS,
428 .out.path = "cheeky/garbagelink", .pass = true },
429 { .name = "[no_symlinks] multiple symlink components with O_NOFOLLOW",
430 .path = "abscheeky/absself", .how.flags = O_NOFOLLOW,
431 .how.resolve = RESOLVE_NO_SYMLINKS,
432 .out.err = -ELOOP, .pass = false },
433 { .name = "[no_symlinks] multiple symlink (and garbage link) components with O_NOFOLLOW",
434 .path = "abscheeky/garbagelink", .how.flags = O_NOFOLLOW,
435 .how.resolve = RESOLVE_NO_SYMLINKS,
436 .out.err = -ELOOP, .pass = false },
437 };
438
439 BUILD_BUG_ON(ARRAY_LEN(tests) != NUM_OPENAT2_OPATH_TESTS);
440
441 for (int i = 0; i < ARRAY_LEN(tests); i++) {
442 int dfd, fd;
443 char *fdpath = NULL;
444 bool failed;
445 void (*resultfn)(const char *msg, ...) = ksft_test_result_pass;
446 struct basic_test *test = &tests[i];
447
448 if (!openat2_supported) {
449 ksft_print_msg("openat2(2) unsupported\n");
450 resultfn = ksft_test_result_skip;
451 goto skip;
452 }
453
454
455 if (!(test->how.flags & O_CREAT))
456 test->how.flags |= O_PATH;
457
458 if (test->dir)
459 dfd = openat(rootfd, test->dir, O_PATH | O_DIRECTORY);
460 else
461 dfd = dup(rootfd);
462 E_assert(dfd, "failed to openat root '%s': %m", test->dir);
463
464 E_dup2(dfd, hardcoded_fd);
465
466 fd = sys_openat2(dfd, test->path, &test->how);
467 if (test->pass)
468 failed = (fd < 0 || !fdequal(fd, rootfd, test->out.path));
469 else
470 failed = (fd != test->out.err);
471 if (fd >= 0) {
472 fdpath = fdreadlink(fd);
473 close(fd);
474 }
475 close(dfd);
476
477 if (failed) {
478 resultfn = ksft_test_result_fail;
479
480 ksft_print_msg("openat2 unexpectedly returned ");
481 if (fdpath)
482 ksft_print_msg("%d['%s']\n", fd, fdpath);
483 else
484 ksft_print_msg("%d (%s)\n", fd, strerror(-fd));
485 }
486
487skip:
488 if (test->pass)
489 resultfn("%s gives path '%s'\n", test->name,
490 test->out.path ?: ".");
491 else
492 resultfn("%s fails with %d (%s)\n", test->name,
493 test->out.err, strerror(-test->out.err));
494
495 fflush(stdout);
496 free(fdpath);
497 }
498
499 free(procselfexe);
500 close(rootfd);
501
502 free(hardcoded_fdpath);
503 close(hardcoded_fd);
504}
505
506#define NUM_TESTS NUM_OPENAT2_OPATH_TESTS
507
508int main(int argc, char **argv)
509{
510 ksft_print_header();
511 ksft_set_plan(NUM_TESTS);
512
513
514 if (geteuid() != 0)
515 ksft_exit_skip("all tests require euid == 0\n");
516
517 test_openat2_opath_tests();
518
519 if (ksft_get_fail_cnt() + ksft_get_error_cnt() > 0)
520 ksft_exit_fail();
521 else
522 ksft_exit_pass();
523}
524