Line data Source code
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 223157 : FILE *fopen(const fs::path& p, const char *mode)
25 : {
26 : #ifndef WIN32
27 223157 : 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 10 : static std::string GetErrorReason()
37 : {
38 2060 : return std::strerror(errno);
39 : }
40 :
41 4100 : FileLock::FileLock(const fs::path& file)
42 2050 : {
43 2050 : fd = open(file.string().c_str(), O_RDWR);
44 2050 : if (fd == -1) {
45 1 : reason = GetErrorReason();
46 1 : }
47 4100 : }
48 :
49 4100 : FileLock::~FileLock()
50 2050 : {
51 2050 : if (fd != -1) {
52 2049 : close(fd);
53 : }
54 4100 : }
55 :
56 538 : static bool IsWSL()
57 : {
58 538 : struct utsname uname_data;
59 538 : return uname(&uname_data) == 0 && std::string(uname_data.version).find("Microsoft") != std::string::npos;
60 538 : }
61 :
62 2050 : bool FileLock::TryLock()
63 : {
64 2050 : if (fd == -1) {
65 1 : 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 2049 : static const bool is_wsl = IsWSL();
71 2049 : if (is_wsl) {
72 0 : if (flock(fd, LOCK_EX | LOCK_NB) == -1) {
73 0 : reason = GetErrorReason();
74 0 : return false;
75 : }
76 : } else {
77 2049 : struct flock lock;
78 2049 : lock.l_type = F_WRLCK;
79 2049 : lock.l_whence = SEEK_SET;
80 2049 : lock.l_start = 0;
81 2049 : lock.l_len = 0;
82 2049 : if (fcntl(fd, F_SETLK, &lock) == -1) {
83 9 : reason = GetErrorReason();
84 9 : return false;
85 : }
86 2049 : }
87 :
88 2040 : return true;
89 2050 : }
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) {
106 : reason = GetErrorReason();
107 : }
108 : }
109 :
110 : FileLock::~FileLock()
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)) {
124 : reason = GetErrorReason();
125 : return false;
126 : }
127 : return true;
128 : }
129 : #endif
130 :
131 0 : std::string get_filesystem_error_message(const fs::filesystem_error& e)
132 : {
133 : #ifndef WIN32
134 0 : 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
|