blob: 1569b32e91538000c6f4b3c93d29dba56a732b15 [file] [log] [blame]
// filesystem system_crypt_random.cpp ------------------------------------------------//
// Copyright Beman Dawes 2010
// Distributed under the Boost Software License, Version 1.0.
// See http://www.boost.org/LICENSE_1_0.txt
// Library home page: http://www.boost.org/libs/filesystem
//--------------------------------------------------------------------------------------//
#include <boost/config.hpp>
#if !defined( BOOST_NO_STD_WSTRING )
// Boost.Filesystem V3 and later requires std::wstring support.
// During the transition to V3, libraries are compiled with both V2 and V3 sources.
// On old compilers that don't support V3 anyhow, we just skip everything so the compile
// will succeed and the library can be built.
// define BOOST_FILESYSTEM_SOURCE so that <boost/filesystem/config.hpp> knows
// the library is being built (possibly exporting rather than importing code)
#define BOOST_FILESYSTEM_SOURCE
#ifndef BOOST_SYSTEM_NO_DEPRECATED
# define BOOST_SYSTEM_NO_DEPRECATED
#endif
#include <boost/filesystem/v3/operations.hpp>
# ifdef BOOST_POSIX_API
# include <fcntl.h>
# else // BOOST_WINDOWS_API
# include <windows.h>
# include <wincrypt.h>
# pragma comment(lib, "Advapi32.lib")
# endif
namespace {
void fail(int err, boost::system::error_code* ec)
{
if (ec == 0)
BOOST_FILESYSTEM_THROW( boost::system::system_error(err,
boost::system::system_category(),
"boost::filesystem::unique_path"));
ec->assign(err, boost::system::system_category());
return;
}
void system_crypt_random(void* buf, std::size_t len, boost::system::error_code* ec)
{
# ifdef BOOST_POSIX_API
int file = open("/dev/urandom", O_RDONLY);
if (file == -1)
{
file = open("/dev/random", O_RDONLY);
if (file == -1)
{
fail(errno, ec);
return;
}
}
size_t bytes_read = 0;
while (bytes_read < len)
{
ssize_t n = read(file, buf, len - bytes_read);
if (n == -1)
{
close(file);
fail(errno, ec);
return;
}
bytes_read += n;
buf = static_cast<char*>(buf) + n;
}
close(file);
# else // BOOST_WINDOWS_API
HCRYPTPROV handle;
int errval = 0;
if (!::CryptAcquireContextW(&handle, 0, 0, PROV_RSA_FULL, 0))
{
errval = ::GetLastError();
if (errval == NTE_BAD_KEYSET)
{
if (!::CryptAcquireContextW(&handle, 0, 0, PROV_RSA_FULL, CRYPT_NEWKEYSET))
{
errval = ::GetLastError();
}
else errval = 0;
}
}
if (!errval)
{
BOOL gen_ok = ::CryptGenRandom(handle, len, static_cast<unsigned char*>(buf));
if (!gen_ok)
errval = ::GetLastError();
::CryptReleaseContext(handle, 0);
}
if (!errval) return;
fail(errval, ec);
# endif
}
} // unnamed namespace
namespace boost { namespace filesystem3 { namespace detail {
BOOST_FILESYSTEM_DECL
path unique_path(const path& model, system::error_code* ec)
{
std::wstring s (model.wstring()); // std::string ng for MBCS encoded POSIX
const wchar_t hex[] = L"0123456789abcdef";
const int n_ran = 16;
const int max_nibbles = 2 * n_ran; // 4-bits per nibble
char ran[n_ran];
int nibbles_used = max_nibbles;
for(std::wstring::size_type i=0; i < s.size(); ++i)
{
if (s[i] == L'%') // digit request
{
if (nibbles_used == max_nibbles)
{
system_crypt_random(ran, sizeof(ran), ec);
if (ec != 0 && *ec)
return "";
nibbles_used = 0;
}
int c = ran[nibbles_used/2];
c >>= 4 * (nibbles_used++ & 1); // if odd, shift right 1 nibble
s[i] = hex[c & 0xf]; // convert to hex digit and replace
}
}
if (ec != 0) ec->clear();
return s;
}
}}}
#endif // no wide character support