Bitcoin Core  22.99.0
P2P Digital Currency
fs.h
Go to the documentation of this file.
1 // Copyright (c) 2017-2021 The Bitcoin Core developers
2 // Distributed under the MIT software license, see the accompanying
3 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
4 
5 #ifndef BITCOIN_FS_H
6 #define BITCOIN_FS_H
7 
8 #include <stdio.h>
9 #include <string>
10 #if defined WIN32 && defined __GLIBCXX__
11 #include <ext/stdio_filebuf.h>
12 #endif
13 
14 #include <boost/filesystem.hpp>
15 #include <boost/filesystem/fstream.hpp>
16 #include <tinyformat.h>
17 
19 namespace fs {
20 
21 using namespace boost::filesystem;
22 
33 class path : public boost::filesystem::path
34 {
35 public:
36  using boost::filesystem::path::path;
37 
38  // Allow path objects arguments for compatibility.
39  path(boost::filesystem::path path) : boost::filesystem::path::path(std::move(path)) {}
40  path& operator=(boost::filesystem::path path) { boost::filesystem::path::operator=(std::move(path)); return *this; }
41  path& operator/=(boost::filesystem::path path) { boost::filesystem::path::operator/=(std::move(path)); return *this; }
42 
43  // Allow literal string arguments, which are safe as long as the literals are ASCII.
44  path(const char* c) : boost::filesystem::path(c) {}
45  path& operator=(const char* c) { boost::filesystem::path::operator=(c); return *this; }
46  path& operator/=(const char* c) { boost::filesystem::path::operator/=(c); return *this; }
47  path& append(const char* c) { boost::filesystem::path::append(c); return *this; }
48 
49  // Disallow std::string arguments to avoid locale-dependent decoding on windows.
50  path(std::string) = delete;
51  path& operator=(std::string) = delete;
52  path& operator/=(std::string) = delete;
53  path& append(std::string) = delete;
54 
55  // Disallow std::string conversion method to avoid locale-dependent encoding on windows.
56  std::string string() const = delete;
57 
58  // Define UTF-8 string conversion method not present in boost::filesystem but present in std::filesystem.
59  std::string u8string() const { return boost::filesystem::path::string(); }
60 };
61 
62 // Define UTF-8 string conversion function not present in boost::filesystem but present in std::filesystem.
63 static inline path u8path(const std::string& string)
64 {
65  return boost::filesystem::path(string);
66 }
67 
68 // Disallow implicit std::string conversion for system_complete to avoid
69 // locale-dependent encoding on windows.
70 static inline path system_complete(const path& p)
71 {
73 }
74 
75 // Disallow implicit std::string conversion for exists to avoid
76 // locale-dependent encoding on windows.
77 static inline bool exists(const path& p)
78 {
79  return boost::filesystem::exists(p);
80 }
81 
82 // Allow explicit quoted stream I/O.
83 static inline auto quoted(const std::string& s)
84 {
85  return boost::io::quoted(s, '&');
86 }
87 
88 // Allow safe path append operations.
89 static inline path operator+(path p1, path p2)
90 {
91  p1 += static_cast<boost::filesystem::path&&>(p2);
92  return p1;
93 }
94 
95 // Disallow implicit std::string conversion for copy_file
96 // to avoid locale-dependent encoding on Windows.
97 static inline void copy_file(const path& from, const path& to, copy_option options)
98 {
99  boost::filesystem::copy_file(from, to, options);
100 }
101 
120 static inline std::string PathToString(const path& path)
121 {
122  // Implementation note: On Windows, the std::filesystem::path(string)
123  // constructor and std::filesystem::path::string() method are not safe to
124  // use here, because these methods encode the path using C++'s narrow
125  // multibyte encoding, which on Windows corresponds to the current "code
126  // page", which is unpredictable and typically not able to represent all
127  // valid paths. So std::filesystem::path::u8string() and
128  // std::filesystem::u8path() functions are used instead on Windows. On
129  // POSIX, u8string/u8path functions are not safe to use because paths are
130  // not always valid UTF-8, so plain string methods which do not transform
131  // the path there are used.
132 #ifdef WIN32
133  return path.u8string();
134 #else
135  static_assert(std::is_same<path::string_type, std::string>::value, "PathToString not implemented on this platform");
136  return path.boost::filesystem::path::string();
137 #endif
138 }
139 
143 static inline path PathFromString(const std::string& string)
144 {
145 #ifdef WIN32
146  return u8path(string);
147 #else
148  return boost::filesystem::path(string);
149 #endif
150 }
151 } // namespace fs
152 
154 namespace fsbridge {
155  FILE *fopen(const fs::path& p, const char *mode);
156 
166  fs::path AbsPathJoin(const fs::path& base, const fs::path& path);
167 
168  class FileLock
169  {
170  public:
171  FileLock() = delete;
172  FileLock(const FileLock&) = delete;
173  FileLock(FileLock&&) = delete;
174  explicit FileLock(const fs::path& file);
175  ~FileLock();
176  bool TryLock();
177  std::string GetReason() { return reason; }
178 
179  private:
180  std::string reason;
181 #ifndef WIN32
182  int fd = -1;
183 #else
184  void* hFile = (void*)-1; // INVALID_HANDLE_VALUE
185 #endif
186  };
187 
188  std::string get_filesystem_error_message(const fs::filesystem_error& e);
189 
190  // GNU libstdc++ specific workaround for opening UTF-8 paths on Windows.
191  //
192  // On Windows, it is only possible to reliably access multibyte file paths through
193  // `wchar_t` APIs, not `char` APIs. But because the C++ standard doesn't
194  // require ifstream/ofstream `wchar_t` constructors, and the GNU library doesn't
195  // provide them (in contrast to the Microsoft C++ library, see
196  // https://stackoverflow.com/questions/821873/how-to-open-an-stdfstream-ofstream-or-ifstream-with-a-unicode-filename/822032#822032),
197  // Boost is forced to fall back to `char` constructors which may not work properly.
198  //
199  // Work around this issue by creating stream objects with `_wfopen` in
200  // combination with `__gnu_cxx::stdio_filebuf`. This workaround can be removed
201  // with an upgrade to C++17, where streams can be constructed directly from
202  // `std::filesystem::path` objects.
203 
204 #if defined WIN32 && defined __GLIBCXX__
205  class ifstream : public std::istream
206  {
207  public:
208  ifstream() = default;
209  explicit ifstream(const fs::path& p, std::ios_base::openmode mode = std::ios_base::in) { open(p, mode); }
210  ~ifstream() { close(); }
211  void open(const fs::path& p, std::ios_base::openmode mode = std::ios_base::in);
212  bool is_open() { return m_filebuf.is_open(); }
213  void close();
214 
215  private:
216  __gnu_cxx::stdio_filebuf<char> m_filebuf;
217  FILE* m_file = nullptr;
218  };
219  class ofstream : public std::ostream
220  {
221  public:
222  ofstream() = default;
223  explicit ofstream(const fs::path& p, std::ios_base::openmode mode = std::ios_base::out) { open(p, mode); }
224  ~ofstream() { close(); }
225  void open(const fs::path& p, std::ios_base::openmode mode = std::ios_base::out);
226  bool is_open() { return m_filebuf.is_open(); }
227  void close();
228 
229  private:
230  __gnu_cxx::stdio_filebuf<char> m_filebuf;
231  FILE* m_file = nullptr;
232  };
233 #else // !(WIN32 && __GLIBCXX__)
236 #endif // WIN32 && __GLIBCXX__
237 };
238 
239 // Disallow path operator<< formatting in tinyformat to avoid locale-dependent
240 // encoding on windows.
241 namespace tinyformat {
242 template<> inline void formatValue(std::ostream&, const char*, const char*, int, const boost::filesystem::path&) = delete;
243 template<> inline void formatValue(std::ostream&, const char*, const char*, int, const fs::path&) = delete;
244 } // namespace tinyformat
245 
246 #endif // BITCOIN_FS_H
tinyformat
Definition: fs.h:241
fs::exists
static bool exists(const path &p)
Definition: fs.h:77
fsbridge::ifstream
fs::ifstream ifstream
Definition: fs.h:234
fsbridge::fopen
FILE * fopen(const fs::path &p, const char *mode)
Definition: fs.cpp:25
fsbridge::get_filesystem_error_message
std::string get_filesystem_error_message(const fs::filesystem_error &e)
Definition: fs.cpp:138
fs::quoted
static auto quoted(const std::string &s)
Definition: fs.h:83
fsbridge::AbsPathJoin
fs::path AbsPathJoin(const fs::path &base, const fs::path &path)
Helper function for joining two paths.
Definition: fs.cpp:35
fs::PathToString
static std::string PathToString(const path &path)
Convert path object to byte string.
Definition: fs.h:120
fsbridge::FileLock::GetReason
std::string GetReason()
Definition: fs.h:177
fs::path::append
path & append(const char *c)
Definition: fs.h:47
fs::path::operator/=
path & operator/=(const char *c)
Definition: fs.h:46
tinyformat.h
fs::path::operator=
path & operator=(boost::filesystem::path path)
Definition: fs.h:40
fsbridge::ofstream
fs::ofstream ofstream
Definition: fs.h:235
fs::path
Path class wrapper to prepare application code for transition from boost::filesystem library to std::...
Definition: fs.h:33
fs::copy_file
static void copy_file(const path &from, const path &to, copy_option options)
Definition: fs.h:97
fs::path::operator=
path & operator=(const char *c)
Definition: fs.h:45
fs::PathFromString
static path PathFromString(const std::string &string)
Convert byte string to path object.
Definition: fs.h:143
fs::path::path
path(boost::filesystem::path path)
Definition: fs.h:39
fsbridge::FileLock
Definition: fs.h:168
fs::path::operator/=
path & operator/=(boost::filesystem::path path)
Definition: fs.h:41
fs::path::u8string
std::string u8string() const
Definition: fs.h:59
fs::system_complete
static path system_complete(const path &p)
Definition: fs.h:70
fs::u8path
static path u8path(const std::string &string)
Definition: fs.h:63
fs
Filesystem operations and types.
Definition: fs.h:19
std
Definition: setup_common.h:33
tinyformat::formatValue
void formatValue(std::ostream &, const char *, const char *, int, const fs::path &)=delete
fs::operator+
static path operator+(path p1, path p2)
Definition: fs.h:89
fsbridge::FileLock::reason
std::string reason
Definition: fs.h:180
fsbridge
Bridge operations to C stdio.
Definition: fs.cpp:23
fs::path::path
path(const char *c)
Definition: fs.h:44