25 #include <boost/iostreams/device/file_descriptor.hpp>
26 #include <boost/iostreams/stream.hpp>
37 #include <boost/system/error_code.hpp>
38 #include <boost/filesystem.hpp>
40 #define WIN32_LEAN_AND_MEAN
47 #define DBG_FS LOG_STREAM(debug, log_filesystem)
48 #define LOG_FS LOG_STREAM(info, log_filesystem)
49 #define WRN_FS LOG_STREAM(warn, log_filesystem)
50 #define ERR_FS LOG_STREAM(err, log_filesystem)
57 namespace biostreams = boost::iostreams;
62 using sink_type = biostreams::file_descriptor_sink;
63 using stream_type = biostreams::stream<sink_type>;
64 using platform_file_handle_type = sink_type::handle_type;
66 const platform_file_handle_type INVALID_FILE_HANDLE =
74 inline void atomic_fail(
const std::string& step_description)
76 const std::string errno_desc = std::strerror(errno);
77 ERR_FS <<
"Atomic commit failed (" << step_description <<
"): " << errno_desc;
88 platform_file_handle_type get_stream_file_descriptor(std::ostream& os)
90 stream_type*
const real =
dynamic_cast<stream_type*
>(&os);
91 return real ? (*real)->handle() : INVALID_FILE_HANDLE;
105 LOG_FS <<
"streaming " << fname <<
" for writing with delete access.";
107 namespace bfs = boost::filesystem;
108 const auto& w_name = unicode_cast<std::wstring>(fname);
111 HANDLE file = CreateFileW(w_name.c_str(),
112 GENERIC_WRITE | DELETE,
113 FILE_SHARE_WRITE | FILE_SHARE_DELETE,
116 FILE_ATTRIBUTE_NORMAL,
119 if(file == INVALID_HANDLE_VALUE) {
120 throw BOOST_IOSTREAMS_FAILURE(
formatter() <<
"CreateFile() failed: " << GetLastError());
124 sink_type fd{file, biostreams::close_handle};
125 return std::make_unique<stream_type>(fd, 4096, 0);
126 }
catch(
const BOOST_IOSTREAMS_FAILURE&
e) {
128 boost::system::error_code ec_unused;
129 if(bfs::create_directories(
bfs::path{fname}.parent_path(), ec_unused)) {
130 return ostream_file_with_delete(fname);
146 bool rename_open_file(
const std::string& new_name, HANDLE open_handle)
148 if(open_handle == INVALID_HANDLE_VALUE) {
149 ERR_FS <<
"replace_open_file(): Bad handle";
153 const auto& w_name = unicode_cast<std::wstring>(new_name);
154 const std::size_t buf_size = w_name.length()*
sizeof(wchar_t) +
sizeof(FILE_RENAME_INFO);
158 std::unique_ptr<BYTE[]> fileinfo_buf{
new BYTE[buf_size]};
159 FILE_RENAME_INFO& fri = *
reinterpret_cast<FILE_RENAME_INFO*
>(fileinfo_buf.get());
161 SecureZeroMemory(fileinfo_buf.get(), buf_size);
162 fri.ReplaceIfExists = TRUE;
163 fri.RootDirectory =
nullptr;
164 fri.FileNameLength =
static_cast<DWORD
>(w_name.length());
165 ::wmemcpy(fri.FileName, w_name.c_str(), w_name.length());
169 if(!SetFileInformationByHandle(open_handle,
172 static_cast<DWORD
>(buf_size)))
174 ERR_FS <<
"replace_open_file(): SetFileInformationByHandle() " << GetLastError();
190 , outfd_(
filesystem::get_stream_file_descriptor(*out_))
192 , out_(
filesystem::ostream_file_with_delete(temp_name_))
193 , handle_(
filesystem::get_stream_file_descriptor(*out_))
215 atomic_fail(
"rename");
219 atomic_fail(
"fsync");
223 atomic_fail(
"rename");
void commit()
Commits the new file contents to disk atomically.
atomic_commit(const std::string &filename)
Constructor.
static lg::log_domain log_filesystem("filesystem")
Atomic filesystem commit functions.
Standard logging facilities (interface).
filesystem::scoped_ostream ostream_file(const std::string &fname, std::ios_base::openmode mode, bool create_directory)
std::unique_ptr< std::ostream > scoped_ostream
std::string filename
Filename.
An exception object used when an IO error occurs.