1 // filesystem unique_path.cpp --------------------------------------------------------//
3 // Copyright Beman Dawes 2010
5 // Distributed under the Boost Software License, Version 1.0.
6 // See http://www.boost.org/LICENSE_1_0.txt
8 // Library home page: http://www.boost.org/libs/filesystem
10 //--------------------------------------------------------------------------------------//
12 #ifndef BOOST_SYSTEM_NO_DEPRECATED
13 # define BOOST_SYSTEM_NO_DEPRECATED
16 #include <boost/filesystem/operations.hpp>
19 # ifdef BOOST_POSIX_API
21 # ifdef BOOST_HAS_UNISTD_H
24 # else // BOOST_WINDOWS_API
26 # include <wincrypt.h>
28 # pragma comment(lib, "Advapi32.lib")
34 void fail(int err, boost::system::error_code* ec)
37 BOOST_FILESYSTEM_THROW( boost::system::system_error(err,
38 boost::system::system_category(),
39 "boost::filesystem::unique_path"));
41 ec->assign(err, boost::system::system_category());
45 #ifdef BOOST_WINDOWS_API
47 int acquire_crypt_handle(HCRYPTPROV& handle)
49 if (::CryptAcquireContextW(&handle, 0, 0, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT | CRYPT_SILENT))
52 int errval = ::GetLastError();
53 if (errval != NTE_BAD_KEYSET)
56 if (::CryptAcquireContextW(&handle, 0, 0, PROV_RSA_FULL, CRYPT_NEWKEYSET | CRYPT_VERIFYCONTEXT | CRYPT_SILENT))
59 errval = ::GetLastError();
60 // Another thread could have attempted to create the keyset at the same time.
61 if (errval != NTE_EXISTS)
64 if (::CryptAcquireContextW(&handle, 0, 0, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT | CRYPT_SILENT))
67 return ::GetLastError();
72 void system_crypt_random(void* buf, std::size_t len, boost::system::error_code* ec)
74 # ifdef BOOST_POSIX_API
76 int file = open("/dev/urandom", O_RDONLY);
79 file = open("/dev/random", O_RDONLY);
87 size_t bytes_read = 0;
88 while (bytes_read < len)
90 ssize_t n = read(file, buf, len - bytes_read);
98 buf = static_cast<char*>(buf) + n;
103 # else // BOOST_WINDOWS_API
106 int errval = acquire_crypt_handle(handle);
110 BOOL gen_ok = ::CryptGenRandom(handle, static_cast<DWORD>(len), static_cast<unsigned char*>(buf));
112 errval = ::GetLastError();
113 ::CryptReleaseContext(handle, 0);
122 } // unnamed namespace
124 namespace boost { namespace filesystem { namespace detail {
126 BOOST_FILESYSTEM_DECL
127 path unique_path(const path& model, system::error_code* ec)
129 // This function used wstring for fear of misidentifying
130 // a part of a multibyte character as a percent sign.
131 // However, double byte encodings only have 80-FF as lead
132 // bytes and 40-7F as trailing bytes, whereas % is 25.
133 // So, use string on POSIX and avoid conversions.
135 path::string_type s( model.native() );
137 #ifdef BOOST_WINDOWS_API
138 const wchar_t hex[] = L"0123456789abcdef";
139 const wchar_t percent = L'%';
141 const char hex[] = "0123456789abcdef";
142 const char percent = '%';
145 char ran[] = "123456789abcdef"; // init to avoid clang static analyzer message
147 assert(sizeof(ran) == 16);
148 const int max_nibbles = 2 * sizeof(ran); // 4-bits per nibble
150 int nibbles_used = max_nibbles;
151 for(path::string_type::size_type i=0; i < s.size(); ++i)
153 if (s[i] == percent) // digit request
155 if (nibbles_used == max_nibbles)
157 system_crypt_random(ran, sizeof(ran), ec);
162 int c = ran[nibbles_used/2];
163 c >>= 4 * (nibbles_used++ & 1); // if odd, shift right 1 nibble
164 s[i] = hex[c & 0xf]; // convert to hex digit and replace
168 if (ec != 0) ec->clear();