#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"
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;
+}
#include "fd-util.h"
#include "fileio.h"
#include "fs-util.h"
+#include "iovec-util.h"
#include "memfd-util.h"
#include "parse-util.h"
#include "path-util.h"
f = safe_fclose(f);
}
+TEST(write_data_file_atomic_at) {
+ struct iovec a = IOVEC_MAKE_STRING("hallo");
+ ASSERT_OK(write_data_file_atomic_at(AT_FDCWD, "/tmp/wdfa", &a, /* flags= */ 0));
+
+ _cleanup_(iovec_done) struct iovec ra = {};
+ ASSERT_OK(read_full_file("/tmp/wdfa", (char**) &ra.iov_base, &ra.iov_len));
+ ASSERT_EQ(iovec_memcmp(&a, &ra), 0);
+ ASSERT_OK_ERRNO(unlink("/tmp/wdfa"));
+
+ ASSERT_OK(write_data_file_atomic_at(XAT_FDROOT, "tmp/wdfa", &a, /* flags= */ 0));
+ iovec_done(&ra);
+ ASSERT_OK(read_full_file("/tmp/wdfa", (char**) &ra.iov_base, &ra.iov_len));
+ ASSERT_EQ(iovec_memcmp(&a, &ra), 0);
+ ASSERT_OK_ERRNO(unlink("/tmp/wdfa"));
+
+ ASSERT_ERROR(write_data_file_atomic_at(AT_FDCWD, NULL, &a, /* flags= */ 0), EINVAL);
+ ASSERT_ERROR(write_data_file_atomic_at(AT_FDCWD, "", &a, /* flags= */ 0), EINVAL);
+ ASSERT_ERROR(write_data_file_atomic_at(AT_FDCWD, "/", &a, /* flags= */ 0), EISDIR);
+ ASSERT_ERROR(write_data_file_atomic_at(AT_FDCWD, ".", &a, /* flags= */ 0), EISDIR);
+ ASSERT_ERROR(write_data_file_atomic_at(AT_FDCWD, "/tmp/", &a, /* flags= */ 0), EISDIR);
+
+ _cleanup_free_ char *cwd = NULL;
+ ASSERT_OK(safe_getcwd(&cwd));
+ ASSERT_OK_ERRNO(chdir("/tmp"));
+
+ ASSERT_OK(write_data_file_atomic_at(AT_FDCWD, "wdfa", &a, /* flags= */ 0));
+ iovec_done(&ra);
+ ASSERT_OK(read_full_file("/tmp/wdfa", (char**) &ra.iov_base, &ra.iov_len));
+ ASSERT_EQ(iovec_memcmp(&a, &ra), 0);
+ ASSERT_OK_ERRNO(unlink("/tmp/wdfa"));
+
+ ASSERT_OK(write_data_file_atomic_at(XAT_FDROOT, "tmp/wdfa", &a, /* flags= */ 0));
+ iovec_done(&ra);
+ ASSERT_OK(read_full_file("/tmp/wdfa", (char**) &ra.iov_base, &ra.iov_len));
+ ASSERT_EQ(iovec_memcmp(&a, &ra), 0);
+ ASSERT_OK_ERRNO(unlink("/tmp/wdfa"));
+
+ ASSERT_OK_ERRNO(chdir(cwd));
+
+ ASSERT_ERROR(write_data_file_atomic_at(XAT_FDROOT, "tmp/zzz/wdfa", &a, /* flags= */ 0), ENOENT);
+ ASSERT_OK(write_data_file_atomic_at(XAT_FDROOT, "tmp/zzz/wdfa", &a, WRITE_DATA_FILE_MKDIR_0755));
+ iovec_done(&ra);
+ ASSERT_OK(read_full_file("/tmp/zzz/wdfa", (char**) &ra.iov_base, &ra.iov_len));
+ ASSERT_EQ(iovec_memcmp(&a, &ra), 0);
+ ASSERT_OK_ERRNO(unlink("/tmp/zzz/wdfa"));
+
+ ASSERT_ERROR(write_data_file_atomic_at(AT_FDCWD, "/tmp/zzz", &a, /* flags= */ 0), EEXIST);
+
+ ASSERT_OK_ERRNO(rmdir("/tmp/zzz"));
+}
+
DEFINE_TEST_MAIN(LOG_DEBUG);