1// filesystem unique_path.cpp --------------------------------------------------------//
2
3// Copyright Beman Dawes 2010
4// Copyright Andrey Semashev 2020, 2024
5
6// Distributed under the Boost Software License, Version 1.0.
7// See http://www.boost.org/LICENSE_1_0.txt
8
9// Library home page: http://www.boost.org/libs/filesystem
10
11//--------------------------------------------------------------------------------------//
12
13#include "platform_config.hpp"
14
15#include <boost/predef/library/c/cloudabi.h>
16#include <boost/predef/os/bsd/open.h>
17#include <boost/predef/os/bsd/free.h>
18
19#ifdef BOOST_POSIX_API
20
21#include <cerrno>
22#include <stddef.h>
23#include <unistd.h>
24#include <fcntl.h>
25
26#if !defined(BOOST_FILESYSTEM_DISABLE_ARC4RANDOM)
27#if BOOST_OS_BSD_OPEN >= BOOST_VERSION_NUMBER(2, 1, 0) || \
28 BOOST_OS_BSD_FREE >= BOOST_VERSION_NUMBER(8, 0, 0) || \
29 BOOST_LIB_C_CLOUDABI
30#include <stdlib.h>
31#define BOOST_FILESYSTEM_HAS_ARC4RANDOM
32#endif
33#endif // !defined(BOOST_FILESYSTEM_DISABLE_ARC4RANDOM)
34
35#if !defined(BOOST_FILESYSTEM_DISABLE_GETRANDOM)
36#if (defined(__linux__) || defined(__linux) || defined(linux)) && \
37 (!defined(__ANDROID__) || __ANDROID_API__ >= 28)
38#include <sys/syscall.h>
39#if defined(SYS_getrandom)
40#define BOOST_FILESYSTEM_HAS_GETRANDOM_SYSCALL
41#endif // defined(SYS_getrandom)
42#if defined(__has_include)
43#if __has_include(<sys/random.h>)
44#define BOOST_FILESYSTEM_HAS_GETRANDOM
45#endif
46#elif defined(__GLIBC__)
47#if __GLIBC_PREREQ(2, 25)
48#define BOOST_FILESYSTEM_HAS_GETRANDOM
49#endif
50#endif // BOOST_FILESYSTEM_HAS_GETRANDOM definition
51#if defined(BOOST_FILESYSTEM_HAS_GETRANDOM)
52#include <sys/random.h>
53#endif
54#endif // (defined(__linux__) || defined(__linux) || defined(linux)) && (!defined(__ANDROID__) || __ANDROID_API__ >= 28)
55#endif // !defined(BOOST_FILESYSTEM_DISABLE_GETRANDOM)
56
57#include <boost/scope/unique_fd.hpp>
58#include "posix_tools.hpp"
59
60#else // BOOST_WINDOWS_API
61
62// We use auto-linking below to help users of static builds of Boost.Filesystem to link to whatever Windows SDK library we selected.
63// The dependency information is currently not exposed in CMake config files generated by Boost.Build (https://github.com/boostorg/boost_install/issues/18),
64// which makes it non-trivial for users to discover the libraries they need. This feature is deprecated and may be removed in the future,
65// when the situation with CMake config files improves.
66// Note that the library build system is the principal source of linking the library, which must work regardless of auto-linking.
67#include <boost/predef/platform.h>
68#include <boost/winapi/basic_types.hpp>
69
70#if defined(BOOST_FILESYSTEM_HAS_BCRYPT) // defined on the command line by the project
71#include <boost/winapi/error_codes.hpp>
72#include <boost/winapi/bcrypt.hpp>
73#if !defined(BOOST_FILESYSTEM_NO_DEPRECATED) && defined(_MSC_VER)
74#pragma comment(lib, "bcrypt.lib")
75#endif // !defined(BOOST_FILESYSTEM_NO_DEPRECATED) && defined(_MSC_VER)
76#else // defined(BOOST_FILESYSTEM_HAS_BCRYPT)
77#include <boost/winapi/crypt.hpp>
78#include <boost/winapi/get_last_error.hpp>
79#if !defined(BOOST_FILESYSTEM_NO_DEPRECATED) && defined(_MSC_VER)
80#if !defined(_WIN32_WCE)
81#pragma comment(lib, "advapi32.lib")
82#else
83#pragma comment(lib, "coredll.lib")
84#endif
85#endif // !defined(BOOST_FILESYSTEM_NO_DEPRECATED) && defined(_MSC_VER)
86#endif // defined(BOOST_FILESYSTEM_HAS_BCRYPT)
87
88#endif // BOOST_POSIX_API
89
90#include <cstddef>
91#include <boost/filesystem/config.hpp>
92#include <boost/filesystem/operations.hpp>
93#include "private_config.hpp"
94#include "atomic_tools.hpp"
95#include "error_handling.hpp"
96
97#include <boost/filesystem/detail/header.hpp> // must be the last #include
98
99#if defined(BOOST_POSIX_API)
100// At least Mac OS X 10.6 and older doesn't support O_CLOEXEC
101#ifndef O_CLOEXEC
102#define O_CLOEXEC 0
103#endif
104#endif // defined(BOOST_POSIX_API)
105
106namespace boost {
107namespace filesystem {
108namespace detail {
109
110namespace {
111
112#if defined(BOOST_POSIX_API) && !defined(BOOST_FILESYSTEM_HAS_ARC4RANDOM)
113
114//! Fills buffer with cryptographically random data obtained from /dev/(u)random
115int fill_random_dev_random(void* buf, std::size_t len)
116{
117 boost::scope::unique_fd file;
118 while (true)
119 {
120 file.reset(res: ::open(file: "/dev/urandom", O_RDONLY | O_CLOEXEC));
121 if (!file)
122 {
123 if (errno == EINTR)
124 continue;
125 }
126
127 break;
128 }
129
130 if (!file)
131 {
132 while (true)
133 {
134 file.reset(res: ::open(file: "/dev/random", O_RDONLY | O_CLOEXEC));
135 if (!file)
136 {
137 const int err = errno;
138 if (err == EINTR)
139 continue;
140 return err;
141 }
142
143 break;
144 }
145 }
146
147 std::size_t bytes_read = 0u;
148 while (bytes_read < len)
149 {
150 ssize_t n = ::read(fd: file.get(), buf: buf, nbytes: len - bytes_read);
151 if (BOOST_UNLIKELY(n < 0))
152 {
153 const int err = errno;
154 if (err == EINTR)
155 continue;
156 return err;
157 }
158
159 bytes_read += n;
160 buf = static_cast< char* >(buf) + n;
161 }
162
163 return 0;
164}
165
166#if defined(BOOST_FILESYSTEM_HAS_GETRANDOM) || defined(BOOST_FILESYSTEM_HAS_GETRANDOM_SYSCALL)
167
168typedef int fill_random_t(void* buf, std::size_t len);
169
170//! Pointer to the implementation of fill_random.
171fill_random_t* fill_random = &fill_random_dev_random;
172
173//! Fills buffer with cryptographically random data obtained from getrandom()
174int fill_random_getrandom(void* buf, std::size_t len)
175{
176 std::size_t bytes_read = 0u;
177 while (bytes_read < len)
178 {
179#if defined(BOOST_FILESYSTEM_HAS_GETRANDOM)
180 ssize_t n = ::getrandom(buffer: buf, length: len - bytes_read, flags: 0u);
181#else
182 ssize_t n = ::syscall(SYS_getrandom, buf, len - bytes_read, 0u);
183#endif
184 if (BOOST_UNLIKELY(n < 0))
185 {
186 const int err = errno;
187 if (err == EINTR)
188 continue;
189
190 if (err == ENOSYS && bytes_read == 0u)
191 {
192 filesystem::detail::atomic_store_relaxed(a&: fill_random, val: &fill_random_dev_random);
193 return fill_random_dev_random(buf, len);
194 }
195
196 return err;
197 }
198
199 bytes_read += n;
200 buf = static_cast< char* >(buf) + n;
201 }
202
203 return 0;
204}
205
206#endif // defined(BOOST_FILESYSTEM_HAS_GETRANDOM) || defined(BOOST_FILESYSTEM_HAS_GETRANDOM_SYSCALL)
207
208#endif // defined(BOOST_POSIX_API) && !defined(BOOST_FILESYSTEM_HAS_ARC4RANDOM)
209
210void system_crypt_random(void* buf, std::size_t len, boost::system::error_code* ec)
211{
212#if defined(BOOST_POSIX_API)
213
214#if defined(BOOST_FILESYSTEM_HAS_GETRANDOM) || defined(BOOST_FILESYSTEM_HAS_GETRANDOM_SYSCALL)
215
216 int err = filesystem::detail::atomic_load_relaxed(a&: fill_random)(buf, len);
217 if (BOOST_UNLIKELY(err != 0))
218 emit_error(error_num: err, ec, message: "boost::filesystem::unique_path");
219
220#elif defined(BOOST_FILESYSTEM_HAS_ARC4RANDOM)
221
222 arc4random_buf(buf, len);
223
224#else
225
226 int err = fill_random_dev_random(buf, len);
227 if (BOOST_UNLIKELY(err != 0))
228 emit_error(err, ec, "boost::filesystem::unique_path");
229
230#endif
231
232#else // defined(BOOST_POSIX_API)
233
234#if defined(BOOST_FILESYSTEM_HAS_BCRYPT)
235
236 boost::winapi::BCRYPT_ALG_HANDLE_ handle;
237 boost::winapi::NTSTATUS_ status = boost::winapi::BCryptOpenAlgorithmProvider(&handle, boost::winapi::BCRYPT_RNG_ALGORITHM_, nullptr, 0);
238 if (BOOST_UNLIKELY(status != 0))
239 {
240 fail:
241 emit_error(translate_ntstatus(status), ec, "boost::filesystem::unique_path");
242 return;
243 }
244
245 status = boost::winapi::BCryptGenRandom(handle, static_cast< boost::winapi::PUCHAR_ >(buf), static_cast< boost::winapi::ULONG_ >(len), 0);
246
247 boost::winapi::BCryptCloseAlgorithmProvider(handle, 0);
248
249 if (BOOST_UNLIKELY(status != 0))
250 goto fail;
251
252#else // defined(BOOST_FILESYSTEM_HAS_BCRYPT)
253
254 boost::winapi::HCRYPTPROV_ handle;
255 boost::winapi::DWORD_ err = 0u;
256 if (BOOST_UNLIKELY(!boost::winapi::CryptAcquireContextW(&handle, nullptr, nullptr, boost::winapi::PROV_RSA_FULL_, boost::winapi::CRYPT_VERIFYCONTEXT_ | boost::winapi::CRYPT_SILENT_)))
257 {
258 err = boost::winapi::GetLastError();
259
260 fail:
261 emit_error(err, ec, "boost::filesystem::unique_path");
262 return;
263 }
264
265 boost::winapi::BOOL_ gen_ok = boost::winapi::CryptGenRandom(handle, static_cast< boost::winapi::DWORD_ >(len), static_cast< boost::winapi::BYTE_* >(buf));
266
267 if (BOOST_UNLIKELY(!gen_ok))
268 err = boost::winapi::GetLastError();
269
270 boost::winapi::CryptReleaseContext(handle, 0);
271
272 if (BOOST_UNLIKELY(!gen_ok))
273 goto fail;
274
275#endif // defined(BOOST_FILESYSTEM_HAS_BCRYPT)
276
277#endif // defined(BOOST_POSIX_API)
278}
279
280#ifdef BOOST_WINDOWS_API
281BOOST_CONSTEXPR_OR_CONST wchar_t hex[] = L"0123456789abcdef";
282BOOST_CONSTEXPR_OR_CONST wchar_t percent = L'%';
283#else
284BOOST_CONSTEXPR_OR_CONST char hex[] = "0123456789abcdef";
285BOOST_CONSTEXPR_OR_CONST char percent = '%';
286#endif
287
288} // unnamed namespace
289
290#if defined(linux) || defined(__linux) || defined(__linux__)
291
292//! Initializes fill_random implementation pointer
293void init_fill_random_impl(unsigned int major_ver, unsigned int minor_ver, unsigned int patch_ver)
294{
295#if defined(BOOST_FILESYSTEM_HAS_INIT_PRIORITY) && \
296 (defined(BOOST_FILESYSTEM_HAS_GETRANDOM) || defined(BOOST_FILESYSTEM_HAS_GETRANDOM_SYSCALL))
297 fill_random_t* fr = &fill_random_dev_random;
298
299 if (major_ver > 3u || (major_ver == 3u && minor_ver >= 17u))
300 fr = &fill_random_getrandom;
301
302 filesystem::detail::atomic_store_relaxed(a&: fill_random, val: fr);
303#endif
304}
305
306#endif // defined(linux) || defined(__linux) || defined(__linux__)
307
308BOOST_FILESYSTEM_DECL
309path unique_path(path const& model, system::error_code* ec)
310{
311 // This function used wstring for fear of misidentifying
312 // a part of a multibyte character as a percent sign.
313 // However, double byte encodings only have 80-FF as lead
314 // bytes and 40-7F as trailing bytes, whereas % is 25.
315 // So, use string on POSIX and avoid conversions.
316
317 path::string_type s(model.native());
318
319 char ran[16] = {}; // init to avoid clang static analyzer message
320 // see ticket #8954
321 BOOST_CONSTEXPR_OR_CONST unsigned int max_nibbles = 2u * sizeof(ran); // 4-bits per nibble
322
323 unsigned int nibbles_used = max_nibbles;
324 for (path::string_type::size_type i = 0, n = s.size(); i < n; ++i)
325 {
326 if (s[i] == percent) // digit request
327 {
328 if (nibbles_used == max_nibbles)
329 {
330 system_crypt_random(buf: ran, len: sizeof(ran), ec);
331 if (ec && *ec)
332 return path();
333 nibbles_used = 0;
334 }
335 unsigned int c = ran[nibbles_used / 2u];
336 c >>= 4u * (nibbles_used++ & 1u); // if odd, shift right 1 nibble
337 s[i] = hex[c & 0xf]; // convert to hex digit and replace
338 }
339 }
340
341 if (ec)
342 ec->clear();
343
344 return s;
345}
346
347} // namespace detail
348} // namespace filesystem
349} // namespace boost
350
351#include <boost/filesystem/detail/footer.hpp>
352

source code of boost/libs/filesystem/src/unique_path.cpp