From b0e7307b3569a5dad0f2606d2736cc8317851598 Mon Sep 17 00:00:00 2001 From: Dominique Martinet Date: Wed, 30 Aug 2023 11:46:01 +0900 Subject: [PATCH] utils: add mkdir_recursive This will be used in the next commit. A test file for utils has also been added to check mkdir works as intended. Signed-off-by: Dominique Martinet --- pppd/Makefile.am | 6 ++ pppd/pppd-private.h | 1 + pppd/utils.c | 82 ++++++++++++++++++++++++++ pppd/utils_utest.c | 139 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 228 insertions(+) create mode 100644 pppd/utils_utest.c diff --git a/pppd/Makefile.am b/pppd/Makefile.am index 7cb3005..c5fe107 100644 --- a/pppd/Makefile.am +++ b/pppd/Makefile.am @@ -20,6 +20,12 @@ utest_pppcrypt_LDFLAGS = check_PROGRAMS += utest_crypto +utest_utils_SOURCES = utils.c utils_utest.c +utest_utils_CPPFLAGS = -DUNIT_TEST +utest_utils_LDFLAGS = + +check_PROGRAMS += utest_utils + if WITH_SRP sbin_PROGRAMS += srp-entry dist_man8_MANS += srp-entry.8 diff --git a/pppd/pppd-private.h b/pppd/pppd-private.h index 2883e46..46ce0c8 100644 --- a/pppd/pppd-private.h +++ b/pppd/pppd-private.h @@ -437,6 +437,7 @@ int sifproxyarp(int, u_int32_t); int cifproxyarp(int, u_int32_t); /* Delete proxy ARP entry for peer */ u_int32_t GetMask(u_int32_t); /* Get appropriate netmask for address */ +int mkdir_recursive(const char *); /* Recursively create directory */ int lock(char *); /* Create lock file for device */ int relock(int); /* Rewrite lock file with new pid */ void unlock(void); /* Delete previously-created lock file */ diff --git a/pppd/utils.c b/pppd/utils.c index c1bdbbb..c47192e 100644 --- a/pppd/utils.c +++ b/pppd/utils.c @@ -781,6 +781,88 @@ complete_read(int fd, void *buf, size_t count) } #endif +/* + * mkdir_check - helper for mkdir_recursive, creates a directory + * but do not error on EEXIST if and only if entry is a directory + * The caller must check for errno == ENOENT if appropriate. + */ +static int +mkdir_check(const char *path) +{ + struct stat statbuf; + + if (mkdir(path, 0755) >= 0) + return 0; + + if (errno == EEXIST) { + if (stat(path, &statbuf) < 0) + /* got raced? */ + return -1; + + if ((statbuf.st_mode & S_IFMT) == S_IFDIR) + return 0; + + /* already exists but not a dir, treat as failure */ + errno = EEXIST; + return -1; + } + + return -1; +} + +/* + * mkdir_parent - helper for mkdir_recursive, modifies the string in place + * Assumes mkdir(path) already failed, so it first creates the parent then + * full path again. + */ +static int +mkdir_parent(char *path) +{ + char *slash; + int rc; + + slash = strrchr(path, '/'); + if (!slash) + return -1; + + *slash = 0; + if (mkdir_check(path) < 0) { + if (errno != ENOENT) { + *slash = '/'; + return -1; + } + if (mkdir_parent(path) < 0) { + *slash = '/'; + return -1; + } + } + *slash = '/'; + + return mkdir_check(path); +} + +/* + * mkdir_recursive - recursively create directory if it didn't exist + */ +int +mkdir_recursive(const char *path) +{ + char *copy; + int rc; + + // optimistically try on full path first to avoid allocation + if (mkdir_check(path) == 0) + return 0; + + copy = strdup(path); + if (!copy) + return -1; + + rc = mkdir_parent(copy); + free(copy); + return rc; +} + /* Procedures for locking the serial device using a lock file. */ static char lock_file[MAXPATHLEN]; diff --git a/pppd/utils_utest.c b/pppd/utils_utest.c new file mode 100644 index 0000000..cdca97e --- /dev/null +++ b/pppd/utils_utest.c @@ -0,0 +1,139 @@ +#include +#include +#include +#include + +#include "pppd-private.h" + +/* globals used in test.c... */ +int debug = 1; +int error_count; +int unsuccess; + +/* check if path exists and returns its type */ +static int +file_type(char *path) +{ + struct stat statbuf; + + if (stat(path, &statbuf) < 0) + return -1; + + return statbuf.st_mode & S_IFMT; +} + +int +test_simple() { + if (mkdir_recursive("dir")) + return -1; + + if (file_type("dir") != S_IFDIR) + return -1; + + rmdir("dir"); + return 0; +} + +int +test_recurse() { + if (mkdir_recursive("dir/subdir/subsubdir")) + return -1; + + if (file_type("dir/subdir/subsubdir") != S_IFDIR) + return -1; + + rmdir("dir/subdir/subsubdir"); + + /* try again with partial existence */ + if (mkdir_recursive("dir/subdir/subsubdir")) + return -1; + + if (file_type("dir/subdir/subsubdir") != S_IFDIR) + return -1; + + rmdir("dir/subdir/subsubdir"); + rmdir("dir/subdir"); + rmdir("dir"); + return 0; +} + +int +test_recurse_multislash() { + if (mkdir_recursive("dir/subdir///subsubdir")) + return -1; + + if (file_type("dir/subdir/subsubdir") != S_IFDIR) + return -1; + + rmdir("dir/subdir/subsubdir"); + rmdir("dir/subdir"); + + /* try again with partial existence */ + if (mkdir_recursive("dir/subdir/subsubdir///")) + return -1; + + if (file_type("dir/subdir/subsubdir") != S_IFDIR) + return -1; + + rmdir("dir/subdir/subsubdir"); + rmdir("dir/subdir"); + rmdir("dir"); + return 0; +} + +int +test_parent_notdir() { + int fd = open("file", O_CREAT, 0600); + if (fd < 0) + return -1; + close(fd); + + if (mkdir_recursive("file") == 0) + return -1; + if (mkdir_recursive("file/dir") == 0) + return -1; + + unlink("file"); + return 0; +} + +int +main() +{ + char *base_dir = strdup("/tmp/ppp_utils_utest.XXXXXX"); + int failure = 0; + + if (mkdtemp(base_dir) == NULL) { + printf("Could not create test directory, aborting\n"); + return 1; + } + + if (chdir(base_dir) < 0) { + printf("Could not enter newly created test dir, aborting\n"); + return 1; + } + + if (test_simple()) { + printf("Could not create simple directory\n"); + failure++; + } + + if (test_recurse()) { + printf("Could not create recursive directory\n"); + failure++; + } + + if (test_recurse_multislash()) { + printf("Could not create recursive directory with multiple slashes\n"); + failure++; + } + + if (test_parent_notdir()) { + printf("Creating over a file appeared to work?\n"); + failure++; + } + + rmdir(base_dir); + free(base_dir); + return failure; +} -- 2.39.2