logo

utils-std

Collection of commonly available Unix tools git clone https://anongit.hacktivis.me/git/utils-std.git/
commit: c562d8f046f22132621b49958ea9fe48db423962
parent 8aec98a517fdbbb0fc303e64ad5c7f61babdfd1b
Author: Haelwenn (lanodan) Monnier <contact@hacktivis.me>
Date:   Sat, 24 Jan 2026 15:35:20 +0100

Revert "libutils/lib_mkdir: mkdir+chmod when making parents, check filetype after EEXIST"

As could be predicted, it ends up creating a concurrency issue
when multiple mkdir(1) invocations for neighboring subdirectories
are done at the same time.

This reverts commit 28b92a6bd493f0a2543ad6709f3a9f6f116ddb88.

As far as I can tell the recursion issue was caused by keeping
the trailing slash so can also be safely reverted.

This reverts commit 7c50cbab04faffa818b127dd3b55047dc01cfc4f.

Diffstat:

MMakefile2+-
Mlibutils/lib_mkdir.c111+++++++++++++++++--------------------------------------------------------------
Dlibutils/strip_lastelem.c28----------------------------
Dlibutils/strip_lastelem.h5-----
Dtest-libutils/t_strip_lastelem.c61-------------------------------------------------------------
5 files changed, 25 insertions(+), 182 deletions(-)

