1 // filesystem unique_path.cpp --------------------------------------------------------//
3 // Copyright Beman Dawes 2010
4 // Copyright Andrey Semashev 2020
6 // Distributed under the Boost Software License, Version 1.0.
7 // See http://www.boost.org/LICENSE_1_0.txt
9 // Library home page: http://www.boost.org/libs/filesystem
11 //--------------------------------------------------------------------------------------//
13 #include "platform_config.hpp"
15 #include <boost/predef/library/c/cloudabi.h>
16 #include <boost/predef/os/bsd/open.h>
17 #include <boost/predef/os/bsd/free.h>
19 #ifdef BOOST_POSIX_API
24 #ifdef BOOST_HAS_UNISTD_H
28 #if !defined(BOOST_FILESYSTEM_DISABLE_ARC4RANDOM)
29 #if BOOST_OS_BSD_OPEN >= BOOST_VERSION_NUMBER(2, 1, 0) || \
30 BOOST_OS_BSD_FREE >= BOOST_VERSION_NUMBER(8, 0, 0) || \
33 #define BOOST_FILESYSTEM_HAS_ARC4RANDOM
35 #endif // !defined(BOOST_FILESYSTEM_DISABLE_ARC4RANDOM)
37 #if !defined(BOOST_FILESYSTEM_DISABLE_GETRANDOM)
38 #if (defined(__linux__) || defined(__linux) || defined(linux)) && \
39 (!defined(__ANDROID__) || __ANDROID_API__ >= 28)
40 #include <sys/syscall.h>
41 #if defined(SYS_getrandom)
42 #define BOOST_FILESYSTEM_HAS_GETRANDOM_SYSCALL
43 #endif // defined(SYS_getrandom)
44 #if defined(__has_include)
45 #if __has_include(<sys/random.h>)
46 #define BOOST_FILESYSTEM_HAS_GETRANDOM
48 #elif defined(__GLIBC__)
49 #if __GLIBC_PREREQ(2, 25)
50 #define BOOST_FILESYSTEM_HAS_GETRANDOM
52 #endif // BOOST_FILESYSTEM_HAS_GETRANDOM definition
53 #if defined(BOOST_FILESYSTEM_HAS_GETRANDOM)
54 #include <sys/random.h>
56 #endif // (defined(__linux__) || defined(__linux) || defined(linux)) && (!defined(__ANDROID__) || __ANDROID_API__ >= 28)
57 #endif // !defined(BOOST_FILESYSTEM_DISABLE_GETRANDOM)
59 #include "posix_tools.hpp"
61 #else // BOOST_WINDOWS_API
63 // We use auto-linking below to help users of static builds of Boost.Filesystem to link to whatever Windows SDK library we selected.
64 // The dependency information is currently not exposed in CMake config files generated by Boost.Build (https://github.com/boostorg/boost_install/issues/18),
65 // which makes it non-trivial for users to discover the libraries they need. This feature is deprecated and may be removed in the future,
66 // when the situation with CMake config files improves.
67 // Note that the library build system is the principal source of linking the library, which must work regardless of auto-linking.
68 #include <boost/predef/platform.h>
69 #include <boost/winapi/basic_types.hpp>
71 #if defined(BOOST_FILESYSTEM_HAS_BCRYPT) // defined on the command line by the project
72 #include <boost/winapi/error_codes.hpp>
73 #include <boost/winapi/bcrypt.hpp>
74 #if !defined(BOOST_FILESYSTEM_NO_DEPRECATED) && defined(_MSC_VER)
75 #pragma comment(lib, "bcrypt.lib")
76 #endif // !defined(BOOST_FILESYSTEM_NO_DEPRECATED) && defined(_MSC_VER)
77 #else // defined(BOOST_FILESYSTEM_HAS_BCRYPT)
78 #include <boost/winapi/crypt.hpp>
79 #include <boost/winapi/get_last_error.hpp>
80 #if !defined(BOOST_FILESYSTEM_NO_DEPRECATED) && defined(_MSC_VER)
81 #if !defined(_WIN32_WCE)
82 #pragma comment(lib, "advapi32.lib")
84 #pragma comment(lib, "coredll.lib")
86 #endif // !defined(BOOST_FILESYSTEM_NO_DEPRECATED) && defined(_MSC_VER)
87 #endif // defined(BOOST_FILESYSTEM_HAS_BCRYPT)
89 #endif // BOOST_POSIX_API
92 #include <boost/filesystem/config.hpp>
93 #include <boost/filesystem/operations.hpp>
94 #include "private_config.hpp"
95 #include "atomic_tools.hpp"
96 #include "error_handling.hpp"
98 #if defined(BOOST_POSIX_API)
99 // At least Mac OS X 10.6 and older doesn't support O_CLOEXEC
103 #endif // defined(BOOST_POSIX_API)
106 namespace filesystem
{
111 #if defined(BOOST_POSIX_API) && !defined(BOOST_FILESYSTEM_HAS_ARC4RANDOM)
113 //! Fills buffer with cryptographically random data obtained from /dev/(u)random
114 int fill_random_dev_random(void* buf
, std::size_t len
)
116 int file
= ::open("/dev/urandom", O_RDONLY
| O_CLOEXEC
);
119 file
= ::open("/dev/random", O_RDONLY
| O_CLOEXEC
);
124 std::size_t bytes_read
= 0u;
125 while (bytes_read
< len
)
127 ssize_t n
= ::read(file
, buf
, len
- bytes_read
);
128 if (BOOST_UNLIKELY(n
== -1))
137 buf
= static_cast< char* >(buf
) + n
;
144 #if defined(BOOST_FILESYSTEM_HAS_GETRANDOM) || defined(BOOST_FILESYSTEM_HAS_GETRANDOM_SYSCALL)
146 typedef int fill_random_t(void* buf
, std::size_t len
);
148 //! Pointer to the implementation of fill_random.
149 fill_random_t
* fill_random
= &fill_random_dev_random
;
151 //! Fills buffer with cryptographically random data obtained from getrandom()
152 int fill_random_getrandom(void* buf
, std::size_t len
)
154 std::size_t bytes_read
= 0u;
155 while (bytes_read
< len
)
157 #if defined(BOOST_FILESYSTEM_HAS_GETRANDOM)
158 ssize_t n
= ::getrandom(buf
, len
- bytes_read
, 0u);
160 ssize_t n
= ::syscall(SYS_getrandom
, buf
, len
- bytes_read
, 0u);
162 if (BOOST_UNLIKELY(n
< 0))
164 const int err
= errno
;
168 if (err
== ENOSYS
&& bytes_read
== 0u)
170 filesystem::detail::atomic_store_relaxed(fill_random
, &fill_random_dev_random
);
171 return fill_random_dev_random(buf
, len
);
178 buf
= static_cast< char* >(buf
) + n
;
184 #endif // defined(BOOST_FILESYSTEM_HAS_GETRANDOM) || defined(BOOST_FILESYSTEM_HAS_GETRANDOM_SYSCALL)
186 #endif // defined(BOOST_POSIX_API) && !defined(BOOST_FILESYSTEM_HAS_ARC4RANDOM)
188 void system_crypt_random(void* buf
, std::size_t len
, boost::system::error_code
* ec
)
190 #if defined(BOOST_POSIX_API)
192 #if defined(BOOST_FILESYSTEM_HAS_GETRANDOM) || defined(BOOST_FILESYSTEM_HAS_GETRANDOM_SYSCALL)
194 int err
= filesystem::detail::atomic_load_relaxed(fill_random
)(buf
, len
);
195 if (BOOST_UNLIKELY(err
!= 0))
196 emit_error(err
, ec
, "boost::filesystem::unique_path");
198 #elif defined(BOOST_FILESYSTEM_HAS_ARC4RANDOM)
200 arc4random_buf(buf
, len
);
204 int err
= fill_random_dev_random(buf
, len
);
205 if (BOOST_UNLIKELY(err
!= 0))
206 emit_error(err
, ec
, "boost::filesystem::unique_path");
210 #else // defined(BOOST_POSIX_API)
212 #if defined(BOOST_FILESYSTEM_HAS_BCRYPT)
214 boost::winapi::BCRYPT_ALG_HANDLE_ handle
;
215 boost::winapi::NTSTATUS_ status
= boost::winapi::BCryptOpenAlgorithmProvider(&handle
, boost::winapi::BCRYPT_RNG_ALGORITHM_
, NULL
, 0);
216 if (BOOST_UNLIKELY(status
!= 0))
219 emit_error(translate_ntstatus(status
), ec
, "boost::filesystem::unique_path");
223 status
= boost::winapi::BCryptGenRandom(handle
, static_cast< boost::winapi::PUCHAR_
>(buf
), static_cast< boost::winapi::ULONG_
>(len
), 0);
225 boost::winapi::BCryptCloseAlgorithmProvider(handle
, 0);
227 if (BOOST_UNLIKELY(status
!= 0))
230 #else // defined(BOOST_FILESYSTEM_HAS_BCRYPT)
232 boost::winapi::HCRYPTPROV_ handle
;
233 boost::winapi::DWORD_ err
= 0u;
234 if (BOOST_UNLIKELY(!boost::winapi::CryptAcquireContextW(&handle
, NULL
, NULL
, boost::winapi::PROV_RSA_FULL_
, boost::winapi::CRYPT_VERIFYCONTEXT_
| boost::winapi::CRYPT_SILENT_
)))
236 err
= boost::winapi::GetLastError();
239 emit_error(err
, ec
, "boost::filesystem::unique_path");
243 boost::winapi::BOOL_ gen_ok
= boost::winapi::CryptGenRandom(handle
, static_cast< boost::winapi::DWORD_
>(len
), static_cast< boost::winapi::BYTE_
* >(buf
));
245 if (BOOST_UNLIKELY(!gen_ok
))
246 err
= boost::winapi::GetLastError();
248 boost::winapi::CryptReleaseContext(handle
, 0);
250 if (BOOST_UNLIKELY(!gen_ok
))
253 #endif // defined(BOOST_FILESYSTEM_HAS_BCRYPT)
255 #endif // defined(BOOST_POSIX_API)
258 #ifdef BOOST_WINDOWS_API
259 BOOST_CONSTEXPR_OR_CONST
wchar_t hex
[] = L
"0123456789abcdef";
260 BOOST_CONSTEXPR_OR_CONST
wchar_t percent
= L
'%';
262 BOOST_CONSTEXPR_OR_CONST
char hex
[] = "0123456789abcdef";
263 BOOST_CONSTEXPR_OR_CONST
char percent
= '%';
266 } // unnamed namespace
268 #if defined(linux) || defined(__linux) || defined(__linux__)
270 //! Initializes fill_random implementation pointer
271 void init_fill_random_impl(unsigned int major_ver
, unsigned int minor_ver
, unsigned int patch_ver
)
273 #if defined(BOOST_FILESYSTEM_HAS_INIT_PRIORITY) && \
274 (defined(BOOST_FILESYSTEM_HAS_GETRANDOM) || defined(BOOST_FILESYSTEM_HAS_GETRANDOM_SYSCALL))
275 fill_random_t
* fr
= &fill_random_dev_random
;
277 if (major_ver
> 3u || (major_ver
== 3u && minor_ver
>= 17u))
278 fr
= &fill_random_getrandom
;
280 filesystem::detail::atomic_store_relaxed(fill_random
, fr
);
284 #endif // defined(linux) || defined(__linux) || defined(__linux__)
286 BOOST_FILESYSTEM_DECL
287 path
unique_path(path
const& model
, system::error_code
* ec
)
289 // This function used wstring for fear of misidentifying
290 // a part of a multibyte character as a percent sign.
291 // However, double byte encodings only have 80-FF as lead
292 // bytes and 40-7F as trailing bytes, whereas % is 25.
293 // So, use string on POSIX and avoid conversions.
295 path::string_type
s(model
.native());
297 char ran
[16] = {}; // init to avoid clang static analyzer message
299 BOOST_CONSTEXPR_OR_CONST
unsigned int max_nibbles
= 2u * sizeof(ran
); // 4-bits per nibble
301 unsigned int nibbles_used
= max_nibbles
;
302 for (path::string_type::size_type i
= 0, n
= s
.size(); i
< n
; ++i
)
304 if (s
[i
] == percent
) // digit request
306 if (nibbles_used
== max_nibbles
)
308 system_crypt_random(ran
, sizeof(ran
), ec
);
313 unsigned int c
= ran
[nibbles_used
/ 2u];
314 c
>>= 4u * (nibbles_used
++ & 1u); // if odd, shift right 1 nibble
315 s
[i
] = hex
[c
& 0xf]; // convert to hex digit and replace
325 } // namespace detail
326 } // namespace filesystem