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