1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * Author: Aleksa Sarai <cyphar@cyphar.com> 4 * Copyright (C) 2018-2019 SUSE LLC. 5 * Copyright (C) 2026 Amutable GmbH 6 */ 7 8 #ifndef __RESOLVEAT_H__ 9 #define __RESOLVEAT_H__ 10 11 #define _GNU_SOURCE 12 #include <stdint.h> 13 #include <stdbool.h> 14 #include <errno.h> 15 #include <limits.h> 16 #include <linux/types.h> 17 #include <linux/unistd.h> 18 #include <linux/openat2.h> 19 #include "kselftest_harness.h" 20 21 #define BUILD_BUG_ON(e) ((void)(sizeof(struct { int:(-!!(e)); }))) 22 23 #define OPEN_HOW_SIZE_VER0 24 /* sizeof first published struct */ 24 #define OPEN_HOW_SIZE_LATEST OPEN_HOW_SIZE_VER0 25 26 __maybe_unused 27 static bool needs_openat2(const struct open_how *how) 28 { 29 return how->resolve != 0; 30 } 31 32 __maybe_unused 33 static int raw_openat2(int dfd, const char *path, void *how, size_t size) 34 { 35 int ret = syscall(__NR_openat2, dfd, path, how, size); 36 37 return ret >= 0 ? ret : -errno; 38 } 39 40 __maybe_unused 41 static int sys_openat2(int dfd, const char *path, struct open_how *how) 42 { 43 return raw_openat2(dfd, path, how, sizeof(*how)); 44 } 45 46 __maybe_unused 47 static int sys_openat(int dfd, const char *path, struct open_how *how) 48 { 49 int ret = openat(dfd, path, how->flags, how->mode); 50 51 return ret >= 0 ? ret : -errno; 52 } 53 54 __maybe_unused 55 static int sys_renameat2(int olddirfd, const char *oldpath, 56 int newdirfd, const char *newpath, unsigned int flags) 57 { 58 int ret = syscall(__NR_renameat2, olddirfd, oldpath, 59 newdirfd, newpath, flags); 60 61 return ret >= 0 ? ret : -errno; 62 } 63 64 __maybe_unused 65 static int touchat(int dfd, const char *path) 66 { 67 int fd = openat(dfd, path, O_CREAT, 0700); 68 69 if (fd >= 0) 70 close(fd); 71 return fd; 72 } 73 74 __maybe_unused 75 static char *fdreadlink(struct __test_metadata *_metadata, int fd) 76 { 77 char *target, *tmp; 78 79 ASSERT_GT(asprintf(&tmp, "/proc/self/fd/%d", fd), 0); 80 81 target = malloc(PATH_MAX); 82 ASSERT_NE(target, NULL); 83 memset(target, 0, PATH_MAX); 84 85 ASSERT_GT(readlink(tmp, target, PATH_MAX), 0); 86 87 free(tmp); 88 return target; 89 } 90 91 __maybe_unused 92 static bool fdequal(struct __test_metadata *_metadata, int fd, 93 int dfd, const char *path) 94 { 95 char *fdpath, *dfdpath, *other; 96 bool cmp; 97 98 fdpath = fdreadlink(_metadata, fd); 99 dfdpath = fdreadlink(_metadata, dfd); 100 101 if (!path) { 102 ASSERT_GT(asprintf(&other, "%s", dfdpath), 0); 103 } else if (*path == '/') { 104 ASSERT_GT(asprintf(&other, "%s", path), 0); 105 } else { 106 ASSERT_GT(asprintf(&other, "%s/%s", dfdpath, path), 0); 107 } 108 109 cmp = !strcmp(fdpath, other); 110 111 free(fdpath); 112 free(dfdpath); 113 free(other); 114 return cmp; 115 } 116 117 static bool openat2_supported = false; 118 119 __attribute__((constructor)) 120 static void __detect_openat2_supported(void) 121 { 122 struct open_how how = {}; 123 int fd; 124 125 BUILD_BUG_ON(sizeof(struct open_how) != OPEN_HOW_SIZE_VER0); 126 127 /* Check openat2(2) support. */ 128 fd = sys_openat2(AT_FDCWD, ".", &how); 129 openat2_supported = (fd >= 0); 130 131 if (fd >= 0) 132 close(fd); 133 } 134 135 #endif /* __RESOLVEAT_H__ */ 136