common: added mrp_mkdir to file-utils.
authorKrisztian Litkey <kli@iki.fi>
Fri, 31 Oct 2014 19:54:28 +0000 (21:54 +0200)
committerKrisztian Litkey <kli@iki.fi>
Fri, 31 Oct 2014 19:57:28 +0000 (21:57 +0200)
src/common/file-utils.c
src/common/file-utils.h
src/common/tests/Makefile.am
src/common/tests/mkdir-test.c [new file with mode: 0644]

index fcb5de7..67456fb 100644 (file)
@@ -37,6 +37,7 @@
 #include <sys/stat.h>
 
 #include <murphy/common/macros.h>
+#include <murphy/common/debug.h>
 #include <murphy/common/file-utils.h>
 
 
@@ -179,3 +180,95 @@ int mrp_find_file(const char *file, const char **dirs, int mode, char *buf,
 
     return 0;
 }
+
+
+int mrp_mkdir(const char *path, mode_t mode)
+{
+    const char *p;
+    char       *q, buf[PATH_MAX];
+    int         n, undo[PATH_MAX / 2];
+    struct stat st;
+
+    if (path == NULL || path[0] == '\0') {
+        errno = path ? EINVAL : EFAULT;
+        return -1;
+    }
+
+    /*
+     * Notes:
+     *     Our directory creation algorithm logic closely resembles what
+     *     'mkdir -p' does. We simply walk the given path component by
+     *     component, testing if each one exist. If an existing one is
+     *     not a directory we bail out. Missing ones we try to create with
+     *     the given mode, bailing out if we fail.
+     *
+     *     Unlike 'mkdir -p' whenever we fail we clean up by removing
+     *     all directories we have created (or at least we try).
+     *
+     *     Similarly to 'mkdir -p' we don't try to be overly 'smart' about
+     *     the path we're handling. Especially we never try to treat '..'
+     *     in any special way. This is very much intentional and the idea
+     *     is to let the caller try to create a full directory hierarchy
+     *     atomically, either succeeeding creating the full hierarchy, or
+     *     none of it. To see the consequences of these design choices,
+     *     consider what are the possible outcomes of a call like
+     *
+     *       mrp_mkdir("/home/kli/bin/../sbin/../scripts/../etc/../doc", 0755);
+     */
+
+    p = path;
+    q = buf;
+    n = 0;
+    while (1) {
+        if (q - buf >= (ptrdiff_t)sizeof(buf) - 1) {
+            errno = ENAMETOOLONG;
+            goto cleanup;
+        }
+
+        if (*p && *p != '/') {
+            *q++ = *p++;
+            continue;
+        }
+
+        *q = '\0';
+
+        mrp_debug("checking/creating '%s'...", buf);
+
+        if (q != buf) {
+            if (stat(buf, &st) < 0) {
+                if (errno != ENOENT)
+                    goto cleanup;
+
+                if (mkdir(buf, mode) < 0)
+                    goto cleanup;
+
+                undo[n++] = q - buf;
+            }
+            else {
+                if (!S_ISDIR(st.st_mode)) {
+                    errno = ENOTDIR;
+                    goto cleanup;
+                }
+            }
+        }
+
+        while (*p == '/')
+            p++;
+
+        if (!*p)
+            break;
+
+        *q++ = '/';
+    }
+
+    return 0;
+
+ cleanup:
+    while (--n >= 0) {
+        buf[undo[n]] = '\0';
+        mrp_debug("cleaning up '%s'...", buf);
+        rmdir(buf);
+    }
+
+    return -1;
+}
index 2fa2fa9..d6e9afc 100644 (file)
@@ -32,7 +32,7 @@
 
 #include <dirent.h>
 #include <sys/types.h>
-
+#include <sys/stat.h>
 
 /*
  * Routines for scanning a directory for matching entries.
@@ -69,4 +69,7 @@ int mrp_scan_dir(const char *path, const char *pattern, mrp_dirent_type_t mask,
 int mrp_find_file(const char *file, const char **dirs, int mode, char *buf,
                   size_t size);
 
+/** Create a directory, creating leading path as necessary. */
+int mrp_mkdir(const char *path, mode_t mode);
+
 #endif /* __MURPHY_FILEUTILS_H__ */
index d7604fb..9fd30fb 100644 (file)
@@ -1,7 +1,8 @@
 AM_CFLAGS = $(WARNING_CFLAGS) -I$(top_builddir)
 
 noinst_PROGRAMS  = mm-test hash-test hash12-test msg-test transport-test \
-                 internal-transport-test process-watch-test native-test
+                 internal-transport-test process-watch-test native-test \
+                mkdir-test
 
 if LIBDBUS_ENABLED
 noinst_PROGRAMS += mainloop-test dbus-test
@@ -127,3 +128,8 @@ endif
 fragbuf_test_SOURCES = fragbuf-test.c
 fragbuf_test_CFLAGS  = $(AM_CFLAGS)
 fragbuf_test_LDADD   = ../../libmurphy-common.la
+
+# mkdir-test
+mkdir_test_SOURCES = mkdir-test.c
+mkdir_test_CFLAGS  = $(AM_CFLAGS) -I.
+mkdir_test_LDADD   = ../../libmurphy-common.la
diff --git a/src/common/tests/mkdir-test.c b/src/common/tests/mkdir-test.c
new file mode 100644 (file)
index 0000000..611b82a
--- /dev/null
@@ -0,0 +1,21 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+
+#include <murphy/common/file-utils.h>
+
+int main(int argc, char *argv[])
+{
+    int i;
+
+    for (i = 1; i < argc; i++) {
+        printf("Trying to create directory '%s'..\n", argv[i]);
+        if (mrp_mkdir(argv[i], 0755) < 0)
+            printf("failed (%d: %s)\n", errno, strerror(errno));
+        else
+            printf("ok\n");
+    }
+
+    return 0;
+}