]> git.proxmox.com Git - ceph.git/blobdiff - ceph/src/boost/libs/filesystem/src/unique_path.cpp
import quincy beta 17.1.0
[ceph.git] / ceph / src / boost / libs / filesystem / src / unique_path.cpp
index 199f7e8b58bc584d75216cc53a2b54dd1460f67e..185cfd8c2569e2b45052b5abc31529983d241801 100644 (file)
@@ -1,6 +1,7 @@
 //  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
 
 //--------------------------------------------------------------------------------------//
 
-#ifndef BOOST_SYSTEM_NO_DEPRECATED
-# define BOOST_SYSTEM_NO_DEPRECATED
-#endif
+#include "platform_config.hpp"
 
-#include <boost/filesystem/operations.hpp>
-#include <cassert>
+#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
+#ifdef BOOST_POSIX_API
+#   include <cerrno>
+#   include <stddef.h>
 #   include <fcntl.h>
 #   ifdef BOOST_HAS_UNISTD_H
 #      include <unistd.h>
 #   endif
-# else // BOOST_WINDOWS_API
-#   include <windows.h>
-#   include <wincrypt.h>
-#   ifdef _MSC_VER
-#      pragma comment(lib, "Advapi32.lib")
+#   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
+#   if (defined(__linux__) || defined(__linux) || defined(linux)) && (!defined(__ANDROID__) || __ANDROID_API__ >= 28)
+#      include <sys/syscall.h>
+#      if defined(SYS_getrandom)
+#          define BOOST_FILESYSTEM_HAS_SYS_GETRANDOM
+#      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
+#      if defined(BOOST_FILESYSTEM_HAS_GETRANDOM)
+#          include <sys/random.h>
+#      endif
+#   endif // (defined(__linux__) || defined(__linux) || defined(linux)) && (!defined(__ANDROID__) || __ANDROID_API__ >= 28)
+#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
 
