0001
0002
0003
0004
0005
0006
0007 #define _GNU_SOURCE
0008 #include <fcntl.h>
0009 #include <sched.h>
0010 #include <sys/stat.h>
0011 #include <sys/types.h>
0012 #include <sys/mount.h>
0013 #include <stdlib.h>
0014 #include <stdbool.h>
0015 #include <string.h>
0016
0017 #include "../kselftest.h"
0018 #include "helpers.h"
0019
0020
0021
0022
0023
0024
0025
0026
0027
0028
0029
0030
0031
0032
0033
0034
0035
0036
0037
0038
0039
0040
0041
0042
0043
0044
0045
0046
0047 int setup_testdir(void)
0048 {
0049 int dfd, tmpfd;
0050 char dirname[] = "/tmp/ksft-openat2-testdir.XXXXXX";
0051
0052
0053 E_unshare(CLONE_NEWNS);
0054 E_mount("", "/tmp", "", MS_PRIVATE, "");
0055
0056
0057 if (!mkdtemp(dirname))
0058 ksft_exit_fail_msg("setup_testdir: failed to create tmpdir\n");
0059 dfd = open(dirname, O_PATH | O_DIRECTORY);
0060 if (dfd < 0)
0061 ksft_exit_fail_msg("setup_testdir: failed to open tmpdir\n");
0062
0063
0064 E_mkdirat(dfd, "root", 0755);
0065 tmpfd = openat(dfd, "root", O_PATH | O_DIRECTORY);
0066 if (tmpfd < 0)
0067 ksft_exit_fail_msg("setup_testdir: failed to open tmpdir\n");
0068 close(dfd);
0069 dfd = tmpfd;
0070
0071 E_symlinkat("/proc/self/exe", dfd, "procexe");
0072 E_symlinkat("/proc/self/root", dfd, "procroot");
0073 E_mkdirat(dfd, "root", 0755);
0074
0075
0076 E_mkdirat(dfd, "mnt", 0755);
0077 E_fchdir(dfd);
0078 E_mount("tmpfs", "./mnt", "tmpfs", MS_NOSUID | MS_NODEV, "");
0079 E_symlinkat("../mnt/", dfd, "mnt/self");
0080 E_symlinkat("/mnt/", dfd, "mnt/absself");
0081
0082 E_mkdirat(dfd, "etc", 0755);
0083 E_touchat(dfd, "etc/passwd");
0084
0085 E_symlinkat("/newfile3", dfd, "creatlink");
0086 E_symlinkat("etc/", dfd, "reletc");
0087 E_symlinkat("etc/passwd", dfd, "relsym");
0088 E_symlinkat("/etc/", dfd, "absetc");
0089 E_symlinkat("/etc/passwd", dfd, "abssym");
0090 E_symlinkat("/cheeky", dfd, "abscheeky");
0091
0092 E_mkdirat(dfd, "cheeky", 0755);
0093
0094 E_symlinkat("/", dfd, "cheeky/absself");
0095 E_symlinkat("../../root/", dfd, "cheeky/self");
0096 E_symlinkat("/../../root/", dfd, "cheeky/garbageself");
0097
0098 E_symlinkat("../cheeky/../etc/../etc/passwd", dfd, "cheeky/passwd");
0099 E_symlinkat("/../cheeky/../etc/../etc/passwd", dfd, "cheeky/abspasswd");
0100
0101 E_symlinkat("../../../../../../../../../../../../../../etc/passwd",
0102 dfd, "cheeky/dotdotlink");
0103 E_symlinkat("/../../../../../../../../../../../../../../etc/passwd",
0104 dfd, "cheeky/garbagelink");
0105
0106 return dfd;
0107 }
0108
0109 struct basic_test {
0110 const char *name;
0111 const char *dir;
0112 const char *path;
0113 struct open_how how;
0114 bool pass;
0115 union {
0116 int err;
0117 const char *path;
0118 } out;
0119 };
0120
0121 #define NUM_OPENAT2_OPATH_TESTS 88
0122
0123 void test_openat2_opath_tests(void)
0124 {
0125 int rootfd, hardcoded_fd;
0126 char *procselfexe, *hardcoded_fdpath;
0127
0128 E_asprintf(&procselfexe, "/proc/%d/exe", getpid());
0129 rootfd = setup_testdir();
0130
0131 hardcoded_fd = open("/dev/null", O_RDONLY);
0132 E_assert(hardcoded_fd >= 0, "open fd to hardcode");
0133 E_asprintf(&hardcoded_fdpath, "self/fd/%d", hardcoded_fd);
0134
0135 struct basic_test tests[] = {
0136
0137
0138 { .name = "[beneath] jump to /",
0139 .path = "/", .how.resolve = RESOLVE_BENEATH,
0140 .out.err = -EXDEV, .pass = false },
0141 { .name = "[beneath] absolute link to $root",
0142 .path = "cheeky/absself", .how.resolve = RESOLVE_BENEATH,
0143 .out.err = -EXDEV, .pass = false },
0144 { .name = "[beneath] chained absolute links to $root",
0145 .path = "abscheeky/absself", .how.resolve = RESOLVE_BENEATH,
0146 .out.err = -EXDEV, .pass = false },
0147 { .name = "[beneath] jump outside $root",
0148 .path = "..", .how.resolve = RESOLVE_BENEATH,
0149 .out.err = -EXDEV, .pass = false },
0150 { .name = "[beneath] temporary jump outside $root",
0151 .path = "../root/", .how.resolve = RESOLVE_BENEATH,
0152 .out.err = -EXDEV, .pass = false },
0153 { .name = "[beneath] symlink temporary jump outside $root",
0154 .path = "cheeky/self", .how.resolve = RESOLVE_BENEATH,
0155 .out.err = -EXDEV, .pass = false },
0156 { .name = "[beneath] chained symlink temporary jump outside $root",
0157 .path = "abscheeky/self", .how.resolve = RESOLVE_BENEATH,
0158 .out.err = -EXDEV, .pass = false },
0159 { .name = "[beneath] garbage links to $root",
0160 .path = "cheeky/garbageself", .how.resolve = RESOLVE_BENEATH,
0161 .out.err = -EXDEV, .pass = false },
0162 { .name = "[beneath] chained garbage links to $root",
0163 .path = "abscheeky/garbageself", .how.resolve = RESOLVE_BENEATH,
0164 .out.err = -EXDEV, .pass = false },
0165
0166 { .name = "[beneath] ordinary path to 'root'",
0167 .path = "root", .how.resolve = RESOLVE_BENEATH,
0168 .out.path = "root", .pass = true },
0169 { .name = "[beneath] ordinary path to 'etc'",
0170 .path = "etc", .how.resolve = RESOLVE_BENEATH,
0171 .out.path = "etc", .pass = true },
0172 { .name = "[beneath] ordinary path to 'etc/passwd'",
0173 .path = "etc/passwd", .how.resolve = RESOLVE_BENEATH,
0174 .out.path = "etc/passwd", .pass = true },
0175 { .name = "[beneath] relative symlink inside $root",
0176 .path = "relsym", .how.resolve = RESOLVE_BENEATH,
0177 .out.path = "etc/passwd", .pass = true },
0178 { .name = "[beneath] chained-'..' relative symlink inside $root",
0179 .path = "cheeky/passwd", .how.resolve = RESOLVE_BENEATH,
0180 .out.path = "etc/passwd", .pass = true },
0181 { .name = "[beneath] absolute symlink component outside $root",
0182 .path = "abscheeky/passwd", .how.resolve = RESOLVE_BENEATH,
0183 .out.err = -EXDEV, .pass = false },
0184 { .name = "[beneath] absolute symlink target outside $root",
0185 .path = "abssym", .how.resolve = RESOLVE_BENEATH,
0186 .out.err = -EXDEV, .pass = false },
0187 { .name = "[beneath] absolute path outside $root",
0188 .path = "/etc/passwd", .how.resolve = RESOLVE_BENEATH,
0189 .out.err = -EXDEV, .pass = false },
0190 { .name = "[beneath] cheeky absolute path outside $root",
0191 .path = "cheeky/abspasswd", .how.resolve = RESOLVE_BENEATH,
0192 .out.err = -EXDEV, .pass = false },
0193 { .name = "[beneath] chained cheeky absolute path outside $root",
0194 .path = "abscheeky/abspasswd", .how.resolve = RESOLVE_BENEATH,
0195 .out.err = -EXDEV, .pass = false },
0196
0197 { .name = "[beneath] tricky '..'-chained symlink outside $root",
0198 .path = "cheeky/dotdotlink", .how.resolve = RESOLVE_BENEATH,
0199 .out.err = -EXDEV, .pass = false },
0200 { .name = "[beneath] tricky absolute + '..'-chained symlink outside $root",
0201 .path = "abscheeky/dotdotlink", .how.resolve = RESOLVE_BENEATH,
0202 .out.err = -EXDEV, .pass = false },
0203 { .name = "[beneath] tricky garbage link outside $root",
0204 .path = "cheeky/garbagelink", .how.resolve = RESOLVE_BENEATH,
0205 .out.err = -EXDEV, .pass = false },
0206 { .name = "[beneath] tricky absolute + garbage link outside $root",
0207 .path = "abscheeky/garbagelink", .how.resolve = RESOLVE_BENEATH,
0208 .out.err = -EXDEV, .pass = false },
0209
0210
0211
0212 { .name = "[in_root] jump to /",
0213 .path = "/", .how.resolve = RESOLVE_IN_ROOT,
0214 .out.path = NULL, .pass = true },
0215 { .name = "[in_root] absolute symlink to /root",
0216 .path = "cheeky/absself", .how.resolve = RESOLVE_IN_ROOT,
0217 .out.path = NULL, .pass = true },
0218 { .name = "[in_root] chained absolute symlinks to /root",
0219 .path = "abscheeky/absself", .how.resolve = RESOLVE_IN_ROOT,
0220 .out.path = NULL, .pass = true },
0221 { .name = "[in_root] '..' at root",
0222 .path = "..", .how.resolve = RESOLVE_IN_ROOT,
0223 .out.path = NULL, .pass = true },
0224 { .name = "[in_root] '../root' at root",
0225 .path = "../root/", .how.resolve = RESOLVE_IN_ROOT,
0226 .out.path = "root", .pass = true },
0227 { .name = "[in_root] relative symlink containing '..' above root",
0228 .path = "cheeky/self", .how.resolve = RESOLVE_IN_ROOT,
0229 .out.path = "root", .pass = true },
0230 { .name = "[in_root] garbage link to /root",
0231 .path = "cheeky/garbageself", .how.resolve = RESOLVE_IN_ROOT,
0232 .out.path = "root", .pass = true },
0233 { .name = "[in_root] chained garbage links to /root",
0234 .path = "abscheeky/garbageself", .how.resolve = RESOLVE_IN_ROOT,
0235 .out.path = "root", .pass = true },
0236 { .name = "[in_root] relative path to 'root'",
0237 .path = "root", .how.resolve = RESOLVE_IN_ROOT,
0238 .out.path = "root", .pass = true },
0239 { .name = "[in_root] relative path to 'etc'",
0240 .path = "etc", .how.resolve = RESOLVE_IN_ROOT,
0241 .out.path = "etc", .pass = true },
0242 { .name = "[in_root] relative path to 'etc/passwd'",
0243 .path = "etc/passwd", .how.resolve = RESOLVE_IN_ROOT,
0244 .out.path = "etc/passwd", .pass = true },
0245 { .name = "[in_root] relative symlink to 'etc/passwd'",
0246 .path = "relsym", .how.resolve = RESOLVE_IN_ROOT,
0247 .out.path = "etc/passwd", .pass = true },
0248 { .name = "[in_root] chained-'..' relative symlink to 'etc/passwd'",
0249 .path = "cheeky/passwd", .how.resolve = RESOLVE_IN_ROOT,
0250 .out.path = "etc/passwd", .pass = true },
0251 { .name = "[in_root] chained-'..' absolute + relative symlink to 'etc/passwd'",
0252 .path = "abscheeky/passwd", .how.resolve = RESOLVE_IN_ROOT,
0253 .out.path = "etc/passwd", .pass = true },
0254 { .name = "[in_root] absolute symlink to 'etc/passwd'",
0255 .path = "abssym", .how.resolve = RESOLVE_IN_ROOT,
0256 .out.path = "etc/passwd", .pass = true },
0257 { .name = "[in_root] absolute path 'etc/passwd'",
0258 .path = "/etc/passwd", .how.resolve = RESOLVE_IN_ROOT,
0259 .out.path = "etc/passwd", .pass = true },
0260 { .name = "[in_root] cheeky absolute path 'etc/passwd'",
0261 .path = "cheeky/abspasswd", .how.resolve = RESOLVE_IN_ROOT,
0262 .out.path = "etc/passwd", .pass = true },
0263 { .name = "[in_root] chained cheeky absolute path 'etc/passwd'",
0264 .path = "abscheeky/abspasswd", .how.resolve = RESOLVE_IN_ROOT,
0265 .out.path = "etc/passwd", .pass = true },
0266 { .name = "[in_root] tricky '..'-chained symlink outside $root",
0267 .path = "cheeky/dotdotlink", .how.resolve = RESOLVE_IN_ROOT,
0268 .out.path = "etc/passwd", .pass = true },
0269 { .name = "[in_root] tricky absolute + '..'-chained symlink outside $root",
0270 .path = "abscheeky/dotdotlink", .how.resolve = RESOLVE_IN_ROOT,
0271 .out.path = "etc/passwd", .pass = true },
0272 { .name = "[in_root] tricky absolute path + absolute + '..'-chained symlink outside $root",
0273 .path = "/../../../../abscheeky/dotdotlink", .how.resolve = RESOLVE_IN_ROOT,
0274 .out.path = "etc/passwd", .pass = true },
0275 { .name = "[in_root] tricky garbage link outside $root",
0276 .path = "cheeky/garbagelink", .how.resolve = RESOLVE_IN_ROOT,
0277 .out.path = "etc/passwd", .pass = true },
0278 { .name = "[in_root] tricky absolute + garbage link outside $root",
0279 .path = "abscheeky/garbagelink", .how.resolve = RESOLVE_IN_ROOT,
0280 .out.path = "etc/passwd", .pass = true },
0281 { .name = "[in_root] tricky absolute path + absolute + garbage link outside $root",
0282 .path = "/../../../../abscheeky/garbagelink", .how.resolve = RESOLVE_IN_ROOT,
0283 .out.path = "etc/passwd", .pass = true },
0284
0285 { .name = "[in_root] O_CREAT of relative path inside $root",
0286 .path = "newfile1", .how.flags = O_CREAT,
0287 .how.mode = 0700,
0288 .how.resolve = RESOLVE_IN_ROOT,
0289 .out.path = "newfile1", .pass = true },
0290 { .name = "[in_root] O_CREAT of absolute path",
0291 .path = "/newfile2", .how.flags = O_CREAT,
0292 .how.mode = 0700,
0293 .how.resolve = RESOLVE_IN_ROOT,
0294 .out.path = "newfile2", .pass = true },
0295 { .name = "[in_root] O_CREAT of tricky symlink outside root",
0296 .path = "/creatlink", .how.flags = O_CREAT,
0297 .how.mode = 0700,
0298 .how.resolve = RESOLVE_IN_ROOT,
0299 .out.path = "newfile3", .pass = true },
0300
0301
0302
0303 { .name = "[no_xdev] cross into $mnt",
0304 .path = "mnt", .how.resolve = RESOLVE_NO_XDEV,
0305 .out.err = -EXDEV, .pass = false },
0306 { .name = "[no_xdev] cross into $mnt/",
0307 .path = "mnt/", .how.resolve = RESOLVE_NO_XDEV,
0308 .out.err = -EXDEV, .pass = false },
0309 { .name = "[no_xdev] cross into $mnt/.",
0310 .path = "mnt/.", .how.resolve = RESOLVE_NO_XDEV,
0311 .out.err = -EXDEV, .pass = false },
0312
0313 { .name = "[no_xdev] goto mountpoint root",
0314 .dir = "mnt", .path = ".", .how.resolve = RESOLVE_NO_XDEV,
0315 .out.path = "mnt", .pass = true },
0316 { .name = "[no_xdev] cross up through '..'",
0317 .dir = "mnt", .path = "..", .how.resolve = RESOLVE_NO_XDEV,
0318 .out.err = -EXDEV, .pass = false },
0319 { .name = "[no_xdev] temporary cross up through '..'",
0320 .dir = "mnt", .path = "../mnt", .how.resolve = RESOLVE_NO_XDEV,
0321 .out.err = -EXDEV, .pass = false },
0322 { .name = "[no_xdev] temporary relative symlink cross up",
0323 .dir = "mnt", .path = "self", .how.resolve = RESOLVE_NO_XDEV,
0324 .out.err = -EXDEV, .pass = false },
0325 { .name = "[no_xdev] temporary absolute symlink cross up",
0326 .dir = "mnt", .path = "absself", .how.resolve = RESOLVE_NO_XDEV,
0327 .out.err = -EXDEV, .pass = false },
0328
0329 { .name = "[no_xdev] jump to / directly",
0330 .dir = "mnt", .path = "/", .how.resolve = RESOLVE_NO_XDEV,
0331 .out.path = "/", .pass = true },
0332 { .name = "[no_xdev] jump to / (from /) directly",
0333 .dir = "/", .path = "/", .how.resolve = RESOLVE_NO_XDEV,
0334 .out.path = "/", .pass = true },
0335 { .name = "[no_xdev] jump to / then proc",
0336 .path = "/proc/1", .how.resolve = RESOLVE_NO_XDEV,
0337 .out.err = -EXDEV, .pass = false },
0338 { .name = "[no_xdev] jump to / then tmp",
0339 .path = "/tmp", .how.resolve = RESOLVE_NO_XDEV,
0340 .out.err = -EXDEV, .pass = false },
0341
0342 { .name = "[no_xdev] cross through magic-link to self/root",
0343 .dir = "/proc", .path = "self/root", .how.resolve = RESOLVE_NO_XDEV,
0344 .out.err = -EXDEV, .pass = false },
0345 { .name = "[no_xdev] cross through magic-link to self/cwd",
0346 .dir = "/proc", .path = "self/cwd", .how.resolve = RESOLVE_NO_XDEV,
0347 .out.err = -EXDEV, .pass = false },
0348
0349 { .name = "[no_xdev] jump through magic-link to same procfs",
0350 .dir = "/proc", .path = hardcoded_fdpath, .how.resolve = RESOLVE_NO_XDEV,
0351 .out.path = "/proc", .pass = true, },
0352
0353
0354
0355 { .name = "[no_magiclinks] ordinary relative symlink",
0356 .path = "relsym", .how.resolve = RESOLVE_NO_MAGICLINKS,
0357 .out.path = "etc/passwd", .pass = true },
0358
0359 { .name = "[no_magiclinks] symlink to magic-link",
0360 .path = "procexe", .how.resolve = RESOLVE_NO_MAGICLINKS,
0361 .out.err = -ELOOP, .pass = false },
0362 { .name = "[no_magiclinks] normal path to magic-link",
0363 .path = "/proc/self/exe", .how.resolve = RESOLVE_NO_MAGICLINKS,
0364 .out.err = -ELOOP, .pass = false },
0365 { .name = "[no_magiclinks] normal path to magic-link with O_NOFOLLOW",
0366 .path = "/proc/self/exe", .how.flags = O_NOFOLLOW,
0367 .how.resolve = RESOLVE_NO_MAGICLINKS,
0368 .out.path = procselfexe, .pass = true },
0369 { .name = "[no_magiclinks] symlink to magic-link path component",
0370 .path = "procroot/etc", .how.resolve = RESOLVE_NO_MAGICLINKS,
0371 .out.err = -ELOOP, .pass = false },
0372 { .name = "[no_magiclinks] magic-link path component",
0373 .path = "/proc/self/root/etc", .how.resolve = RESOLVE_NO_MAGICLINKS,
0374 .out.err = -ELOOP, .pass = false },
0375 { .name = "[no_magiclinks] magic-link path component with O_NOFOLLOW",
0376 .path = "/proc/self/root/etc", .how.flags = O_NOFOLLOW,
0377 .how.resolve = RESOLVE_NO_MAGICLINKS,
0378 .out.err = -ELOOP, .pass = false },
0379
0380
0381
0382 { .name = "[no_symlinks] ordinary path to '.'",
0383 .path = ".", .how.resolve = RESOLVE_NO_SYMLINKS,
0384 .out.path = NULL, .pass = true },
0385 { .name = "[no_symlinks] ordinary path to 'root'",
0386 .path = "root", .how.resolve = RESOLVE_NO_SYMLINKS,
0387 .out.path = "root", .pass = true },
0388 { .name = "[no_symlinks] ordinary path to 'etc'",
0389 .path = "etc", .how.resolve = RESOLVE_NO_SYMLINKS,
0390 .out.path = "etc", .pass = true },
0391 { .name = "[no_symlinks] ordinary path to 'etc/passwd'",
0392 .path = "etc/passwd", .how.resolve = RESOLVE_NO_SYMLINKS,
0393 .out.path = "etc/passwd", .pass = true },
0394
0395 { .name = "[no_symlinks] relative symlink target",
0396 .path = "relsym", .how.resolve = RESOLVE_NO_SYMLINKS,
0397 .out.err = -ELOOP, .pass = false },
0398 { .name = "[no_symlinks] relative symlink component",
0399 .path = "reletc/passwd", .how.resolve = RESOLVE_NO_SYMLINKS,
0400 .out.err = -ELOOP, .pass = false },
0401 { .name = "[no_symlinks] absolute symlink target",
0402 .path = "abssym", .how.resolve = RESOLVE_NO_SYMLINKS,
0403 .out.err = -ELOOP, .pass = false },
0404 { .name = "[no_symlinks] absolute symlink component",
0405 .path = "absetc/passwd", .how.resolve = RESOLVE_NO_SYMLINKS,
0406 .out.err = -ELOOP, .pass = false },
0407 { .name = "[no_symlinks] cheeky garbage link",
0408 .path = "cheeky/garbagelink", .how.resolve = RESOLVE_NO_SYMLINKS,
0409 .out.err = -ELOOP, .pass = false },
0410 { .name = "[no_symlinks] cheeky absolute + garbage link",
0411 .path = "abscheeky/garbagelink", .how.resolve = RESOLVE_NO_SYMLINKS,
0412 .out.err = -ELOOP, .pass = false },
0413 { .name = "[no_symlinks] cheeky absolute + absolute symlink",
0414 .path = "abscheeky/absself", .how.resolve = RESOLVE_NO_SYMLINKS,
0415 .out.err = -ELOOP, .pass = false },
0416
0417 { .name = "[no_symlinks] relative symlink with O_NOFOLLOW",
0418 .path = "relsym", .how.flags = O_NOFOLLOW,
0419 .how.resolve = RESOLVE_NO_SYMLINKS,
0420 .out.path = "relsym", .pass = true },
0421 { .name = "[no_symlinks] absolute symlink with O_NOFOLLOW",
0422 .path = "abssym", .how.flags = O_NOFOLLOW,
0423 .how.resolve = RESOLVE_NO_SYMLINKS,
0424 .out.path = "abssym", .pass = true },
0425 { .name = "[no_symlinks] trailing symlink with O_NOFOLLOW",
0426 .path = "cheeky/garbagelink", .how.flags = O_NOFOLLOW,
0427 .how.resolve = RESOLVE_NO_SYMLINKS,
0428 .out.path = "cheeky/garbagelink", .pass = true },
0429 { .name = "[no_symlinks] multiple symlink components with O_NOFOLLOW",
0430 .path = "abscheeky/absself", .how.flags = O_NOFOLLOW,
0431 .how.resolve = RESOLVE_NO_SYMLINKS,
0432 .out.err = -ELOOP, .pass = false },
0433 { .name = "[no_symlinks] multiple symlink (and garbage link) components with O_NOFOLLOW",
0434 .path = "abscheeky/garbagelink", .how.flags = O_NOFOLLOW,
0435 .how.resolve = RESOLVE_NO_SYMLINKS,
0436 .out.err = -ELOOP, .pass = false },
0437 };
0438
0439 BUILD_BUG_ON(ARRAY_LEN(tests) != NUM_OPENAT2_OPATH_TESTS);
0440
0441 for (int i = 0; i < ARRAY_LEN(tests); i++) {
0442 int dfd, fd;
0443 char *fdpath = NULL;
0444 bool failed;
0445 void (*resultfn)(const char *msg, ...) = ksft_test_result_pass;
0446 struct basic_test *test = &tests[i];
0447
0448 if (!openat2_supported) {
0449 ksft_print_msg("openat2(2) unsupported\n");
0450 resultfn = ksft_test_result_skip;
0451 goto skip;
0452 }
0453
0454
0455 if (!(test->how.flags & O_CREAT))
0456 test->how.flags |= O_PATH;
0457
0458 if (test->dir)
0459 dfd = openat(rootfd, test->dir, O_PATH | O_DIRECTORY);
0460 else
0461 dfd = dup(rootfd);
0462 E_assert(dfd, "failed to openat root '%s': %m", test->dir);
0463
0464 E_dup2(dfd, hardcoded_fd);
0465
0466 fd = sys_openat2(dfd, test->path, &test->how);
0467 if (test->pass)
0468 failed = (fd < 0 || !fdequal(fd, rootfd, test->out.path));
0469 else
0470 failed = (fd != test->out.err);
0471 if (fd >= 0) {
0472 fdpath = fdreadlink(fd);
0473 close(fd);
0474 }
0475 close(dfd);
0476
0477 if (failed) {
0478 resultfn = ksft_test_result_fail;
0479
0480 ksft_print_msg("openat2 unexpectedly returned ");
0481 if (fdpath)
0482 ksft_print_msg("%d['%s']\n", fd, fdpath);
0483 else
0484 ksft_print_msg("%d (%s)\n", fd, strerror(-fd));
0485 }
0486
0487 skip:
0488 if (test->pass)
0489 resultfn("%s gives path '%s'\n", test->name,
0490 test->out.path ?: ".");
0491 else
0492 resultfn("%s fails with %d (%s)\n", test->name,
0493 test->out.err, strerror(-test->out.err));
0494
0495 fflush(stdout);
0496 free(fdpath);
0497 }
0498
0499 free(procselfexe);
0500 close(rootfd);
0501
0502 free(hardcoded_fdpath);
0503 close(hardcoded_fd);
0504 }
0505
0506 #define NUM_TESTS NUM_OPENAT2_OPATH_TESTS
0507
0508 int main(int argc, char **argv)
0509 {
0510 ksft_print_header();
0511 ksft_set_plan(NUM_TESTS);
0512
0513
0514 if (geteuid() != 0)
0515 ksft_exit_skip("all tests require euid == 0\n");
0516
0517 test_openat2_opath_tests();
0518
0519 if (ksft_get_fail_cnt() + ksft_get_error_cnt() > 0)
0520 ksft_exit_fail();
0521 else
0522 ksft_exit_pass();
0523 }