From a74f4a72504d1c5923bd2b4e85941f34d9fce79f Mon Sep 17 00:00:00 2001 From: Xavier Del Campo Romero Date: Mon, 24 Jul 2023 23:17:48 +0200 Subject: Introduce crealpath crealpath (for "custom realpath") is a custom implementation of realpath(3) that aims to work similarly to GNU's realpath(1). This implementation is provided due to the following reasons: - Future commits will require extracting an absolute path from a relative path, and/or process relative components from a path, such as ".." or ".". - realpath(3) is defined by POSIX.1-2008 as a XSI extension, and extensions are generally avoided in this repository whenever possible. - Additionally, realpath(3) requires the file or directory pointed to by the path to exist, which might not always be the case for slcl. - auth.c uses its own implementation to extract a dynamically allocated string by repeatedly calling getcwd(3). Future commits will also require this future, so it makes sense to keep it on a separate component. --- CMakeLists.txt | 1 + configure | 1 + crealpath.c | 174 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ crealpath.h | 8 +++ 4 files changed, 184 insertions(+) create mode 100644 crealpath.c create mode 100644 crealpath.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 8c216d7..d34a876 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,6 +4,7 @@ add_executable(${PROJECT_NAME} auth.c base64.c cftw.c + crealpath.c hex.c jwt.c main.c diff --git a/configure b/configure index afe9057..3a6b1ce 100755 --- a/configure +++ b/configure @@ -96,6 +96,7 @@ OBJECTS = \ auth.o \ base64.o \ cftw.o \ + crealpath.o \ hex.o \ jwt.o \ main.o \ diff --git a/crealpath.c b/crealpath.c new file mode 100644 index 0000000..9403911 --- /dev/null +++ b/crealpath.c @@ -0,0 +1,174 @@ +#define _POSIX_C_SOURCE 200809L + +#include "crealpath.h" +#include +#include +#include +#include +#include +#include +#include + +static char *alloc_cwd(void) +{ + size_t len = 1; + char *p = NULL; + + for (;;) + { + char *const pp = realloc(p, len); + + if (!pp) + { + fprintf(stderr, "%s: realloc(3): %s\n", __func__, strerror(errno)); + break; + } + + p = pp; + + if (!getcwd(pp, len)) + { + if (errno != ERANGE) + { + fprintf(stderr, "%s: getcwd(3): %s\n", + __func__, strerror(errno)); + break; + } + else + len++; + } + else + return p; + } + + free(p); + return NULL; +} + +static char *get_parent(char *p) +{ + char *last; + size_t len; + + if (!p || !(last = strrchr(p, '/')) || strlen(last) < strlen("/a")) + { + fprintf(stderr, "%s: parent folder expected\n", __func__); + return NULL; + } + + len = last == p ? strlen("/") : last - p; + + if (!(p = realloc(p, len + 1))) + { + fprintf(stderr, "%s: realloc(3): %s\n", __func__, strerror(errno)); + return NULL; + } + + p[len] = '\0'; + return p; +} + +static char *get_component(const char **const path) +{ + char *ret; + + while (**path == '/') + (*path)++; + + const char *const next = strchr(*path, '/'); + const size_t len = next ? next - *path : strlen(*path); + + if (!(ret = malloc(len + 1))) + { + fprintf(stderr, "%s: malloc(3): %s\n", __func__, strerror(errno)); + return NULL; + } + + memcpy(ret, *path, len); + ret[len] = '\0'; + *path += len; + return ret; +} + +static char *resolve_component(const char **const path, char *p) +{ + char *const c = get_component(path); + + if (!*c) + { + if (!p && !(p = strdup("/"))) + { + fprintf(stderr, "%s: strdup(3): %s\n", __func__, strerror(errno)); + goto end; + } + + goto end; + } + else if (!strcmp(c, "..") && !(p = get_parent(p))) + { + fprintf(stderr, "%s: get_parent failed\n", __func__); + goto end; + } + else if (strcmp(c, ".")) + { + const size_t len = p ? strlen(p) : 0, clen = strlen(c); + + if (!(p = realloc(p, len + strlen("/") + clen + 1))) + { + fprintf(stderr, "%s: realloc(3): %s\n", + __func__, strerror(errno)); + goto end; + } + else + { + p[len] = '/'; + memcpy(&p[len + 1], c, clen); + p[len + 1 + clen] = '\0'; + } + } + +end: + free(c); + return p; +} + +static char *resolve(const char *path, char *p) +{ + while (*path) + if (!(p = resolve_component(&path, p))) + { + fprintf(stderr, "%s: resolve_component failed\n", __func__); + return NULL; + } + + return p; +} + +char *crealpath(const char *const path) +{ + char *p = NULL, *ret = NULL; + + if (!*path) + { + fprintf(stderr, "%s: expected non-empty path\n", __func__); + goto end; + } + else if (*path != '/' && !(p = alloc_cwd())) + { + fprintf(stderr, "%s: alloc_cwd failed\n", __func__); + goto end; + } + else if (!(p = resolve(path, p))) + { + fprintf(stderr, "%s: resolve failed\n", __func__); + goto end; + } + + ret = p; + +end: + if (!ret) + free(p); + + return ret; +} diff --git a/crealpath.h b/crealpath.h new file mode 100644 index 0000000..8a178c4 --- /dev/null +++ b/crealpath.h @@ -0,0 +1,8 @@ +#ifndef CREALPATH_H +#define CREALPATH_H + +/* Custom implementation of a GNU's realpath(1)-like function that + * does not require GNU extensions. */ +char *crealpath(const char *path); + +#endif -- cgit v1.2.3