From 365be71dfcc394fa5a3749834174c02f76afa7d1 Mon Sep 17 00:00:00 2001 From: Jan Polensky Date: Mon, 29 Sep 2025 17:17:26 +0200 Subject: [PATCH] util_libc: Add util_readlink() and util_readlinkat() helpers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce util_readlinkat() to read symbolic links relative to a directory file descriptor, and util_readlink() as a convenience wrapper using AT_FDCWD. util_readlink() delegates to util_readlinkat() instead of duplicating logic, ensuring a single implementation for both interfaces. Reviewed-by: Jan Höppner Signed-off-by: Jan Polensky Signed-off-by: Jan Höppner --- include/lib/util_libc.h | 24 ++++++++++++++++++++++++ libutil/util_libc.c | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+) diff --git a/include/lib/util_libc.h b/include/lib/util_libc.h index 8aaf1199..6c4a080b 100644 --- a/include/lib/util_libc.h +++ b/include/lib/util_libc.h @@ -12,6 +12,7 @@ #ifndef LIB_UTIL_LIBC_H #define LIB_UTIL_LIBC_H +#include #include #ifdef __cplusplus @@ -124,6 +125,29 @@ do { \ va_end(ap); \ } while (0) +/** + * Reads the target of a symbolic link at the given path. + * + * @param[in] path Path to the symbolic link + * @return Newly allocated string with the link target, or NULL on error + */ +#define util_readlink(path) __util_readlinkat(__func__, __FILE__, __LINE__, AT_FDCWD, path) + +/** + * Reads the target of a symbolic link relative to a directory file descriptor. + * + * Semantics: + * - If path is absolute, dirfd is ignored, per readlinkat semantics. + * - If path is relative, it is resolved relative to dirfd. + * + * @param[in] dirfd Directory file descriptor or AT_FDCWD + * @param[in] path Path to the symbolic link + * @return Newly allocated string with the link target, or NULL on error + */ +#define util_readlinkat(dirfd, path) __util_readlinkat(__func__, __FILE__, __LINE__, dirfd, path) + +char *__util_readlinkat(const char *func, const char *file, int line, int dirfd, const char *path); + int __util_vsprintf(const char *func, const char *file, int line, char *str, const char *fmt, va_list ap); char *util_strcat_realloc(char *str1, const char *str2); diff --git a/libutil/util_libc.c b/libutil/util_libc.c index d038a7dc..ca8965ff 100644 --- a/libutil/util_libc.c +++ b/libutil/util_libc.c @@ -10,11 +10,15 @@ */ #include +#include #include +#include #include #include #include #include +#include +#include #include "lib/util_base.h" #include "lib/util_libc.h" @@ -295,3 +299,31 @@ size_t util_strlcpy(char *dest, const char *src, size_t size) return str_len; } + +char *__util_readlinkat(const char *func, const char *file, int line, int dirfd, const char *path) +{ + ssize_t link_len = PATH_MAX; + struct stat st; + char *linkdir; + ssize_t len; + + if (fstatat(dirfd, path, &st, AT_SYMLINK_NOFOLLOW) == 0 && st.st_size > 0) + link_len = st.st_size + 1; + + linkdir = __util_malloc(func, file, line, link_len); + + len = readlinkat(dirfd, path, linkdir, link_len); + if (len == -1) { + free(linkdir); + return NULL; + } + + if (len >= link_len) { + warnx("%s: Link target too long", path); + free(linkdir); + return NULL; + } + + linkdir[len] = '\0'; + return __util_realloc(func, file, line, linkdir, (size_t)len + 1); +}