commit 2e108c5: [Minor] Add some more methods and tests to the file raii abstraction

Vsevolod Stakhov vsevolod at rspamd.com
Sat Oct 8 14:49:05 UTC 2022


Author: Vsevolod Stakhov
Date: 2022-10-08 15:19:11 +0100
URL: https://github.com/rspamd/rspamd/commit/2e108c577370151e9ee0b063de1963e05c4a3522

[Minor] Add some more methods and tests to the file raii abstraction

---
 src/libutil/cxx/locked_file.cxx | 66 +++++++++++++++++++++++++++++++++++++++--
 src/libutil/cxx/locked_file.hxx | 38 ++++++++++++++++++++++++
 2 files changed, 101 insertions(+), 3 deletions(-)

diff --git a/src/libutil/cxx/locked_file.cxx b/src/libutil/cxx/locked_file.cxx
index 6abc4a1b0..bd25b0fc7 100644
--- a/src/libutil/cxx/locked_file.cxx
+++ b/src/libutil/cxx/locked_file.cxx
@@ -127,6 +127,40 @@ auto raii_locked_file::create_temp(const char *fname, int flags, int perms) -> t
 	return ret;
 }
 
+auto raii_locked_file::mkstemp(const char *pattern, 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
+	if (pattern == nullptr) {
+		return tl::make_unexpected("cannot open file; pattern is nullptr");
+	}
+
+	std::string mutable_pattern = pattern;
+
+	auto fd = g_mkstemp_full(mutable_pattern.data(), oflags, perms);
+
+	if (fd == -1) {
+		return tl::make_unexpected(fmt::format("cannot create file {}: {}", pattern, ::strerror(errno)));
+	}
+
+	if (!rspamd_file_lock(fd, TRUE)) {
+		close(fd);
+		(void)unlink(mutable_pattern.c_str());
+		return tl::make_unexpected(fmt::format("cannot lock file {}: {}", pattern, ::strerror(errno)));
+	}
+
+	auto ret = raii_locked_file{mutable_pattern.c_str(), fd, true};
+
+	if (fstat(ret.fd, &ret.st) == -1) {
+		return tl::make_unexpected(fmt::format("cannot stat file {}: {}",
+				mutable_pattern.c_str(), ::strerror(errno)));
+	}
+
+	return ret;
+}
+
 raii_mmaped_locked_file::raii_mmaped_locked_file(raii_locked_file &&_file, void *_map)
 		: file(std::move(_file)), map(_map)
 {
@@ -242,7 +276,7 @@ static auto test_write_file(const T& f, const std::string_view &buf) {
 	(void)::lseek(fd, 0, SEEK_SET);
 	return ::write(fd, buf.data(), buf.size());
 }
-auto random_fname() {
+auto random_fname(std::string_view extension) {
 	const auto *tmpdir = getenv("TMPDIR");
 	if (tmpdir == nullptr) {
 		tmpdir = G_DIR_SEPARATOR_S "tmp";
@@ -254,16 +288,21 @@ auto random_fname() {
 	unsigned char hexbuf[32];
 	rspamd_random_hex(hexbuf, sizeof(hexbuf));
 	out_fname.append((const char *)hexbuf, sizeof(hexbuf));
+	if (!extension.empty()) {
+		out_fname.append(".");
+		out_fname.append(extension);
+	}
 
 	return out_fname;
 }
 TEST_SUITE("loked files utils") {
 
 TEST_CASE("create and delete file") {
-	auto fname = random_fname();
+	auto fname = random_fname("tmp");
 	{
 		auto raii_locked_file = raii_locked_file::create_temp(fname.c_str(), O_RDONLY, 00600);
 		CHECK(raii_locked_file.has_value());
+		CHECK(raii_locked_file.value().get_extension() == "tmp");
 		CHECK(::access(fname.c_str(), R_OK) == 0);
 	}
 	// File must be deleted after this call
@@ -284,10 +323,11 @@ TEST_CASE("create and delete file") {
 }
 
 TEST_CASE("check lock") {
-	auto fname = random_fname();
+	auto fname = random_fname("");
 	{
 		auto raii_locked_file = raii_locked_file::create_temp(fname.c_str(), O_RDONLY, 00600);
 		CHECK(raii_locked_file.has_value());
+		CHECK(raii_locked_file.value().get_extension() == "");
 		CHECK(::access(fname.c_str(), R_OK) == 0);
 		auto raii_locked_file2 = raii_locked_file::open(fname.c_str(), O_RDONLY);
 		CHECK(!raii_locked_file2.has_value());
@@ -300,6 +340,26 @@ TEST_CASE("check lock") {
 	CHECK(serrno == ENOENT);
 }
 
+TEST_CASE("tempfile") {
+	std::string tmpname;
+	{
+		auto raii_locked_file = raii_locked_file::mkstemp("/tmp//doctest-XXXXXXXX",
+				O_RDONLY, 00600);
+		CHECK(raii_locked_file.has_value());
+		CHECK(raii_locked_file.value().get_dir() == "/tmp");
+		CHECK(access(raii_locked_file.value().get_name().data(), R_OK) == 0);
+		auto raii_locked_file2 = raii_locked_file::open(raii_locked_file.value().get_name().data(), O_RDONLY);
+		CHECK(!raii_locked_file2.has_value());
+		CHECK(access(raii_locked_file.value().get_name().data(), R_OK) == 0);
+		tmpname = raii_locked_file.value().get_name();
+	}
+	// File must be deleted after this call
+	auto ret = ::access(tmpname.c_str(), R_OK);
+	auto serrno = errno;
+	CHECK(ret == -1);
+	CHECK(serrno == ENOENT);
+}
+
 } // TEST_SUITE
 
 } // namespace tests
diff --git a/src/libutil/cxx/locked_file.hxx b/src/libutil/cxx/locked_file.hxx
index 6ac4ffd2c..ce25b0a5a 100644
--- a/src/libutil/cxx/locked_file.hxx
+++ b/src/libutil/cxx/locked_file.hxx
@@ -17,6 +17,7 @@
 #define RSPAMD_LOCKED_FILE_HXX
 #pragma once
 
+#include "config.h"
 #include "contrib/expected/expected.hpp"
 #include <string>
 #include <sys/stat.h>
@@ -32,6 +33,7 @@ struct raii_locked_file final {
 	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>;
 	static auto create_temp(const char *fname, int flags, int perms) -> tl::expected<raii_locked_file, std::string>;
+	static auto mkstemp(const char *pattern, int flags, int perms) -> tl::expected<raii_locked_file, std::string>;
 
 	auto get_fd() const -> int {
 		return fd;
@@ -41,6 +43,42 @@ struct raii_locked_file final {
 		return st;
 	};
 
+	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};
+	}
+
+	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_locked_file& operator=(raii_locked_file &&other) noexcept {
 		std::swap(fd, other.fd);
 		std::swap(temp, other.temp);


More information about the Commits mailing list