fs.cpp raw

   1  // Copyright (c) 2017-present The Limenka 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 <util/check.h>
   6  #include <util/fs.h>
   7  #include <util/syserror.h>
   8  
   9  #ifndef WIN32
  10  #include <cstring>
  11  #include <fcntl.h>
  12  #include <sys/file.h>
  13  #include <sys/utsname.h>
  14  #include <unistd.h>
  15  #else
  16  #include <codecvt>
  17  #include <cstring>
  18  #include <fcntl.h>
  19  #include <io.h>
  20  #include <limits>
  21  #include <locale>
  22  #include <share.h>
  23  #include <sys/stat.h>
  24  #include <windows.h>
  25  #endif
  26  
  27  #include <cassert>
  28  #include <cerrno>
  29  #include <string>
  30  
  31  namespace fsbridge {
  32  
  33  FILE *fopen(const fs::path& p, const char *mode)
  34  {
  35      const bool exclusive{strchr(mode, 'x') != nullptr};
  36  #ifndef WIN32
  37      Assume((!exclusive) || !strcmp(mode, "wbx"));
  38      return ::fopen(p.c_str(), mode);
  39  #else
  40      if (exclusive) {
  41          Assert(!strcmp(mode, "wbx"));
  42          int fd;
  43          if (::_wsopen_s(&fd, p.wstring().c_str(), _O_WRONLY | _O_CREAT | _O_EXCL | _O_BINARY, _SH_DENYNO, _S_IREAD | _S_IWRITE)) {
  44              return nullptr;
  45          }
  46          FILE* fp = ::_fdopen(fd, "wb");
  47          if (!fp) ::_close(fd);
  48          return fp;
  49      }
  50  
  51      std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>,wchar_t> utf8_cvt;
  52      return ::_wfopen(p.wstring().c_str(), utf8_cvt.from_bytes(mode).c_str());
  53  #endif
  54  }
  55  
  56  fs::path AbsPathJoin(const fs::path& base, const fs::path& path)
  57  {
  58      assert(base.is_absolute());
  59      return path.empty() ? base : fs::path(base / path);
  60  }
  61  
  62  #ifndef WIN32
  63  
  64  static std::string GetErrorReason()
  65  {
  66      return SysErrorString(errno);
  67  }
  68  
  69  FileLock::FileLock(const fs::path& file)
  70  {
  71      fd = open(file.c_str(), O_RDWR);
  72      if (fd == -1) {
  73          reason = GetErrorReason();
  74      }
  75  }
  76  
  77  FileLock::~FileLock()
  78  {
  79      if (fd != -1) {
  80          close(fd);
  81      }
  82  }
  83  
  84  bool FileLock::TryLock()
  85  {
  86      if (fd == -1) {
  87          return false;
  88      }
  89  
  90      struct flock lock;
  91      lock.l_type = F_WRLCK;
  92      lock.l_whence = SEEK_SET;
  93      lock.l_start = 0;
  94      lock.l_len = 0;
  95      if (fcntl(fd, F_SETLK, &lock) == -1) {
  96          reason = GetErrorReason();
  97          return false;
  98      }
  99  
 100      return true;
 101  }
 102  #else
 103  
 104  static std::string GetErrorReason() {
 105      return Win32ErrorString(GetLastError());
 106  }
 107  
 108  FileLock::FileLock(const fs::path& file)
 109  {
 110      hFile = CreateFileW(file.wstring().c_str(),  GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
 111          nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
 112      if (hFile == INVALID_HANDLE_VALUE) {
 113          reason = GetErrorReason();
 114      }
 115  }
 116  
 117  FileLock::~FileLock()
 118  {
 119      if (hFile != INVALID_HANDLE_VALUE) {
 120          CloseHandle(hFile);
 121      }
 122  }
 123  
 124  bool FileLock::TryLock()
 125  {
 126      if (hFile == INVALID_HANDLE_VALUE) {
 127          return false;
 128      }
 129      _OVERLAPPED overlapped = {};
 130      if (!LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK | LOCKFILE_FAIL_IMMEDIATELY, 0, std::numeric_limits<DWORD>::max(), std::numeric_limits<DWORD>::max(), &overlapped)) {
 131          reason = GetErrorReason();
 132          return false;
 133      }
 134      return true;
 135  }
 136  #endif
 137  
 138  std::string get_filesystem_error_message(const fs::filesystem_error& e)
 139  {
 140      return e.code().message();
 141  #if 0
 142      return e.what();
 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(), mb_string.size(), nullptr, 0);
 146  
 147      std::wstring utf16_string(size, L'\0');
 148      MultiByteToWideChar(CP_ACP, 0, mb_string.data(), mb_string.size(), &*utf16_string.begin(), size);
 149      // Convert from utf-16 to utf-8
 150      return std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>, wchar_t>().to_bytes(utf16_string);
 151  #endif
 152  }
 153  
 154  } // namespace fsbridge
 155