]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
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> | |
b32b8144 FG |
31 | # ifdef _MSC_VER |
32 | # pragma comment(lib, "Advapi32.lib") | |
33 | # endif | |
7c673cae FG |
34 | # endif |
35 | ||
36 | namespace { | |
37 | ||
38 | void 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 | ||
51 | int 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 | ||
76 | void 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, 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 | ||
128 | namespace boost { namespace filesystem { namespace detail { | |
129 | ||
130 | BOOST_FILESYSTEM_DECL | |
131 | path unique_path(const path& model, system::error_code* ec) | |
132 | { | |
133 | std::wstring s (model.wstring()); // std::string ng for MBCS encoded POSIX | |
134 | const wchar_t hex[] = L"0123456789abcdef"; | |
135 | char ran[] = "123456789abcdef"; // init to avoid clang static analyzer message | |
136 | // see ticket #8954 | |
137 | assert(sizeof(ran) == 16); | |
138 | const int max_nibbles = 2 * sizeof(ran); // 4-bits per nibble | |
139 | ||
140 | int nibbles_used = max_nibbles; | |
141 | for(std::wstring::size_type i=0; i < s.size(); ++i) | |
142 | { | |
143 | if (s[i] == L'%') // digit request | |
144 | { | |
145 | if (nibbles_used == max_nibbles) | |
146 | { | |
147 | system_crypt_random(ran, sizeof(ran), ec); | |
148 | if (ec != 0 && *ec) | |
149 | return ""; | |
150 | nibbles_used = 0; | |
151 | } | |
152 | int c = ran[nibbles_used/2]; | |
153 | c >>= 4 * (nibbles_used++ & 1); // if odd, shift right 1 nibble | |
154 | s[i] = hex[c & 0xf]; // convert to hex digit and replace | |
155 | } | |
156 | } | |
157 | ||
158 | if (ec != 0) ec->clear(); | |
159 | ||
160 | return s; | |
161 | } | |
162 | ||
163 | }}} |