shutil: Add mkdir -p API

I looked at the systemd code but it didn't have a variant of
mkdir_parents that used `*at()`.  This is a fresh implementation, with
the risk that entails.

However I am changing libgsystem to call it now for testing, and
libgsystem APIs are covered by ostree usage at least.
This commit is contained in:
Colin Walters
2015-04-07 12:29:07 -04:00
parent d59a63e3e6
commit 19885b8a20
2 changed files with 105 additions and 0 deletions

View File

@@ -188,3 +188,101 @@ glnx_shutil_rm_rf_at (int dfd,
out:
return ret;
}
static gboolean
mkdir_p_at_internal (int dfd,
char *path,
int mode,
GCancellable *cancellable,
GError **error)
{
gboolean ret = FALSE;
gboolean did_recurse = FALSE;
if (g_cancellable_set_error_if_cancelled (cancellable, error))
goto out;
again:
if (mkdirat (dfd, path, mode) == -1)
{
if (errno == ENOENT)
{
char *lastslash;
g_assert (!did_recurse);
lastslash = strrchr (path, '/');
g_assert (lastslash != NULL);
/* Note we can mutate the buffer as we dup'd it */
*lastslash = '\0';
if (!glnx_shutil_mkdir_p_at (dfd, path, mode,
cancellable, error))
goto out;
/* Now restore it for another mkdir attempt */
*lastslash = '/';
did_recurse = TRUE;
goto again;
}
else if (errno == EEXIST)
{
/* Fall through; it may not have been a directory,
* but we'll find that out on the next call up.
*/
}
else
{
glnx_set_error_from_errno (error);
goto out;
}
}
ret = TRUE;
out:
return ret;
}
/**
* glnx_shutil_mkdir_p_at:
* @dfd: Directory fd
* @path: Directory path to be created
* @mode: Mode for newly created directories
* @cancellable: Cancellable
* @error: Error
*
* Similar to g_mkdir_with_parents(), except operates relative to the
* directory fd @dfd.
*/
gboolean
glnx_shutil_mkdir_p_at (int dfd,
const char *path,
int mode,
GCancellable *cancellable,
GError **error)
{
gboolean ret = FALSE;
struct stat stbuf;
/* Fast path stat to see whether it already exists */
if (fstatat (dfd, path, &stbuf, AT_SYMLINK_NOFOLLOW) == 0)
{
if (S_ISDIR (stbuf.st_mode))
{
ret = TRUE;
goto out;
}
}
{
char *buf = strdupa (path);
if (!mkdir_p_at_internal (dfd, buf, mode, cancellable, error))
goto out;
}
ret = TRUE;
out:
return ret;
}

View File

@@ -30,4 +30,11 @@ glnx_shutil_rm_rf_at (int dfd,
GCancellable *cancellable,
GError **error);
gboolean
glnx_shutil_mkdir_p_at (int dfd,
const char *path,
int mode,
GCancellable *cancellable,
GError **error);
G_END_DECLS