diff options
author | Denis Kenzior <denkenz@gmail.com> | 2024-01-29 13:39:34 -0600 |
---|---|---|
committer | Denis Kenzior <denkenz@gmail.com> | 2024-01-29 20:38:18 -0600 |
commit | ff4b6acfea7e9c30869798da5ed265fadb20e962 (patch) | |
tree | b0542c8cc3c9f06560d63e116477ac5cd28f22d3 | |
parent | e859d6f5b9defb9ed130afd7c502d127daf3933e (diff) |
file: Add l_file_set_contents
-rw-r--r-- | ell/ell.sym | 1 | ||||
-rw-r--r-- | ell/file.c | 56 | ||||
-rw-r--r-- | ell/file.h | 1 |
3 files changed, 58 insertions, 0 deletions
diff --git a/ell/ell.sym b/ell/ell.sym index 17acd406..6ef885dd 100644 --- a/ell/ell.sym +++ b/ell/ell.sym @@ -312,6 +312,7 @@ global: l_dir_watch_destroy; /* file */ l_file_get_contents; + l_file_set_contents; /* genl */ l_genl_new; l_genl_ref; @@ -15,9 +15,12 @@ #include <fcntl.h> #include <unistd.h> #include <errno.h> +#include <stdlib.h> +#include <stdio.h> #include "file.h" #include "private.h" +#include "useful.h" /** * l_file_get_contents: @@ -72,3 +75,56 @@ error: close(fd); return NULL; } + +/** + * l_file_set_contents: + * @filename: Destination filename + * @contents: Pointer to the contents + * @len: Length in bytes of the contents buffer + * + * Given a content buffer, write it to a file named @filename. This function + * ensures that the contents are consistent (i.e. due to a crash right after + * opening or during write() by writing the contents to a temporary which is then + * renamed to @filename. + * + * Returns: 0 if successful, a negative errno otherwise + **/ +LIB_EXPORT int l_file_set_contents(const char *filename, + const void *contents, size_t len) +{ + _auto_(l_free) char *tmp_path = NULL; + ssize_t r; + int fd; + + if (!filename || !contents) + return -EINVAL; + + tmp_path = l_strdup_printf("%s.XXXXXX.tmp", filename); + + fd = L_TFR(mkostemps(tmp_path, 4, O_CLOEXEC)); + if (fd == -1) + return -errno; + + r = L_TFR(write(fd, contents, len)); + L_TFR(close(fd)); + + if (r != (ssize_t) len) { + r = -EIO; + goto error_write; + } + + /* + * Now that the file contents are written, rename to the real + * file name; this way we are uniquely sure that the whole + * thing is there. + * conserve @r's value from 'write' + */ + if (rename(tmp_path, filename) == -1) + r = -errno; + +error_write: + if (r < 0) + unlink(tmp_path); + + return r < 0 ? r : 0; +} @@ -13,6 +13,7 @@ extern "C" { #endif void *l_file_get_contents(const char *filename, size_t *out_len); +int l_file_set_contents(const char *filename, const void *data, size_t len); #ifdef __cplusplus } |