}
tl::expected<void, std::string>
-write_file(const fs::path& path, std::string_view data, InPlace in_place)
+write_file(const fs::path& path, std::string_view data, WriteFileMode mode)
{
auto path_str = pstr(path);
- if (in_place == InPlace::no) {
+ if (mode == WriteFileMode::unlink) {
unlink(path_str);
}
- Fd fd(open(path_str, O_WRONLY | O_CREAT | O_TRUNC | O_TEXT, 0666));
+ int flags = O_WRONLY | O_CREAT | O_TRUNC | O_TEXT;
+ if (mode == WriteFileMode::exclusive) {
+ flags |= O_EXCL;
+ }
+ Fd fd(open(path_str, flags, 0666));
if (!fd) {
return tl::unexpected(strerror(errno));
}
tl::expected<void, std::string>
write_file(const fs::path& path,
nonstd::span<const uint8_t> data,
- InPlace in_place)
+ WriteFileMode mode)
{
auto path_str = pstr(path);
- if (in_place == InPlace::no) {
+ if (mode == WriteFileMode::unlink) {
unlink(path_str);
}
- Fd fd(open(path_str, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0666));
+ int flags = O_WRONLY | O_CREAT | O_TRUNC | O_BINARY;
+ if (mode == WriteFileMode::exclusive) {
+ flags |= O_EXCL;
+ }
+ Fd fd(open(path_str, flags, 0666));
if (!fd) {
return tl::unexpected(strerror(errno));
}
// --- Interface ---
-enum class InPlace { yes, no };
+enum class WriteFileMode {
+ unlink, // unlink existing file before writing (break hard links)
+ in_place, // don't unlink before writing (don't break hard links)
+ exclusive, // return error if the file already exists (O_EXCL)
+};
enum class LogFailure { yes, no };
enum class ViaTmpFile { yes, no };
// Write `size` bytes from binary `data` to `fd`.
tl::expected<void, std::string> write_fd(int fd, const void* data, size_t size);
-// Write text `data` to `path`. If `in_place` is no, unlink any existing file
-// first (i.e., break hard links).
-tl::expected<void, std::string> write_file(const std::filesystem::path& path,
- std::string_view data,
- InPlace in_place = InPlace::no);
-
-// Write binary `data` to `path`. If `in_place` is no, unlink any existing
-// file first (i.e., break hard links).
-tl::expected<void, std::string> write_file(const std::filesystem::path& path,
- nonstd::span<const uint8_t> data,
- InPlace in_place = InPlace::no);
+// Write text `data` to `path`.
+tl::expected<void, std::string>
+write_file(const std::filesystem::path& path,
+ std::string_view data,
+ WriteFileMode mode = WriteFileMode::unlink);
+
+// Write binary `data` to `path`.
+tl::expected<void, std::string>
+write_file(const std::filesystem::path& path,
+ nonstd::span<const uint8_t> data,
+ WriteFileMode mode = WriteFileMode::unlink);
// --- Inline implementations ---
DirEntry entry("a");
CHECK(entry.size() == 0);
- util::write_file("a", "123", util::InPlace::yes);
+ util::write_file("a", "123", util::WriteFileMode::in_place);
CHECK(entry.size() == 0);
entry.refresh();
CHECK(entry.size() == 3);
CHECK(entry_a.same_inode_as(entry_a));
CHECK(!entry_a.same_inode_as(entry_b));
- util::write_file("a", "change size", util::InPlace::yes);
+ util::write_file("a", "change size", util::WriteFileMode::in_place);
CHECK(DirEntry("a").same_inode_as(entry_a));
CHECK(!DirEntry("nonexistent").same_inode_as(DirEntry("nonexistent")));
CHECK(entry_a.inode() == entry_b.inode());
CHECK(entry_a.same_inode_as(entry_b));
- util::write_file("a", "1234567", util::InPlace::yes);
+ util::write_file("a", "1234567", util::WriteFileMode::in_place);
entry_b.refresh();
CHECK(entry_b.size() == 7);
}
#include <doctest.h>
#include <fcntl.h>
+#ifndef _WIN32
+# include <unistd.h>
+#endif
+
#include <cstring>
#include <string>
#include <string_view>
}
}
+#ifndef _WIN32
+TEST_CASE("util::write_file modes")
+{
+ TestContext test_context;
+
+ CHECK(util::write_file("test", "foo"));
+ CHECK(link("test", "test2") == 0);
+
+ SUBCASE("WriteFileMode::unlink")
+ {
+ CHECK(util::write_file("test", "bar", util::WriteFileMode::unlink));
+ CHECK(*util::read_file<std::string>("test2") == "foo");
+ }
+
+ SUBCASE("WriteFileMode::in_place")
+ {
+ CHECK(util::write_file("test", "bar", util::WriteFileMode::in_place));
+ CHECK(*util::read_file<std::string>("test2") == "bar");
+ }
+
+ SUBCASE("WriteFileMode::exclusive")
+ {
+ auto result =
+ util::write_file("test", "bar", util::WriteFileMode::exclusive);
+ CHECK(result.error() == "File exists");
+ CHECK(util::write_file("test3", "bar", util::WriteFileMode::exclusive));
+ CHECK(*util::read_file<std::string>("test3") == "bar");
+ }
+}
+#endif
+
TEST_CASE("util::traverse_directory")
{
TestContext test_context;