]> granicus.if.org Git - transmission/commitdiff
Take another approach to creating intermediate dirs on *NIX gh426-create-dir-with-parents
authorMike Gelfand <mikedld@mikedld.com>
Fri, 27 Oct 2017 13:12:55 +0000 (16:12 +0300)
committerMike Gelfand <mikedld@mikedld.com>
Fri, 27 Oct 2017 13:30:48 +0000 (16:30 +0300)
Walk up one level at a time until the directory creation succeeds, then go
back down one level at a time. Should help in situations where we're not
allowed to stat at some level but could still create.

libtransmission/file-posix.c

index b857be514df1863941e541f376bed35bb270615a..7c982f6221923fa1d251533b9357bfb12da060e9 100644 (file)
@@ -140,79 +140,115 @@ static void set_file_for_single_pass(tr_sys_file_t handle)
 
 static bool create_path(char const* path_in, int permissions, tr_error** error)
 {
-    char* p;
-    char* pp;
-    bool done;
-    int tmperr;
-    int rv;
-    struct stat sb;
-    char* path;
-
     /* make a temporary copy of path */
-    path = tr_strdup(path_in);
+    char* path = tr_strdup(path_in);
 
     /* walk past the root */
-    p = path;
+    char* p = path;
 
     while (*p == TR_PATH_DELIMITER)
     {
         ++p;
     }
 
-    pp = p;
-    done = false;
+    char* path_end = p + strlen(p);
+
+    while (path_end > path && *path_end == TR_PATH_DELIMITER)
+    {
+        --path_end;
+    }
+
+    char* pp;
+    bool up_walk_ok = false;
 
-    while ((p = strchr(pp, TR_PATH_DELIMITER)) || (p = strchr(pp, '\0')))
+    /* Go one level up on each iteration and attempt to create */
+    for (pp = path_end; pp != NULL; pp = strrchr(p, TR_PATH_DELIMITER))
     {
-        if (!*p)
+        *pp = '\0';
+
+        if (mkdir(path, permissions) != -1)
         {
-            done = true;
+            up_walk_ok = true;
+            break;
         }
-        else
+
+        if (errno == EEXIST)
         {
-            *p = '\0';
+            break;
         }
 
-        tmperr = errno;
-        rv = stat(path, &sb);
-        errno = tmperr;
-
-        if (rv)
+        if (errno != ENOENT)
         {
-            tr_error* my_error = NULL;
-
-            /* Folder doesn't exist yet */
-            if (!tr_sys_dir_create(path, 0, permissions, &my_error))
-            {
-                tr_logAddError(_("Couldn't create \"%1$s\": %2$s"), path, my_error->message);
-                tr_free(path);
-                tr_error_propagate(error, &my_error);
-                return false;
-            }
+            goto unable_to_create;
         }
-        else if ((sb.st_mode & S_IFMT) != S_IFDIR)
+    }
+
+    if (up_walk_ok && pp == path_end)
+    {
+        goto success;
+    }
+
+    /* Go one level down on each iteration and attempt to create */
+    for (; pp < path_end; pp += strlen(pp))
+    {
+        *pp = TR_PATH_DELIMITER;
+
+        if (mkdir(path, permissions) != -1)
         {
-            /* Node exists but isn't a folder */
-            char* const buf = tr_strdup_printf(_("File \"%s\" is in the way"), path);
-            tr_logAddError(_("Couldn't create \"%1$s\": %2$s"), path_in, buf);
-            tr_free(buf);
-            tr_free(path);
-            set_system_error(error, ENOTDIR);
-            return false;
+            continue;
         }
 
-        if (done)
+        if (errno != EEXIST)
         {
-            break;
+            goto unable_to_create;
         }
+    }
+
+    struct stat sb;
+
+    if (stat(path, &sb) == -1)
+    {
+        goto unable_to_create;
+    }
 
-        *p = TR_PATH_DELIMITER;
-        p++;
-        pp = p;
+    if ((sb.st_mode & S_IFMT) != S_IFDIR)
+    {
+        goto not_a_directory;
     }
 
+success:
+
     tr_free(path);
     return true;
+
+not_a_directory:
+
+    {
+        char* const buf = tr_strdup_printf(_("File \"%s\" is in the way"), path);
+
+        tr_logAddError(_("Couldn't create \"%1$s\": %2$s"), path_in, buf);
+        set_system_error(error, ENOTDIR);
+
+        tr_free(buf);
+        goto fail;
+    }
+
+unable_to_create:
+
+    {
+        tr_error* my_error = NULL;
+        set_system_error(&my_error, errno);
+
+        tr_logAddError(_("Couldn't create \"%1$s\": %2$s"), path, my_error->message);
+        tr_error_propagate(error, &my_error);
+
+        goto fail;
+    }
+
+fail:
+
+    tr_free(path);
+    return false;
 }
 
 #endif