Recursive mkdir() system call on Unix
Solution 1
There is not a system call to do it for you, unfortunately. I'm guessing that's because there isn't a way to have really well-defined semantics for what should happen in error cases. Should it leave the directories that have already been created? Delete them? What if the deletions fail? And so on...
It is pretty easy to roll your own, however, and a quick google for 'recursive mkdir' turned up a number of solutions. Here's one that was near the top:
http://nion.modprobe.de/blog/archives/357-Recursive-directory-creation.html
static void _mkdir(const char *dir) {
char tmp[256];
char *p = NULL;
size_t len;
snprintf(tmp, sizeof(tmp),"%s",dir);
len = strlen(tmp);
if (tmp[len - 1] == '/')
tmp[len - 1] = 0;
for (p = tmp + 1; *p; p++)
if (*p == '/') {
*p = 0;
mkdir(tmp, S_IRWXU);
*p = '/';
}
mkdir(tmp, S_IRWXU);
}
Solution 2
hmm I thought that mkdir -p does that?
mkdir -p this/is/a/full/path/of/stuff
Solution 3
Here is my solution. By calling the function below you ensure that all dirs leading to the file path specified exist. Note that file_path
argument is not directory name here but rather a path to a file that you are going to create after calling mkpath()
.
Eg., mkpath("/home/me/dir/subdir/file.dat", 0755)
shall create /home/me/dir/subdir
if it does not exist. mkpath("/home/me/dir/subdir/", 0755)
does the same.
Works with relative paths as well.
Returns -1
and sets errno
in case of an error.
int mkpath(char* file_path, mode_t mode) {
assert(file_path && *file_path);
for (char* p = strchr(file_path + 1, '/'); p; p = strchr(p + 1, '/')) {
*p = '\0';
if (mkdir(file_path, mode) == -1) {
if (errno != EEXIST) {
*p = '/';
return -1;
}
}
*p = '/';
}
return 0;
}
Note that file_path
is modified during the action but gets restored afterwards. Therefore file_path
is not strictly const
.
Solution 4
Here's another take on mkpath()
, using recursion, which is both small and readable. It makes use of strdupa()
to avoid altering the given dir
string argument directly and to avoid using malloc()
& free()
. Make sure to compile with -D_GNU_SOURCE
to activate strdupa()
... meaning this code only works on GLIBC, EGLIBC, uClibc, and other GLIBC compatible C libraries.
int mkpath(char *dir, mode_t mode)
{
if (!dir) {
errno = EINVAL;
return 1;
}
if (strlen(dir) == 1 && dir[0] == '/')
return 0;
mkpath(dirname(strdupa(dir)), mode);
return mkdir(dir, mode);
}
After input both here and from Valery Frolov, in the Inadyn project, the following revised version of mkpath()
has now been pushed to libite
int mkpath(char *dir, mode_t mode)
{
struct stat sb;
if (!dir) {
errno = EINVAL;
return 1;
}
if (!stat(dir, &sb))
return 0;
mkpath(dirname(strdupa(dir)), mode);
return mkdir(dir, mode);
}
It uses one more syscall, but otoh the code is more readable now.
Solution 5
Take a look at the bash source code here, and specifically look in examples/loadables/mkdir.c especially lines 136-210. If you don't want to do that, here's some of the source that deals with this (taken straight from the tar.gz that I've linked):
/* Make all the directories leading up to PATH, then create PATH. Note that
this changes the process's umask; make sure that all paths leading to a
return reset it to ORIGINAL_UMASK */
static int
make_path (path, nmode, parent_mode)
char *path;
int nmode, parent_mode;
{
int oumask;
struct stat sb;
char *p, *npath;
if (stat (path, &sb) == 0)
{
if (S_ISDIR (sb.st_mode) == 0)
{
builtin_error ("`%s': file exists but is not a directory", path);
return 1;
}
if (chmod (path, nmode))
{
builtin_error ("%s: %s", path, strerror (errno));
return 1;
}
return 0;
}
oumask = umask (0);
npath = savestring (path); /* So we can write to it. */
/* Check whether or not we need to do anything with intermediate dirs. */
/* Skip leading slashes. */
p = npath;
while (*p == '/')
p++;
while (p = strchr (p, '/'))
{
*p = '\0';
if (stat (npath, &sb) != 0)
{
if (mkdir (npath, parent_mode))
{
builtin_error ("cannot create directory `%s': %s", npath, strerror (errno));
umask (original_umask);
free (npath);
return 1;
}
}
else if (S_ISDIR (sb.st_mode) == 0)
{
builtin_error ("`%s': file exists but is not a directory", npath);
umask (original_umask);
free (npath);
return 1;
}
*p++ = '/'; /* restore slash */
while (*p == '/')
p++;
}
/* Create the final directory component. */
if (stat (npath, &sb) && mkdir (npath, nmode))
{
builtin_error ("cannot create directory `%s': %s", npath, strerror (errno));
umask (original_umask);
free (npath);
return 1;
}
umask (original_umask);
free (npath);
return 0;
}
You can probably get away with a less general implementation.
![D3X](https://i.stack.imgur.com/VYRYy.jpg?s=256&g=1)
D3X
Solution Architect and Full Stack Developer from Canada. I've worked on numerous types of projects from embedded systems right up to enterprise web applications using service meshes, in numerous languages : C/C++/C#, Java, Ruby, Perl, Python, PHP, assembly and many of the underlaying 4GLs and technologies that support them like SQL, XML, HQL, and many more. For any project I take on, I'm responsible and accountable for the project design, technical design, team building, project execution and delivery.
Updated on August 20, 2021Comments
-
D3X almost 3 years
After reading the mkdir(2) man page for the Unix system call with that name, it appears that the call doesn't create intermediate directories in a path, only the last directory in the path. Is there any way (or other function) to create all the directories in the path without resorting to manually parsing my directory string and individually creating each directory ?
-
Admin over 14 yearsThe actual question was "Is there any way (or other function) to create all the directories in the path without resorting to manually parsing my directory string and individually creating each directory " so mkdir(1) is another way!
-
D3X over 14 yearsI pretty much can't use the system command because I'm using a severely impaired (even for busybox) version of busybox. I can't assume that any command has all the standard arguments, or is even installed for that matter, because I'm working on an embedded system. I went with Carl Norum's answer because it works the best in my specific scenario, but your answer was good as well.
-
Exectron over 11 yearsYes it does, but the question pertains to a C function call.
-
Kamiccolo almost 11 years@Jeff, Yup. Any problem with that? In this case looks easier to understand than plenty of ifs or something like that. It's readable and works well.
-
nemo over 10 yearsWouldn't this leak memory every time
strdupa
is called? -
troglobit over 10 yearsNope :) When the function goes out of context the memory allocated (on the stack) by
strdupa()
is freed automatically. See the man page for more details. -
nemo over 10 yearsAh, sorry, you're right. I thought of
strdup
all the time. :) -
Lothar about 10 yearsOMG - it's 2014, file paths have usually spaces now. Please don't code like this
-
SiegeX about 10 years@Lothar Perhaps you didn't realize that this answer was made more than 4 years ago, so
OMG - it's 2010
would be more apt. Perhaps it's just me, but a lack of quotes around%s
hardly seems appropriate to bring a deity into the mix. If you'd like to suggest the edit, please feel free to do so. -
Nick almost 10 years@Lothar There's also the bad choice to use system() for this in the first place, the security flaw from not using an absolute path to mkdir, and the incorrectly coded 'char dirpath[]' line; every line has something wrong with it so had to downvote it; sorry SiegeX, but this was a really bad answer, but as you said, it was in 2010 ;)
-
SiegeX almost 10 years@Nick I can see a security flaw if I was passing a user-provided path to
system()
, but I am not, the path is hard coded so other than portable limitations, I don't see the issue. Also, what was "incorrectly coded" in thechar dirpath[]
line, the extra 56 bytes allocated on the stack? -
Nick almost 10 years/bin/sh will use the PATH environment variable to locate mkdir. If an executable called mkdir is in a path location before /bin, that will be executed instead.
-
Chris Stratton over 9 yearsIndeed - the upvotes presumably reflect that this has been a useful answer for many, but it is an answer to a different question than the one which was asked.
-
teambob over 9 yearsOops buffer overflow. And you better hope no-one creates a path ending in ;rm -rf ~
-
Chris Maes over 9 yearsbetter than the accepted answer; with error handling!
-
rouzier over 9 yearsThe only thing I would change is tmp[256] to tmp[PATH_MAX] also #include <limits.h>
-
gamen over 9 yearsOne could however have a look at the source code for mkdir to see how it does it. Doing a quick google, it seems the relevant code is in mkancestdirs.c in
coreutils
-
Naman over 8 yearsDoesn't create the last directory. If recursive_mkdir("/home/test/hello",mode), doesn't create hello directory.
-
Kamiccolo over 8 years@Namchester, apparently it does not work that well with absolute paths. Fixing.
-
Exectron about 8 yearsThe
make_dir_parents()
function is probably the most interesting bit, but it's not in that file. It is inmkdir-p.c
in the gnulib repository. -
gnac about 8 yearsI know this is a very old thread, but I was looking for a solution to this and I liked this answer the best out of everything I've seen. HOWEVER, this does not work with a relative path. Adding a check for dir[0] == '.' to the check for dir[0]=='/' would correct this as that is what dirname ends up with.
-
gnac about 8 yearsThis still isn't ideal as it tries to create the ".." folder, but that just returns a file exists error and keeps going. You could also do a mkdir first, and only recurse if mkdir returns ENOENT. Downside to that is you would have to call mkdir twice in the method.
-
Seng Cheong about 8 yearsImproved version: gist.github.com/JonathonReinhart/…
-
troglobit about 8 yearsThanks, my personal use-case didn't trigger that. Got input on this relative path problem also in the Inadyn project. See revised code above.
-
Jarosław Bielawski about 8 years
snprintf()
returns the length of the string formatted, the call tostrlen()
is superfluous.len = snprintf(tmp, ...
. You can check for buffer overflow that wayif(len >= sizeof tmp)
. With thestrlen()
it's not possible. -
Peter VARGA almost 8 yearsVery elegant solution! I ran it under
valgrind
and indeed it confirms there is no leak memory but the man page states the memory has to be released callingfree
: http://linux.die.net/man/3/strdupa. How ever, adding the#include
statements would make it a bit easier to test it... :-) -
troglobit almost 8 years@AlBundy check the man page again, the statement regarding
free()
is forstrdup()
, notstrdupa()
. -
Peter VARGA almost 8 yearsDe verdad! Mea culpa muchisomo! I implemented it as a lambda function and it works perfectly!
-
jaybny almost 8 yearsI would change this to return int. so its just a 1 word change to refactor.
-
iwtu almost 8 yearsWhat if mkdir in loop failed to create a directory? For instance of permissions? I would strongly recommend mentioned version from github gist.github.com/JonathonReinhart/…
-
Nenad Radulovic over 7 yearsThis is an answer for different question asked.
-
merlin2011 over 7 years@gamen, The link in your comment is stale.
-
gamen over 7 years@merlin2011 thanks for noticing. The latest equivalent URL (for coreutils 8.26) is: fossies.org/dox/coreutils-8.26/mkancesdirs_8c_source.html. Reflecting on past criticisms of this suggested answer, I agree. It should rather have been a comment to the question.
-
Felipe Tonello about 3 yearsThe only problem is that it uses a non-const
char *
as parameter because it changes the contents of the original pointer. This is not ideal since it wont work with static const strings, for example, and it has an unnecessary API requirement. -
Yaroslav Stavnichiy about 3 years@FelipeTonello You can easily allocate memory and make a copy of the parameter as suggested in other answers. My goal was performance so I tried to avoid expensive operations such as memory allocation.
-
Andrew Henle almost 3 years@rouzier
PATH_MAX
may not be an improvement asPATH_MAX
will not be defined on POSIX-compliant systems where the value varies between different file systems (bolding mine): "A definition of one of the symbolic constants in the following list shall be omitted from the<limits.h>
header on specific implementations where the corresponding value is equal to or greater than the stated minimum, but where the value can vary depending on the file to which it is applied." -
Jason C about 2 years@CraigMcQueen Sure.
system("mkdir -p this/is/a/full/path/of/stuff");
-
Jason C about 2 yearsWow that's a lot of completely non-constructive comments. The issues raised can be avoided with e.g.
!fork() && execlp("mkdir", "mkdir", "-p", dirpath, NULL)
instead. Same idea. Withsystem
, though -- buffer safety:asprintf
orsnprintf_s
, space support: you'll have to escape quotes then use\"%s\"
, sanitization: non-trivial which is kind of a dealbreaker. -
Exectron about 2 years@JasonC Perhaps, though using
system("mkdir -p ...")
is risky if the directory name is user input; then the user input requires careful sanitisation/validation. C system() function vulnerability - Code Review Stack Exchange -
Jason C about 2 years@CraigMcQueen Truth. Then:
!fork() && execlp("mkdir", "mkdir", "-p", pathname, NULL)
.