LCOV - code coverage report
Current view: top level - src - fs.cpp (source / functions) Hit Total Coverage
Test: total_coverage.info Lines: 36 41 87.8 %
Date: 2020-09-26 01:30:44 Functions: 8 9 88.9 %

          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

Generated by: LCOV version 1.15