]> git.ipfire.org Git - thirdparty/rspamd.git/commitdiff
[Project] Add raii_sink file helper
authorVsevolod Stakhov <vsevolod@rspamd.com>
Sat, 2 Apr 2022 19:51:32 +0000 (20:51 +0100)
committerVsevolod Stakhov <vsevolod@rspamd.com>
Sat, 2 Apr 2022 19:51:32 +0000 (20:51 +0100)
src/libutil/cxx/locked_file.cxx
src/libutil/cxx/locked_file.hxx

index 9d47304a99b15b3ae6159519f493449988518b96..adac994681b8f0e5eaca359e6eb7ad9a9929c10d 100644 (file)
@@ -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)
+{
+}
+
 }
index 712c75a19e15087f26044a9550b1c5c3da212038..3934c0c2eb528e01260fe2a669f5bcdc2c744031 100644 (file)
@@ -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