I need to write a binary file to some specified directory using a C program. But, while writing I want to make sure that if the directory structure does not exist it should create one for me.
How can I achieve this?
I need to write a binary file to some specified directory using a C program. But, while writing I want to make sure that if the directory structure does not exist it should create one for me.
How can I achieve this?
You have to check whether the leaf directory exists, using stat()
, and iteratively create directories in a top-down manner, using mkdir()
. Or you may even abandon the check and start with a directory creation - it will just fail with bunch of EEXIST
errors if your directories are already there. This is just an easy way.
If you're concerned about correctness, there is a better way. You should use openat()
on subsequent available components (directories) of the desired path and mkdirat()
to create lacking ones. That way you can avoid possible races. In the end, you should open your file using openat()
too.
Check relevant manpages to see how you should use mentioned system calls.
If the above rod was not sufficiently long enough for you, then below you can find my simple Linux-flavored fish. xopen()
function allows you to open the file in a given path, creating directories along the way.
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
/* Look out! It modifies pathname! */
int xopen(char *pathname, int flags, mode_t mode, mode_t dirmode)
{
int fd, errsv;
int dirfd = AT_FDCWD;
char *ptr = pathname;
while (*ptr == '/')
ptr++;
for (;;) {
strsep(&ptr, "/");
if (ptr == NULL) {
fd = openat(dirfd, pathname, flags, mode);
break;
}
while (*ptr == '/')
ptr++;
if (*ptr == '\0') {
errno = EISDIR;
fd = -1;
break;
}
if ((fd = mkdirat(dirfd, pathname, dirmode)) < 0 && errno != EEXIST)
break;
if ((fd = openat(dirfd, pathname, O_DIRECTORY)) < 0)
break;
if (dirfd != AT_FDCWD)
close(dirfd);
dirfd = fd;
pathname = ptr;
}
errsv = errno;
if (dirfd != AT_FDCWD)
close(dirfd);
errno = errsv;
return fd;
}