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)
{
(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";
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
}
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());
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
#define RSPAMD_LOCKED_FILE_HXX
#pragma once
+#include "config.h"
#include "contrib/expected/expected.hpp"
#include <string>
#include <sys/stat.h>
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;
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);