/*- * Copyright (c) 2026, Mr. Goblin * * SPDX-License-Identifier: BSD-2-Clause * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials provided * with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY * OF SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include "db.h" #ifndef PATH_MAX # define PATH_MAX 4096 #endif #ifndef NAME_MAX # define NAME_MAX 255 #endif #define DIR_NAME_BUF_SIZE (NAME_MAX+1) static const char * find_chr_or_zero(const char *s, char ch) { while (*s && *s != ch) s++; return s; } static const char * find_chr_or_zero_n(const char *s, char ch, int n) { int i = 0; while (i < n && *s && *s != ch) { s++; i++; } return s; } static struct db_value_int does_dir_exist(const char *path) { struct db_value_int res = {0}; struct stat st = {0}; int code; res.code = dbe_ok; res.data = 1; code = stat(path, &st); if (0 != code) { if (ENOENT == errno) res.data = 0; else if (EACCES == errno) res.code = dbe_access; else res.code = dbe_unknown; } else { res.data = S_IFDIR == (st.st_mode & S_IFMT); } return res; } static enum db_error make_dir_ifne(const char *path, int rights) { struct db_value_int val = {0}; int code; val = does_dir_exist(path); if (dbe_ok != val.code) return val.code; else if (val.data) return dbe_ok; code = mkdir(path, rights); if (0 != code) { if (EACCES == errno) return dbe_access; else if (EEXIST == errno) return dbe_notdir; } return dbe_ok; } static enum db_error do_make_dir_path(const char *path, int rights) { char buf[DIR_NAME_BUF_SIZE] = {0}; const char *p = find_chr_or_zero(path, '/'); unsigned sz = p - path + 1, code; if (sz > sizeof(buf)) return dbe_inval; strncpy(buf, path, sz); buf[sz-1] = '\0'; code = make_dir_ifne(buf, rights); if (dbe_ok != code) return code; if (*p) { code = chdir(buf); if (0 != code) return dbe_access; code = do_make_dir_path(p+1, rights); if (dbe_ok != code) return code; code = chdir(".."); if (0 != code) return dbe_access; } return dbe_ok; } /* mkdir -p */ enum db_error make_dir_path(const char *path, int rights) { return do_make_dir_path(path, rights); } /* ==================================================================== */ #ifdef BTASKTD_DB_TEST #include #define TEST_CODE(code) \ do { \ const char *s = NULL; \ switch (code) { \ case dbe_ok: s = "NO_ERROR"; break; \ case dbe_notdir: s = "NOT_DIR"; break; \ case dbe_inval: s = "INV_INPUT"; break; \ case dbe_access: s = "ACCESS"; break; \ default: s = "UNKNOWN"; \ } \ fprintf(stderr, "%s\n", s); \ } while (0) int main(void) { int i, pid; const char *path = "test_db/super_test/mtest"; for (i = 0; i < 10; i++) TEST_CODE(make_dir_path(path, 0777)); pid = fork(); if (-1 == pid) { fprintf(stderr, "can't fork\n"); return 1; } else if (0 == pid) { execlp("ls", "ls", "-laR", NULL); _exit(128); } else { wait(NULL); } pid = fork(); if (-1 == pid) { fprintf(stderr, "can't fork\n"); return 1; } else if (0 == pid) { execlp("rm", "rm", "-rf", "test_db", NULL); _exit(127); } else { wait(NULL); } return 0; } #endif