From febb9c275b5247df596876e4eea7fa17b7ec45e7 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Wed, 22 Aug 2018 17:26:08 +0200 Subject: Add support for UUID generation --- libbutl/uuid-windows.cxx | 131 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 131 insertions(+) create mode 100644 libbutl/uuid-windows.cxx (limited to 'libbutl/uuid-windows.cxx') diff --git a/libbutl/uuid-windows.cxx b/libbutl/uuid-windows.cxx new file mode 100644 index 0000000..825505b --- /dev/null +++ b/libbutl/uuid-windows.cxx @@ -0,0 +1,131 @@ +// file : libbutl/uuid-windows.cxx -*- C++ -*- +// copyright : Copyright (c) 2014-2018 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#ifndef BUILD2_BOOTSTRAP + +#include + +#include // UuidCreate*() +#include + +#include +#include + +using namespace std; + +namespace butl +{ + void uuid:: + assign (const _GUID& g) + { + time_low = g.Data1; + time_mid = g.Data2; + time_hiv = g.Data3; + clock_seq_hir = g.Data4[0]; + clock_seq_low = g.Data4[1]; + memcpy (node, &g.Data4[2], 6); + } + + template<> + LIBBUTL_SYMEXPORT _GUID uuid:: + guid<_GUID> () const + { + _GUID r; + r.Data1 = time_low; + r.Data2 = time_mid; + r.Data3 = time_hiv; + r.Data4[0] = clock_seq_hir; + r.Data4[1] = clock_seq_low; + memcpy (&r.Data4[2], node, 6); + return r; + } + + void + uuid_throw_weak (); // uuid.cxx + + uuid uuid_system_generator:: + generate (bool strong) + { + // The common way to generate a UUID on Windows is with the CoCreateGuid() + // function which, according to the documentation, calls UuidCreate(). + // There is some talk of it somehow generating stronger UUIDs but it is + // probably bogus (it most likely simply returns an error if UuidCreate() + // returns a "weak" UUID). + // + // UuidCreate(), on the other hand, has various interesting return codes + // as well as the UuidCreateSequential() variant which always generates a + // MAC/time-based UUID (according to the documentation, since Windows 2000 + // UuidCreate() always generates a random UUID). So let's use that. + // + // While the documentation doesn't explicitly state this, presumably + // UuidCreate() uses a "decent" source of randomness. See this post for + // some background: + // + // https://stackoverflow.com/questions/35366368/does-uuidcreate-use-a-csprng + // + // So we assume a random UUID returned by UuidCreate() is strong. What to + // do if it's not is a tricky question (is it even possible?): we can call + // UuidCreateSequential() to generate a MAC/time-based UUID but it's + // possible the time was not obtained in a collision-safe manner (see the + // Linux implementation for background). However, the documentation + // suggests that it is safe ("... guaranteed to be unique among all UUIDs + // generated on the computer"). And so we assume it is. + // + auto rpcfail = [strong] (RPC_STATUS s) + { + if (s != RPC_S_UUID_LOCAL_ONLY) + { + char m [DCE_C_ERROR_STRING_LEN]; + + throw system_error ( + ENOSYS, + system_category (), + (DceErrorInqTextA (s, reinterpret_cast (m)) == RPC_S_OK + ? m + : "unknown RPC error")); + } + else if (strong) + uuid_throw_weak (); + }; + + UUID d; + RPC_STATUS s (UuidCreate (&d)); + + if (s != RPC_S_OK) + rpcfail (s); + + uuid r (d); + assert (r.variant () == uuid_variant::dce); // Sanity check. + + if (strong && r.version () != uuid_version::random) + { + s = UuidCreateSequential (&d); + + if (s != RPC_S_OK) + rpcfail (s); + + r.assign (d); + + // UuidCreateSequential() on Wine returns the Microsoft variant. + // + if (r.variant () != uuid_variant::dce || + r.version () != uuid_version::time) + rpcfail (RPC_S_UUID_LOCAL_ONLY); + } + + return r; + } + + void uuid_system_generator:: + initialize () + { + } + + void uuid_system_generator:: + terminate () + { + } +} + +#endif // BUILD2_BOOTSTRAP -- cgit v1.1