fileio: introduce write_data_file_atomic_at() helper
This is very similar to write_string_file_atomic(), but is intentionally kept separate (after long consideration). It focusses on arbitrary struct iovec data, not just strings, and hence also doesn't do stdio at all. It's hence a lot more low-level. We might want to consider moving write_string_file*() on top of write_data_file_atomic_at(), but for now don't.
This commit is contained in:
@@ -7,12 +7,15 @@
|
||||
#include <unistd.h>
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "chase.h"
|
||||
#include "errno-util.h"
|
||||
#include "extract-word.h"
|
||||
#include "fd-util.h"
|
||||
#include "fileio.h"
|
||||
#include "fs-util.h"
|
||||
#include "hexdecoct.h"
|
||||
#include "io-util.h"
|
||||
#include "iovec-util.h"
|
||||
#include "label.h"
|
||||
#include "log.h"
|
||||
#include "mkdir.h"
|
||||
@@ -1655,3 +1658,62 @@ int warn_file_is_world_accessible(const char *filename, struct stat *st, const c
|
||||
filename, st->st_mode & 07777);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int write_data_file_atomic_at(
|
||||
int dir_fd,
|
||||
const char *path,
|
||||
const struct iovec *iovec,
|
||||
WriteDataFileFlags flags) {
|
||||
|
||||
int r;
|
||||
|
||||
assert(dir_fd >= 0 || IN_SET(dir_fd, AT_FDCWD, XAT_FDROOT));
|
||||
|
||||
/* This is a cousin of write_string_file_atomic(), but operates with arbitrary struct iovec binary
|
||||
* data (rather than strings), works without FILE* streams, and does direct syscalls instead. */
|
||||
|
||||
_cleanup_free_ char *dn = NULL, *fn = NULL;
|
||||
r = path_split_prefix_filename(path, &dn, &fn);
|
||||
if (IN_SET(r, -EADDRNOTAVAIL, O_DIRECTORY))
|
||||
return -EISDIR; /* path refers to "." or "/" (which are dirs, which we cannot write), or is suffixed with "/" */
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
_cleanup_close_ int mfd = -EBADF;
|
||||
if (dn) {
|
||||
/* If there's a directory component, readjust our position */
|
||||
r = chaseat(dir_fd,
|
||||
dn,
|
||||
FLAGS_SET(flags, WRITE_DATA_FILE_MKDIR_0755) ? CHASE_MKDIR_0755 : 0,
|
||||
/* ret_path= */ NULL,
|
||||
&mfd);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
dir_fd = mfd;
|
||||
}
|
||||
|
||||
_cleanup_free_ char *t = NULL;
|
||||
_cleanup_close_ int fd = open_tmpfile_linkable_at(dir_fd, fn, O_WRONLY|O_CLOEXEC, &t);
|
||||
if (fd < 0)
|
||||
return fd;
|
||||
|
||||
CLEANUP_TMPFILE_AT(dir_fd, t);
|
||||
|
||||
if (iovec_is_set(iovec)) {
|
||||
r = loop_write(fd, iovec->iov_base, iovec->iov_len);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
r = fchmod_umask(fd, 0644);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = link_tmpfile_at(fd, dir_fd, t, fn, LINK_TMPFILE_REPLACE);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
t = mfree(t); /* disarm CLEANUP_TMPFILE_AT */
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -163,3 +163,9 @@ int safe_fgetc(FILE *f, char *ret);
|
||||
int warn_file_is_world_accessible(const char *filename, struct stat *st, const char *unit, unsigned line);
|
||||
|
||||
int fopen_mode_to_flags(const char *mode);
|
||||
|
||||
typedef enum WriteDataFileFlags {
|
||||
WRITE_DATA_FILE_MKDIR_0755 = 1 << 0,
|
||||
} WriteDataFileFlags;
|
||||
|
||||
int write_data_file_atomic_at(int dir_fd, const char *path, const struct iovec *iovec, WriteDataFileFlags flags);
|
||||
|
||||
Reference in New Issue
Block a user