server/src/file_handler.cpp
Rama main · réplica de lectura del repositorio original.
/**
* @file src/file_handler.cpp
* @brief Default operating-system adapter for file handling policy.
*/
#include "file_handler.h"
#include "logging.h"
#include <chrono>
#include <fstream>
#include <functional>
#include <system_error>
#include <thread>
#ifdef _WIN32
#include <Windows.h>
#else
#include <cerrno>
#include <fcntl.h>
#include <sys/stat.h>
#include <unistd.h>
#endif
namespace file_handler {
namespace {
class native_filesystem_t: public filesystem_t {
public:
bool exists(const std::filesystem::path &path) const override {
std::error_code error;
return std::filesystem::exists(path, error);
}
bool create_directories(const std::filesystem::path &path) override {
std::error_code error;
const bool created = std::filesystem::create_directories(path, error);
return !error && (created || std::filesystem::exists(path));
}
std::string read(const std::filesystem::path &path) const override {
std::ifstream input(path);
return std::string {(std::istreambuf_iterator<char>(input)), std::istreambuf_iterator<char>()};
}
bool write(const std::filesystem::path &path, std::string_view contents) override {
#ifdef _WIN32
std::ofstream output(path, std::ios::binary | std::ios::trunc);
if (!output.is_open()) {
return false;
}
output.write(contents.data(), static_cast<std::streamsize>(contents.size()));
output.flush();
output.close();
return static_cast<bool>(output);
#else
// Atomic callers always write a fresh temporary path. O_EXCL and
// O_NOFOLLOW prevent a same-directory attacker from redirecting a
// predictable temporary name through a pre-positioned symlink.
const int fd = ::open(
path.c_str(),
O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC | O_NOFOLLOW,
S_IRUSR | S_IWUSR
);
if (fd < 0) {
return false;
}
size_t offset = 0;
bool success = true;
while (offset < contents.size()) {
const auto written = ::write(fd, contents.data() + offset, contents.size() - offset);
if (written < 0) {
if (errno == EINTR) {
continue;
}
success = false;
break;
}
if (written == 0) {
success = false;
break;
}
offset += static_cast<size_t>(written);
}
if (success && ::fsync(fd) != 0) {
success = false;
}
if (::close(fd) != 0) {
success = false;
}
return success;
#endif
}
replace_result_e replace(const std::filesystem::path &temporary,
const std::filesystem::path &target) override {
#ifdef _WIN32
if (MoveFileExW(
temporary.c_str(),
target.c_str(),
MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH
)) {
return replace_result_e::success;
}
return GetLastError() == ERROR_ACCESS_DENIED ? replace_result_e::access_denied : replace_result_e::error;
#else
std::error_code error;
std::filesystem::rename(temporary, target, error);
if (!error) {
return replace_result_e::success;
}
return error == std::errc::permission_denied ? replace_result_e::access_denied : replace_result_e::error;
#endif
}
std::optional<bool> read_only(const std::filesystem::path &path) const override {
#ifdef _WIN32
const auto attributes = GetFileAttributesW(path.c_str());
if (attributes == INVALID_FILE_ATTRIBUTES) {
return std::nullopt;
}
return (attributes & FILE_ATTRIBUTE_READONLY) != 0;
#else
(void) path;
return false;
#endif
}
bool set_read_only(const std::filesystem::path &path, bool value) override {
#ifdef _WIN32
const auto attributes = GetFileAttributesW(path.c_str());
if (attributes == INVALID_FILE_ATTRIBUTES) {
return false;
}
const auto updated = value ? attributes | FILE_ATTRIBUTE_READONLY : attributes & ~FILE_ATTRIBUTE_READONLY;
return SetFileAttributesW(path.c_str(), updated) != 0;
#else
(void) path;
(void) value;
return false;
#endif
}
void remove(const std::filesystem::path &path) override {
std::error_code error;
std::filesystem::remove(path, error);
}
std::filesystem::path temporary_path(const std::filesystem::path &target) override {
const auto stamp = std::chrono::steady_clock::now().time_since_epoch().count();
const auto tid = std::hash<std::thread::id> {}(std::this_thread::get_id());
auto temporary = target;
temporary += ".tmp." + std::to_string(stamp) + "." + std::to_string(tid);
return temporary;
}
};
handler_t &default_handler() {
static native_filesystem_t filesystem;
static handler_t handler {filesystem, [](std::string_view message) {
BOOST_LOG(error) << message;
}};
return handler;
}
} // namespace
std::string get_parent_directory(const std::string &path) {
return default_handler().get_parent_directory(path);
}
bool make_directory(const std::string &path) {
return default_handler().make_directory(path);
}
std::string read_file(const char *path) {
return default_handler().read_file(path);
}
int write_file(const char *path, const std::string_view &contents) {
return default_handler().write_file(path, contents);
}
} // namespace file_handler