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