diff --git a/Makefile b/Makefile @@ -33,7 +33,7 @@ selfcheck-cmds: $(EXE) $(TEST_CMDS) LDSTATIC="$(LDSTATIC)" ./check-cmds.sh .PHONY: selfcheck-libs -TEST_LIBS = test-libutils/t_mode test-libutils/t_strtodur test-libutils/t_symbolize_mode test-libutils/t_truncation test-lib/t_sha1 test-lib/t_sha256 test-lib/t_sha512 test-libutils/t_humanize test-libutils/t_strlcpy test-libutils/t_strip_lastelem +TEST_LIBS = test-libutils/t_mode test-libutils/t_strtodur test-libutils/t_symbolize_mode test-libutils/t_truncation test-lib/t_sha1 test-lib/t_sha256 test-lib/t_sha512 test-libutils/t_humanize test-libutils/t_strlcpy selfcheck-libs: $(TEST_LIBS) LDSTATIC="$(LDSTATIC)" ./check-libs.sh $(TEST_LIBS) diff --git a/libutils/lib_mkdir.c b/libutils/lib_mkdir.c @@ -7,8 +7,6 @@ #include "./lib_mkdir.h" #include "./lib_string.h" -#include "./mode.h" -#include "./strip_lastelem.h" #include <errno.h> #include <limits.h> // PATH_MAX @@ -16,107 +14,46 @@ #include <string.h> // strlen, strerror #include <sys/stat.h> // mkdir -static int -wrap_mkdir(const char *path, mode_t mode) +int +mkdir_parents(char *path, mode_t mode) { - if(mkdir(path, mode) < 0) + for(int i = strlen(path) - 1; i >= 0; i--) { - if(errno != EEXIST) - { - fprintf( - stderr, "%s: error: Failed making directory '%s': %s\n", argv0, path, strerror(errno)); - errno = 0; - return -1; - } - - errno = 0; - - /* - * As seen from GNU. I believe it shouldn't introduce a TOCTOU kind - * of problem as it's a check which only invalidates. - */ - struct stat dir_stat; - if(stat(path, &dir_stat) < 0) - { - fprintf(stderr, - "%s: error: Failed getting status of existing file or directory at '%s': %s\n", - argv0, - path, - strerror(errno)); - return -1; - } + if(path[i] != '/') break; - if(!S_ISDIR(dir_stat.st_mode)) - { - fprintf(stderr, - "%s: error: Location '%s' already used by %s\n", - argv0, - path, - filetype(&dir_stat)); - return -1; - } - - errno = EEXIST; - return 0; + path[i] = 0; } - else if(mkdir_parents_verbose) - fprintf(stderr, "%s: Made directory: %s\n", argv0, path); - - return 0; -} - -static int -mkdir_elders(char *path, mode_t mode) -{ - if(path[0] == '\0') return 0; char parent[PATH_MAX] = ""; lib_strlcpy(parent, path, PATH_MAX); - strip_lastelem(parent); - - if(mkdir_elders(parent, mode) < 0) return -1; - errno = 0; - if(wrap_mkdir(path, 0) < 0) return -1; - - /* - * mkdir()+chmod() flow introduced with http://austingroupbugs.net/view.php?id=161 - * released in Technical Corrigendum 1 XCU/TC1-2008/0122 - * - * Sadly the October 9, 2009 meeting https://www.opengroup.org/austin/docs/austin_467.txt - * doesn't provides more details - */ - if(errno != EEXIST && chmod(path, mode) < 0) + for(int i = strlen(parent) - 1; i >= 0; i--) { - char mode_buf[11] = ""; - symbolize_mode(mode, mode_buf); - fprintf(stderr, - "%s: error: Failed applying mode (%07o/%s) on '%s': %s\n", - argv0, - mode, - mode_buf, - path, - strerror(errno)); - return -1; - } + if(parent[i] == '/') break; - return 0; -} + parent[i] = 0; + } -int -mkdir_parents(char *path, mode_t mode) -{ if(path[0] == 0) return 0; - char parent[PATH_MAX] = ""; - lib_strlcpy(parent, path, PATH_MAX); - strip_lastelem(parent); + mode_t parent_mode = (S_IWUSR | S_IXUSR | ~mkdir_parents_filemask) & 0777; + + if(mkdir_parents(parent, parent_mode) < 0) return -1; - mode_t elders_mode = (S_IWUSR | S_IXUSR | ~mkdir_parents_filemask) & 0777; + if(mkdir(path, mode) < 0) + { + if(errno == EEXIST) + { + errno = 0; + return 0; + } - if(mkdir_elders(parent, elders_mode) < 0) return -1; + fprintf(stderr, "%s: error: Failed making directory '%s': %s\n", argv0, path, strerror(errno)); + errno = 0; + return -1; + } - if(wrap_mkdir(path, mode) < 0) return -1; + if(mkdir_parents_verbose) fprintf(stderr, "%s: Made directory: %s\n", argv0, path); return 0; } diff --git a/libutils/strip_lastelem.c b/libutils/strip_lastelem.c @@ -1,28 +0,0 @@ -// utils-std: Collection of commonly available Unix tools -// SPDX-FileCopyrightText: 2017 Haelwenn (lanodan) Monnier <contact+utils@hacktivis.me> -// SPDX-License-Identifier: MPL-2.0 - -#include "./strip_lastelem.h" - -#include <string.h> - -// "/foo/bar/" -> "/foo/" -// "/foo/bar" -> "/foo/" -// "/" -> "" -void -strip_lastelem(char *parent) -{ - size_t i = strlen(parent); - if(i == 0) - return; - else - i--; - - while(i > 0 && parent[i] == '/') - parent[i--] = '\0'; - - while(i > 0 && parent[i] != '/') - parent[i--] = '\0'; - - if(i == 0) parent[i--] = '\0'; -} diff --git a/libutils/strip_lastelem.h b/libutils/strip_lastelem.h @@ -1,5 +0,0 @@ -// utils-std: Collection of commonly available Unix tools -// SPDX-FileCopyrightText: 2017 Haelwenn (lanodan) Monnier <contact+utils@hacktivis.me> -// SPDX-License-Identifier: MPL-2.0 - -void strip_lastelem(char *parent); diff --git a/test-libutils/t_strip_lastelem.c b/test-libutils/t_strip_lastelem.c @@ -1,61 +0,0 @@ -// utils-std: Collection of commonly available Unix tools -// SPDX-FileCopyrightText: 2017 Haelwenn (lanodan) Monnier <contact+utils@hacktivis.me> -// SPDX-License-Identifier: MPL-2.0 - -#define _POSIX_C_SOURCE 200809L -#include "../libutils/strip_lastelem.h" - -#include <assert.h> -#include <stdio.h> // printf -#include <string.h> // strcmp - -int counter = 0; -int err = 0; - -static void -t_strip_lastelem(const char *in, const char *exp) -{ - int id = ++counter; - static char buf[512]; - - strcpy(buf, in); - - strip_lastelem(buf); - - if(strcmp(buf, exp) == 0) - { - printf("ok %d - \"%s\" -> \"%s\"\n", id, in, exp); - return; - } - - err = 1; - printf("not ok %d - \"%s\" -> \"%s\"\n", id, in, exp); - printf("# Got: \"%s\"\n", buf); -} - -int -main(void) -{ - int plan = 11; - printf("1..%d\n", plan); - - t_strip_lastelem("", ""); - - t_strip_lastelem("/", ""); - t_strip_lastelem("//", ""); - - t_strip_lastelem("/foo", ""); - t_strip_lastelem("/foo/", ""); - - t_strip_lastelem("/foo/bar", "/foo/"); - t_strip_lastelem("/foo/bar/", "/foo/"); - - t_strip_lastelem("a", ""); - t_strip_lastelem("a/", ""); - - t_strip_lastelem("a/b", "a/"); - t_strip_lastelem("a/b/", "a/"); - - assert(counter == plan); - return err; -}