• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #   include <cerrno>
21 #   include <stddef.h>
22 #   include <fcntl.h>
23 #   ifdef BOOST_HAS_UNISTD_H
24 #      include <unistd.h>
25 #   endif
26 #   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
27 #      include <stdlib.h>
28 #      define BOOST_FILESYSTEM_HAS_ARC4RANDOM
29 #   endif
30 #   if (defined(__linux__) || defined(__linux) || defined(linux)) && (!defined(__ANDROID__) || __ANDROID_API__ >= 28)
31 #      include <sys/syscall.h>
32 #      if defined(SYS_getrandom)
33 #          define BOOST_FILESYSTEM_HAS_SYS_GETRANDOM
34 #      endif // defined(SYS_getrandom)
35 #      if defined(__has_include)
36 #          if __has_include(<sys/random.h>)
37 #              define BOOST_FILESYSTEM_HAS_GETRANDOM
38 #          endif
39 #      elif defined(__GLIBC__)
40 #          if __GLIBC_PREREQ(2, 25)
41 #              define BOOST_FILESYSTEM_HAS_GETRANDOM
42 #          endif
43 #      endif
44 #      if defined(BOOST_FILESYSTEM_HAS_GETRANDOM)
45 #          include <sys/random.h>
46 #      endif
47 #   endif // (defined(__linux__) || defined(__linux) || defined(linux)) && (!defined(__ANDROID__) || __ANDROID_API__ >= 28)
48 #else // BOOST_WINDOWS_API
49     // We use auto-linking below to help users of static builds of Boost.Filesystem to link to whatever Windows SDK library we selected.
50     // The dependency information is currently not exposed in CMake config files generated by Boost.Build (https://github.com/boostorg/boost_install/issues/18),
51     // which makes it non-trivial for users to discover the libraries they need. This feature is deprecated and may be removed in the future,
52     // when the situation with CMake config files improves.
53     // Note that the library build system is the principal source of linking the library, which must work regardless of auto-linking.
54 #   include <boost/predef/platform.h>
55 #   include <boost/winapi/basic_types.hpp>
56 #   if defined(BOOST_FILESYSTEM_HAS_BCRYPT) // defined on the command line by the project
57 #      include <boost/winapi/error_codes.hpp>
58 #      include <boost/winapi/bcrypt.hpp>
59 #      if !defined(BOOST_FILESYSTEM_NO_DEPRECATED) && defined(_MSC_VER)
60 #          pragma comment(lib, "bcrypt.lib")
61 #      endif // !defined(BOOST_FILESYSTEM_NO_DEPRECATED) && defined(_MSC_VER)
62 #   else // defined(BOOST_FILESYSTEM_HAS_BCRYPT)
63 #      include <boost/winapi/crypt.hpp>
64 #      include <boost/winapi/get_last_error.hpp>
65 #      if !defined(BOOST_FILESYSTEM_NO_DEPRECATED) && defined(_MSC_VER)
66 #          if !defined(_WIN32_WCE)
67 #              pragma comment(lib, "advapi32.lib")
68 #          else
69 #              pragma comment(lib, "coredll.lib")
70 #          endif
71 #      endif // !defined(BOOST_FILESYSTEM_NO_DEPRECATED) && defined(_MSC_VER)
72 #   endif // defined(BOOST_FILESYSTEM_HAS_BCRYPT)
73 #endif
74 
75 #include <cstddef>
76 #include <boost/filesystem/operations.hpp>
77 #include "error_handling.hpp"
78 
79 #if defined(BOOST_POSIX_API)
80 // At least Mac OS X 10.6 and older doesn't support O_CLOEXEC
81 #ifndef O_CLOEXEC
82 #define O_CLOEXEC 0
83 #endif
84 #endif // defined(BOOST_POSIX_API)
85 
86 namespace boost { namespace filesystem { namespace detail {
87 
88 namespace {
89 
90 #if defined(BOOST_FILESYSTEM_HAS_BCRYPT)
91 //! Converts NTSTATUS error codes to Win32 error codes for reporting
translate_ntstatus(boost::winapi::NTSTATUS_ status)92 inline boost::winapi::DWORD_ translate_ntstatus(boost::winapi::NTSTATUS_ status)
93 {
94   // Note: Legacy MinGW doesn't have ntstatus.h and doesn't define NTSTATUS error codes other than STATUS_SUCCESS.
95   //       Because of this we have to use hardcoded integer literals here. Also, we have to cast to unsigned
96   //       integral type to avoid signed overflow and narrowing conversion in the constants.
97   switch (static_cast< boost::winapi::ULONG_ >(status))
98   {
99   case 0xC0000017ul: // STATUS_NO_MEMORY
100     return boost::winapi::ERROR_OUTOFMEMORY_;
101   case 0xC0000008ul: // STATUS_INVALID_HANDLE
102     return boost::winapi::ERROR_INVALID_HANDLE_;
103   case 0xC000000Dul: // STATUS_INVALID_PARAMETER
104     return boost::winapi::ERROR_INVALID_PARAMETER_;
105   default:
106     return boost::winapi::ERROR_NOT_SUPPORTED_;
107   }
108 }
109 #endif // defined(BOOST_FILESYSTEM_HAS_BCRYPT)
110 
system_crypt_random(void * buf,std::size_t len,boost::system::error_code * ec)111 void system_crypt_random(void* buf, std::size_t len, boost::system::error_code* ec)
112 {
113 #if defined(BOOST_POSIX_API)
114 
115 #if defined(BOOST_FILESYSTEM_HAS_GETRANDOM) || defined(BOOST_FILESYSTEM_HAS_SYS_GETRANDOM)
116 
117   std::size_t bytes_read = 0;
118   while (bytes_read < len)
119   {
120 #if defined(BOOST_FILESYSTEM_HAS_GETRANDOM)
121     ssize_t n = ::getrandom(buf, len - bytes_read, 0u);
122 #else
123     ssize_t n = ::syscall(SYS_getrandom, buf, len - bytes_read, 0u);
124 #endif
125     if (BOOST_UNLIKELY(n < 0))
126     {
127       int err = errno;
128       if (err == EINTR)
129         continue;
130       emit_error(err, ec, "boost::filesystem::unique_path");
131       return;
132     }
133 
134     bytes_read += n;
135     buf = static_cast<char*>(buf) + n;
136   }
137 
138 #elif defined(BOOST_FILESYSTEM_HAS_ARC4RANDOM)
139 
140   arc4random_buf(buf, len);
141 
142 #else
143 
144   int file = open("/dev/urandom", O_RDONLY | O_CLOEXEC);
145   if (file == -1)
146   {
147     file = open("/dev/random", O_RDONLY | O_CLOEXEC);
148     if (file == -1)
149     {
150       emit_error(errno, ec, "boost::filesystem::unique_path");
151       return;
152     }
153   }
154 
155   std::size_t bytes_read = 0;
156   while (bytes_read < len)
157   {
158     ssize_t n = read(file, buf, len - bytes_read);
159     if (BOOST_UNLIKELY(n == -1))
160     {
161       int err = errno;
162       if (err == EINTR)
163         continue;
164       close(file);
165       emit_error(err, ec, "boost::filesystem::unique_path");
166       return;
167     }
168     bytes_read += n;
169     buf = static_cast<char*>(buf) + n;
170   }
171 
172   close(file);
173 
174 #endif
175 
176 #else // defined(BOOST_POSIX_API)
177 
178 #if defined(BOOST_FILESYSTEM_HAS_BCRYPT)
179 
180   boost::winapi::BCRYPT_ALG_HANDLE_ handle;
181   boost::winapi::NTSTATUS_ status = boost::winapi::BCryptOpenAlgorithmProvider(&handle, boost::winapi::BCRYPT_RNG_ALGORITHM_, NULL, 0);
182   if (BOOST_UNLIKELY(status != 0))
183   {
184   fail:
185     emit_error(translate_ntstatus(status), ec, "boost::filesystem::unique_path");
186     return;
187   }
188 
189   status = boost::winapi::BCryptGenRandom(handle, static_cast<boost::winapi::PUCHAR_>(buf), static_cast<boost::winapi::ULONG_>(len), 0);
190 
191   boost::winapi::BCryptCloseAlgorithmProvider(handle, 0);
192 
193   if (BOOST_UNLIKELY(status != 0))
194     goto fail;
195 
196 #else // defined(BOOST_FILESYSTEM_HAS_BCRYPT)
197 
198   boost::winapi::HCRYPTPROV_ handle;
199   boost::winapi::DWORD_ err = 0u;
200   if (BOOST_UNLIKELY(!boost::winapi::CryptAcquireContextW(&handle, NULL, NULL, boost::winapi::PROV_RSA_FULL_, boost::winapi::CRYPT_VERIFYCONTEXT_ | boost::winapi::CRYPT_SILENT_)))
201   {
202     err = boost::winapi::GetLastError();
203 
204   fail:
205     emit_error(err, ec, "boost::filesystem::unique_path");
206     return;
207   }
208 
209   boost::winapi::BOOL_ gen_ok = boost::winapi::CryptGenRandom(handle, static_cast<boost::winapi::DWORD_>(len), static_cast<boost::winapi::BYTE_*>(buf));
210 
211   if (BOOST_UNLIKELY(!gen_ok))
212     err = boost::winapi::GetLastError();
213 
214   boost::winapi::CryptReleaseContext(handle, 0);
215 
216   if (BOOST_UNLIKELY(!gen_ok))
217     goto fail;
218 
219 #endif // defined(BOOST_FILESYSTEM_HAS_BCRYPT)
220 
221 #endif // defined(BOOST_POSIX_API)
222 }
223 
224 #ifdef BOOST_WINDOWS_API
225 BOOST_CONSTEXPR_OR_CONST wchar_t hex[] = L"0123456789abcdef";
226 BOOST_CONSTEXPR_OR_CONST wchar_t percent = L'%';
227 #else
228 BOOST_CONSTEXPR_OR_CONST char hex[] = "0123456789abcdef";
229 BOOST_CONSTEXPR_OR_CONST char percent = '%';
230 #endif
231 
232 }  // unnamed namespace
233 
234 BOOST_FILESYSTEM_DECL
unique_path(const path & model,system::error_code * ec)235 path unique_path(const path& model, system::error_code* ec)
236 {
237   // This function used wstring for fear of misidentifying
238   // a part of a multibyte character as a percent sign.
239   // However, double byte encodings only have 80-FF as lead
240   // bytes and 40-7F as trailing bytes, whereas % is 25.
241   // So, use string on POSIX and avoid conversions.
242 
243   path::string_type s( model.native() );
244 
245   char ran[16] = {};  // init to avoid clang static analyzer message
246                       // see ticket #8954
247   BOOST_CONSTEXPR_OR_CONST unsigned int max_nibbles = 2u * sizeof(ran);   // 4-bits per nibble
248 
249   unsigned int nibbles_used = max_nibbles;
250   for (path::string_type::size_type i = 0, n = s.size(); i < n; ++i)
251   {
252     if (s[i] == percent)                     // digit request
253     {
254       if (nibbles_used == max_nibbles)
255       {
256         system_crypt_random(ran, sizeof(ran), ec);
257         if (ec != 0 && *ec)
258           return path();
259         nibbles_used = 0;
260       }
261       unsigned int c = ran[nibbles_used / 2u];
262       c >>= 4u * (nibbles_used++ & 1u);  // if odd, shift right 1 nibble
263       s[i] = hex[c & 0xf];               // convert to hex digit and replace
264     }
265   }
266 
267   if (ec != 0) ec->clear();
268 
269   return s;
270 }
271 
272 }}}
273