1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123
|
/* Icinga 2 | (c) 2022 Icinga GmbH | GPLv2+ */
#include "base/atomic-file.hpp"
#include "base/exception.hpp"
#include "base/utility.hpp"
#include <utility>
#ifdef _WIN32
# include <io.h>
# include <windows.h>
#else /* _WIN32 */
# include <errno.h>
# include <unistd.h>
#endif /* _WIN32 */
using namespace icinga;
void AtomicFile::Write(String path, int mode, const String& content)
{
AtomicFile af (path, mode);
af << content;
af.Commit();
}
AtomicFile::AtomicFile(String path, int mode) : m_Path(std::move(path))
{
m_TempFilename = m_Path + ".tmp.XXXXXX";
#ifdef _WIN32
m_Fd = Utility::MksTemp(&m_TempFilename[0]);
#else /* _WIN32 */
m_Fd = mkstemp(&m_TempFilename[0]);
#endif /* _WIN32 */
if (m_Fd < 0) {
auto error (errno);
BOOST_THROW_EXCEPTION(posix_error()
<< boost::errinfo_api_function("mkstemp")
<< boost::errinfo_errno(error)
<< boost::errinfo_file_name(m_TempFilename));
}
try {
exceptions(failbit | badbit);
open(boost::iostreams::file_descriptor(
m_Fd,
// Rationale: https://github.com/boostorg/iostreams/issues/152
boost::iostreams::never_close_handle
));
if (chmod(m_TempFilename.CStr(), mode) < 0) {
auto error (errno);
BOOST_THROW_EXCEPTION(posix_error()
<< boost::errinfo_api_function("chmod")
<< boost::errinfo_errno(error)
<< boost::errinfo_file_name(m_TempFilename));
}
} catch (...) {
if (is_open()) {
close();
}
(void)::close(m_Fd);
(void)unlink(m_TempFilename.CStr());
throw;
}
}
AtomicFile::~AtomicFile()
{
if (is_open()) {
try {
close();
} catch (...) {
// Destructor must not throw
}
}
if (m_Fd >= 0) {
(void)::close(m_Fd);
}
if (!m_TempFilename.IsEmpty()) {
(void)unlink(m_TempFilename.CStr());
}
}
void AtomicFile::Commit()
{
flush();
auto h ((*this)->handle());
#ifdef _WIN32
if (!FlushFileBuffers(h)) {
auto err (GetLastError());
BOOST_THROW_EXCEPTION(win32_error()
<< boost::errinfo_api_function("FlushFileBuffers")
<< errinfo_win32_error(err)
<< boost::errinfo_file_name(m_TempFilename));
}
#else /* _WIN32 */
if (fsync(h)) {
auto err (errno);
BOOST_THROW_EXCEPTION(posix_error()
<< boost::errinfo_api_function("fsync")
<< boost::errinfo_errno(err)
<< boost::errinfo_file_name(m_TempFilename));
}
#endif /* _WIN32 */
close();
(void)::close(m_Fd);
m_Fd = -1;
Utility::RenameFile(m_TempFilename, m_Path);
m_TempFilename = "";
}
|