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 | |
106 | namespace boost { |
107 | namespace filesystem { |
108 | namespace detail { |
109 | |
110 | namespace { |
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 |
115 | int 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 | |
168 | typedef int fill_random_t(void* buf, std::size_t len); |
169 | |
170 | //! Pointer to the implementation of fill_random. |
171 | fill_random_t* fill_random = &fill_random_dev_random; |
172 | |
173 | //! Fills buffer with cryptographically random data obtained from getrandom() |
174 | int 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 | |
210 | void 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 |
281 | BOOST_CONSTEXPR_OR_CONST wchar_t hex[] = L"0123456789abcdef" ; |
282 | BOOST_CONSTEXPR_OR_CONST wchar_t percent = L'%'; |
283 | #else |
284 | BOOST_CONSTEXPR_OR_CONST char hex[] = "0123456789abcdef" ; |
285 | BOOST_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 |
293 | void 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 | |
308 | BOOST_FILESYSTEM_DECL |
309 | path 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 | |