// SPDX-License-Identifier: EUPL-1.2+ // SPDX-FileCopyrightText: 2022 Alyssa Ross #include #include #include #include #include #include #include #include #include #include #define STRINGIZE(x) STRINGIZE2(x) #define STRINGIZE2(x) #x [[gnu::malloc]] [[gnu::nonnull]] char *areadlink(const char *pathname) { char *target = NULL; size_t link_size, target_size = 4096; for (;;) { free(target); if (!(target = malloc(target_size))) return NULL; if ((link_size = readlink(pathname, target, target_size)) == -1) return NULL; if (link_size < target_size) break; if (target_size > (((size_t)-1) >> 1)) { errno = ENOMEM; return NULL; } target_size <<= 1; } target[link_size] = '\0'; return target; } int main(int argc, char **argv) { // -1 because both paths include a null terminator. char fdpath[sizeof "/proc/self/fd/" + sizeof(STRINGIZE(INT_MAX)) - 1]; char *target; int rootfd, targetfd; struct open_how how = { .flags = O_PATH }; // Ensure INT_MAX is actually defined, because otherwise // sizeof("INT_MAX") will silently be used instead in fdpath's // size. (void) INT_MAX; if (argc != 3) { fprintf(stderr, "Usage: %s root dir\n", argc ? argv[0] : "resolve_in_root"); return 1; } if ((rootfd = syscall(SYS_openat2, AT_FDCWD, argv[1], &how, sizeof how)) == -1) err(EXIT_FAILURE, "opening %s", argv[1]); how.resolve = RESOLVE_IN_ROOT | RESOLVE_NO_MAGICLINKS | RESOLVE_NO_XDEV; if ((targetfd = syscall(SYS_openat2, rootfd, argv[2], &how, sizeof how)) == -1) err(EXIT_FAILURE, "opening %s with %s:/", argv[2], argv[1]); assert(snprintf(fdpath, sizeof fdpath, "/proc/self/fd/%d", targetfd) < sizeof fdpath); target = areadlink(fdpath); if (!target) err(EXIT_FAILURE, "reading %s", fdpath); puts(target); }