commit 6b5e725: [Project] Add raii_sink file helper

Vsevolod Stakhov vsevolod at rspamd.com
Sat Apr 30 19:21:12 UTC 2022


Author: Vsevolod Stakhov
Date: 2022-04-02 20:51:32 +0100
URL: https://github.com/rspamd/rspamd/commit/6b5e725d6bbf5028648a80d29bf10ff2adfa1375

[Project] Add raii_sink file helper

---
 src/libutil/cxx/locked_file.cxx | 88 +++++++++++++++++++++++++++++++++++++++--
 src/libutil/cxx/locked_file.hxx | 28 +++++++++++++
 2 files changed, 113 insertions(+), 3 deletions(-)

diff --git a/src/libutil/cxx/locked_file.cxx b/src/libutil/cxx/locked_file.cxx
index 9d47304a9..adac99468 100644
--- a/src/libutil/cxx/locked_file.cxx
+++ b/src/libutil/cxx/locked_file.cxx
@@ -7,6 +7,10 @@
 #include "libutil/util.h"
 #include "libutil/unix-std.h"
 
+#define DOCTEST_CONFIG_IMPLEMENTATION_IN_DLL
+
+#include "doctest/doctest.h"
+
 namespace rspamd::util {
 
 auto raii_locked_file::open(const char *fname, int flags) -> tl::expected<raii_locked_file, std::string>
@@ -43,8 +47,36 @@ raii_locked_file::~raii_locked_file()
 	}
 }
 
+auto raii_locked_file::create(const char *fname, int flags, int perms) -> tl::expected<raii_locked_file, std::string>
+{
+	int oflags = flags;
+#ifdef O_CLOEXEC
+	oflags |= O_CLOEXEC | O_CREAT | O_EXCL;
+#endif
+	auto fd = ::open(fname, oflags, perms);
+
+	if (fd == -1) {
+		return tl::make_unexpected(fmt::format("cannot create file {}: {}", fname, ::strerror(errno)));
+	}
+
+	if (!rspamd_file_lock(fd, FALSE)) {
+		close(fd);
+		return tl::make_unexpected(fmt::format("cannot lock file {}: {}", fname, ::strerror(errno)));
+	}
+
+	auto ret = raii_locked_file{fd};
+
+	if (fstat(ret.fd, &ret.st) == -1) {
+		return tl::make_unexpected(fmt::format("cannot stat file {}: {}", fname, ::strerror(errno)));
+	}
+
+	return ret;
+}
+
 raii_mmaped_locked_file::raii_mmaped_locked_file(raii_locked_file &&_file, void *_map)
-	: file(std::move(_file)), map(_map) {}
+		: file(std::move(_file)), map(_map)
+{
+}
 
 auto raii_mmaped_locked_file::mmap_shared(raii_locked_file &&file,
 										  int flags) -> tl::expected<raii_mmaped_locked_file, std::string>
@@ -79,10 +111,60 @@ raii_mmaped_locked_file::~raii_mmaped_locked_file()
 	munmap(map, file.get_stat().st_size);
 }
 
-raii_mmaped_locked_file::raii_mmaped_locked_file(raii_mmaped_locked_file &&other)  noexcept
-	: file(std::move(other.file))
+raii_mmaped_locked_file::raii_mmaped_locked_file(raii_mmaped_locked_file &&other) noexcept
+		: file(std::move(other.file))
 {
 	std::swap(map, other.map);
 }
 
+auto raii_file_sink::create(const char *fname, int flags, int perms,
+							const char *suffix) -> tl::expected<raii_file_sink, std::string>
+{
+	auto tmp_fname = fmt::format("{}.{}", fname, suffix);
+	auto file = raii_locked_file::create(tmp_fname.c_str(), flags, perms);
+
+	if (!file.has_value()) {
+		return tl::make_unexpected(file.error());
+	}
+
+	return raii_file_sink{std::move(file.value()), fname, std::move(tmp_fname)};
+}
+
+auto raii_file_sink::write_output() -> bool
+{
+	if (success) {
+		/* We cannot write output twice */
+		return false;
+	}
+
+	if (rename(tmp_fname.c_str(), output_fname.c_str()) == -1) {
+		return false;
+	}
+
+	success = true;
+
+	return true;
+}
+
+raii_file_sink::~raii_file_sink()
+{
+	if (!success) {
+		/* Unlink sink */
+		unlink(tmp_fname.c_str());
+	}
+}
+
+raii_file_sink::raii_file_sink(raii_locked_file &&_file, const char *_output, std::string &&_tmp_fname)
+		: file(std::move(_file)), output_fname(_output), tmp_fname(std::move(_tmp_fname)), success(false)
+{
+}
+
+raii_file_sink::raii_file_sink(raii_file_sink &&other) noexcept
+		: file(std::move(other.file)),
+		  output_fname(std::move(other.output_fname)),
+		  tmp_fname(std::move(other.tmp_fname)),
+		  success(other.success)
+{
+}
+
 }
diff --git a/src/libutil/cxx/locked_file.hxx b/src/libutil/cxx/locked_file.hxx
index 712c75a19..3934c0c2e 100644
--- a/src/libutil/cxx/locked_file.hxx
+++ b/src/libutil/cxx/locked_file.hxx
@@ -30,6 +30,7 @@ struct raii_locked_file final {
 	~raii_locked_file();
 
 	static auto open(const char *fname, int flags) -> tl::expected<raii_locked_file, std::string>;
+	static auto create(const char *fname, int flags, int perms) -> tl::expected<raii_locked_file, std::string>;
 
 	auto get_fd() const -> int
 	{
@@ -93,6 +94,33 @@ private:
 	void *map{};
 };
 
+/**
+ * A helper to have a file to write that will be renamed to the
+ * target file if successful or deleted in the case of failure
+ */
+struct raii_file_sink final {
+	static auto create(const char *fname, int flags, int perms, const char *suffix = "new")
+		-> tl::expected<raii_file_sink, std::string>;
+	auto write_output() -> bool;
+	~raii_file_sink();
+	auto get_fd() const -> int
+	{
+		return file.get_fd();
+	}
+
+	raii_file_sink(raii_file_sink &&other) noexcept;
+	/* Do not allow copy/default ctor */
+	const raii_file_sink& operator=(const raii_file_sink &other) = delete;
+	raii_file_sink() = delete;
+	raii_file_sink(const raii_file_sink &other) = delete;
+private:
+	explicit raii_file_sink(raii_locked_file &&_file, const char *_output, std::string &&_tmp_fname);
+	raii_locked_file file;
+	std::string output_fname;
+	std::string tmp_fname;
+	bool success;
+};
+
 }
 
 #endif //RSPAMD_LOCKED_FILE_HXX


More information about the Commits mailing list