]> git.ozlabs.org Git - ppp.git/commitdiff
utils: add mkdir_recursive
authorDominique Martinet <dominique.martinet@atmark-techno.com>
Wed, 30 Aug 2023 02:46:01 +0000 (11:46 +0900)
committerDominique Martinet <dominique.martinet@atmark-techno.com>
Tue, 10 Oct 2023 03:13:26 +0000 (12:13 +0900)
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 <dominique.martinet@atmark-techno.com>
pppd/Makefile.am
pppd/pppd-private.h
pppd/utils.c
pppd/utils_utest.c [new file with mode: 0644]

index 7cb30053322ef6df756e8af7990a465e2ef2d8ad..c5fe10776ede7daf777e1e4f9edfa7623d26d30d 100644 (file)
@@ -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
index 2883e4622acbc740c31da793a6c35e15c64890f6..46ce0c8bdceba2898ba2ad93b76201e647104272 100644 (file)
@@ -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 */
index c1bdbbbfe4e52d8a06edb3c8b22462580b3644a3..c47192e67fef1a853830bb3d3ffd6885d756abbd 100644 (file)
@@ -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 (file)
index 0000000..cdca97e
--- /dev/null
@@ -0,0 +1,139 @@
+#include <fcntl.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#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;
+}