// 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 #include #include #ifdef BOOST_POSIX_API # include # include # include # ifdef BOOST_HAS_UNISTD_H # include # endif # 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 # define BOOST_FILESYSTEM_HAS_ARC4RANDOM # endif # if (defined(__linux__) || defined(__linux) || defined(linux)) && (!defined(__ANDROID__) || __ANDROID_API__ >= 28) # include # if defined(SYS_getrandom) # define BOOST_FILESYSTEM_HAS_SYS_GETRANDOM # endif // defined(SYS_getrandom) # if defined(__has_include) # if __has_include() # 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 # 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 # include # if defined(BOOST_FILESYSTEM_HAS_BCRYPT) // defined on the command line by the project # include # include # 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 # include # 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 #include #include #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) 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_SYS_GETRANDOM) 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; } bytes_read += n; buf = static_cast(buf) + n; } #elif defined(BOOST_FILESYSTEM_HAS_ARC4RANDOM) arc4random_buf(buf, len); #else int file = open("/dev/urandom", O_RDONLY | O_CLOEXEC); if (file == -1) { file = open("/dev/random", O_RDONLY | O_CLOEXEC); if (file == -1) { emit_error(errno, ec, "boost::filesystem::unique_path"); return; } } std::size_t bytes_read = 0; 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(file); emit_error(err, ec, "boost::filesystem::unique_path"); return; } bytes_read += n; buf = static_cast(buf) + n; } close(file); #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(buf), static_cast(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(len), static_cast(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 BOOST_FILESYSTEM_DECL path unique_path(const path& 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 != 0 && *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 != 0) ec->clear(); return s; } }}}