From 97dc4bb0dd5702038fdcf01308f03a195c0f834d Mon Sep 17 00:00:00 2001 From: Matthew LeGendre Date: Mon, 26 Oct 2026 18:01:10 -0700 Subject: [PATCH 1/6] Rewrite spindle_mkdir with Sol to handle '../' properly and symlinks. spindle_mkdir was failing to do the right thing if it was asked to make a directory with '../' path components that took it above the cachepath directory root. In the process of fixing that, I've also updated spindle_mkdir to handle symlinks properly in the cache. We now have a spindle_mkdir variant that takes the cachepath root and treats that as a root directory (so cachepath/../../ loops realpath's back to just cachepath/). This was mostly written by GPT-6/Sol with modifications/fixes by me. Co-authored-by: Codex --- src/utils/spindle_mkdir.c | 469 ++++++++++++++++++++++++++++++-------- 1 file changed, 368 insertions(+), 101 deletions(-) diff --git a/src/utils/spindle_mkdir.c b/src/utils/spindle_mkdir.c index b820482b..020403ff 100644 --- a/src/utils/spindle_mkdir.c +++ b/src/utils/spindle_mkdir.c @@ -29,7 +29,7 @@ Place, Suite 330, Boston, MA 02111-1307 USA #include "ldcs_api.h" #include "config.h" - +#include "spindle_debug.h" #if defined(TRACK_MKDIR) extern void track_mkdir(const char *dir); @@ -38,132 +38,399 @@ extern void track_mkdir(const char *dir); extern int lookup_prev_mkdir(const char *dir); #endif +#ifndef _XOPEN_SOURCE +#define _XOPEN_SOURCE 700 +#endif + +#include +#include +#include +#include +#include +#include +#include -static int checkdir(char *path) +#ifndef PATH_MAX +#define PATH_MAX 4096 +#endif + +/* Keep filesystem calls in one place for the startup replacement layer. */ +static int spindle_mkdir_stat(const char *path, struct stat *buf) { - struct stat buf; - int result = stat(path, &buf); - if (result == -1) { - err_printf("spindle_mkdir failed because stat on existing directory %s failed: %s\n", - path, strerror(errno)); + return stat(path, buf); +} + +static int spindle_mkdir_lstat(const char *path, struct stat *buf) +{ + return lstat(path, buf); +} + +static ssize_t spindle_mkdir_readlink(const char *path, char *buf, size_t size) +{ + return readlink(path, buf, size); +} + +static int spindle_mkdir_mkdir(const char *path, mode_t mode) +{ + return mkdir(path, mode); +} + +static int within(const char *path, const char *prefix, size_t length) +{ + return !strncmp(path, prefix, length) && + (length == 1 || path[length] == '/' || path[length] == '\0'); +} + +static int append(char *path, size_t *length, const char *part, size_t size) +{ + size_t separator = !(*length == 1 && path[0] == '/'); + + if (*length + separator > PATH_MAX - 1 || + size > PATH_MAX - 1 - *length - separator) { + errno = ENAMETOOLONG; + debug_printf3("WARNING: apend failing with ENAMETOLONG on '%s' and '%s'\n", path, part); return -1; } - if (!S_ISDIR(buf.st_mode) || S_ISLNK(buf.st_mode)) { - err_printf("spindle_mkdir failed because non-directory %s appeared in path during mkdir\n", - path); + if (separator) + path[(*length)++] = '/'; + memcpy(path + *length, part, size); + *length += size; + path[*length] = '\0'; + return 0; +} + +/* Move up from a resolved path. Relative paths retain leading '..' because + * the current directory cannot be named absolutely with the allowed calls. */ +static int parent(char path[PATH_MAX], size_t *length) +{ + if (path[0] != '/' && + ((*length == 1 && path[0] == '.') || + (*length >= 3 && path[*length - 3] == '/' && + path[*length - 2] == '.' && path[*length - 1] == '.'))) + return append(path, length, "..", 2); + + while (*length > 1 && path[*length - 1] != '/') + --*length; + if (*length > 1) + --*length; + path[*length] = '\0'; + return 0; +} + +/* Resolve suffix onto path in place. path starts at "/", ".", or the already + * resolved prefix; pending holds components not yet applied to path. + * + * Symlinks are expanded before later '..' components. With floor, '..' stops + * at the prefix and symlink targets reached inside it must stay inside it. + * required is set only while resolving the prefix, which must already exist. */ +static int resolve(const char *suffix, const char *floor, int required, + char path[PATH_MAX]) +{ + char pending[PATH_MAX], target[PATH_MAX]; + size_t length = strlen(path), floor_length = floor ? strlen(floor) : 0; + size_t cursor = 0, pending_length = strlen(suffix); + int links = 0; + + if (pending_length >= sizeof pending) { + errno = ENAMETOOLONG; + debug_printf3("WARNING: resolve ENAMETOLONG on '%s'\n", suffix); + return -1; + } + /* Expansion can insert NUL separators, so pending_length, not strlen, + * defines the part of pending still in use. */ + memcpy(pending, suffix, pending_length); + + while (cursor < pending_length) { + size_t start, end, old_length; + size_t boundary; + struct stat st; + ssize_t n; + + /* Skip empty components. An inserted NUL ends one symlink target; + * check it before applying the caller's remaining components. */ + while (cursor < pending_length && pending[cursor] == '/') + ++cursor; + if (cursor == pending_length) + break; + /* A NUL marks the end of a symlink target, before its caller's tail. */ + if (pending[cursor] == '\0') { + if (floor && !within(path, floor, floor_length)) { + debug_printf3("WARNING within failing with EACCES on '%s' in '%s'\n", path, floor); + errno = EACCES; + return -1; + } + ++cursor; + continue; + } + start = cursor; + while (cursor < pending_length && pending[cursor] && pending[cursor] != '/') + ++cursor; + end = cursor; + if (end - start == 1 && pending[start] == '.') + continue; + if (end - start == 2 && pending[start] == '.' && pending[start + 1] == '.') { + /* Clamp at floor, just as '..' at '/' stays at '/'. */ + if (!floor || !within(path, floor, floor_length) || + length > floor_length) { + if (parent(path, &length) < 0) { + debug_printf3("resolv failing because could not determine parent of '%s' with floor '%s'\n", + path ? path : "[NULL]", floor ? floor : "[NULL]"); + return -1; + } + } + continue; + } + + old_length = length; + if (append(path, &length, pending + start, end - start) < 0) + return -1; + /* Missing components are kept in the resolved path for the later + * mkdir pass. Existing non-directories cannot be traversed. */ + if (spindle_mkdir_lstat(path, &st) < 0) { + if (errno == ENOENT && !required) + continue; + debug_printf3("lstat failed during resolve for '%s'\n", path); + return -1; + } + if (!S_ISLNK(st.st_mode)) { + if (!S_ISDIR(st.st_mode)) { + errno = ENOTDIR; + debug_printf3("resolve failing for '%s' because ENOTDIR\n", path); + return -1; + } + continue; + } + /* Check targets of links inside the prefix, even if the caller's + * remaining path would later return inside. Links in a prefix alias + * may resolve to an ancestor, so they get no boundary check here. */ + boundary = floor && within(path, floor, floor_length); + if (++links > 40) { + errno = ELOOP; + debug_printf3("resolve failing for '%s' because ELOOP\n", path); + return -1; + } + n = spindle_mkdir_readlink(path, target, sizeof target - 1); + if (n < 0) { + debug_printf3("resolve failing for '%s' because readlink failed\n", path); + return -1; + } + if (n == 0) { + errno = ENOENT; + debug_printf3("resolve failing for '%s' with ENOENT\n", path); + return -1; + } + if ((size_t)n + boundary + pending_length - end >= sizeof pending) { + errno = ENAMETOOLONG; + debug_printf3("resolve failing for '%s' with ENAMETOOLONG\n", path); + return -1; + } + /* Replace the link with its target and keep the unread tail. The + * optional NUL marks where this target ends. Nested expansions keep + * earlier markers in that tail without a separate stack. */ + memmove(pending + n + boundary, pending + end, pending_length - end); + memcpy(pending, target, n); + if (boundary) + pending[n] = '\0'; + pending_length = (size_t)n + boundary + pending_length - end; + cursor = 0; + /* Absolute targets restart at root; relative targets restart at the + * link's parent, saved in old_length. */ + if (target[0] == '/') { + path[0] = '/'; + path[1] = '\0'; + length = 1; + } else { + length = old_length; + path[length] = '\0'; + } + } + /* Also reject an ordinary path that ends outside the prefix. */ + if (floor && !within(path, floor, floor_length)) { + errno = EACCES; + debug_printf3("resolve failing for '%s' with EACCESs and floor of %s\n", path, floor); return -1; } - if (buf.st_uid != geteuid()) { - err_printf("spindle_mkdir failed because component %s was owned by %d rather than expected %d\n", - path, buf.st_uid, geteuid()); + return 0; +} + +/* Leading relative '..' components name ancestors, not directories to make. */ +static size_t creation_start(const char *path) +{ + size_t i = 0; + + while (path[i] == '.') { + size_t dots = path[i + 1] == '.' ? 2 : 1; + if (path[i + dots] != '/' && path[i + dots] != '\0') + break; + i += dots; + if (path[i] == '/') + ++i; + } + return i; +} + +static int check_directory(const char *path) +{ + struct stat st; + + if (spindle_mkdir_stat(path, &st) < 0) { + debug_printf3("WARNING: Failed to stat %s\n", path); return -1; } - if (buf.st_gid != getegid()) { - err_printf("spindle_mkdir failed because component %s had group %d rather than expected %d\n", - path, buf.st_gid, getegid()); + if (!S_ISDIR(st.st_mode)) { + errno = ENOTDIR; + debug_printf3("WARNING: %s is not a directory\n", path); return -1; } - if ((buf.st_mode & 0777) != 0700) { - err_printf("spindle_mkdir failed because component %s had unexpected permissions %o\n", - path, buf.st_mode & 0777); + if (st.st_uid != geteuid() || st.st_gid != getegid() || + (st.st_mode & 07777) != 0700) { + errno = EACCES; + debug_printf3("WARNING: Permissions error on %s\n", path); return -1; } - return 0; } -int spindle_mkdir(char *orig_path) +/* EEXIST may come from another process; verify what is there either way. */ +static int make_directory(const char *path, int should_check_perms) { - char path[MAX_PATH_LEN+1]; - int i, path_len, result, do_mkdir = 0, error; - struct stat buf; - char orig_char; - - debug_printf2("spindle_mkdir on %s\n", orig_path); - - - strncpy(path, orig_path, sizeof(path)); - path[MAX_PATH_LEN] = '\0'; - path_len = strlen(path); - - i = 0; - while (path[i] == '/') - i++; - - for (; i < path_len+1; i++) { - if (path[i] != '/' && path[i] != '\0') - continue; - orig_char = path[i]; - path[i] = '\0'; + int result; + if (spindle_mkdir_mkdir(path, 0700) < 0 && errno != EEXIST) { + debug_printf3("WARNING: Failed to mkdir(%s)\n", path); + return -1; + } + result = check_directory(path); + if (result == -1) { + debug_printf3("WARNING: check_directory failed here\n"); + return -1; + } +#if defined(TRACK_MKDIR) + track_mkdir(path); +#endif + return result; +} -#if defined(LOOKUP_PREV_MKDIR) - if (!do_mkdir) { - result = lookup_prev_mkdir(path); - if (result == 1) { - do_mkdir = 1; - } +static int spindle_mkdir_impl(const char *path, const char *cachepath) +{ + char resolved[PATH_MAX], floor[PATH_MAX], literal[PATH_MAX]; + size_t prefix_length, physical_length, length, i; + + if (!path || !*path || (cachepath && !*cachepath)) { + debug_printf3("WARNING: spindle_mkdir failing with EINVAL on '%s' with cachepath '%s'\n", + path ? path : "[NULL]", cachepath ? cachepath : "[NULL]"); + errno = EINVAL; + return -1; + } + if (cachepath) { + /* Resolve the existing prefix to establish a physical boundary. */ + if (cachepath[0] != '/' || path[0] != '/') { + debug_printf3("WARNING: spindle_mkdir failing with EINVAL on '%s' and '%s'\n", + path ? path : "[NULL]", cachepath ? cachepath : "[NULL]"); + errno = EINVAL; + return -1; } -#endif - if (!do_mkdir) { - //Run a stat on an existing path component. As long as a directory - //component already exists, we won't be too picky about its ownership. - result = stat(path, &buf); - if (result == -1) { - error = errno; - if (error == ENOENT) { - do_mkdir = 1; - } - else { - err_printf("spindle_mkdir failed to stat path component %s: %s\n", - path, strerror(error)); - return -1; - } - } - if (!S_ISDIR(buf.st_mode) && !S_ISLNK(buf.st_mode)) { - err_printf("spindle_mkdir failed because path component %s is not a directory or symlink\n", - path); - return -1; - } + prefix_length = strlen(cachepath); + while (prefix_length > 1 && cachepath[prefix_length - 1] == '/') + --prefix_length; + if (prefix_length >= PATH_MAX) { + debug_printf3("WARNING: spindle_mkdir failing with ENAMETOOLONG on '%s'\n", + path ? path : "[NULL]"); + errno = ENAMETOOLONG; + return -1; } - - if (do_mkdir) { - result = mkdir(path, 0700); - if (result == -1) { - error = errno; - if (error != EEXIST) { - err_printf("spindle_mkdir failed to make path component %s: %s\n", path, strerror(error)); - return -1; - } - //Someone created this path component while we were doing the mkdir. - //May be a race with other Spindle libraries. Check that it's owned - //by us with appropriate permissions. - if (checkdir(path) == -1) - return -1; - } - else { - debug_printf3("Did a mkdir(%s)\n", path); - } -#if defined(TRACK_MKDIR) - track_mkdir(path); -#endif + if (!within(path, cachepath, prefix_length)) { + debug_printf3("WARNING: spindle_mkdir failing with EACCESS on '%s' and '%s'\n", + path ? path : "[NULL]", cachepath ? cachepath : "[NULL]"); + errno = EACCES; + return -1; + } + strcpy(resolved, "/"); + if (resolve(cachepath, NULL, 1, resolved) < 0) { + debug_printf3("WARNING: resolve failed here\n"); + return -1; + } + if (check_directory(resolved) < 0) { + debug_printf3("WARNING: resolve failed here\n"); + return -1; } - path[i] = orig_char; - if (path[i] == '/') - while (path[i+1] == '/') - i++; + strcpy(floor, resolved); + physical_length = strlen(resolved); + /* Resolve below that boundary, then map back to the literal prefix + * so every mkdir call uses the spelling required by cleanup. */ + if (resolve(path + prefix_length, floor, 0, resolved) < 0) + return -1; + length = strlen(resolved) - physical_length; + if (length > PATH_MAX - 1 - prefix_length) { + debug_printf3("WARNING: spindle_mkdir failing with ENAMETOOLONG on '%s'\n", + resolved ? resolved : "[NULL]"); + errno = ENAMETOOLONG; + return -1; + } + memcpy(literal, cachepath, prefix_length); + memcpy(literal + prefix_length, resolved + physical_length, length + 1); + length += prefix_length; + i = prefix_length; + } else { + /* Relative paths stay relative; absolute paths start at root. */ + strcpy(resolved, path[0] == '/' ? "/" : "."); + if (resolve(path, NULL, 0, resolved) < 0) { + debug_printf3("WARNING: resolve failed here\n"); + return -1; + } + length = strlen(resolved); + memcpy(literal, resolved, length + 1); + i = creation_start(literal); } - if (!do_mkdir) { - //We never did any mkdirs. Ensure that the final directory in the existing path - // is exclusively ours. - if (checkdir(path) == -1) { + /* Walk the resolved path once, creating and verifying each directory. */ + while (i < length) { + int should_check_perms; + size_t end, next; + char saved; + + while (i < length && literal[i] == '/') + ++i; + if (i == length) + break; + end = i; + while (end < length && literal[end] != '/') + ++end; + next = end; + while (next < length && literal[next] == '/') + ++next; + saved = literal[end]; + literal[end] = '\0'; + /* Public and system-owned directories may lead to an unprefixed + * path. Directories managed by this function must be private. */ + if (!cachepath && next < length) { + struct stat st; + if (spindle_mkdir_stat(literal, &st) == 0 && S_ISDIR(st.st_mode)) { + literal[end] = saved; + i = end; + continue; + } + } + + /* Only check permissions on the parent when making withinin the cachepath. + When making cachepath or commpaths we may be in some other directories */ + should_check_perms = (cachepath != NULL); + if (make_directory(literal, should_check_perms) < 0) { + debug_printf3("WARNING: make_directory failed here\n"); return -1; } -#if defined(TRACK_MKDIR) - track_mkdir(path); -#endif + literal[end] = saved; + i = end; } return 0; } +int spindle_mkdir(const char *path) +{ + return spindle_mkdir_impl(path, NULL); +} + +int spindle_mkdir_in_cache(const char *path, const char *cachepath) +{ + return spindle_mkdir_impl(path, cachepath); +} From 52878f9874b19bbaf26eb62a98960834d5e26361 Mon Sep 17 00:00:00 2001 From: Matthew LeGendre Date: Mon, 26 Oct 2026 18:00:17 -0700 Subject: [PATCH 2/6] Fix a bug discovered in java where $ORIGIN/subdir rpath libraries may be missed. The issue was that we were dynmacially creating directories in the cache when we identified a file that needed to be cached in that directory. But ld.so was also maintaining a list of exists/not-exists directory caches, which could conflict with spindle's dynamic cache creation. To reproduce we needed a library located via $ORIGIN/something, which meant the dynamic linker was searching the spindle cache for libraries. We first needed to load another library (say libm.so in some testcases) that didn't exist in that $ORIGIN/something location. ld.so would iterate through its search list, notice that $ORIGIN/something didn't exist yet and mark it not-exists. Then we needed to try to load the original library that was in $ORIGIN/something, but ld.so would no longer search that path and the library wouldn't be found. Also this didn't happen on systems where we successfully install ld.so trampoline hooks, as the interceptions of the stat() calls would fix the result of the 'does this directory exist' test in ld.so. To fix this, we now create a directory in the cache when it is first searched, even if we do not yet have files to put in it. --- .../auditserver/ldcs_audit_server_filemngt.c | 48 ++++++++++++++----- .../auditserver/ldcs_audit_server_filemngt.h | 3 +- .../auditserver/ldcs_audit_server_handlers.c | 23 ++++++++- src/server/cache/ldcs_cache.cc | 4 +- 4 files changed, 61 insertions(+), 17 deletions(-) diff --git a/src/server/auditserver/ldcs_audit_server_filemngt.c b/src/server/auditserver/ldcs_audit_server_filemngt.c index db2d163b..d56290b3 100644 --- a/src/server/auditserver/ldcs_audit_server_filemngt.c +++ b/src/server/auditserver/ldcs_audit_server_filemngt.c @@ -50,6 +50,7 @@ char *_ldcs_audit_server_cachepath; static char *normalized_tmpdir; static char *_ldcs_audit_server_commpath; extern int spindle_mkdir(char *path); +extern int spindle_mkdir_in_cache(char *path, char *cachepath); static char *filemngt_normalize_dir(char *dir) { char *newpath = realpath(dir, NULL); @@ -95,6 +96,32 @@ int ldcs_is_a_localfile(ldcs_process_data_t *procdata, char *filename) { #define MAX_FILENAME_LEN 256 +void filemngt_calc_dirname(const char *global_name, char *local_name_result, size_t local_name_result_size) +{ + char *lastslash, *endslash; + int starts_with_slash, endslash_position; + + lastslash = strrchr(_ldcs_audit_server_cachepath, '/'); + if (lastslash && lastslash[1] == '\0') + endslash = ""; + else + endslash = "/"; + + starts_with_slash = (global_name[0] == '/') ? 1 : 0; + + snprintf(local_name_result, local_name_result_size, "%s%s%s", _ldcs_audit_server_cachepath, endslash, global_name+starts_with_slash); + + //Take out any trailing slashes + endslash = strrchr(local_name_result, '/'); + endslash_position = endslash ? (int) (endslash - local_name_result) : 0; + if (endslash_position+1 >= local_name_result_size || endslash[1] == '\0') { + while (*endslash == '/' && endslash != local_name_result) { + *endslash = '\0'; + endslash--; + } + } +} + char *filemngt_calc_localname(char *global_name, calc_local_t reqtype) { //The naming decisions here need to be cordinated with name parsing in @@ -103,16 +130,10 @@ char *filemngt_calc_localname(char *global_name, calc_local_t reqtype) char target[MAX_PATH_LEN+1]; char dirpart[MAX_PATH_LEN+1]; char filepart[MAX_PATH_LEN+1]; - char *endslash, *lastslash; const char *prefix = NULL; + char *lastslash; size_t dirpart_size, filepart_size; - int cut_dirpart_slash; - - lastslash = strrchr(_ldcs_audit_server_cachepath, '/'); - if (lastslash && lastslash[1] == '\0') - endslash = ""; - else - endslash = "/"; + ssize_t bytes_left; GCC7_DISABLE_WARNING("-Wformat-truncation"); @@ -154,12 +175,13 @@ char *filemngt_calc_localname(char *global_name, calc_local_t reqtype) strncpy(filepart, target, sizeof(filepart)); filepart[sizeof(filepart)-1] = '\0'; - cut_dirpart_slash = (dirpart[0] == '/') ? 1 : 0; - - snprintf(target, sizeof(target), "%s%s%s", _ldcs_audit_server_cachepath, endslash, dirpart+cut_dirpart_slash); - spindle_mkdir(target); + filemngt_calc_dirname(dirpart, target, sizeof(target)); + spindle_mkdir_in_cache(target, _ldcs_audit_server_cachepath); - snprintf(target, sizeof(target), "%s%s%s/%s", _ldcs_audit_server_cachepath, endslash, dirpart+cut_dirpart_slash, filepart); + bytes_left = sizeof(target) - strlen(target); + strncat(target, "/", --bytes_left); + strncat(target, filepart, bytes_left - strlen(filepart)); + target[sizeof(target)-1] = '\0'; GCC7_ENABLE_WARNING; diff --git a/src/server/auditserver/ldcs_audit_server_filemngt.h b/src/server/auditserver/ldcs_audit_server_filemngt.h index 592b9c82..4336f7c0 100644 --- a/src/server/auditserver/ldcs_audit_server_filemngt.h +++ b/src/server/auditserver/ldcs_audit_server_filemngt.h @@ -51,7 +51,8 @@ typedef struct size_t extended_stat_size(extended_stat_t *st); char *filemngt_calc_localname(char *global_name, calc_local_t reqtype); - +void filemngt_calc_dirname(const char *global_name, char *local_name_result, size_t local_name_result_size); + int ldcs_audit_server_filemngt_clean(); int filemngt_create_file_space(char *filename, size_t size, void **buffer_out, int *fd_out); diff --git a/src/server/auditserver/ldcs_audit_server_handlers.c b/src/server/auditserver/ldcs_audit_server_handlers.c index c7e73e6b..955cc4fa 100644 --- a/src/server/auditserver/ldcs_audit_server_handlers.c +++ b/src/server/auditserver/ldcs_audit_server_handlers.c @@ -141,6 +141,7 @@ static int handle_send_file_query(ldcs_process_data_t *procdata, char *fullpath, static int handle_file_recv(ldcs_process_data_t *procdata, ldcs_message_t *msg, node_peer_t peer, broadcast_t bcast); static int handle_directory_recv(ldcs_process_data_t *procdata, ldcs_message_t *msg, broadcast_t bcast); +static int handle_process_directory(ldcs_process_data_t *procdata, const char *dirname); static int handle_alias_recv(ldcs_process_data_t *procdata, ldcs_message_t *msg, broadcast_t bcast); static int handle_exit_broadcast(ldcs_process_data_t *procdata); @@ -633,6 +634,7 @@ static int handle_read_directory(ldcs_process_data_t *procdata, char *dir) if (cache_dir_result == LDCS_CACHE_DIR_PARSED_AND_EXISTS || cache_dir_result == LDCS_CACHE_DIR_PARSED_AND_NOT_EXISTS) { + handle_process_directory(procdata, dir); return 0; } else { @@ -1665,6 +1667,20 @@ static int handle_file_recv(ldcs_process_data_t *procdata, ldcs_message_t *msg, return global_error; } +/** + * We're processing a directory that exists, either from reading it or recv'ing it on the + * network. + **/ +extern int spindle_mkdir_in_cache(char *orig_path, char *cachepath); +static int handle_process_directory(ldcs_process_data_t *procdata, const char *dirname) +{ + char local_dirname[MAX_PATH_LEN+1]; + + filemngt_calc_dirname(dirname, local_dirname, sizeof(local_dirname)); + spindle_mkdir_in_cache(local_dirname, procdata->cachepath); + return 0; +} + /** * We've received a packet with directory info. Process it. **/ @@ -1672,11 +1688,16 @@ static int handle_directory_recv(ldcs_process_data_t *procdata, ldcs_message_t * { double starttime = ldcs_get_time(); char dirname[MAX_PATH_LEN+1]; + int num_entries_in_dir; debug_printf2("New directory cache entries received from %s\n", bcast == preload_broadcast ? "preload" : "request"); - ldcs_cache_decodeDirContents(msg->data, msg->header.len, dirname, MAX_PATH_LEN); + num_entries_in_dir = ldcs_cache_decodeDirContents(msg->data, msg->header.len, dirname, MAX_PATH_LEN); + debug_printf2("Directory %s has %d entries\n", dirname, num_entries_in_dir); + if (num_entries_in_dir) { + handle_process_directory(procdata, dirname); + } handle_broadcast_dir(procdata, dirname, bcast); diff --git a/src/server/cache/ldcs_cache.cc b/src/server/cache/ldcs_cache.cc index 2930d888..5a6f78ac 100644 --- a/src/server/cache/ldcs_cache.cc +++ b/src/server/cache/ldcs_cache.cc @@ -445,6 +445,7 @@ int ldcs_cache_decodeDirContents(char *buffer, size_t len, while (pos < len) { int fn_length; string filename; + num_entries++; memcpy(&fn_length, buffer + pos, sizeof(int)); assert(fn_length <= MAX_PATH_LEN+1); pos += sizeof(int); @@ -459,14 +460,13 @@ int ldcs_cache_decodeDirContents(char *buffer, size_t len, assert(pos <= len); addFile(dirname, filename); - num_entries++; } strncpy(dirbuffer, dirname.c_str(), dirbuffer_sz); dirbuffer[dirbuffer_sz - 1] = '\0'; debug_printf3("Decoded packet for directory with %d entries: %s\n", num_entries, dirbuffer); - return 0; + return num_entries; } void ldcs_cache_addFileDir(char *dname, char *fname) From 20339cf4b73ad1206cf90ae6d17124e9f20d54bb Mon Sep 17 00:00:00 2001 From: Matthew LeGendre Date: Mon, 26 Oct 2026 18:01:44 -0700 Subject: [PATCH 3/6] Add tests for $ORIGIN/something in executable after other library. Also fix issue with other tests when running fork variants of tests. Some libraries in the child process were not logged as loaded after a fork, which wasn't a problem because they were also not be logged as 'should be loaded' because those messages were being incorrect attributed to the parent process. Everything was loading okay, just the logging was working correct. Fixed both. --- testsuite/Makefile.am | 18 ++++++++++------ testsuite/Makefile.in | 18 ++++++++++------ testsuite/origintarget.c | 16 +++++++++++++- testsuite/run_driver_template | 2 +- testsuite/test_driver.c | 39 +++++++++++++++++++++++++---------- 5 files changed, 68 insertions(+), 25 deletions(-) diff --git a/testsuite/Makefile.am b/testsuite/Makefile.am index 4e20fb8b..43a0d299 100644 --- a/testsuite/Makefile.am +++ b/testsuite/Makefile.am @@ -18,12 +18,12 @@ libgenerator_SOURCES = libgenerator.c test_driverSOURCES = $(top_srcdir)/testsuite/test_driver.c test_driverCFLAGS = -DLPATH=$(top_builddir)/testsuite -I$(top_srcdir)/src/client/spindle_api -I$(top_srcdir)/src/utils $(MPI_CFLAGS) -Wall -I$(top_builddir) test_driverLDADD = -ldl -lpthread -ltestoutput -lfuncdict -lspindle -test_driverLDFLAGS = -Wl,-E -L$(top_builddir)/testsuite -L$(top_builddir)/src/client/spindle_api $(MPI_CLDFLAGS) $(LDFLAGS) -L. $(DYNAMIC_FLAG) -no-install +test_driverLDFLAGS = -Wl,-E -L$(top_builddir)/testsuite -L$(top_builddir)/src/client/spindle_api $(MPI_CLDFLAGS) $(LDFLAGS) -L. $(DYNAMIC_FLAG) -no-install '-Wl,-rpath,$$ORIGIN/exeorigin_dir' test_driver_libsSOURCES = $(top_srcdir)/testsuite/test_driver.c -test_driver_libsCFLAGS = -DLPATH=$(top_builddir)/testsuite -I$(top_srcdir)/src/client/spindle_api -I$(top_srcdir)/src/utils $(MPI_CFLAGS) -Wall +test_driver_libsCFLAGS = -DLPATH=$(top_builddir)/testsuite -I$(top_srcdir)/src/client/spindle_api -I$(top_srcdir)/src/utils -I$(top_builddir) $(MPI_CFLAGS) -Wall test_driver_libsLDADD = -ltest10 -ltest11 -ltest12 -ltest13 -ltest14 -ltest15 -ltest16 -ltest17 -ltest18 -ltest19 -ltest20 -ltest50 -ltest100 -ltest500 -ltest1000 -ltest2000 -ltest4000 -ltest6000 -ltest8000 -ltest10000 -ldepA -lcxxexceptA -loriginlib -ldl -lpthread -ltestoutput -lfuncdict -ldepB -ldepC -lcxxexceptB -lspindle -test_driver_libsLDFLAGS = -Wl,-E -L$(top_builddir)/testsuite $(MPI_CLDFLAGS) -L$(top_builddir)/src/client/spindle_api -L. $(DYNAMIC_FLAG) -no-install $(LDFLAGS) -Wl,-rpath,$(PWD)/origin_dir -L$(PWD)/origin_dir -Wl,-rpath-link,$(PWD)/origin_dir/origin_subdir -I$(top_builddir) +test_driver_libsLDFLAGS = -Wl,-E -L$(top_builddir)/testsuite $(MPI_CLDFLAGS) -L$(top_builddir)/src/client/spindle_api -L. $(DYNAMIC_FLAG) -no-install $(LDFLAGS) -Wl,-rpath,$(PWD)/origin_dir -L$(PWD)/origin_dir -Wl,-rpath-link,$(PWD)/origin_dir/origin_subdir -L$(PWD)/exeorigin_dir '-Wl,-rpath,$$ORIGIN/exeorigin_dir' REGLIB_SRC = $(srcdir)/registerlib.c LD_FUNCDICT = -L$(top_builddir)/testsuite -lfuncdict @@ -31,8 +31,8 @@ LD_FUNCDICT = -L$(top_builddir)/testsuite -lfuncdict test_driver: $(test_driverSOURCES) $(REGLIB_SRC) libtestoutput.so libfuncdict.so libbigtls1048576.so libbigtls204800.so libbigtls1024.so $(AM_V_CCLD) $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(MPICC) -o $@ $(test_driverSOURCES) $(REGLIB_SRC) -DSO_NAME=$@ $(test_driverCFLAGS) $(test_driverLDFLAGS) $(test_driverLDADD) -lbigtls1048576 -lbigtls204800 -lbigtls1024 -test_driver_libs: $(test_driverSOURCES) $(REGLIB_SRC) libtestoutput.so libfuncdict.so libtest10.so libtest11.so libtest12.so libtest13.so libtest14.so libtest15.so libtest16.so libtest17.so libtest18.so libtest19.so libtest20.so libtest100.so libtest500.so libtest1000.so libtest2000.so libtest4000.so libtest6000.so libtest8000.so libtest10000.so libtls1.so libtls2.so libtls3.so libtls4.so libtls5.so libtls6.so libtls7.so libtls8.so libtls9.so libtls10.so libtls11.so libtls12.so libtls13.so libtls14.so libtls15.so libtls16.so libtls17.so libtls18.so libtls19.so libtls20.so libdepA.so libcxxexceptA.so libdepB.so libdepC.so libcxxexceptB.so origin_dir/liboriginlib.so libbigtls1048576.so libbigtls204800.so libbigtls1024.so - $(AM_V_CCLD) $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(MPICC) -o $@ $(test_driver_libsSOURCES) $(REGLIB_SRC) -DSO_NAME=$@ -DUSE_NEEDED $(test_driver_libsCFLAGS) $(test_driver_libsLDFLAGS) $(test_driver_libsLDADD) -lbigtls1048576 -lbigtls204800 -lbigtls1024 +test_driver_libs: $(test_driverSOURCES) $(REGLIB_SRC) libtestoutput.so libfuncdict.so libtest10.so libtest11.so libtest12.so libtest13.so libtest14.so libtest15.so libtest16.so libtest17.so libtest18.so libtest19.so libtest20.so libtest100.so libtest500.so libtest1000.so libtest2000.so libtest4000.so libtest6000.so libtest8000.so libtest10000.so libtls1.so libtls2.so libtls3.so libtls4.so libtls5.so libtls6.so libtls7.so libtls8.so libtls9.so libtls10.so libtls11.so libtls12.so libtls13.so libtls14.so libtls15.so libtls16.so libtls17.so libtls18.so libtls19.so libtls20.so libdepA.so libcxxexceptA.so libdepB.so libdepC.so libcxxexceptB.so origin_dir/liboriginlib.so libbigtls1048576.so libbigtls204800.so libbigtls1024.so exeorigin_dir/libexeorigin.so + $(AM_V_CCLD) $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(MPICC) -o $@ $(test_driver_libsSOURCES) $(REGLIB_SRC) -DSO_NAME=$@ -DUSE_NEEDED $(test_driver_libsCFLAGS) $(test_driver_libsLDFLAGS) $(test_driver_libsLDADD) -lbigtls1048576 -lbigtls204800 -lbigtls1024 -lexeorigin libtest10.c: libgenerator $(AM_V_GEN)./libgenerator libtest10.c 10 t10 @@ -307,6 +307,12 @@ libtestoutput.so: testoutput.c $(REGLIB_SRC) libfuncdict.so: funcdict.c $(REGLIB_SRC) $(AM_V_CCLD)$(MPICC) -o $@ -fPIC -Wall $< $(REGLIB_SRC) -DSO_NAME=$@ -shared +exeorigin_dir: + $(AM_V_GEN) mkdir -p exeorigin_dir + +exeorigin_dir/libexeorigin.so: origintarget.c exeorigin_dir libfuncdict.so + $(AM_V_CCLD)$(MPICC) -o $@ -fPIC -shared -Wall -DSO_NAME=libexeorigin.so -DFUNC_NAME=origintarget_calc $< $(REGLIB_SRC) $(LD_FUNCDICT) -L$(PWD) + origin_dir/origin_subdir: $(AM_V_GEN) mkdir -p origin_dir/origin_subdir @@ -315,7 +321,7 @@ origin_dir/origin_subdir/liborigintarget.so: origintarget.c origin_dir/origin_su @touch $@ origin_dir/liboriginlib.so: originlib.c $(REGLIB_SRC) origin_dir/origin_subdir/liborigintarget.so - $(AM_V_CCLD)$(MPICC) -o $@ -fPIC -Wall $< $(REGLIB_SRC) -DSO_NAME=liboriginlib.so -DFUNC_NAME=origin_calc -shared '-Wl,-rpath,$$ORIGIN/origin_subdir' -Lorigin_dir/origin_subdir -lorigintarget + $(AM_V_CCLD)$(MPICC) -o $@ -fPIC -Wall $< $(REGLIB_SRC) -DSO_NAME=liboriginlib.so -DFUNC_NAME=origintarget_calc -shared '-Wl,-rpath,$$ORIGIN/origin_subdir' -Lorigin_dir/origin_subdir -lm -lorigintarget libsymbind_a.so: $(srcdir)/symbindlib.c $(srcdir)/symbind_fileops.c libsymbind_b.so $(AM_V_CCLD)$(CC) $(CFLAGS) -o $@ -shared -fPIC -Wall -DAUTORUN=a $(srcdir)/symbindlib.c -L. -Wl,--no-as-needed -lsymbind_b -I$(srcdir) -Wl,-rpath,$(top_builddir)/testsuite diff --git a/testsuite/Makefile.in b/testsuite/Makefile.in index ff41207b..ad2439e8 100644 --- a/testsuite/Makefile.in +++ b/testsuite/Makefile.in @@ -374,11 +374,11 @@ libgenerator_SOURCES = libgenerator.c test_driverSOURCES = $(top_srcdir)/testsuite/test_driver.c test_driverCFLAGS = -DLPATH=$(top_builddir)/testsuite -I$(top_srcdir)/src/client/spindle_api -I$(top_srcdir)/src/utils $(MPI_CFLAGS) -Wall -I$(top_builddir) test_driverLDADD = -ldl -lpthread -ltestoutput -lfuncdict -lspindle -test_driverLDFLAGS = -Wl,-E -L$(top_builddir)/testsuite -L$(top_builddir)/src/client/spindle_api $(MPI_CLDFLAGS) $(LDFLAGS) -L. $(DYNAMIC_FLAG) -no-install +test_driverLDFLAGS = -Wl,-E -L$(top_builddir)/testsuite -L$(top_builddir)/src/client/spindle_api $(MPI_CLDFLAGS) $(LDFLAGS) -L. $(DYNAMIC_FLAG) -no-install '-Wl,-rpath,$$ORIGIN/exeorigin_dir' test_driver_libsSOURCES = $(top_srcdir)/testsuite/test_driver.c -test_driver_libsCFLAGS = -DLPATH=$(top_builddir)/testsuite -I$(top_srcdir)/src/client/spindle_api -I$(top_srcdir)/src/utils $(MPI_CFLAGS) -Wall +test_driver_libsCFLAGS = -DLPATH=$(top_builddir)/testsuite -I$(top_srcdir)/src/client/spindle_api -I$(top_srcdir)/src/utils -I$(top_builddir) $(MPI_CFLAGS) -Wall test_driver_libsLDADD = -ltest10 -ltest11 -ltest12 -ltest13 -ltest14 -ltest15 -ltest16 -ltest17 -ltest18 -ltest19 -ltest20 -ltest50 -ltest100 -ltest500 -ltest1000 -ltest2000 -ltest4000 -ltest6000 -ltest8000 -ltest10000 -ldepA -lcxxexceptA -loriginlib -ldl -lpthread -ltestoutput -lfuncdict -ldepB -ldepC -lcxxexceptB -lspindle -test_driver_libsLDFLAGS = -Wl,-E -L$(top_builddir)/testsuite $(MPI_CLDFLAGS) -L$(top_builddir)/src/client/spindle_api -L. $(DYNAMIC_FLAG) -no-install $(LDFLAGS) -Wl,-rpath,$(PWD)/origin_dir -L$(PWD)/origin_dir -Wl,-rpath-link,$(PWD)/origin_dir/origin_subdir -I$(top_builddir) +test_driver_libsLDFLAGS = -Wl,-E -L$(top_builddir)/testsuite $(MPI_CLDFLAGS) -L$(top_builddir)/src/client/spindle_api -L. $(DYNAMIC_FLAG) -no-install $(LDFLAGS) -Wl,-rpath,$(PWD)/origin_dir -L$(PWD)/origin_dir -Wl,-rpath-link,$(PWD)/origin_dir/origin_subdir -L$(PWD)/exeorigin_dir '-Wl,-rpath,$$ORIGIN/exeorigin_dir' REGLIB_SRC = $(srcdir)/registerlib.c LD_FUNCDICT = -L$(top_builddir)/testsuite -lfuncdict EXTRA_DIST = run_crash_tests_template.sh crash_test.c crash_functions.c crash_functions.h libcrashfuncs.c libcrashctor.c libcrashfixed.c @@ -727,8 +727,8 @@ uninstall-am: test_driver: $(test_driverSOURCES) $(REGLIB_SRC) libtestoutput.so libfuncdict.so libbigtls1048576.so libbigtls204800.so libbigtls1024.so $(AM_V_CCLD) $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(MPICC) -o $@ $(test_driverSOURCES) $(REGLIB_SRC) -DSO_NAME=$@ $(test_driverCFLAGS) $(test_driverLDFLAGS) $(test_driverLDADD) -lbigtls1048576 -lbigtls204800 -lbigtls1024 -test_driver_libs: $(test_driverSOURCES) $(REGLIB_SRC) libtestoutput.so libfuncdict.so libtest10.so libtest11.so libtest12.so libtest13.so libtest14.so libtest15.so libtest16.so libtest17.so libtest18.so libtest19.so libtest20.so libtest100.so libtest500.so libtest1000.so libtest2000.so libtest4000.so libtest6000.so libtest8000.so libtest10000.so libtls1.so libtls2.so libtls3.so libtls4.so libtls5.so libtls6.so libtls7.so libtls8.so libtls9.so libtls10.so libtls11.so libtls12.so libtls13.so libtls14.so libtls15.so libtls16.so libtls17.so libtls18.so libtls19.so libtls20.so libdepA.so libcxxexceptA.so libdepB.so libdepC.so libcxxexceptB.so origin_dir/liboriginlib.so libbigtls1048576.so libbigtls204800.so libbigtls1024.so - $(AM_V_CCLD) $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(MPICC) -o $@ $(test_driver_libsSOURCES) $(REGLIB_SRC) -DSO_NAME=$@ -DUSE_NEEDED $(test_driver_libsCFLAGS) $(test_driver_libsLDFLAGS) $(test_driver_libsLDADD) -lbigtls1048576 -lbigtls204800 -lbigtls1024 +test_driver_libs: $(test_driverSOURCES) $(REGLIB_SRC) libtestoutput.so libfuncdict.so libtest10.so libtest11.so libtest12.so libtest13.so libtest14.so libtest15.so libtest16.so libtest17.so libtest18.so libtest19.so libtest20.so libtest100.so libtest500.so libtest1000.so libtest2000.so libtest4000.so libtest6000.so libtest8000.so libtest10000.so libtls1.so libtls2.so libtls3.so libtls4.so libtls5.so libtls6.so libtls7.so libtls8.so libtls9.so libtls10.so libtls11.so libtls12.so libtls13.so libtls14.so libtls15.so libtls16.so libtls17.so libtls18.so libtls19.so libtls20.so libdepA.so libcxxexceptA.so libdepB.so libdepC.so libcxxexceptB.so origin_dir/liboriginlib.so libbigtls1048576.so libbigtls204800.so libbigtls1024.so exeorigin_dir/libexeorigin.so + $(AM_V_CCLD) $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(MPICC) -o $@ $(test_driver_libsSOURCES) $(REGLIB_SRC) -DSO_NAME=$@ -DUSE_NEEDED $(test_driver_libsCFLAGS) $(test_driver_libsLDFLAGS) $(test_driver_libsLDADD) -lbigtls1048576 -lbigtls204800 -lbigtls1024 -lexeorigin libtest10.c: libgenerator $(AM_V_GEN)./libgenerator libtest10.c 10 t10 @@ -1003,6 +1003,12 @@ libtestoutput.so: testoutput.c $(REGLIB_SRC) libfuncdict.so: funcdict.c $(REGLIB_SRC) $(AM_V_CCLD)$(MPICC) -o $@ -fPIC -Wall $< $(REGLIB_SRC) -DSO_NAME=$@ -shared +exeorigin_dir: + $(AM_V_GEN) mkdir -p exeorigin_dir + +exeorigin_dir/libexeorigin.so: origintarget.c exeorigin_dir libfuncdict.so + $(AM_V_CCLD)$(MPICC) -o $@ -fPIC -shared -Wall -DSO_NAME=libexeorigin.so -DFUNC_NAME=origintarget_calc $< $(REGLIB_SRC) $(LD_FUNCDICT) -L$(PWD) + origin_dir/origin_subdir: $(AM_V_GEN) mkdir -p origin_dir/origin_subdir @@ -1011,7 +1017,7 @@ origin_dir/origin_subdir/liborigintarget.so: origintarget.c origin_dir/origin_su @touch $@ origin_dir/liboriginlib.so: originlib.c $(REGLIB_SRC) origin_dir/origin_subdir/liborigintarget.so - $(AM_V_CCLD)$(MPICC) -o $@ -fPIC -Wall $< $(REGLIB_SRC) -DSO_NAME=liboriginlib.so -DFUNC_NAME=origin_calc -shared '-Wl,-rpath,$$ORIGIN/origin_subdir' -Lorigin_dir/origin_subdir -lorigintarget + $(AM_V_CCLD)$(MPICC) -o $@ -fPIC -Wall $< $(REGLIB_SRC) -DSO_NAME=liboriginlib.so -DFUNC_NAME=origintarget_calc -shared '-Wl,-rpath,$$ORIGIN/origin_subdir' -Lorigin_dir/origin_subdir -lm -lorigintarget libsymbind_a.so: $(srcdir)/symbindlib.c $(srcdir)/symbind_fileops.c libsymbind_b.so $(AM_V_CCLD)$(CC) $(CFLAGS) -o $@ -shared -fPIC -Wall -DAUTORUN=a $(srcdir)/symbindlib.c -L. -Wl,--no-as-needed -lsymbind_b -I$(srcdir) -Wl,-rpath,$(top_builddir)/testsuite diff --git a/testsuite/origintarget.c b/testsuite/origintarget.c index e6eab90f..8c5a9ecc 100644 --- a/testsuite/origintarget.c +++ b/testsuite/origintarget.c @@ -1,6 +1,20 @@ +#include + +__thread int tlsvar_originexe = -1; +static void on_load() __attribute__((constructor)); +static void on_load() { tlsvar_originexe = 10; } +int get_tlsvar_originexe() { return tlsvar_originexe; } + +volatile int is_zero = 0; + int origin_target() { + if (is_zero) + printf("HERE\n"); return 2; } - +int origintarget_calc() +{ + return origin_target(); +} diff --git a/testsuite/run_driver_template b/testsuite/run_driver_template index a486766b..a3d61ce3 100644 --- a/testsuite/run_driver_template +++ b/testsuite/run_driver_template @@ -75,7 +75,7 @@ if [ $1 == --serial ] ; then exec SPINDLE_EXEC --level=high --launcher=serial $2 fi -export LIBRARY_LIST="`pwd`/libcxxexceptA.so:`pwd`/libtest100.so:`pwd`/libtest2000.so:`pwd`/libtest500.so:`pwd`/libtest1000.so:`pwd`/libtest4000.so:`pwd`/libtest6000.so:`pwd`/libdepA.so:`pwd`/libtest10.so:`pwd`/libtest11.so:`pwd`/libtest12.so:`pwd`/libtest13.so:`pwd`/libtest14.so:`pwd`/libtest15.so:`pwd`/libtest16.so:`pwd`/libtest17.so:`pwd`/libtest18.so:`pwd`/libtest19.so:`pwd`/libtest20.so:`pwd`/libtest10000.so:`pwd`/libtest50.so:`pwd`/libtest8000.so:`pwd`/origin_dir/liboriginlib.so" +export LIBRARY_LIST="`pwd`/libcxxexceptA.so:`pwd`/libtest100.so:`pwd`/libtest2000.so:`pwd`/libtest500.so:`pwd`/libtest1000.so:`pwd`/libtest4000.so:`pwd`/libtest6000.so:`pwd`/libdepA.so:`pwd`/libtest10.so:`pwd`/libtest11.so:`pwd`/libtest12.so:`pwd`/libtest13.so:`pwd`/libtest14.so:`pwd`/libtest15.so:`pwd`/libtest16.so:`pwd`/libtest17.so:`pwd`/libtest18.so:`pwd`/libtest19.so:`pwd`/libtest20.so:`pwd`/libtest10000.so:`pwd`/libtest50.so:`pwd`/libtest8000.so:`pwd`/origin_dir/liboriginlib.so:`pwd`/exeorigin_dir/libexeorigin.so" if [ $1 == --dependency ] ; then diff --git a/testsuite/test_driver.c b/testsuite/test_driver.c index 50ed9102..6ff56783 100644 --- a/testsuite/test_driver.c +++ b/testsuite/test_driver.c @@ -112,8 +112,11 @@ typedef struct { #define FLAGS_TLSLIB (1 << 5) #define FLAGS_CHECKSYMT (1 << 6) #define FLAGS_LOCAL (1 << 7) +#define FLAGS_OPEN_AFTER_FORK (1 << 8) + int abort_mode = 0; int fork_mode = 0; +int fork_mode_is_child = 0; int fork_child = 0; int forkexec_mode = 0; int nompi_mode = 0; @@ -151,11 +154,11 @@ open_libraries_t libraries[] = { { "libtest6000.so", NULL, NULL, NULL, UNLOADED, 0, NULL }, { "libtest8000.so", NULL, NULL, NULL, UNLOADED, 0, NULL }, { "libtest10000.so", NULL, NULL, NULL, UNLOADED, 0, NULL }, - { "libdepA.so", NULL, NULL, NULL, UNLOADED, 0, NULL }, - { "libcxxexceptA.so", NULL, NULL, NULL, UNLOADED, 0, NULL }, + { "libdepA.so", NULL, NULL, NULL, UNLOADED, FLAGS_OPEN_AFTER_FORK, NULL }, + { "libcxxexceptA.so", NULL, NULL, NULL, UNLOADED, FLAGS_OPEN_AFTER_FORK, NULL }, { "libnoexist.so", NULL, NULL, NULL, UNLOADED, FLAGS_NOEXIST, NULL }, { "libsymlink.so", NULL, NULL, NULL, UNLOADED, FLAGS_SYMLINK | FLAGS_SKIP | FLAGS_WONTLOAD, NULL }, - { "liboriginlib.so", NULL, NULL, NULL, UNLOADED, 0, "origin_dir/" }, + { "liboriginlib.so", NULL, NULL, NULL, UNLOADED, FLAGS_OPEN_AFTER_FORK, "origin_dir/" }, { "libtls1.so", NULL, NULL, NULL, UNLOADED, FLAGS_TLSLIB | FLAGS_SKIP, NULL }, { "libtls2.so", NULL, NULL, NULL, UNLOADED, FLAGS_TLSLIB | FLAGS_SKIP, NULL }, { "libtls3.so", NULL, NULL, NULL, UNLOADED, FLAGS_TLSLIB | FLAGS_SKIP, NULL }, @@ -197,6 +200,7 @@ open_libraries_t libraries[] = { { "libtest8000.so", NULL, NULL, NULL, UNLOADED, FLAGS_CHECKSYMT | FLAGS_SKIP, NULL }, { "libtest10000.so", NULL, NULL, NULL, UNLOADED, FLAGS_CHECKSYMT | FLAGS_SKIP, NULL }, { "liblocal.so", NULL, NULL, NULL, UNLOADED, FLAGS_LOCAL, NULL }, + { "libexeorigin.so", NULL, NULL, NULL, UNLOADED, FLAGS_OPEN_AFTER_FORK, "exeorigin_dir/" }, { NULL, NULL, NULL, 0, 0 } }; int num_libraries; @@ -453,10 +457,11 @@ static char *create_local_file(int libnum) static void open_library(int i) { - char *fullpath, *result; + char *fullpath; + void *result; if (libraries[i].flags & FLAGS_TLSLIB) return; - if (!(libraries[i].flags & FLAGS_LOCAL)) + else if (!(libraries[i].flags & FLAGS_LOCAL)) fullpath = libpath(libraries[i].libname, libraries[i].subdir); else fullpath = create_local_file(i); @@ -465,12 +470,13 @@ static void open_library(int i) check_symt(fullpath); return; } - if (!(libraries[i].flags & FLAGS_WONTLOAD) && !(libraries[i].flags & FLAGS_LOCAL)) + if (!(libraries[i].flags & FLAGS_WONTLOAD) && !(libraries[i].flags & FLAGS_LOCAL)) { test_printf("dlstart %s\n", libraries[i].libname); - + } + result = dlopen(fullpath, RTLD_LAZY | RTLD_GLOBAL); if (libraries[i].flags & FLAGS_NOEXIST) { - if (result != 0) + if (result) err_printf("Failure, opened a library that doesn't exist\n"); return; } @@ -501,6 +507,11 @@ void dependency_mode() /* Should be auto loaded */ int i; for (i = 0; i Date: Wed, 28 Oct 2026 10:44:25 -0700 Subject: [PATCH 4/6] Fix bug where non-existant directories were being created in the build cache. --- src/server/auditserver/ldcs_audit_server_handlers.c | 7 +++++-- src/utils/spindle_mkdir.c | 3 +-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/server/auditserver/ldcs_audit_server_handlers.c b/src/server/auditserver/ldcs_audit_server_handlers.c index 955cc4fa..47e1e8d7 100644 --- a/src/server/auditserver/ldcs_audit_server_handlers.c +++ b/src/server/auditserver/ldcs_audit_server_handlers.c @@ -632,11 +632,13 @@ static int handle_read_directory(ldcs_process_data_t *procdata, char *dir) procdata->server_stat.procdir.bytes += rc; procdata->server_stat.procdir.time += (ldcs_get_time() - starttime); - if (cache_dir_result == LDCS_CACHE_DIR_PARSED_AND_EXISTS || - cache_dir_result == LDCS_CACHE_DIR_PARSED_AND_NOT_EXISTS) { + if (cache_dir_result == LDCS_CACHE_DIR_PARSED_AND_EXISTS) { handle_process_directory(procdata, dir); return 0; } + if (cache_dir_result == LDCS_CACHE_DIR_PARSED_AND_NOT_EXISTS) { + return 0; + } else { err_printf("Failed to process directory %s\n", dir); return -1; @@ -1676,6 +1678,7 @@ static int handle_process_directory(ldcs_process_data_t *procdata, const char *d { char local_dirname[MAX_PATH_LEN+1]; + debug_printf2("Creating directory %s in cache\n", dirname); filemngt_calc_dirname(dirname, local_dirname, sizeof(local_dirname)); spindle_mkdir_in_cache(local_dirname, procdata->cachepath); return 0; diff --git a/src/utils/spindle_mkdir.c b/src/utils/spindle_mkdir.c index 020403ff..e831a010 100644 --- a/src/utils/spindle_mkdir.c +++ b/src/utils/spindle_mkdir.c @@ -362,8 +362,7 @@ static int spindle_mkdir_impl(const char *path, const char *cachepath) return -1; length = strlen(resolved) - physical_length; if (length > PATH_MAX - 1 - prefix_length) { - debug_printf3("WARNING: spindle_mkdir failing with ENAMETOOLONG on '%s'\n", - resolved ? resolved : "[NULL]"); + debug_printf3("WARNING: spindle_mkdir failing with ENAMETOOLONG on '%s'\n", resolved); errno = ENAMETOOLONG; return -1; } From 252c27f93cda42601ac4ca4ca1bc8c3d58e165da Mon Sep 17 00:00:00 2001 From: Matthew LeGendre Date: Thu, 29 Oct 2026 16:33:45 -0700 Subject: [PATCH 5/6] Rewrite and simplify spindle_mkdir. Handle cases around relative paths through non-existant directories. --- .../auditserver/ldcs_audit_server_filemngt.c | 10 +- src/utils/spindle_mkdir.c | 407 +++++++++--------- 2 files changed, 212 insertions(+), 205 deletions(-) diff --git a/src/server/auditserver/ldcs_audit_server_filemngt.c b/src/server/auditserver/ldcs_audit_server_filemngt.c index d56290b3..dc482a31 100644 --- a/src/server/auditserver/ldcs_audit_server_filemngt.c +++ b/src/server/auditserver/ldcs_audit_server_filemngt.c @@ -134,6 +134,7 @@ char *filemngt_calc_localname(char *global_name, calc_local_t reqtype) char *lastslash; size_t dirpart_size, filepart_size; ssize_t bytes_left; + int result, error; GCC7_DISABLE_WARNING("-Wformat-truncation"); @@ -176,7 +177,14 @@ char *filemngt_calc_localname(char *global_name, calc_local_t reqtype) filepart[sizeof(filepart)-1] = '\0'; filemngt_calc_dirname(dirpart, target, sizeof(target)); - spindle_mkdir_in_cache(target, _ldcs_audit_server_cachepath); + debug_printf3("Calling spindle_mkdir(%s, %s)\n", target, _ldcs_audit_server_cachepath); + result = spindle_mkdir_in_cache(target, _ldcs_audit_server_cachepath); + if (result == -1) { + error = errno; + err_printf("spindle_mkdir_in_cache(%s, %s) failed with error %s\n", target, _ldcs_audit_server_cachepath, strerror(error)); + return NULL; + } + debug_printf3("Calculated local directory name for %s as %s and made directory\n", global_name, target); bytes_left = sizeof(target) - strlen(target); strncat(target, "/", --bytes_left); diff --git a/src/utils/spindle_mkdir.c b/src/utils/spindle_mkdir.c index e831a010..cd5dde11 100644 --- a/src/utils/spindle_mkdir.c +++ b/src/utils/spindle_mkdir.c @@ -20,17 +20,11 @@ Place, Suite 330, Boston, MA 02111-1307 USA #include #include -#if !defined(USE_PLUGIN_DEBUG) -#include "spindle_debug.h" -#else -#include "plugin_utils.h" -#define SPINDLE_DEBUG_H_ -#endif - -#include "ldcs_api.h" #include "config.h" #include "spindle_debug.h" + + #if defined(TRACK_MKDIR) extern void track_mkdir(const char *dir); #endif @@ -54,12 +48,6 @@ extern int lookup_prev_mkdir(const char *dir); #define PATH_MAX 4096 #endif -/* Keep filesystem calls in one place for the startup replacement layer. */ -static int spindle_mkdir_stat(const char *path, struct stat *buf) -{ - return stat(path, buf); -} - static int spindle_mkdir_lstat(const char *path, struct stat *buf) { return lstat(path, buf); @@ -88,7 +76,7 @@ static int append(char *path, size_t *length, const char *part, size_t size) if (*length + separator > PATH_MAX - 1 || size > PATH_MAX - 1 - *length - separator) { errno = ENAMETOOLONG; - debug_printf3("WARNING: apend failing with ENAMETOLONG on '%s' and '%s'\n", path, part); + debug_printf("WARNING: append failing with ENAMETOLONG on '%s' and '%s'\n", path, part); return -1; } if (separator) @@ -117,23 +105,69 @@ static int parent(char path[PATH_MAX], size_t *length) return 0; } -/* Resolve suffix onto path in place. path starts at "/", ".", or the already - * resolved prefix; pending holds components not yet applied to path. - * - * Symlinks are expanded before later '..' components. With floor, '..' stops - * at the prefix and symlink targets reached inside it must stay inside it. - * required is set only while resolving the prefix, which must already exist. */ -static int resolve(const char *suffix, const char *floor, int required, - char path[PATH_MAX]) +static int check_directory(const char *path, struct stat *existing_stat) { - char pending[PATH_MAX], target[PATH_MAX]; - size_t length = strlen(path), floor_length = floor ? strlen(floor) : 0; - size_t cursor = 0, pending_length = strlen(suffix); - int links = 0; + struct stat st; + int error; + + if (!existing_stat) { + if (spindle_mkdir_lstat(path, &st) < 0) { + error = errno; + debug_printf("WARNING: Failed to stat %s: %s\n", path, strerror(error)); + errno = error; + return -1; + } + existing_stat = &st; + } + if (!S_ISDIR(existing_stat->st_mode)) { + errno = ENOTDIR; + debug_printf("WARNING: %s is not a directory\n", path); + return -1; + } + if (existing_stat->st_uid != geteuid() || existing_stat->st_gid != getegid() || + (existing_stat->st_mode & 07777) != 0700) { + errno = EACCES; + debug_printf("WARNING: Permissions error on %s\n", path); + return -1; + } + return 0; +} - if (pending_length >= sizeof pending) { +/* EEXIST may come from another process; verify what is there either way. */ +static int make_directory(const char *path) +{ + int result; + if (spindle_mkdir_mkdir(path, 0700) < 0 && errno != EEXIST) { + debug_printf("WARNING: Failed to mkdir(%s)\n", path); + return -1; + } + result = check_directory(path, NULL); + if (result == -1) { + debug_printf("WARNING: check_directory failed here\n"); + return -1; + } +#if defined(TRACK_MKDIR) + track_mkdir(path); +#endif + return result; +} + +/* Process the path 'suffix' and make new directories. path starts at "/", ".", or the already + resolved cachepath. ../, ./, and symlinks are resolved and, if a cachepath is provided, + must stay within the cachepath. */ +static int mkdir_impl(const char *suffix, const char *cachepath, char path[PATH_MAX]) +{ + char pending[PATH_MAX], target[PATH_MAX]; + size_t length, cachepath_length; + size_t cursor = 0, pending_length; + int links = 0, result, error; + + length = strlen(path); + cachepath_length = cachepath ? strlen(cachepath) : 0; + pending_length = strlen(suffix); + if (pending_length >= sizeof(pending)) { + debug_printf("WARNING: mkdir_impl ENAMETOLONG on '%s'\n", suffix); errno = ENAMETOOLONG; - debug_printf3("WARNING: resolve ENAMETOLONG on '%s'\n", suffix); return -1; } /* Expansion can insert NUL separators, so pending_length, not strlen, @@ -144,7 +178,7 @@ static int resolve(const char *suffix, const char *floor, int required, size_t start, end, old_length; size_t boundary; struct stat st; - ssize_t n; + ssize_t symlink_target_size; /* Skip empty components. An inserted NUL ends one symlink target; * check it before applying the caller's remaining components. */ @@ -152,29 +186,39 @@ static int resolve(const char *suffix, const char *floor, int required, ++cursor; if (cursor == pending_length) break; + /* A NUL marks the end of a symlink target, before its caller's tail. */ if (pending[cursor] == '\0') { - if (floor && !within(path, floor, floor_length)) { - debug_printf3("WARNING within failing with EACCES on '%s' in '%s'\n", path, floor); + if (cachepath && !within(path, cachepath, cachepath_length)) { + debug_printf("WARNING within failing with EACCES on '%s' in '%s'\n", path, cachepath); errno = EACCES; return -1; } ++cursor; continue; } + start = cursor; while (cursor < pending_length && pending[cursor] && pending[cursor] != '/') ++cursor; end = cursor; - if (end - start == 1 && pending[start] == '.') + + /* Path component is a './'. Move on. */ + if (end - start == 1 && pending[start] == '.') { continue; + } + + /* Path component is a '../'. Use parent() to move up a directory as long, as we're not on the + cachepath root */ if (end - start == 2 && pending[start] == '.' && pending[start + 1] == '.') { - /* Clamp at floor, just as '..' at '/' stays at '/'. */ - if (!floor || !within(path, floor, floor_length) || - length > floor_length) { - if (parent(path, &length) < 0) { - debug_printf3("resolv failing because could not determine parent of '%s' with floor '%s'\n", - path ? path : "[NULL]", floor ? floor : "[NULL]"); + /* Clamp at cachepath, just as '..' at '/' stays at '/'. */ + if (!cachepath || length > cachepath_length) { + result = parent(path, &length); + if (result < 0) { + error = errno; + debug_printf("WARNING mkdir_impl failing because could not determine parent of '%s' with cachepath '%s'\n", + path ? path : "[NULL]", cachepath ? cachepath : "[NULL]"); + errno = error; return -1; } } @@ -186,52 +230,76 @@ static int resolve(const char *suffix, const char *floor, int required, return -1; /* Missing components are kept in the resolved path for the later * mkdir pass. Existing non-directories cannot be traversed. */ - if (spindle_mkdir_lstat(path, &st) < 0) { - if (errno == ENOENT && !required) - continue; - debug_printf3("lstat failed during resolve for '%s'\n", path); + result = spindle_mkdir_lstat(path, &st); + + /* Create the directory if the path component does not exist */ + if (result == -1 && errno == ENOENT) { + result = make_directory(path); + if (result == -1) { + return -1; + } + continue; + } + if (result == -1) { + error = errno; + debug_printf("WARNING: lstat failed during mkdir_impl for '%s': %s\n", path, strerror(error)); + errno = error; return -1; } - if (!S_ISLNK(st.st_mode)) { - if (!S_ISDIR(st.st_mode)) { - errno = ENOTDIR; - debug_printf3("resolve failing for '%s' because ENOTDIR\n", path); - return -1; + if (S_ISDIR(st.st_mode)) { + if (cachepath && length > cachepath_length) { + /* We're strictly underneath the cachepath on an existing directory. Permissions should be locked down. */ + result = check_directory(path, &st); + if (result == -1) { + error = errno; + debug_printf("WARNING: Permission error underneath cachepath %s: %s\n", path, strerror(error)); + errno = error; + return -1; + } } continue; } + if (!S_ISLNK(st.st_mode)) { + debug_printf("WARNING: mkdir_impl failing for '%s' because ENOTDIR\n", path); + errno = ENOTDIR; + return -1; + } + /* Check targets of links inside the prefix, even if the caller's * remaining path would later return inside. Links in a prefix alias * may resolve to an ancestor, so they get no boundary check here. */ - boundary = floor && within(path, floor, floor_length); + boundary = cachepath && within(path, cachepath, cachepath_length); if (++links > 40) { + debug_printf("WARNING: mkdir_impl failing for '%s' because ELOOP\n", path); errno = ELOOP; - debug_printf3("resolve failing for '%s' because ELOOP\n", path); return -1; } - n = spindle_mkdir_readlink(path, target, sizeof target - 1); - if (n < 0) { - debug_printf3("resolve failing for '%s' because readlink failed\n", path); + symlink_target_size = spindle_mkdir_readlink(path, target, sizeof(target) - 1); + if (symlink_target_size < 0) { + error = errno; + debug_printf("WARNING: mkdir_impl failing for '%s' because readlink failed: %s\n", path, strerror(error)); + errno = error; return -1; } - if (n == 0) { + if (symlink_target_size == 0) { + debug_printf("WARNING: mkdir_impl failing for '%s' with ENOENT\n", path); errno = ENOENT; - debug_printf3("resolve failing for '%s' with ENOENT\n", path); return -1; } - if ((size_t)n + boundary + pending_length - end >= sizeof pending) { + if ((size_t)symlink_target_size + boundary + pending_length - end >= sizeof(pending)) { + debug_printf("WARNING: mkdir_impl failing for '%s' with ENAMETOOLONG\n", path); errno = ENAMETOOLONG; - debug_printf3("resolve failing for '%s' with ENAMETOOLONG\n", path); return -1; } + /* Replace the link with its target and keep the unread tail. The * optional NUL marks where this target ends. Nested expansions keep * earlier markers in that tail without a separate stack. */ - memmove(pending + n + boundary, pending + end, pending_length - end); - memcpy(pending, target, n); + memmove(pending + symlink_target_size + boundary, pending + end, pending_length - end); + memcpy(pending, target, symlink_target_size); if (boundary) - pending[n] = '\0'; - pending_length = (size_t)n + boundary + pending_length - end; + pending[symlink_target_size] = '\0'; + pending_length = (size_t) symlink_target_size + boundary + pending_length - end; cursor = 0; /* Absolute targets restart at root; relative targets restart at the * link's parent, saved in old_length. */ @@ -245,191 +313,122 @@ static int resolve(const char *suffix, const char *floor, int required, } } /* Also reject an ordinary path that ends outside the prefix. */ - if (floor && !within(path, floor, floor_length)) { + if (cachepath && !within(path, cachepath, cachepath_length)) { + debug_printf("WARNING: mkdir_impl failing for '%s' with EACCESs and cachepath of %s\n", path, cachepath); errno = EACCES; - debug_printf3("resolve failing for '%s' with EACCESs and floor of %s\n", path, floor); return -1; } return 0; } -/* Leading relative '..' components name ancestors, not directories to make. */ -static size_t creation_start(const char *path) -{ - size_t i = 0; - - while (path[i] == '.') { - size_t dots = path[i + 1] == '.' ? 2 : 1; - if (path[i + dots] != '/' && path[i + dots] != '\0') - break; - i += dots; - if (path[i] == '/') - ++i; - } - return i; -} - -static int check_directory(const char *path) +static ssize_t validate_path_under_cachepath(const char *path, const char *cachepath) { - struct stat st; - - if (spindle_mkdir_stat(path, &st) < 0) { - debug_printf3("WARNING: Failed to stat %s\n", path); - return -1; - } - if (!S_ISDIR(st.st_mode)) { - errno = ENOTDIR; - debug_printf3("WARNING: %s is not a directory\n", path); + ssize_t prefix_length; + prefix_length = strlen(cachepath); + while (prefix_length > 1 && cachepath[prefix_length - 1] == '/') + --prefix_length; + if (prefix_length >= PATH_MAX) { + debug_printf("WARNING: spindle_mkdir failing with ENAMETOOLONG on '%s'\n", + path ? path : "[NULL]"); + errno = ENAMETOOLONG; return -1; } - if (st.st_uid != geteuid() || st.st_gid != getegid() || - (st.st_mode & 07777) != 0700) { + if (!within(path, cachepath, prefix_length)) { + debug_printf("WARNING: spindle_mkdir failing with EACCESS on '%s' and '%s'\n", + path ? path : "[NULL]", cachepath ? cachepath : "[NULL]"); errno = EACCES; - debug_printf3("WARNING: Permissions error on %s\n", path); return -1; } - return 0; + return prefix_length; } -/* EEXIST may come from another process; verify what is there either way. */ -static int make_directory(const char *path, int should_check_perms) +static int check_mkdir_args(const char *path, const char *cachepath) { - int result; - if (spindle_mkdir_mkdir(path, 0700) < 0 && errno != EEXIST) { - debug_printf3("WARNING: Failed to mkdir(%s)\n", path); - return -1; - } - result = check_directory(path); - if (result == -1) { - debug_printf3("WARNING: check_directory failed here\n"); + if (!path || !*path || (cachepath && (cachepath[0] != '/' || path[0] != '/'))) { + debug_printf("WARNING: spindle_mkdir failing with EINVAL on '%s' with cachepath '%s'\n", + path ? path : "[NULL]", cachepath ? cachepath : "[NULL]"); + errno = EINVAL; return -1; } -#if defined(TRACK_MKDIR) - track_mkdir(path); -#endif - return result; + return 0; } static int spindle_mkdir_impl(const char *path, const char *cachepath) { - char resolved[PATH_MAX], floor[PATH_MAX], literal[PATH_MAX]; - size_t prefix_length, physical_length, length, i; + char resolved[PATH_MAX]; + ssize_t prefix_length; + int error; - if (!path || !*path || (cachepath && !*cachepath)) { - debug_printf3("WARNING: spindle_mkdir failing with EINVAL on '%s' with cachepath '%s'\n", - path ? path : "[NULL]", cachepath ? cachepath : "[NULL]"); - errno = EINVAL; + if (check_mkdir_args(path, cachepath) == -1) return -1; - } + if (cachepath) { - /* Resolve the existing prefix to establish a physical boundary. */ - if (cachepath[0] != '/' || path[0] != '/') { - debug_printf3("WARNING: spindle_mkdir failing with EINVAL on '%s' and '%s'\n", - path ? path : "[NULL]", cachepath ? cachepath : "[NULL]"); - errno = EINVAL; - return -1; - } - prefix_length = strlen(cachepath); - while (prefix_length > 1 && cachepath[prefix_length - 1] == '/') - --prefix_length; - if (prefix_length >= PATH_MAX) { - debug_printf3("WARNING: spindle_mkdir failing with ENAMETOOLONG on '%s'\n", - path ? path : "[NULL]"); - errno = ENAMETOOLONG; - return -1; - } - if (!within(path, cachepath, prefix_length)) { - debug_printf3("WARNING: spindle_mkdir failing with EACCESS on '%s' and '%s'\n", - path ? path : "[NULL]", cachepath ? cachepath : "[NULL]"); - errno = EACCES; + /* Validate that path is prefixed by the cachepath */ + prefix_length = validate_path_under_cachepath(path, cachepath); + if (prefix_length == -1) { + error = errno; + debug_printf("WARNING: Failed to validate that mkdir cache is under cachepath '%s' and '%s'\n", + path, cachepath); + errno = error; return -1; - } - strcpy(resolved, "/"); - if (resolve(cachepath, NULL, 1, resolved) < 0) { - debug_printf3("WARNING: resolve failed here\n"); - return -1; - } - if (check_directory(resolved) < 0) { - debug_printf3("WARNING: resolve failed here\n"); - return -1; - } + } - strcpy(floor, resolved); - physical_length = strlen(resolved); - /* Resolve below that boundary, then map back to the literal prefix - * so every mkdir call uses the spelling required by cleanup. */ - if (resolve(path + prefix_length, floor, 0, resolved) < 0) - return -1; - length = strlen(resolved) - physical_length; - if (length > PATH_MAX - 1 - prefix_length) { - debug_printf3("WARNING: spindle_mkdir failing with ENAMETOOLONG on '%s'\n", resolved); - errno = ENAMETOOLONG; + strncpy(resolved, cachepath, sizeof(resolved)); + if (mkdir_impl(path + prefix_length, cachepath, resolved) < 0) { return -1; } - memcpy(literal, cachepath, prefix_length); - memcpy(literal + prefix_length, resolved + physical_length, length + 1); - length += prefix_length; - i = prefix_length; } else { /* Relative paths stay relative; absolute paths start at root. */ strcpy(resolved, path[0] == '/' ? "/" : "."); - if (resolve(path, NULL, 0, resolved) < 0) { - debug_printf3("WARNING: resolve failed here\n"); + if (mkdir_impl(path, NULL, resolved) < 0) { return -1; } - length = strlen(resolved); - memcpy(literal, resolved, length + 1); - i = creation_start(literal); } - /* Walk the resolved path once, creating and verifying each directory. */ - while (i < length) { - int should_check_perms; - size_t end, next; - char saved; - - while (i < length && literal[i] == '/') - ++i; - if (i == length) - break; - end = i; - while (end < length && literal[end] != '/') - ++end; - next = end; - while (next < length && literal[next] == '/') - ++next; - saved = literal[end]; - literal[end] = '\0'; - /* Public and system-owned directories may lead to an unprefixed - * path. Directories managed by this function must be private. */ - if (!cachepath && next < length) { - struct stat st; - if (spindle_mkdir_stat(literal, &st) == 0 && S_ISDIR(st.st_mode)) { - literal[end] = saved; - i = end; - continue; - } - } - - /* Only check permissions on the parent when making withinin the cachepath. - When making cachepath or commpaths we may be in some other directories */ - should_check_perms = (cachepath != NULL); - if (make_directory(literal, should_check_perms) < 0) { - debug_printf3("WARNING: make_directory failed here\n"); - return -1; - } - literal[end] = saved; - i = end; - } return 0; } int spindle_mkdir(const char *path) { - return spindle_mkdir_impl(path, NULL); + char resolved[PATH_MAX]; + + if (check_mkdir_args(path, NULL) == -1) + return -1; + + strcpy(resolved, path[0] == '/' ? "/" : "."); + if (mkdir_impl(path, NULL, resolved) < 0) { + return -1; + } + + return 0; } int spindle_mkdir_in_cache(const char *path, const char *cachepath) { - return spindle_mkdir_impl(path, cachepath); + char resolved[PATH_MAX]; + ssize_t prefix_length; + int error; + + if (!cachepath) + return spindle_mkdir(path); + + if (check_mkdir_args(path, cachepath) == -1) + return -1; + + /* Validate that path is prefixed by the cachepath */ + prefix_length = validate_path_under_cachepath(path, cachepath); + if (prefix_length == -1) { + error = errno; + debug_printf("WARNING: Failed to validate that mkdir on '%s' is under cachepath '%s'\n", + path, cachepath); + errno = error; + return -1; + } + + strncpy(resolved, cachepath, sizeof(resolved)); + if (mkdir_impl(path + prefix_length, cachepath, resolved) < 0) { + return -1; + } + + return 0; } From 9150527db380f92a1571ade298ef13bd0938447c Mon Sep 17 00:00:00 2001 From: Matthew LeGendre Date: Fri, 30 Oct 2026 11:22:52 -0700 Subject: [PATCH 6/6] Restore special debug includes so spindle_mkdir can be used in slurm plugin --- src/utils/spindle_mkdir.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/utils/spindle_mkdir.c b/src/utils/spindle_mkdir.c index cd5dde11..e367db19 100644 --- a/src/utils/spindle_mkdir.c +++ b/src/utils/spindle_mkdir.c @@ -20,10 +20,14 @@ Place, Suite 330, Boston, MA 02111-1307 USA #include #include -#include "config.h" +#if !defined(USE_PLUGIN_DEBUG) #include "spindle_debug.h" +#else +#include "plugin_utils.h" +#define SPINDLE_DEBUG_H_ +#endif - +#include "config.h" #if defined(TRACK_MKDIR) extern void track_mkdir(const char *dir);