text: //  filesystem unique_path.cpp  --------------------------------------------------------//
  
  //  Copyright Beman Dawes 2010
  //  Copyright Andrey Semashev 2020
  
  //  Distributed under the Boost Software License, Version 1.0.
  //  See http://www.boost.org/LICENSE_1_0.txt
  
  //  Library home page: http://www.boost.org/libs/filesystem
  
  //--------------------------------------------------------------------------------------//
  
  #include "platform_config.hpp"
  
  #include <boost/predef/library/c/cloudabi.h>
  #include <boost/predef/os/bsd/open.h>
  #include <boost/predef/os/bsd/free.h>
  
  #ifdef BOOST_POSIX_API
  
  #include <cerrno>
  #include <stddef.h>
  #include <fcntl.h>
  #ifdef BOOST_HAS_UNISTD_H
  #include <unistd.h>
  #endif
  
  #if !defined(BOOST_FILESYSTEM_DISABLE_ARC4RANDOM)
  #if BOOST_OS_BSD_OPEN >= BOOST_VERSION_NUMBER(2, 1, 0) || \
      BOOST_OS_BSD_FREE >= BOOST_VERSION_NUMBER(8, 0, 0) || \
      BOOST_LIB_C_CLOUDABI
  #include <stdlib.h>
  #define BOOST_FILESYSTEM_HAS_ARC4RANDOM
  #endif
  #endif // !defined(BOOST_FILESYSTEM_DISABLE_ARC4RANDOM)
  
  #if !defined(BOOST_FILESYSTEM_DISABLE_GETRANDOM)
  #if (defined(__linux__) || defined(__linux) || defined(linux)) && \
      (!defined(__ANDROID__) || __ANDROID_API__ >= 28)
  #include <sys/syscall.h>
  #if defined(SYS_getrandom)
  #define BOOST_FILESYSTEM_HAS_GETRANDOM_SYSCALL
  #endif // defined(SYS_getrandom)
  #if defined(__has_include)
  #if __has_include(<sys/random.h>)
  #define BOOST_FILESYSTEM_HAS_GETRANDOM
  #endif
  #elif defined(__GLIBC__)
  #if __GLIBC_PREREQ(2, 25)
  #define BOOST_FILESYSTEM_HAS_GETRANDOM
  #endif
  #endif // BOOST_FILESYSTEM_HAS_GETRANDOM definition
  #if defined(BOOST_FILESYSTEM_HAS_GETRANDOM)
  #include <sys/random.h>
  #endif
  #endif // (defined(__linux__) || defined(__linux) || defined(linux)) && (!defined(__ANDROID__) || __ANDROID_API__ >= 28)
  #endif // !defined(BOOST_FILESYSTEM_DISABLE_GETRANDOM)
  
  #include "posix_tools.hpp"
  
  #else  // BOOST_WINDOWS_API
  
  // We use auto-linking below to help users of static builds of Boost.Filesystem to link to whatever Windows SDK library we selected.
  // The dependency information is currently not exposed in CMake config files generated by Boost.Build (https://github.com/boostorg/boost_install/issues/18),
  // which makes it non-trivial for users to discover the libraries they need. This feature is deprecated and may be removed in the future,
  // when the situation with CMake config files improves.
  // Note that the library build system is the principal source of linking the library, which must work regardless of auto-linking.
  #include <boost/predef/platform.h>
  #include <boost/winapi/basic_types.hpp>
  
  #if defined(BOOST_FILESYSTEM_HAS_BCRYPT) // defined on the command line by the project
  #include <boost/winapi/error_codes.hpp>
  #include <boost/winapi/bcrypt.hpp>
  #if !defined(BOOST_FILESYSTEM_NO_DEPRECATED) && defined(_MSC_VER)
  #pragma comment(lib, "bcrypt.lib")
  #endif // !defined(BOOST_FILESYSTEM_NO_DEPRECATED) && defined(_MSC_VER)
  #else  // defined(BOOST_FILESYSTEM_HAS_BCRYPT)
  #include <boost/winapi/crypt.hpp>
  #include <boost/winapi/get_last_error.hpp>
  #if !defined(BOOST_FILESYSTEM_NO_DEPRECATED) && defined(_MSC_VER)
  #if !defined(_WIN32_WCE)
  #pragma comment(lib, "advapi32.lib")
  #else
  #pragma comment(lib, "coredll.lib")
  #endif
  #endif // !defined(BOOST_FILESYSTEM_NO_DEPRECATED) && defined(_MSC_VER)
  #endif // defined(BOOST_FILESYSTEM_HAS_BCRYPT)
  
  #endif // BOOST_POSIX_API
  
  #include <cstddef>
  #include <boost/filesystem/config.hpp>
  #include <boost/filesystem/operations.hpp>
  #include "private_config.hpp"
  #include "atomic_tools.hpp"
  #include "error_handling.hpp"
  
  #if defined(BOOST_POSIX_API)
  // At least Mac OS X 10.6 and older doesn't support O_CLOEXEC
  #ifndef O_CLOEXEC
  #define O_CLOEXEC 0
  #endif
  #endif // defined(BOOST_POSIX_API)
  
  namespace boost {
  namespace filesystem {
  namespace detail {
  
  namespace {
  
  #if defined(BOOST_FILESYSTEM_HAS_BCRYPT)
  //! Converts NTSTATUS error codes to Win32 error codes for reporting
  inline boost::winapi::DWORD_ translate_ntstatus(boost::winapi::NTSTATUS_ status)
  {
      // Note: Legacy MinGW doesn't have ntstatus.h and doesn't define NTSTATUS error codes other than STATUS_SUCCESS.
      //       Because of this we have to use hardcoded integer literals here. Also, we have to cast to unsigned
      //       integral type to avoid signed overflow and narrowing conversion in the constants.
      switch (static_cast< boost::winapi::ULONG_ >(status))
      {
      case 0xC0000017ul: // STATUS_NO_MEMORY
          return boost::winapi::ERROR_OUTOFMEMORY_;
      case 0xC0000008ul: // STATUS_INVALID_HANDLE
          return boost::winapi::ERROR_INVALID_HANDLE_;
      case 0xC000000Dul: // STATUS_INVALID_PARAMETER
          return boost::winapi::ERROR_INVALID_PARAMETER_;
      default:
          return boost::winapi::ERROR_NOT_SUPPORTED_;
      }
  }
  #endif // defined(BOOST_FILESYSTEM_HAS_BCRYPT)
  
  #if defined(BOOST_POSIX_API) && !defined(BOOST_FILESYSTEM_HAS_ARC4RANDOM)
  
  //! Fills buffer with cryptographically random data obtained from /dev/(u)random
  int fill_random_dev_random(void* buf, std::size_t len)
  {
      int file = ::open("/dev/urandom", O_RDONLY | O_CLOEXEC);
      if (file == -1)
      {
          file = ::open("/dev/random", O_RDONLY | O_CLOEXEC);
          if (file == -1)
              return errno;
      }
  
      std::size_t bytes_read = 0u;
      while (bytes_read < len)
      {
          ssize_t n = ::read(file, buf, len - bytes_read);
          if (BOOST_UNLIKELY(n == -1))
          {
              int err = errno;
              if (err == EINTR)
                  continue;
              close_fd(file);
              return err;
          }
          bytes_read += n;
          buf = static_cast< char* >(buf) + n;
      }
  
      close_fd(file);
      return 0;
  }
  
  #if defined(BOOST_FILESYSTEM_HAS_GETRANDOM) || defined(BOOST_FILESYSTEM_HAS_GETRANDOM_SYSCALL)
  
  typedef int fill_random_t(void* buf, std::size_t len);
  
  //! Pointer to the implementation of fill_random.
  fill_random_t* fill_random = &fill_random_dev_random;
  
  //! Fills buffer with cryptographically random data obtained from getrandom()
  int fill_random_getrandom(void* buf, std::size_t len)
  {
      std::size_t bytes_read = 0u;
      while (bytes_read < len)
      {
  #if defined(BOOST_FILESYSTEM_HAS_GETRANDOM)
          ssize_t n = ::getrandom(buf, len - bytes_read, 0u);
  #else
          ssize_t n = ::syscall(SYS_getrandom, buf, len - bytes_read, 0u);
  #endif
          if (BOOST_UNLIKELY(n < 0))
          {
              const int err = errno;
              if (err == EINTR)
                  continue;
  
              if (err == ENOSYS && bytes_read == 0u)
              {
                  filesystem::detail::atomic_store_relaxed(fill_random, &fill_random_dev_random);
                  return fill_random_dev_random(buf, len);
              }
  
              return err;
          }
  
          bytes_read += n;
          buf = static_cast< char* >(buf) + n;
      }
  
      return 0;
  }
  
  #endif // defined(BOOST_FILESYSTEM_HAS_GETRANDOM) || defined(BOOST_FILESYSTEM_HAS_GETRANDOM_SYSCALL)
  
  #endif // defined(BOOST_POSIX_API) && !defined(BOOST_FILESYSTEM_HAS_ARC4RANDOM)
  
  void system_crypt_random(void* buf, std::size_t len, boost::system::error_code* ec)
  {
  #if defined(BOOST_POSIX_API)
  
  #if defined(BOOST_FILESYSTEM_HAS_GETRANDOM) || defined(BOOST_FILESYSTEM_HAS_GETRANDOM_SYSCALL)
  
      int err = filesystem::detail::atomic_load_relaxed(fill_random)(buf, len);
      if (BOOST_UNLIKELY(err != 0))
          emit_error(err, ec, "boost::filesystem::unique_path");
  
  #elif defined(BOOST_FILESYSTEM_HAS_ARC4RANDOM)
  
      arc4random_buf(buf, len);
  
  #else
  
      int err = fill_random_dev_random(buf, len);
      if (BOOST_UNLIKELY(err != 0))
          emit_error(err, ec, "boost::filesystem::unique_path");
  
  #endif
  
  #else // defined(BOOST_POSIX_API)
  
  #if defined(BOOST_FILESYSTEM_HAS_BCRYPT)
  
      boost::winapi::BCRYPT_ALG_HANDLE_ handle;
      boost::winapi::NTSTATUS_ status = boost::winapi::BCryptOpenAlgorithmProvider(&handle, boost::winapi::BCRYPT_RNG_ALGORITHM_, NULL, 0);
      if (BOOST_UNLIKELY(status != 0))
      {
      fail:
          emit_error(translate_ntstatus(status), ec, "boost::filesystem::unique_path");
          return;
      }
  
      status = boost::winapi::BCryptGenRandom(handle, static_cast< boost::winapi::PUCHAR_ >(buf), static_cast< boost::winapi::ULONG_ >(len), 0);
  
      boost::winapi::BCryptCloseAlgorithmProvider(handle, 0);
  
      if (BOOST_UNLIKELY(status != 0))
          goto fail;
  
  #else // defined(BOOST_FILESYSTEM_HAS_BCRYPT)
  
      boost::winapi::HCRYPTPROV_ handle;
      boost::winapi::DWORD_ err = 0u;
      if (BOOST_UNLIKELY(!boost::winapi::CryptAcquireContextW(&handle, NULL, NULL, boost::winapi::PROV_RSA_FULL_, boost::winapi::CRYPT_VERIFYCONTEXT_ | boost::winapi::CRYPT_SILENT_)))
      {
          err = boost::winapi::GetLastError();
  
      fail:
          emit_error(err, ec, "boost::filesystem::unique_path");
          return;
      }
  
      boost::winapi::BOOL_ gen_ok = boost::winapi::CryptGenRandom(handle, static_cast< boost::winapi::DWORD_ >(len), static_cast< boost::winapi::BYTE_* >(buf));
  
      if (BOOST_UNLIKELY(!gen_ok))
          err = boost::winapi::GetLastError();
  
      boost::winapi::CryptReleaseContext(handle, 0);
  
      if (BOOST_UNLIKELY(!gen_ok))
          goto fail;
  
  #endif // defined(BOOST_FILESYSTEM_HAS_BCRYPT)
  
  #endif // defined(BOOST_POSIX_API)
  }
  
  #ifdef BOOST_WINDOWS_API
  BOOST_CONSTEXPR_OR_CONST wchar_t hex[] = L"0123456789abcdef";
  BOOST_CONSTEXPR_OR_CONST wchar_t percent = L'%';
  #else
  BOOST_CONSTEXPR_OR_CONST char hex[] = "0123456789abcdef";
  BOOST_CONSTEXPR_OR_CONST char percent = '%';
  #endif
  
  } // unnamed namespace
  
  #if defined(linux) || defined(__linux) || defined(__linux__)
  
  //! Initializes fill_random implementation pointer
  void init_fill_random_impl(unsigned int major_ver, unsigned int minor_ver, unsigned int patch_ver)
  {
  #if defined(BOOST_FILESYSTEM_HAS_INIT_PRIORITY) && \
      (defined(BOOST_FILESYSTEM_HAS_GETRANDOM) || defined(BOOST_FILESYSTEM_HAS_GETRANDOM_SYSCALL))
      fill_random_t* fr = &fill_random_dev_random;
  
      if (major_ver > 3u || (major_ver == 3u && minor_ver >= 17u))
          fr = &fill_random_getrandom;
  
      filesystem::detail::atomic_store_relaxed(fill_random, fr);
  #endif
  }
  
  #endif // defined(linux) || defined(__linux) || defined(__linux__)
  
  BOOST_FILESYSTEM_DECL
  path unique_path(path const& model, system::error_code* ec)
  {
      // This function used wstring for fear of misidentifying
      // a part of a multibyte character as a percent sign.
      // However, double byte encodings only have 80-FF as lead
      // bytes and 40-7F as trailing bytes, whereas % is 25.
      // So, use string on POSIX and avoid conversions.
  
      path::string_type s(model.native());
  
      char ran[16] = {};                                                    // init to avoid clang static analyzer message
                                                                            // see ticket #8954
      BOOST_CONSTEXPR_OR_CONST unsigned int max_nibbles = 2u * sizeof(ran); // 4-bits per nibble
  
      unsigned int nibbles_used = max_nibbles;
      for (path::string_type::size_type i = 0, n = s.size(); i < n; ++i)
      {
          if (s[i] == percent) // digit request
          {
              if (nibbles_used == max_nibbles)
              {
                  system_crypt_random(ran, sizeof(ran), ec);
                  if (ec && *ec)
                      return path();
                  nibbles_used = 0;
              }
              unsigned int c = ran[nibbles_used / 2u];
              c >>= 4u * (nibbles_used++ & 1u); // if odd, shift right 1 nibble
              s[i] = hex[c & 0xf];              // convert to hex digit and replace
          }
      }
  
      if (ec)
          ec->clear();
  
      return s;
  }
  
  } // namespace detail
  } // namespace filesystem
  } // namespace boost
  