Here are the three POSIX.1-2008 C functions I would use for something like this:
error = verify_directory(parent, directory);
This function verifies that directory exists, relative to parent.
If it does not, it will attempt to create it. The parent directory will be fsync()'d, so that the metadata should have hit the storage when the function returns.
If successful, the function returns zero. Otherwise it returns a nonzero errno error code.
error = read_carefully(directory, filename, data, length);
This function reads length bytes from the start of file filename in directory directory, with an interface symmetric to write_carefully().
If successful, the function returns zero. Otherwise it returns a nonzero errno error code.
error = write_carefully(directory, filename, data, length);
This function replaces the contents of file filename in directory directory with length bytes of data.
If the file does not exist, it will be created. (The directory will not be automatically created.)
The function will attempt to overwrite the file. If it fails, it will try to remove the file altogether, and fsync() the directory. This way either the contents of the file are in storage when the function returns, or if it cannot guarantee consistent file contents, it will have tried its best to remove the file from storage.
If successful, the function returns zero, and both the file and the directory will have been fsync()'d to storage. Otherwise the function returns a nonzero errno error code.
Code:
#define _POSIX_C_SOURCE 200809L
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <errno.h>
static inline int opendirfd(const char *const directory)
{
int descriptor;
do {
descriptor = open(directory, O_RDONLY | O_DIRECTORY);
} while (descriptor == -1 && errno == EINTR);
return descriptor;
}
static inline int closefd(const int descriptor)
{
int result;
do {
result = close(descriptor);
} while (result == -1 && errno == EINTR);
if (result == -1)
return errno;
else
return 0;
}
static inline int fsyncfd(const int descriptor)
{
int result;
do {
result = fsync(descriptor);
} while (result == -1 && errno == EINTR);
if (result == -1)
return errno;
else
return 0;
}
static inline int ftruncatefd(const int descriptor, off_t length)
{
int result;
do {
result = ftruncate(descriptor, length);
} while (result == -1 && errno == EINTR);
if (result == -1)
return errno;
else
return 0;
}
static inline int readfd(const int descriptor, void *const ptr, const size_t len)
{
unsigned char *head = (unsigned char *)ptr;
unsigned char *const tail = len + (unsigned char *)ptr;
ssize_t n;
while (head < tail) {
n = read(descriptor, head, (size_t)(tail - head));
if (n > (ssize_t)0)
head += n;
else
if (n == (ssize_t)0)
return errno = ENOMSG; /* Not enough data available */
else
if (n != (ssize_t)-1)
return errno = EIO;
else
if (errno != EINTR)
return errno;
}
return 0;
}
static inline int writefd(const int descriptor, const void *const ptr, const size_t len)
{
const unsigned char *head = (const unsigned char *)ptr;
const unsigned char *const tail = len + (const unsigned char *)ptr;
ssize_t n;
while (head < tail) {
n = write(descriptor, head, (size_t)(tail - head));
if (n > (ssize_t)0)
head += n;
else
if (n != (ssize_t)-1)
return errno = EIO;
else
if (errno != EINTR)
return errno;
}
return 0;
}
/* Make sure directory exists, creating it if necessary.
* Returns 0 if successful, errno error code otherwise.
*/
int verify_directory(const char *const parent,
const char *const directory)
{
int parentd, dird, saved_errno, result;
/* NULL or empty parent directory name? */
if (!parent || !*parent)
return errno = EINVAL;
/* NULL or empty directory name? */
if (!directory || !*directory)
return errno = EINVAL;
saved_errno = errno;
/* Open the parent directory. */
parentd = opendirfd(parent);
if (parentd == -1)
return errno;
/* Try opening the target directory, too. */
do {
dird = openat(parentd, directory, O_RDONLY | O_DIRECTORY);
} while (dird == -1 && errno == EINTR);
if (dird != -1) {
/* Success; all done. */
closefd(dird);
closefd(parentd);
errno = saved_errno;
return 0;
}
/* Create the target directory. */
do {
result = mkdirat(parentd, directory, 0777);
} while (result == -1 && errno == EINTR);
if (result == -1) {
saved_errno = errno;
closefd(parentd);
return errno = saved_errno;
}
/* Sync the parent directory. */
result = fsyncfd(parentd);
if (result) {
closefd(parentd);
return errno = result;
}
/* Close the parent directory. */
if (closefd(parentd))
return errno;
/* Success. */
errno = saved_errno;
return 0;
}
/* Read data from a file in a specific directory.
* Returns 0 if successful, errno error code otherwise.
*/
int read_carefully(const char *const directory,
const char *const filename,
void *const data, const size_t size)
{
int dird, fd, saved_errno;
/* No directory specified? */
if (!directory || !*directory)
return errno = EINVAL;
/* No filename specified? */
if (!filename || !*filename)
return errno = EINVAL;
saved_errno = errno;
/* Open the directory. */
dird = opendirfd(directory);
if (dird == -1)
return errno;
/* Open the file. */
do {
fd = openat(dird, filename, O_RDONLY | O_NOCTTY);
} while (fd == -1 && errno == EINTR);
if (fd == -1) {
saved_errno = errno;
closefd(dird);
return errno = saved_errno;
}
/* Read the data. */
if (readfd(fd, data, size)) {
saved_errno = errno;
closefd(fd);
closefd(dird);
return errno = saved_errno;
}
/* Close file. */
if (closefd(fd))
return errno;
/* Close directory. */
if (closefd(dird))
return errno;
/* Success. */
errno = saved_errno;
return 0;
}
/* Write data to a file in a specific directory.
* Returns 0 if successful, errno error code otherwise.
*/
int write_carefully(const char *const directory,
const char *const filename,
const void *const data, const size_t size)
{
int dird, fd;
int result, saved_errno;
/* No directory specified? */
if (!directory || !*directory)
return errno = EINVAL;
/* No filename specified? */
if (!filename || !*filename)
return errno = EINVAL;
saved_errno = errno;
/* Open the directory. */
dird = opendirfd(directory);
if (dird == -1)
return errno;
/* Open the file. Create if necessary. Do not truncate the file. */
do {
fd = openat(dird, filename, O_WRONLY | O_CREAT | O_NOCTTY, 0666);
} while (fd == -1 && errno == EINTR);
if (fd == -1) {
saved_errno = errno;
closefd(dird);
return errno = saved_errno;
}
/* Write the new contents. */
result = writefd(fd, data, size);
/* Truncate to new size. */
if (!result)
result = ftruncatefd(fd, (off_t)size);
/* Sync contents. */
if (!result)
result = fsyncfd(fd);
/* Close file. */
if (!result)
result = closefd(fd);
else
closefd(fd);
/* Failed? */
if (result) {
saved_errno = result;
/* Remove partially overwritten file. */
do {
result = unlinkat(dird, filename, 0);
} while (result == -1 && errno == EINTR);
/* Make sure directory metadata is up to date. */
fsyncfd(dird);
/* Close directory. */
closefd(dird);
/* Return failure. */
return errno = saved_errno;
}
/* Sync directory. */
result = fsyncfd(dird);
if (result) {
closefd(dird);
return errno = result;
}
/* Close directory. */
if (closefd(dird))
return errno;
/* Success. */
errno = saved_errno;
return 0;
}
Questions?