]> git.proxmox.com Git - ceph.git/blob - ceph/src/boost/libs/filesystem/src/unique_path.cpp
update ceph source to reef 18.1.2
[ceph.git] / ceph / src / boost / libs / filesystem / src / unique_path.cpp
1 // filesystem unique_path.cpp --------------------------------------------------------//
2
3 // Copyright Beman Dawes 2010
4 // Copyright Andrey Semashev 2020
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 <fcntl.h>
24 #ifdef BOOST_HAS_UNISTD_H
25 #include <unistd.h>
26 #endif
27
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) || \
31 BOOST_LIB_C_CLOUDABI
32 #include <stdlib.h>
33 #define BOOST_FILESYSTEM_HAS_ARC4RANDOM
34 #endif
35 #endif // !defined(BOOST_FILESYSTEM_DISABLE_ARC4RANDOM)
36
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
47 #endif
48 #elif defined(__GLIBC__)
49 #if __GLIBC_PREREQ(2, 25)
50 #define BOOST_FILESYSTEM_HAS_GETRANDOM
51 #endif
52 #endif // BOOST_FILESYSTEM_HAS_GETRANDOM definition
53 #if defined(BOOST_FILESYSTEM_HAS_GETRANDOM)
54 #include <sys/random.h>
55 #endif
56 #endif // (defined(__linux__) || defined(__linux) || defined(linux)) && (!defined(__ANDROID__) || __ANDROID_API__ >= 28)
57 #endif // !defined(BOOST_FILESYSTEM_DISABLE_GETRANDOM)
58
59 #include "posix_tools.hpp"
60
61 #else // BOOST_WINDOWS_API
62
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>
70
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")
83 #else
84 #pragma comment(lib, "coredll.lib")
85 #endif
86 #endif // !defined(BOOST_FILESYSTEM_NO_DEPRECATED) && defined(_MSC_VER)
87 #endif // defined(BOOST_FILESYSTEM_HAS_BCRYPT)
88
89 #endif // BOOST_POSIX_API
90
91 #include <cstddef>
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"
97
98 #if defined(BOOST_POSIX_API)
99 // At least Mac OS X 10.6 and older doesn't support O_CLOEXEC
100 #ifndef O_CLOEXEC
101 #define O_CLOEXEC 0
102 #endif
103 #endif // defined(BOOST_POSIX_API)
104
105 namespace boost {
106 namespace filesystem {
107 namespace detail {
108
109 namespace {
110
111 #if defined(BOOST_POSIX_API) && !defined(BOOST_FILESYSTEM_HAS_ARC4RANDOM)
112
113 //! Fills buffer with cryptographically random data obtained from /dev/(u)random
114 int fill_random_dev_random(void* buf, std::size_t len)
115 {
116 int file = ::open("/dev/urandom", O_RDONLY | O_CLOEXEC);
117 if (file == -1)
118 {
119 file = ::open("/dev/random", O_RDONLY | O_CLOEXEC);
120 if (file == -1)
121 return errno;
122 }
123
124 std::size_t bytes_read = 0u;
125 while (bytes_read < len)
126 {
127 ssize_t n = ::read(file, buf, len - bytes_read);
128 if (BOOST_UNLIKELY(n == -1))
129 {
130 int err = errno;
131 if (err == EINTR)
132 continue;
133 close_fd(file);
134 return err;
135 }
136 bytes_read += n;
137 buf = static_cast< char* >(buf) + n;
138 }
139
140 close_fd(file);
141 return 0;
142 }
143
144 #if defined(BOOST_FILESYSTEM_HAS_GETRANDOM) || defined(BOOST_FILESYSTEM_HAS_GETRANDOM_SYSCALL)
145
146 typedef int fill_random_t(void* buf, std::size_t len);
147
148 //! Pointer to the implementation of fill_random.
149 fill_random_t* fill_random = &fill_random_dev_random;
150
151 //! Fills buffer with cryptographically random data obtained from getrandom()
152 int fill_random_getrandom(void* buf, std::size_t len)
153 {
154 std::size_t bytes_read = 0u;
155 while (bytes_read < len)
156 {
157 #if defined(BOOST_FILESYSTEM_HAS_GETRANDOM)
158 ssize_t n = ::getrandom(buf, len - bytes_read, 0u);
159 #else
160 ssize_t n = ::syscall(SYS_getrandom, buf, len - bytes_read, 0u);
161 #endif
162 if (BOOST_UNLIKELY(n < 0))
163 {
164 const int err = errno;
165 if (err == EINTR)
166 continue;
167
168 if (err == ENOSYS && bytes_read == 0u)
169 {
170 filesystem::detail::atomic_store_relaxed(fill_random, &fill_random_dev_random);
171 return fill_random_dev_random(buf, len);
172 }
173
174 return err;
175 }
176
177 bytes_read += n;
178 buf = static_cast< char* >(buf) + n;
179 }
180
181 return 0;
182 }
183
184 #endif // defined(BOOST_FILESYSTEM_HAS_GETRANDOM) || defined(BOOST_FILESYSTEM_HAS_GETRANDOM_SYSCALL)
185
186 #endif // defined(BOOST_POSIX_API) && !defined(BOOST_FILESYSTEM_HAS_ARC4RANDOM)
187
188 void system_crypt_random(void* buf, std::size_t len, boost::system::error_code* ec)
189 {
190 #if defined(BOOST_POSIX_API)
191
192 #if defined(BOOST_FILESYSTEM_HAS_GETRANDOM) || defined(BOOST_FILESYSTEM_HAS_GETRANDOM_SYSCALL)
193
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");
197
198 #elif defined(BOOST_FILESYSTEM_HAS_ARC4RANDOM)
199
200 arc4random_buf(buf, len);
201
202 #else
203
204 int err = fill_random_dev_random(buf, len);
205 if (BOOST_UNLIKELY(err != 0))
206 emit_error(err, ec, "boost::filesystem::unique_path");
207
208 #endif
209
210 #else // defined(BOOST_POSIX_API)
211
212 #if defined(BOOST_FILESYSTEM_HAS_BCRYPT)
213
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))
217 {
218 fail:
219 emit_error(translate_ntstatus(status), ec, "boost::filesystem::unique_path");
220 return;
221 }
222
223 status = boost::winapi::BCryptGenRandom(handle, static_cast< boost::winapi::PUCHAR_ >(buf), static_cast< boost::winapi::ULONG_ >(len), 0);
224
225 boost::winapi::BCryptCloseAlgorithmProvider(handle, 0);
226
227 if (BOOST_UNLIKELY(status != 0))
228 goto fail;
229
230 #else // defined(BOOST_FILESYSTEM_HAS_BCRYPT)
231
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_)))
235 {
236 err = boost::winapi::GetLastError();
237
238 fail:
239 emit_error(err, ec, "boost::filesystem::unique_path");
240 return;
241 }
242
243 boost::winapi::BOOL_ gen_ok = boost::winapi::CryptGenRandom(handle, static_cast< boost::winapi::DWORD_ >(len), static_cast< boost::winapi::BYTE_* >(buf));
244
245 if (BOOST_UNLIKELY(!gen_ok))
246 err = boost::winapi::GetLastError();
247
248 boost::winapi::CryptReleaseContext(handle, 0);
249
250 if (BOOST_UNLIKELY(!gen_ok))
251 goto fail;
252
253 #endif // defined(BOOST_FILESYSTEM_HAS_BCRYPT)
254
255 #endif // defined(BOOST_POSIX_API)
256 }
257
258 #ifdef BOOST_WINDOWS_API
259 BOOST_CONSTEXPR_OR_CONST wchar_t hex[] = L"0123456789abcdef";
260 BOOST_CONSTEXPR_OR_CONST wchar_t percent = L'%';
261 #else
262 BOOST_CONSTEXPR_OR_CONST char hex[] = "0123456789abcdef";
263 BOOST_CONSTEXPR_OR_CONST char percent = '%';
264 #endif
265
266 } // unnamed namespace
267
268 #if defined(linux) || defined(__linux) || defined(__linux__)
269
270 //! Initializes fill_random implementation pointer
271 void init_fill_random_impl(unsigned int major_ver, unsigned int minor_ver, unsigned int patch_ver)
272 {
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;
276
277 if (major_ver > 3u || (major_ver == 3u && minor_ver >= 17u))
278 fr = &fill_random_getrandom;
279
280 filesystem::detail::atomic_store_relaxed(fill_random, fr);
281 #endif
282 }
283
284 #endif // defined(linux) || defined(__linux) || defined(__linux__)
285
286 BOOST_FILESYSTEM_DECL
287 path unique_path(path const& model, system::error_code* ec)
288 {
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.
294
295 path::string_type s(model.native());
296
297 char ran[16] = {}; // init to avoid clang static analyzer message
298 // see ticket #8954
299 BOOST_CONSTEXPR_OR_CONST unsigned int max_nibbles = 2u * sizeof(ran); // 4-bits per nibble
300
301 unsigned int nibbles_used = max_nibbles;
302 for (path::string_type::size_type i = 0, n = s.size(); i < n; ++i)
303 {
304 if (s[i] == percent) // digit request
305 {
306 if (nibbles_used == max_nibbles)
307 {
308 system_crypt_random(ran, sizeof(ran), ec);
309 if (ec && *ec)
310 return path();
311 nibbles_used = 0;
312 }
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
316 }
317 }
318
319 if (ec)
320 ec->clear();
321
322 return s;
323 }
324
325 } // namespace detail
326 } // namespace filesystem
327 } // namespace boost