Skip to content
Snippets Groups Projects
Commit 19885b8a authored by Colin Walters's avatar Colin Walters
Browse files

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.
parent d59a63e3
No related branches found
No related tags found
No related merge requests found
......@@ -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;
}
......@@ -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
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment