mirror of
https://github.com/transmission/transmission
synced 2025-03-17 17:25:32 +00:00
Take another approach to creating intermediate dirs on *NIX
Walk up one level at a time until the directory creation succeeds, then go back down one level at a time. This reduces the number of operations in the most common case (when directory already exists).
This commit is contained in:
parent
0378ee8298
commit
474aabbbc4
1 changed files with 87 additions and 51 deletions
|
@ -138,81 +138,117 @@ static void set_file_for_single_pass(tr_sys_file_t handle)
|
||||||
|
|
||||||
#ifndef HAVE_MKDIRP
|
#ifndef HAVE_MKDIRP
|
||||||
|
|
||||||
|
static bool create_path_require_dir(char const* path, tr_error** error)
|
||||||
|
{
|
||||||
|
struct stat sb;
|
||||||
|
|
||||||
|
if (stat(path, &sb) == -1)
|
||||||
|
{
|
||||||
|
set_system_error(error, errno);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((sb.st_mode & S_IFMT) != S_IFDIR)
|
||||||
|
{
|
||||||
|
tr_error_set(error, ENOTDIR, _("File \"%s\" is in the way"), path);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
static bool create_path(char const* path_in, int permissions, tr_error** error)
|
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 */
|
/* make a temporary copy of path */
|
||||||
path = tr_strdup(path_in);
|
char* path = tr_strdup(path_in);
|
||||||
|
|
||||||
/* walk past the root */
|
/* walk past the root */
|
||||||
p = path;
|
char* p = path;
|
||||||
|
|
||||||
while (*p == TR_PATH_DELIMITER)
|
while (*p == TR_PATH_DELIMITER)
|
||||||
{
|
{
|
||||||
++p;
|
++p;
|
||||||
}
|
}
|
||||||
|
|
||||||
pp = p;
|
char* path_end = p + strlen(p);
|
||||||
done = false;
|
|
||||||
|
|
||||||
while ((p = strchr(pp, TR_PATH_DELIMITER)) || (p = strchr(pp, '\0')))
|
while (path_end > path && *path_end == TR_PATH_DELIMITER)
|
||||||
{
|
{
|
||||||
if (!*p)
|
--path_end;
|
||||||
{
|
}
|
||||||
done = true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
*p = '\0';
|
|
||||||
}
|
|
||||||
|
|
||||||
tmperr = errno;
|
char* pp;
|
||||||
rv = stat(path, &sb);
|
bool ret = false;
|
||||||
errno = tmperr;
|
tr_error* my_error = NULL;
|
||||||
|
|
||||||
if (rv)
|
/* Go one level up on each iteration and attempt to create */
|
||||||
{
|
for (pp = path_end; pp != NULL; pp = strrchr(p, TR_PATH_DELIMITER))
|
||||||
tr_error* my_error = NULL;
|
{
|
||||||
|
*pp = '\0';
|
||||||
|
|
||||||
/* Folder doesn't exist yet */
|
ret = mkdir(path, permissions) != -1;
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if ((sb.st_mode & S_IFMT) != S_IFDIR)
|
|
||||||
{
|
|
||||||
/* 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (done)
|
if (ret)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
*p = TR_PATH_DELIMITER;
|
if (errno == EEXIST)
|
||||||
p++;
|
{
|
||||||
pp = p;
|
ret = create_path_require_dir(path, &my_error);
|
||||||
|
|
||||||
|
if (ret)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
goto failure;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (errno != ENOENT)
|
||||||
|
{
|
||||||
|
set_system_error(&my_error, errno);
|
||||||
|
goto failure;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ret && pp == path_end)
|
||||||
|
{
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = create_path_require_dir(path, &my_error);
|
||||||
|
|
||||||
|
if (ret)
|
||||||
|
{
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
failure:
|
||||||
|
|
||||||
|
TR_ASSERT(!ret);
|
||||||
|
TR_ASSERT(my_error != NULL);
|
||||||
|
|
||||||
|
tr_logAddError(_("Couldn't create \"%1$s\": %2$s"), path, my_error->message);
|
||||||
|
tr_error_propagate(error, &my_error);
|
||||||
|
|
||||||
|
cleanup:
|
||||||
|
|
||||||
|
TR_ASSERT(my_error == NULL);
|
||||||
|
|
||||||
tr_free(path);
|
tr_free(path);
|
||||||
return true;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
Loading…
Add table
Reference in a new issue