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