Skip to content

Instantly share code, notes, and snippets.

@JonathonReinhart
Last active May 19, 2025 04:39
Show Gist options
  • Save JonathonReinhart/8c0d90191c38af2dcadb102c4e202950 to your computer and use it in GitHub Desktop.
Save JonathonReinhart/8c0d90191c38af2dcadb102c4e202950 to your computer and use it in GitHub Desktop.
mkdir -p implemented in C
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h> /* mkdir(2) */
#include <errno.h>
/* 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;
}
int mkdir_p(const char *path)
{
/* Adapted from http://stackoverflow.com/a/2336245/119527 */
char *_path = NULL;
char *p;
int result = -1;
mode_t mode = 0777;
errno = 0;
/* Copy string so it's mutable */
_path = 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, mode) != 0)
goto out;
*p = '/';
}
}
if (maybe_mkdir(_path, mode) != 0)
goto out;
result = 0;
out:
free(_path);
return result;
}
#ifndef MKDIR_P_H
#define MKDIR_P_H
int mkdir_p(const char *path);
#endif /* MKDIR_P_H */
env = Environment(
CCFLAGS = ['-Wall', '-Werror'],
)
env.Program('mkdir_p_test', ['mkdir_p.c', 'test.c'])
#include <stdio.h>
#include "mkdir_p.h"
int main(int argc, char **argv)
{
const char *path;
int rc;
if (argc < 2) {
fprintf(stderr, "Missing argument: path\n");
return 1;
}
path = argv[1];
rc = mkdir_p(path);
fprintf(stderr, "mkdir_p(\"%s\") returned %d: %m\n", path, rc);
return (rc == 0) ? 0 : 2;
}
@gblargg
Copy link

gblargg commented Feb 23, 2023

BTW mkdir_p( "" ) accesses beyond allocated memory since it assumes the path length is non-zero. Seems it should return ENOENT in this case, as mkdir does for an empty string.

As for optimization, would it make sense to just try maybe_mkdir in the beginning, for the probably-common case where the parent directories already exist?

Thanks for the useful functions and careful implementation.

@SamuelMarks
Copy link

This doesn't make sense:

if (_path == NULL)
        goto out;

Because it executes free(_path); which is undefined for NULL.

@JonathonReinhart
Copy link
Author

JonathonReinhart commented May 19, 2025

@SamuelMarks

This doesn't make sense:

if (_path == NULL)
        goto out;

Because it executes free(_path); which is undefined for NULL.

Incorrect. From free(3p):

If ptr is a null pointer, no action shall occur.

@SamuelMarks
Copy link

https://stackoverflow.com/a/2360462 oh looks like my best-practices is a bit out-of-date…

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment