The Battle for Wesnoth  1.13.10+dev
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
fs_commit.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2016 - 2017 by Ignacio R. Morelle <shadowm2006@gmail.com>
3  Part of the Battle for Wesnoth Project http://www.wesnoth.org/
4 
5  This program is free software; you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published by
7  the Free Software Foundation; either version 2 of the License, or
8  (at your option) any later version.
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY.
11 
12  See the COPYING file for more details.
13 */
14 
16 
17 #include "log.hpp"
18 #include "serialization/parser.hpp"
19 
20 #include <cerrno>
21 #include <cstdio>
22 #include <cstring>
23 
24 #ifndef _WIN32
25 #include <unistd.h>
26 #include <boost/iostreams/device/file_descriptor.hpp>
27 #include <boost/iostreams/stream.hpp>
28 #endif
29 
30 static lg::log_domain log_filesystem("filesystem");
31 
32 #define DBG_FS LOG_STREAM(debug, log_filesystem)
33 #define LOG_FS LOG_STREAM(info, log_filesystem)
34 #define WRN_FS LOG_STREAM(warn, log_filesystem)
35 #define ERR_FS LOG_STREAM(err, log_filesystem)
36 
37 namespace filesystem
38 {
39 
40 namespace
41 {
42 
43 inline void atomic_fail(const std::string& step_description)
44 {
45  const std::string errno_desc = std::strerror(errno);
46  ERR_FS << "Atomic commit failed (" << step_description << "): "
47  << errno_desc << '\n';
48  throw filesystem::io_exception(std::string("Atomic commit failed (") + step_description + ")");
49 }
50 
51 #ifndef _WIN32
52 
53 /**
54  * Returns the POSIX file descriptor associated with the stream.
55  *
56  * This only makes sense for valid streams returned by ostream_file(). Anything
57  * else will yield 0.
58  */
59 int get_stream_file_descriptor(std::ostream& os)
60 {
61  // NOTE: This is insider knowledge of filesystem::ostream_file(), but it will
62  // do for 1.12 at least.
63  typedef boost::iostreams::stream<boost::iostreams::file_descriptor_sink> fd_stream_type;
64  fd_stream_type* const real = dynamic_cast<fd_stream_type*>(&os);
65  return real ? (*real)->handle() : 0;
66 }
67 
68 #endif // ! _WIN32
69 
70 } // unnamed namespace
71 
73  : temp_name_(filename + ".new")
74  , dest_name_(filename)
75  , out_(filesystem::ostream_file(temp_name_))
76 #ifndef _WIN32
77  , outfd_(filesystem::get_stream_file_descriptor(*out_))
78 #endif
79 {
80  LOG_FS << "Atomic write guard created for " << dest_name_ << " using " << temp_name_ << '\n';
81 }
82 
84 {
85  if(!temp_name_.empty()) {
86  ERR_FS << "Temporary file for atomic write leaked: " << temp_name_ << '\n';
87  }
88 }
89 
91 {
92  if(temp_name_.empty()) {
93  ERR_FS << "Attempted to commit " << dest_name_ << " more than once!\n";
94  return;
95  }
96 
97 #ifdef _WIN32
98  // WARNING:
99  // Obviously not atomic at all. Perhaps there's an alternate way to achieve
100  // the same more securely using the Win32 API, but I don't think anyone
101  // cares about running campaignd on this platform, let alone making it
102  // resilient against environment errors. This is just here for reference.
104  atomic_fail("remove");
105  }
106 #else
107  if(fsync(outfd_) != 0) {
108  atomic_fail("fsync");
109  }
110 #endif
111 
112  if(std::rename(temp_name_.c_str(), dest_name_.c_str()) != 0) {
113  atomic_fail("rename");
114  }
115 
116  LOG_FS << "Atomic commit succeeded: " << temp_name_ << " -> " << dest_name_ << '\n';
117 
118  temp_name_.clear();
119 }
120 
121 } // namespace filesystem
void remove()
Removes a tip.
Definition: tooltip.cpp:189
std::vector< char_t > string
#define LOG_FS
Definition: fs_commit.cpp:33
atomic_commit(const std::string &filename)
Constructor.
Definition: fs_commit.cpp:72
void commit()
Commits the new file contents to disk atomically.
Definition: fs_commit.cpp:90
filesystem::scoped_ostream ostream_file(const std::string &fname, bool create_directory=true)
static lg::log_domain log_filesystem("filesystem")
Atomic filesystem commit functions.
An exception object used when an IO error occurs.
Definition: filesystem.hpp:46
#define ERR_FS
Definition: fs_commit.cpp:35
Standard logging facilities (interface).
bool file_exists(const std::string &name)
Returns true if a file or directory with such name already exists.