1// filesystem unique_path.cpp --------------------------------------------------------//
2
3// Copyright Beman Dawes 2010
4
5// Distributed under the Boost Software License, Version 1.0.
6// See http://www.boost.org/LICENSE_1_0.txt
7
8// Library home page: http://www.boost.org/libs/filesystem
9
10//--------------------------------------------------------------------------------------//
11
12// define BOOST_FILESYSTEM_SOURCE so that <boost/filesystem/config.hpp> knows
13// the library is being built (possibly exporting rather than importing code)
14#define BOOST_FILESYSTEM_SOURCE
15
16#ifndef BOOST_SYSTEM_NO_DEPRECATED
17# define BOOST_SYSTEM_NO_DEPRECATED
18#endif
19
20#include <boost/filesystem/operations.hpp>
21#include <cassert>
22
23# ifdef BOOST_POSIX_API
24# include <fcntl.h>
25# ifdef BOOST_HAS_UNISTD_H
26# include <unistd.h>
27# endif
28# else // BOOST_WINDOWS_API
29# include <windows.h>
30# include <wincrypt.h>
31# ifdef _MSC_VER
32# pragma comment(lib, "Advapi32.lib")
33# endif
34# endif
35
36namespace {
37
38void fail(int err, boost::system::error_code* ec)
39{
40 if (ec == 0)
41 BOOST_FILESYSTEM_THROW( boost::system::system_error(err,
42 boost::system::system_category(),
43 "boost::filesystem::unique_path"));
44
45 ec->assign(err, boost::system::system_category());
46 return;
47}
48
49#ifdef BOOST_WINDOWS_API
50
51int acquire_crypt_handle(HCRYPTPROV& handle)
52{
53 if (::CryptAcquireContextW(&handle, 0, 0, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT | CRYPT_SILENT))
54 return 0;
55
56 int errval = ::GetLastError();
57 if (errval != NTE_BAD_KEYSET)
58 return errval;
59
60 if (::CryptAcquireContextW(&handle, 0, 0, PROV_RSA_FULL, CRYPT_NEWKEYSET | CRYPT_VERIFYCONTEXT | CRYPT_SILENT))
61 return 0;
62
63 errval = ::GetLastError();
64 // Another thread could have attempted to create the keyset at the same time.
65 if (errval != NTE_EXISTS)
66 return errval;
67
68 if (::CryptAcquireContextW(&handle, 0, 0, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT | CRYPT_SILENT))
69 return 0;
70
71 return ::GetLastError();
72}
73
74#endif
75
76void system_crypt_random(void* buf, std::size_t len, boost::system::error_code* ec)
77{
78# ifdef BOOST_POSIX_API
79
80 int file = open("/dev/urandom", O_RDONLY);
81 if (file == -1)
82 {
83 file = open("/dev/random", O_RDONLY);
84 if (file == -1)
85 {
86 fail(errno, ec);
87 return;
88 }
89 }
90
91 size_t bytes_read = 0;
92 while (bytes_read < len)
93 {
94 ssize_t n = read(file, buf, len - bytes_read);
95 if (n == -1)
96 {
97 close(file);
98 fail(errno, ec);
99 return;
100 }
101 bytes_read += n;
102 buf = static_cast<char*>(buf) + n;
103 }
104
105 close(file);
106
107# else // BOOST_WINDOWS_API
108
109 HCRYPTPROV handle;
110 int errval = acquire_crypt_handle(handle);
111
112 if (!errval)
113 {
114 BOOL gen_ok = ::CryptGenRandom(handle, static_cast<DWORD>(len), static_cast<unsigned char*>(buf));
115 if (!gen_ok)
116 errval = ::GetLastError();
117 ::CryptReleaseContext(handle, 0);
118 }
119
120 if (!errval) return;
121
122 fail(errval, ec);
123# endif
124}
125
126} // unnamed namespace
127
128namespace boost { namespace filesystem { namespace detail {
129
130BOOST_FILESYSTEM_DECL
131path unique_path(const path& model, system::error_code* ec)
132{
133 // This function used wstring for fear of misidentifying
134 // a part of a multibyte character as a percent sign.
135 // However, double byte encodings only have 80-FF as lead
136 // bytes and 40-7F as trailing bytes, whereas % is 25.
137 // So, use string on POSIX and avoid conversions.
138
139 path::string_type s( model.native() );
140
141#ifdef BOOST_WINDOWS_API
142 const wchar_t hex[] = L"0123456789abcdef";
143 const wchar_t percent = L'%';
144#else
145 const char hex[] = "0123456789abcdef";
146 const char percent = '%';
147#endif
148
149 char ran[] = "123456789abcdef"; // init to avoid clang static analyzer message
150 // see ticket #8954
151 assert(sizeof(ran) == 16);
152 const int max_nibbles = 2 * sizeof(ran); // 4-bits per nibble
153
154 int nibbles_used = max_nibbles;
155 for(path::string_type::size_type i=0; i < s.size(); ++i)
156 {
157 if (s[i] == percent) // digit request
158 {
159 if (nibbles_used == max_nibbles)
160 {
161 system_crypt_random(ran, sizeof(ran), ec);
162 if (ec != 0 && *ec)
163 return "";
164 nibbles_used = 0;
165 }
166 int c = ran[nibbles_used/2];
167 c >>= 4 * (nibbles_used++ & 1); // if odd, shift right 1 nibble
168 s[i] = hex[c & 0xf]; // convert to hex digit and replace
169 }
170 }
171
172 if (ec != 0) ec->clear();
173
174 return s;
175}
176
177}}}
178