-namespace {
+#include <cstddef>
+#include <boost/filesystem/operations.hpp>
+#include "error_handling.hpp"
 
-void fail(int err, boost::system::error_code* ec)
-{
-  if (ec == 0)
-    BOOST_FILESYSTEM_THROW( boost::system::system_error(err,
-      boost::system::system_category(),
-      "boost::filesystem::unique_path"));
+#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)
 
-  ec->assign(err, boost::system::system_category());
-  return;
-}
+namespace boost { namespace filesystem { namespace detail {
 
-#ifdef BOOST_WINDOWS_API
+namespace {
 
-int acquire_crypt_handle(HCRYPTPROV& handle)
+#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)
 {
-  if (::CryptAcquireContextW(&handle, 0, 0, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT | CRYPT_SILENT))
-    return 0;
+  // 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)
 
-  int errval = ::GetLastError();
-  if (errval != NTE_BAD_KEYSET)
-    return errval;
+void system_crypt_random(void* buf, std::size_t len, boost::system::error_code* ec)
+{
+#if defined(BOOST_POSIX_API)
 
-  if (::CryptAcquireContextW(&handle, 0, 0, PROV_RSA_FULL, CRYPT_NEWKEYSET | CRYPT_VERIFYCONTEXT | CRYPT_SILENT))
-    return 0;
+#if defined(BOOST_FILESYSTEM_HAS_GETRANDOM) || defined(BOOST_FILESYSTEM_HAS_SYS_GETRANDOM)
 
-  errval = ::GetLastError();
-  // Another thread could have attempted to create the keyset at the same time.
-  if (errval != NTE_EXISTS)
-    return errval;
+  std::size_t bytes_read = 0;
+  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))
+    {
+      int err = errno;
+      if (err == EINTR)
+        continue;
+      emit_error(err, ec, "boost::filesystem::unique_path");
+      return;
+    }
 
-  if (::CryptAcquireContextW(&handle, 0, 0, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT | CRYPT_SILENT))
-    return 0;
+    bytes_read += n;
+    buf = static_cast<char*>(buf) + n;
+  }
 
-  return ::GetLastError();
-}
+#elif defined(BOOST_FILESYSTEM_HAS_ARC4RANDOM)
 
-#endif
+  arc4random_buf(buf, len);
 
-void system_crypt_random(void* buf, std::size_t len, boost::system::error_code* ec)
-{
-# ifdef BOOST_POSIX_API
+#else
 
-  int file = open("/dev/urandom", O_RDONLY);
+  int file = open("/dev/urandom", O_RDONLY | O_CLOEXEC);
   if (file == -1)
   {
-    file = open("/dev/random", O_RDONLY);
+    file = open("/dev/random", O_RDONLY | O_CLOEXEC);
     if (file == -1)
     {
-      fail(errno, ec);
+      emit_error(errno, ec, "boost::filesystem::unique_path");
       return;
     }
   }
 
-  size_t bytes_read = 0;
+  std::size_t bytes_read = 0;
   while (bytes_read < len)
   {
     ssize_t n = read(file, buf, len - bytes_read);
-    if (n == -1)
+    if (BOOST_UNLIKELY(n == -1))
     {
+      int err = errno;
+      if (err == EINTR)
+        continue;
       close(file);
-      fail(errno, ec);
+      emit_error(err, ec, "boost::filesystem::unique_path");
       return;
     }
     bytes_read += n;
@@ -100,28 +171,65 @@ void system_crypt_random(void* buf, std::size_t len, boost::system::error_code*
 
   close(file);
 
-# else // BOOST_WINDOWS_API
+#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);
 
-  HCRYPTPROV handle;
-  int errval = acquire_crypt_handle(handle);
+  if (BOOST_UNLIKELY(status != 0))
+    goto fail;
 
-  if (!errval)
+#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_)))
   {
-    BOOL gen_ok = ::CryptGenRandom(handle, static_cast<DWORD>(len), static_cast<unsigned char*>(buf));
-    if (!gen_ok)
-      errval = ::GetLastError();
-    ::CryptReleaseContext(handle, 0);
+    err = boost::winapi::GetLastError();
+
+  fail:
+    emit_error(err, ec, "boost::filesystem::unique_path");
+    return;
   }
 
-  if (!errval) 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();
 
-  fail(errval, ec);
-# endif
+  boost::winapi::CryptReleaseContext(handle, 0);
+
+  if (BOOST_UNLIKELY(!gen_ok))
+    goto fail;
+
+#endif // defined(BOOST_FILESYSTEM_HAS_BCRYPT)
+
+#endif // defined(BOOST_POSIX_API)
 }
 
-}  // unnamed namespace
+#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
 
-namespace boost { namespace filesystem { namespace detail {
+}  // unnamed namespace
 
 BOOST_FILESYSTEM_DECL
 path unique_path(const path& model, system::error_code* ec)
@@ -134,21 +242,12 @@ path unique_path(const path& model, system::error_code* ec)
 
   path::string_type s( model.native() );
 
-#ifdef BOOST_WINDOWS_API
-  const wchar_t hex[] = L"0123456789abcdef";
-  const wchar_t percent = L'%';
-#else
-  const char hex[] = "0123456789abcdef";
-  const char percent = '%';
-#endif
-
-  char ran[] = "123456789abcdef";  // init to avoid clang static analyzer message
-                                   // see ticket #8954
-  assert(sizeof(ran) == 16);
-  const int max_nibbles = 2 * sizeof(ran);   // 4-bits per nibble
+  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
 
-  int nibbles_used = max_nibbles;
-  for(path::string_type::size_type i=0; i < s.size(); ++i)
+  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
     {
@@ -156,12 +255,12 @@ path unique_path(const path& model, system::error_code* ec)
       {
         system_crypt_random(ran, sizeof(ran), ec);
         if (ec != 0 && *ec)
-          return "";
+          return path();
         nibbles_used = 0;
       }
-      int c = ran[nibbles_used/2];
-      c >>= 4 * (nibbles_used++ & 1);  // if odd, shift right 1 nibble
-      s[i] = hex[c & 0xf];             // convert to hex digit and replace
+      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
     }
   }