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