Bitcoin Core  21.99.0
P2P Digital Currency
fs.cpp
Go to the documentation of this file.
1 // Copyright (c) 2017-2020 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 #include <fs.h>
6 
7 #ifndef WIN32
8 #include <cstring>
9 #include <fcntl.h>
10 #include <string>
11 #include <sys/file.h>
12 #include <sys/utsname.h>
13 #include <unistd.h>
14 #else
15 #ifndef NOMINMAX
16 #define NOMINMAX
17 #endif
18 #include <codecvt>
19 #include <windows.h>
20 #endif
21 
22 namespace fsbridge {
23 
24 FILE *fopen(const fs::path& p, const char *mode)
25 {
26 #ifndef WIN32
27  return ::fopen(p.string().c_str(), mode);
28 #else
29  std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>,wchar_t> utf8_cvt;
30  return ::_wfopen(p.wstring().c_str(), utf8_cvt.from_bytes(mode).c_str());
31 #endif
32 }
33 
34 fs::path AbsPathJoin(const fs::path& base, const fs::path& path)
35 {
36  assert(base.is_absolute());
37  return fs::absolute(path, base);
38 }
39 
40 #ifndef WIN32
41 
42 static std::string GetErrorReason()
43 {
44  return std::strerror(errno);
45 }
46 
47 FileLock::FileLock(const fs::path& file)
48 {
49  fd = open(file.string().c_str(), O_RDWR);
50  if (fd == -1) {
52  }
53 }
54 
56 {
57  if (fd != -1) {
58  close(fd);
59  }
60 }
61 
62 static bool IsWSL()
63 {
64  struct utsname uname_data;
65  return uname(&uname_data) == 0 && std::string(uname_data.version).find("Microsoft") != std::string::npos;
66 }
67 
69 {
70  if (fd == -1) {
71  return false;
72  }
73 
74  // Exclusive file locking is broken on WSL using fcntl (issue #18622)
75  // This workaround can be removed once the bug on WSL is fixed
76  static const bool is_wsl = IsWSL();
77  if (is_wsl) {
78  if (flock(fd, LOCK_EX | LOCK_NB) == -1) {
80  return false;
81  }
82  } else {
83  struct flock lock;
84  lock.l_type = F_WRLCK;
85  lock.l_whence = SEEK_SET;
86  lock.l_start = 0;
87  lock.l_len = 0;
88  if (fcntl(fd, F_SETLK, &lock) == -1) {
90  return false;
91  }
92  }
93 
94  return true;
95 }
96 #else
97 
98 static std::string GetErrorReason() {
99  wchar_t* err;
100  FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
101  nullptr, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), reinterpret_cast<WCHAR*>(&err), 0, nullptr);
102  std::wstring err_str(err);
103  LocalFree(err);
104  return std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>>().to_bytes(err_str);
105 }
106 
107 FileLock::FileLock(const fs::path& file)
108 {
109  hFile = CreateFileW(file.wstring().c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
110  nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
111  if (hFile == INVALID_HANDLE_VALUE) {
113  }
114 }
115 
117 {
118  if (hFile != INVALID_HANDLE_VALUE) {
119  CloseHandle(hFile);
120  }
121 }
122 
123 bool FileLock::TryLock()
124 {
125  if (hFile == INVALID_HANDLE_VALUE) {
126  return false;
127  }
128  _OVERLAPPED overlapped = {0};
129  if (!LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK | LOCKFILE_FAIL_IMMEDIATELY, 0, std::numeric_limits<DWORD>::max(), std::numeric_limits<DWORD>::max(), &overlapped)) {
131  return false;
132  }
133  return true;
134 }
135 #endif
136 
137 std::string get_filesystem_error_message(const fs::filesystem_error& e)
138 {
139 #ifndef WIN32
140  return e.what();
141 #else
142  // Convert from Multi Byte to utf-16
143  std::string mb_string(e.what());
144  int size = MultiByteToWideChar(CP_ACP, 0, mb_string.data(), mb_string.size(), nullptr, 0);
145 
146  std::wstring utf16_string(size, L'\0');
147  MultiByteToWideChar(CP_ACP, 0, mb_string.data(), mb_string.size(), &*utf16_string.begin(), size);
148  // Convert from utf-16 to utf-8
149  return std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>, wchar_t>().to_bytes(utf16_string);
150 #endif
151 }
152 
153 #ifdef WIN32
154 #ifdef __GLIBCXX__
155 
156 // reference: https://github.com/gcc-mirror/gcc/blob/gcc-7_3_0-release/libstdc%2B%2B-v3/include/std/fstream#L270
157 
158 static std::string openmodeToStr(std::ios_base::openmode mode)
159 {
160  switch (mode & ~std::ios_base::ate) {
161  case std::ios_base::out:
162  case std::ios_base::out | std::ios_base::trunc:
163  return "w";
164  case std::ios_base::out | std::ios_base::app:
165  case std::ios_base::app:
166  return "a";
167  case std::ios_base::in:
168  return "r";
169  case std::ios_base::in | std::ios_base::out:
170  return "r+";
171  case std::ios_base::in | std::ios_base::out | std::ios_base::trunc:
172  return "w+";
173  case std::ios_base::in | std::ios_base::out | std::ios_base::app:
174  case std::ios_base::in | std::ios_base::app:
175  return "a+";
176  case std::ios_base::out | std::ios_base::binary:
177  case std::ios_base::out | std::ios_base::trunc | std::ios_base::binary:
178  return "wb";
179  case std::ios_base::out | std::ios_base::app | std::ios_base::binary:
180  case std::ios_base::app | std::ios_base::binary:
181  return "ab";
182  case std::ios_base::in | std::ios_base::binary:
183  return "rb";
184  case std::ios_base::in | std::ios_base::out | std::ios_base::binary:
185  return "r+b";
186  case std::ios_base::in | std::ios_base::out | std::ios_base::trunc | std::ios_base::binary:
187  return "w+b";
188  case std::ios_base::in | std::ios_base::out | std::ios_base::app | std::ios_base::binary:
189  case std::ios_base::in | std::ios_base::app | std::ios_base::binary:
190  return "a+b";
191  default:
192  return std::string();
193  }
194 }
195 
196 void ifstream::open(const fs::path& p, std::ios_base::openmode mode)
197 {
198  close();
199  mode |= std::ios_base::in;
200  m_file = fsbridge::fopen(p, openmodeToStr(mode).c_str());
201  if (m_file == nullptr) {
202  return;
203  }
204  m_filebuf = __gnu_cxx::stdio_filebuf<char>(m_file, mode);
205  rdbuf(&m_filebuf);
206  if (mode & std::ios_base::ate) {
207  seekg(0, std::ios_base::end);
208  }
209 }
210 
211 void ifstream::close()
212 {
213  if (m_file != nullptr) {
214  m_filebuf.close();
215  fclose(m_file);
216  }
217  m_file = nullptr;
218 }
219 
220 void ofstream::open(const fs::path& p, std::ios_base::openmode mode)
221 {
222  close();
223  mode |= std::ios_base::out;
224  m_file = fsbridge::fopen(p, openmodeToStr(mode).c_str());
225  if (m_file == nullptr) {
226  return;
227  }
228  m_filebuf = __gnu_cxx::stdio_filebuf<char>(m_file, mode);
229  rdbuf(&m_filebuf);
230  if (mode & std::ios_base::ate) {
231  seekp(0, std::ios_base::end);
232  }
233 }
234 
235 void ofstream::close()
236 {
237  if (m_file != nullptr) {
238  m_filebuf.close();
239  fclose(m_file);
240  }
241  m_file = nullptr;
242 }
243 #else // __GLIBCXX__
244 
245 static_assert(sizeof(*fs::path().BOOST_FILESYSTEM_C_STR) == sizeof(wchar_t),
246  "Warning: This build is using boost::filesystem ofstream and ifstream "
247  "implementations which will fail to open paths containing multibyte "
248  "characters. You should delete this static_assert to ignore this warning, "
249  "or switch to a different C++ standard library like the Microsoft C++ "
250  "Standard Library (where boost uses non-standard extensions to construct "
251  "stream objects with wide filenames), or the GNU libstdc++ library (where "
252  "a more complicated workaround has been implemented above).");
253 
254 #endif // __GLIBCXX__
255 #endif // WIN32
256 
257 } // fsbridge
fsbridge::FileLock::fd
int fd
Definition: fs.h:49
fs.h
fsbridge::fopen
FILE * fopen(const fs::path &p, const char *mode)
Definition: fs.cpp:24
fsbridge::get_filesystem_error_message
std::string get_filesystem_error_message(const fs::filesystem_error &e)
Definition: fs.cpp:137
fsbridge::GetErrorReason
static std::string GetErrorReason()
Definition: fs.cpp:42
fsbridge::AbsPathJoin
fs::path AbsPathJoin(const fs::path &base, const fs::path &path)
Helper function for joining two paths.
Definition: fs.cpp:34
fsbridge::FileLock::TryLock
bool TryLock()
Definition: fs.cpp:68
fsbridge::IsWSL
static bool IsWSL()
Definition: fs.cpp:62
fsbridge::FileLock::~FileLock
~FileLock()
Definition: fs.cpp:55
fsbridge::FileLock::FileLock
FileLock()=delete
assert
assert(std::addressof(::ChainstateActive().CoinsTip())==std::addressof(coins_cache))
fsbridge::FileLock::reason
std::string reason
Definition: fs.h:47
fsbridge
Filesystem operations and types.
Definition: fs.cpp:22