summaryrefslogtreecommitdiffstats
path: root/src/libutil/cxx/file_util.hxx
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/libutil/cxx/file_util.hxx312
1 files changed, 312 insertions, 0 deletions
diff --git a/src/libutil/cxx/file_util.hxx b/src/libutil/cxx/file_util.hxx
new file mode 100644
index 0000000..4528905
--- /dev/null
+++ b/src/libutil/cxx/file_util.hxx
@@ -0,0 +1,312 @@
+/*
+ * Copyright 2023 Vsevolod Stakhov
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef RSPAMD_FILE_UTIL_HXX
+#define RSPAMD_FILE_UTIL_HXX
+#pragma once
+
+#include "config.h"
+#include "contrib/expected/expected.hpp"
+#include "libutil/cxx/error.hxx"
+#include <string>
+#include <sys/stat.h>
+
+namespace rspamd::util {
+/**
+ * A simple RAII object to contain a move only file descriptor
+ * A file is unlocked and closed when not needed
+ */
+struct raii_file {
+public:
+ virtual ~raii_file() noexcept;
+
+ static auto open(const char *fname, int flags) -> tl::expected<raii_file, error>;
+ static auto open(const std::string &fname, int flags) -> tl::expected<raii_file, error>
+ {
+ return open(fname.c_str(), flags);
+ };
+ static auto create(const char *fname, int flags, int perms) -> tl::expected<raii_file, error>;
+ static auto create(const std::string &fname, int flags, int perms) -> tl::expected<raii_file, error>
+ {
+ return create(fname.c_str(), flags, perms);
+ };
+
+ static auto create_temp(const char *fname, int flags, int perms) -> tl::expected<raii_file, error>;
+ static auto mkstemp(const char *pattern, int flags, int perms) -> tl::expected<raii_file, error>;
+
+ auto get_fd() const -> int
+ {
+ return fd;
+ }
+
+ auto get_stat() const -> const struct stat &
+ {
+ return st;
+ };
+
+ auto get_size() const -> std::size_t
+ {
+ return st.st_size;
+ };
+
+ auto get_name() const -> std::string_view
+ {
+ return std::string_view{fname};
+ }
+
+ auto get_dir() const -> std::string_view
+ {
+ auto sep_pos = fname.rfind(G_DIR_SEPARATOR);
+
+ if (sep_pos == std::string::npos) {
+ return std::string_view{fname};
+ }
+
+ while (sep_pos >= 1 && fname[sep_pos - 1] == G_DIR_SEPARATOR) {
+ sep_pos--;
+ }
+
+ return std::string_view{fname.c_str(), sep_pos + 1};
+ }
+
+ auto get_extension() const -> std::string_view
+ {
+ auto sep_pos = fname.rfind(G_DIR_SEPARATOR);
+
+ if (sep_pos == std::string::npos) {
+ sep_pos = 0;
+ }
+
+ auto filename = std::string_view{fname.c_str() + sep_pos};
+ auto dot_pos = filename.find('.');
+
+ if (dot_pos == std::string::npos) {
+ return std::string_view{};
+ }
+ else {
+ return std::string_view{filename.data() + dot_pos + 1, filename.size() - dot_pos - 1};
+ }
+ }
+
+ raii_file &operator=(raii_file &&other) noexcept
+ {
+ std::swap(fd, other.fd);
+ std::swap(temp, other.temp);
+ std::swap(fname, other.fname);
+ std::swap(st, other.st);
+
+ return *this;
+ }
+
+ raii_file(raii_file &&other) noexcept
+ {
+ *this = std::move(other);
+ }
+
+ /**
+ * Prevent file from being deleted
+ * @return
+ */
+ auto make_immortal() noexcept
+ {
+ temp = false;
+ }
+
+ /**
+ * Performs fstat on an opened file to refresh internal stat
+ * @return
+ */
+ auto update_stat() noexcept -> bool;
+
+ auto is_valid() noexcept -> bool
+ {
+ return fd != -1;
+ }
+
+ /* Do not allow copy/default ctor */
+ const raii_file &operator=(const raii_file &other) = delete;
+ raii_file() = delete;
+ raii_file(const raii_file &other) = delete;
+
+protected:
+ int fd = -1;
+ bool temp;
+ std::string fname;
+ struct stat st;
+
+ explicit raii_file(const char *fname, int fd, bool temp);
+};
+/**
+ * A simple RAII object to contain a file descriptor with an flock wrap
+ * A file is unlocked and closed when not needed
+ */
+struct raii_locked_file final : public raii_file {
+public:
+ ~raii_locked_file() noexcept override;
+
+ static auto open(const char *fname, int flags) -> tl::expected<raii_locked_file, error>
+ {
+ auto locked = raii_file::open(fname, flags).and_then([]<class T>(T &&file) {
+ return lock_raii_file(std::forward<T>(file));
+ });
+
+ return locked;
+ }
+ static auto create(const char *fname, int flags, int perms) -> tl::expected<raii_locked_file, error>
+ {
+ auto locked = raii_file::create(fname, flags, perms).and_then([]<class T>(T &&file) {
+ return lock_raii_file(std::forward<T>(file));
+ });
+
+ return locked;
+ }
+ static auto create_temp(const char *fname, int flags, int perms) -> tl::expected<raii_locked_file, error>
+ {
+ auto locked = raii_file::create_temp(fname, flags, perms).and_then([]<class T>(T &&file) {
+ return lock_raii_file(std::forward<T>(file));
+ });
+
+ return locked;
+ }
+ static auto mkstemp(const char *pattern, int flags, int perms) -> tl::expected<raii_locked_file, error>
+ {
+ auto locked = raii_file::mkstemp(pattern, flags, perms).and_then([]<class T>(T &&file) {
+ return lock_raii_file(std::forward<T>(file));
+ });
+
+ return locked;
+ }
+
+ raii_locked_file &operator=(raii_locked_file &&other) noexcept
+ {
+ std::swap(fd, other.fd);
+ std::swap(temp, other.temp);
+ std::swap(fname, other.fname);
+ std::swap(st, other.st);
+
+ return *this;
+ }
+
+ /**
+ * Unlock a locked file and return back unlocked file transferring ownership.
+ * A locked file cannot be used after this method.
+ */
+ auto unlock() -> raii_file;
+
+ raii_locked_file(raii_locked_file &&other) noexcept
+ : raii_file(static_cast<raii_file &&>(std::move(other)))
+ {
+ }
+ /* Do not allow copy/default ctor */
+ const raii_locked_file &operator=(const raii_locked_file &other) = delete;
+ raii_locked_file() = delete;
+ raii_locked_file(const raii_locked_file &other) = delete;
+
+private:
+ static auto lock_raii_file(raii_file &&unlocked) -> tl::expected<raii_locked_file, error>;
+ raii_locked_file(raii_file &&other) noexcept
+ : raii_file(std::move(other))
+ {
+ }
+ explicit raii_locked_file(const char *fname, int fd, bool temp)
+ : raii_file(fname, fd, temp)
+ {
+ }
+};
+
+/**
+ * A mmap wrapper on top of a locked file
+ */
+struct raii_mmaped_file final {
+ ~raii_mmaped_file();
+ static auto mmap_shared(raii_file &&file, int flags, std::int64_t offset = 0) -> tl::expected<raii_mmaped_file, error>;
+ static auto mmap_shared(const char *fname, int open_flags, int mmap_flags, std::int64_t offset = 0) -> tl::expected<raii_mmaped_file, error>;
+ // Returns a constant pointer to the underlying map
+ auto get_map() const -> void *
+ {
+ return map;
+ }
+ auto get_file() const -> const raii_file &
+ {
+ return file;
+ }
+ // Passes the ownership of the mmaped memory to the callee
+ auto steal_map() -> std::tuple<void *, std::size_t>
+ {
+ auto ret = std::make_tuple(this->map, map_size);
+ this->map = nullptr;
+ return ret;
+ }
+
+ auto get_size() const -> std::size_t
+ {
+ return file.get_stat().st_size;
+ }
+
+ raii_mmaped_file &operator=(raii_mmaped_file &&other) noexcept
+ {
+ std::swap(map, other.map);
+ std::swap(map_size, other.map_size);
+ file = std::move(other.file);
+
+ return *this;
+ }
+
+ raii_mmaped_file(raii_mmaped_file &&other) noexcept;
+
+ /* Do not allow copy/default ctor */
+ const raii_mmaped_file &operator=(const raii_mmaped_file &other) = delete;
+ raii_mmaped_file() = delete;
+ raii_mmaped_file(const raii_mmaped_file &other) = delete;
+
+private:
+ /* Is intended to be used with map_shared */
+ explicit raii_mmaped_file(raii_file &&_file, void *_map, std::size_t sz);
+ raii_file file;
+ void *map = nullptr;
+ std::size_t map_size;
+};
+
+/**
+ * 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, error>;
+ 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;
+};
+
+}// namespace rspamd::util
+
+#endif//RSPAMD_FILE_UTIL_HXX