]> git.ipfire.org Git - thirdparty/ccache.git/commitdiff
fix: Unlink destination before opening in util::write_file
authorJoel Rosdahl <joel@rosdahl.net>
Mon, 5 Sep 2022 18:22:00 +0000 (20:22 +0200)
committerJoel Rosdahl <joel@rosdahl.net>
Tue, 6 Sep 2022 07:12:38 +0000 (09:12 +0200)
This allows for writing to a file that already exists with read-only
permissions.

src/util/file.cpp
src/util/file.hpp
unittest/test_Stat.cpp

index b933e3d28eba4af8470b0d34d627270b1210c3c2..7b2b27c906b319a47a75e5b6378cc364b851ee08 100644 (file)
@@ -236,8 +236,11 @@ write_fd(int fd, const void* data, size_t size)
 }
 
 nonstd::expected<void, std::string>
-write_file(const std::string& path, std::string_view data)
+write_file(const std::string& path, std::string_view data, InPlace in_place)
 {
+  if (in_place == InPlace::no) {
+    unlink(path.c_str());
+  }
   Fd fd(open(path.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_TEXT, 0666));
   if (!fd) {
     return nonstd::make_unexpected(strerror(errno));
@@ -246,8 +249,13 @@ write_file(const std::string& path, std::string_view data)
 }
 
 nonstd::expected<void, std::string>
-write_file(const std::string& path, nonstd::span<const uint8_t> data)
+write_file(const std::string& path,
+           nonstd::span<const uint8_t> data,
+           InPlace in_place)
 {
+  if (in_place == InPlace::no) {
+    unlink(path.c_str());
+  }
   Fd fd(open(path.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0666));
   if (!fd) {
     return nonstd::make_unexpected(strerror(errno));
index 641cdf671bb7b666cccebb0af3bc542179ca31e6..b766b5570d5737d3fa62c2596eb5885b71e9d010 100644 (file)
@@ -32,6 +32,8 @@ namespace util {
 
 // --- Interface ---
 
+enum class InPlace { yes, no };
+
 void create_cachedir_tag(const std::string& dir);
 
 // Read data from `fd` until end of file and call `data_receiver` with the read
@@ -57,12 +59,16 @@ void set_timestamps(const std::string& path,
 nonstd::expected<void, std::string>
 write_fd(int fd, const void* data, size_t size);
 
-// Write text `data` to `path`.
+// Write text `data` to `path`. If `in_place` is no, unlink any existing file
+// first (i.e., break hard links).
 nonstd::expected<void, std::string> write_file(const std::string& path,
-                                               std::string_view data);
+                                               std::string_view data,
+                                               InPlace in_place = InPlace::no);
 
-// Write binary `data` to `path`.
-nonstd::expected<void, std::string>
-write_file(const std::string& path, nonstd::span<const uint8_t> data);
+// Write binary `data` to `path`. If `in_place` is no, unlink any existing
+// file first (i.e., break hard links).
+nonstd::expected<void, std::string> write_file(const std::string& path,
+                                               nonstd::span<const uint8_t> data,
+                                               InPlace in_place = InPlace::no);
 
 } // namespace util
index 09e4131802bf1d0c9944e7c039be4e928b1dd9e6..688656040a6c13cdebb4897ccc475d6f27165445 100644 (file)
@@ -195,7 +195,7 @@ TEST_CASE("Same i-node as")
   CHECK(a_stat.same_inode_as(a_stat));
   CHECK(!a_stat.same_inode_as(b_stat));
 
-  util::write_file("a", "change size");
+  util::write_file("a", "change size", util::InPlace::yes);
   auto new_a_stat = Stat::stat("a");
   CHECK(new_a_stat.same_inode_as(a_stat));
 
@@ -442,7 +442,7 @@ TEST_CASE("Hard links")
   CHECK(stat_a.inode() == stat_b.inode());
   CHECK(stat_a.same_inode_as(stat_b));
 
-  util::write_file("a", "1234567");
+  util::write_file("a", "1234567", util::InPlace::yes);
   stat_a = Stat::stat("a");
   stat_b = Stat::stat("b");