]> git.ipfire.org Git - thirdparty/ccache.git/commitdiff
Add AtomicFile class for updating a file with rename-in-place
authorJoel Rosdahl <joel@rosdahl.net>
Wed, 7 Aug 2019 17:48:49 +0000 (19:48 +0200)
committerJoel Rosdahl <joel@rosdahl.net>
Wed, 14 Aug 2019 19:42:34 +0000 (21:42 +0200)
Makefile.in
configure.ac
dev.mk.in
src/AtomicFile.cpp [new file with mode: 0644]
src/AtomicFile.hpp [new file with mode: 0644]
src/Error.hpp [new file with mode: 0644]

index 07dc40f7e66648eb27058b163770491abe2e856a..763d8d780487c723e036b39d2b5e37e008fd37d1 100644 (file)
@@ -31,6 +31,7 @@ quiet := $(v_at_$(V))
 Q=$(if $(quiet),@)
 
 non_third_party_sources = \
+    src/AtomicFile.cpp \
     src/args.cpp \
     src/ccache.cpp \
     src/cleanup.cpp \
index faca5597c150cb4a1f746483f2a8f25186564bcf..049f44684f136a26e1edb69933845386e3523bbe 100644 (file)
@@ -67,6 +67,7 @@ if test "$ac_compiler_clang" = yes; then
     more_warnings="$more_warnings -Wno-shorten-64-to-32"
     more_warnings="$more_warnings -Wno-sign-conversion"
     more_warnings="$more_warnings -Wno-undefined-func-template"
+    more_warnings="$more_warnings -Wno-weak-vtables"
 
 # TODO: Enable these in the future:
     more_warnings="$more_warnings -Wno-old-style-cast"
index 71c6875981712f7c658a9f5854f2224bd8dd74f9..8537f68b730f0c0e2c4797f9ca4f9b697cf052b0 100644 (file)
--- a/dev.mk.in
+++ b/dev.mk.in
@@ -36,6 +36,8 @@ generated_docs = \
 built_dist_files = $(generated_sources) $(generated_docs)
 
 non_third_party_headers = \
+    src/AtomicFile.hpp \
+    src/Error.hpp \
     src/ccache.hpp \
     src/common_header.hpp \
     src/compopt.hpp \
diff --git a/src/AtomicFile.cpp b/src/AtomicFile.cpp
new file mode 100644 (file)
index 0000000..994b8c1
--- /dev/null
@@ -0,0 +1,80 @@
+// Copyright (C) 2019 Joel Rosdahl and other contributors
+//
+// See doc/AUTHORS.adoc for a complete list of contributors.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3 of the License, or (at your option)
+// any later version.
+//
+// This program is distributed in the hope that it will be useful, but WITHOUT
+// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+// more details.
+//
+// You should have received a copy of the GNU General Public License along with
+// this program; if not, write to the Free Software Foundation, Inc., 51
+// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+#include "AtomicFile.hpp"
+
+#include "Error.hpp"
+#include "ccache.hpp"
+
+#include <cerrno>
+#include <fmt/core.h>
+#include <unistd.h>
+
+AtomicFile::AtomicFile(const std::string& path, Mode mode) : m_path(path)
+{
+  char* tmp_path = x_strdup(path.c_str());
+  int fd = create_tmp_fd(&tmp_path);
+  m_tmp_path = tmp_path;
+  m_stream.open(tmp_path,
+                mode == Mode::Binary ? std::ios::out | std::ios::binary
+                                     : std::ios::out);
+  free(tmp_path);
+  ::close(fd);
+
+  if (!m_stream) {
+    throw Error(fmt::format("failed to create {}: {}", path, strerror(errno)));
+  }
+}
+
+AtomicFile::~AtomicFile()
+{
+  if (m_stream.is_open()) {
+    // close() was not called so remove the lingering temporary file.
+    m_stream.close();
+    tmp_unlink(m_tmp_path.c_str());
+  }
+}
+
+void
+AtomicFile::write(const std::string& data)
+{
+  m_stream.write(data.data(), data.size());
+  if (!m_stream) {
+    throw Error(
+      fmt::format("failed to write data to {}: {}", m_path, strerror(errno)));
+  }
+}
+
+void
+AtomicFile::write(const std::vector<uint8_t>& data)
+{
+  m_stream.write(reinterpret_cast<const char*>(data.data()), data.size());
+  if (!m_stream) {
+    throw Error(
+      fmt::format("failed to write data to {}: {}", m_path, strerror(errno)));
+  }
+}
+
+void
+AtomicFile::close()
+{
+  m_stream.close();
+  if (x_rename(m_tmp_path.c_str(), m_path.c_str()) != 0) {
+    throw Error(fmt::format("failed to rename {} to {}", m_tmp_path, m_path));
+  }
+}
diff --git a/src/AtomicFile.hpp b/src/AtomicFile.hpp
new file mode 100644 (file)
index 0000000..68c09a3
--- /dev/null
@@ -0,0 +1,49 @@
+// Copyright (C) 2019 Joel Rosdahl and other contributors
+//
+// See doc/AUTHORS.adoc for a complete list of contributors.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3 of the License, or (at your option)
+// any later version.
+//
+// This program is distributed in the hope that it will be useful, but WITHOUT
+// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+// more details.
+//
+// You should have received a copy of the GNU General Public License along with
+// this program; if not, write to the Free Software Foundation, Inc., 51
+// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+#pragma once
+
+#include "system.hpp"
+
+#include <fstream>
+#include <string>
+#include <vector>
+
+// This class represents a file whose data will be atomically written to a path
+// by renaming a temporary file in place.
+class AtomicFile
+{
+public:
+  enum class Mode { Binary, Text };
+
+  AtomicFile(const std::string& destination_path, Mode mode);
+  ~AtomicFile();
+
+  void write(const std::string& data);
+  void write(const std::vector<uint8_t>& data);
+
+  // Close the temporary file and rename it to the destination file. Note: The
+  // destructor will not do this automatically to avoid half-written data in the
+  // file.
+  void close();
+
+private:
+  const std::string m_path;
+  std::string m_tmp_path;
+  std::ofstream m_stream;
+};
diff --git a/src/Error.hpp b/src/Error.hpp
new file mode 100644 (file)
index 0000000..3d622f8
--- /dev/null
@@ -0,0 +1,26 @@
+// Copyright (C) 2019 Joel Rosdahl and other contributors
+//
+// See doc/AUTHORS.adoc for a complete list of contributors.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3 of the License, or (at your option)
+// any later version.
+//
+// This program is distributed in the hope that it will be useful, but WITHOUT
+// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+// more details.
+//
+// You should have received a copy of the GNU General Public License along with
+// this program; if not, write to the Free Software Foundation, Inc., 51
+// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+#pragma once
+
+#include <stdexcept>
+
+class Error : public std::runtime_error
+{
+  using std::runtime_error::runtime_error;
+};