This is an automated email from the ASF dual-hosted git repository.

pengzheng pushed a commit to branch feature/refactor_bundle_cache
in repository https://gitbox.apache.org/repos/asf/celix.git


The following commit(s) were added to refs/heads/feature/refactor_bundle_cache 
by this push:
     new 8a6ec904 Reduce dynamic memory allocation in 
celix_utils_createDirectory.
8a6ec904 is described below

commit 8a6ec9041494383f034eea37c01ff59e37690986
Author: PengZheng <[email protected]>
AuthorDate: Wed Mar 29 22:20:44 2023 +0800

    Reduce dynamic memory allocation in celix_utils_createDirectory.
---
 .../celix_utils/src/celix_utils_ei.cc              |  2 +
 .../gtest/src/FileUtilsErrorInjectionTestSuite.cc  | 15 ++++
 libs/utils/src/celix_file_utils.c                  | 79 ++++++++++++++++------
 3 files changed, 76 insertions(+), 20 deletions(-)

diff --git a/libs/error_injector/celix_utils/src/celix_utils_ei.cc 
b/libs/error_injector/celix_utils/src/celix_utils_ei.cc
index 2db67233..24496158 100644
--- a/libs/error_injector/celix_utils/src/celix_utils_ei.cc
+++ b/libs/error_injector/celix_utils/src/celix_utils_ei.cc
@@ -26,7 +26,9 @@ extern "C" {
 char* __real_celix_utils_strdup(const char *);
 CELIX_EI_DEFINE(celix_utils_strdup, char*)
 char* __wrap_celix_utils_strdup(const char* str) {
+    errno = ENOMEM;
     CELIX_EI_IMPL(celix_utils_strdup);
+    errno = 0;
     return __real_celix_utils_strdup(str);
 }
 
diff --git a/libs/utils/gtest/src/FileUtilsErrorInjectionTestSuite.cc 
b/libs/utils/gtest/src/FileUtilsErrorInjectionTestSuite.cc
index 6000c21e..c0b60a68 100644
--- a/libs/utils/gtest/src/FileUtilsErrorInjectionTestSuite.cc
+++ b/libs/utils/gtest/src/FileUtilsErrorInjectionTestSuite.cc
@@ -37,6 +37,7 @@ public:
         celix_ei_expect_zip_fopen_index(nullptr, 0, nullptr);
         celix_ei_expect_zip_fread(nullptr, 0, -1);
         celix_ei_expect_celix_utils_writeOrCreateString(nullptr, 0, nullptr);
+        celix_ei_expect_celix_utils_strdup(nullptr, 0, nullptr);
         celix_ei_expect_fopen(nullptr, 0, nullptr);
         celix_ei_expect_fwrite(nullptr, 0, 0);
     }
@@ -94,4 +95,18 @@ TEST_F(FileUtilsWithErrorInjectionTestSuite, 
ExtractZipFileTest) {
     status = celix_utils_extractZipFile(TEST_ZIP_LOCATION, extractLocation, 
&error);
     EXPECT_EQ(status, CELIX_ERROR_MAKE(CELIX_FACILITY_CERRNO,ENOSPC));
     EXPECT_NE(error, nullptr);
+}
+
+TEST_F(FileUtilsWithErrorInjectionTestSuite, CreateDirectory) {
+    const char* testDir = "celix_file_utils_test/directory";
+    celix_utils_deleteDirectory(testDir, nullptr);
+
+    //I can create a new directory.
+    const char* error = nullptr;
+    EXPECT_FALSE(celix_utils_directoryExists(testDir));
+    celix_ei_expect_celix_utils_strdup(CELIX_EI_UNKNOWN_CALLER, 0, nullptr);
+    auto status = celix_utils_createDirectory(testDir, true, &error);
+    EXPECT_EQ(status, CELIX_ENOMEM);
+    EXPECT_NE(error, nullptr);
+    EXPECT_FALSE(celix_utils_fileExists(testDir));
 }
\ No newline at end of file
diff --git a/libs/utils/src/celix_file_utils.c 
b/libs/utils/src/celix_file_utils.c
index 18203e54..97689995 100644
--- a/libs/utils/src/celix_file_utils.c
+++ b/libs/utils/src/celix_file_utils.c
@@ -48,8 +48,41 @@ bool celix_utils_directoryExists(const char* path) {
     return stat(path, &st) == 0 && S_ISDIR(st.st_mode);
 }
 
+// https://gist.github.com/JonathonReinhart/8c0d90191c38af2dcadb102c4e202950
+/* Make a directory; already existing dir okay */
+static int maybe_mkdir(const char* path, mode_t mode)
+{
+    struct stat st;
+    errno = 0;
+
+    /* Try to make the directory */
+    if (mkdir(path, mode) == 0)
+        return 0;
+
+    /* If it fails for any reason but EEXIST, fail */
+    if (errno != EEXIST)
+        return -1;
+
+    /* Check if the existing path is a directory */
+    if (stat(path, &st) != 0)
+        return -1;
+
+    /* If not, fail with ENOTDIR */
+    if (!S_ISDIR(st.st_mode)) {
+        errno = ENOTDIR;
+        return -1;
+    }
+
+    errno = 0;
+    return 0;
+}
+
 celix_status_t celix_utils_createDirectory(const char* path, bool 
failIfPresent, const char** errorOut) {
     const char *dummyErrorOut = NULL;
+    if (*path == '\0') {
+        //empty path, nothing to do
+        return CELIX_SUCCESS;
+    }
     if (errorOut) {
         //reset errorOut
         *errorOut = NULL;
@@ -77,30 +110,36 @@ celix_status_t celix_utils_createDirectory(const char* 
path, bool failIfPresent,
 
     celix_status_t status = CELIX_SUCCESS;
 
-    //loop over al finding of / and create intermediate dirs
-    for (char* slashAt = strchr(path, '/'); slashAt != NULL; slashAt = 
strchr(slashAt + 1, '/'))  {
-        // skip the first / of the absolute path
-        if( slashAt == path) {
-            continue;
-        }
-        char* subPath = strndup(path, slashAt - path);
-        if (!celix_utils_directoryExists(subPath)) {
-            int rc = mkdir(subPath, S_IRWXU);
-            if (rc != 0) {
-                status = CELIX_FILE_IO_EXCEPTION;
-                *errorOut = strerror(errno);
+    char *_path = NULL;
+    char *p;
+    int result = -1;
+    errno = 0;
+    /* Copy string so it's mutable */
+    _path = celix_utils_strdup(path);
+    if (_path == NULL)
+        goto out;
+
+    /* Iterate the string */
+    for (p = _path + 1; *p; p++) {
+        if (*p == '/') {
+            /* Temporarily truncate */
+            *p = '\0';
+            if (maybe_mkdir(_path, S_IRWXU) != 0) {
+                goto out;
             }
+            *p = '/';
         }
-        free(subPath);
     }
+    if (maybe_mkdir(_path, S_IRWXU) != 0) {
+        goto out;
+    }
+    result = 0;
 
-    //create last part of the dir (expect when path ends with / then this is 
already done in the loop)
-    if (status == CELIX_SUCCESS && strlen(path) >0 && path[strlen(path)-1] != 
'/') {
-        int rc = mkdir(path, S_IRWXU);
-        if (rc != 0) {
-            status = CELIX_FILE_IO_EXCEPTION;
-            *errorOut = strerror(errno);
-        }
+out:
+    free(_path);
+    if (result != 0) {
+        status = CELIX_ERROR_MAKE(CELIX_FACILITY_CERRNO,errno);
+        *errorOut = strerror(errno);
     }
     return status;
 }

Reply via email to