24 #include <boost/iostreams/device/file_descriptor.hpp> 25 #include <boost/iostreams/stream.hpp> 36 #include <boost/system/error_code.hpp> 37 #include <boost/filesystem.hpp> 39 #define WIN32_LEAN_AND_MEAN 46 #define DBG_FS LOG_STREAM(debug, log_filesystem) 47 #define LOG_FS LOG_STREAM(info, log_filesystem) 48 #define WRN_FS LOG_STREAM(warn, log_filesystem) 49 #define ERR_FS LOG_STREAM(err, log_filesystem) 56 namespace biostreams = boost::iostreams;
61 using sink_type = biostreams::file_descriptor_sink;
62 using stream_type = biostreams::stream<sink_type>;
63 using platform_file_handle_type = sink_type::handle_type;
65 const platform_file_handle_type INVALID_FILE_HANDLE =
73 inline void atomic_fail(
const std::string& step_description)
75 const std::string errno_desc = std::strerror(errno);
76 ERR_FS <<
"Atomic commit failed (" << step_description <<
"): " << errno_desc <<
'\n';
87 platform_file_handle_type get_stream_file_descriptor(std::ostream& os)
89 stream_type*
const real =
dynamic_cast<stream_type*
>(&os);
90 return real ? (*real)->handle() : INVALID_FILE_HANDLE;
104 LOG_FS <<
"streaming " << fname <<
" for writing with delete access.\n";
106 namespace bfs = boost::filesystem;
110 HANDLE file = CreateFileW(w_name.c_str(),
111 GENERIC_WRITE | DELETE,
112 FILE_SHARE_WRITE | FILE_SHARE_DELETE,
115 FILE_ATTRIBUTE_NORMAL,
118 if(file == INVALID_HANDLE_VALUE) {
119 throw BOOST_IOSTREAMS_FAILURE(
formatter() <<
"CreateFile() failed: " << GetLastError());
123 sink_type fd{file, biostreams::close_handle};
124 return std::make_unique<stream_type>(fd, 4096, 0);
125 }
catch(
const BOOST_IOSTREAMS_FAILURE&
e) {
127 boost::system::error_code ec_unused;
128 if(bfs::create_directories(
bfs::path{fname}.parent_path(), ec_unused)) {
129 return ostream_file_with_delete(fname);
145 bool rename_open_file(
const std::string& new_name, HANDLE open_handle)
147 if(open_handle == INVALID_HANDLE_VALUE) {
148 ERR_FS <<
"replace_open_file(): Bad handle\n";
152 const auto& w_name =
unicode_cast<std::wstring>(new_name);
153 const std::size_t buf_size = w_name.length()*
sizeof(wchar_t) +
sizeof(FILE_RENAME_INFO);
157 std::unique_ptr<BYTE[]> fileinfo_buf{
new BYTE[buf_size]};
158 FILE_RENAME_INFO& fri = *
reinterpret_cast<FILE_RENAME_INFO*
>(fileinfo_buf.get());
160 SecureZeroMemory(fileinfo_buf.get(), buf_size);
161 fri.ReplaceIfExists = TRUE;
162 fri.RootDirectory =
nullptr;
163 fri.FileNameLength =
static_cast<DWORD
>(w_name.length());
164 ::wmemcpy(fri.FileName, w_name.c_str(), w_name.length());
168 if(!SetFileInformationByHandle(open_handle,
171 static_cast<DWORD
>(buf_size)))
173 ERR_FS <<
"replace_open_file(): SetFileInformationByHandle() " << GetLastError() <<
'\n';
180 #endif // !defined(_WIN32) 185 : temp_name_(filename +
".new")
186 , dest_name_(filename)
189 , outfd_(
filesystem::get_stream_file_descriptor(*out_))
191 , out_(
filesystem::ostream_file_with_delete(temp_name_))
192 , handle_(
filesystem::get_stream_file_descriptor(*out_))
214 atomic_fail(
"rename");
218 atomic_fail(
"fsync");
222 atomic_fail(
"rename");
atomic_commit(const std::string &filename)
Constructor.
ucs4_convert_impl::enableif< TD, typename TS::value_type >::type unicode_cast(const TS &source)
void commit()
Commits the new file contents to disk atomically.
std::unique_ptr< std::ostream > scoped_ostream
static lg::log_domain log_filesystem("filesystem")
Atomic filesystem commit functions.
An exception object used when an IO error occurs.
Standard logging facilities (interface).
filesystem::scoped_ostream ostream_file(const std::string &fname, bool create_directory)