diff options
author | Michael J. Spencer <bigcheesegs@gmail.com> | 2010-12-03 01:21:28 +0000 |
---|---|---|
committer | Michael J. Spencer <bigcheesegs@gmail.com> | 2010-12-03 01:21:28 +0000 |
commit | 3cb84ef65dd417bc152fdaa173127966ca949318 (patch) | |
tree | 133d904ceb53dd1218f7c6857f0087c837960393 /lib/Support/Windows | |
parent | f4e7b167a09df546b4b339ca9ebc445f747a724a (diff) |
Support/FileSystem: Add unique_file and exists implementations.
git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@120776 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'lib/Support/Windows')
-rw-r--r-- | lib/Support/Windows/PathV2.inc | 204 | ||||
-rw-r--r-- | lib/Support/Windows/system_error.inc | 1 |
2 files changed, 205 insertions, 0 deletions
diff --git a/lib/Support/Windows/PathV2.inc b/lib/Support/Windows/PathV2.inc index b1f8ae00d3..909deb0418 100644 --- a/lib/Support/Windows/PathV2.inc +++ b/lib/Support/Windows/PathV2.inc @@ -17,6 +17,8 @@ //===----------------------------------------------------------------------===// #include "Windows.h" +#include <WinCrypt.h> +#include <io.h> using namespace llvm; @@ -46,6 +48,62 @@ namespace { return make_error_code(errc::success); } + + error_code UTF16ToUTF8(const wchar_t *utf16, size_t utf16_len, + SmallVectorImpl<char> &utf8) { + // Get length. + int len = ::WideCharToMultiByte(CP_UTF8, NULL, + utf16, utf16_len, + utf8.begin(), 0, + NULL, NULL); + + if (len == 0) + return make_error_code(windows_error(::GetLastError())); + + utf8.reserve(len); + utf8.set_size(len); + + // Now do the actual conversion. + len = ::WideCharToMultiByte(CP_UTF8, NULL, + utf16, utf16_len, + utf8.data(), utf8.size(), + NULL, NULL); + + if (len == 0) + return make_error_code(windows_error(::GetLastError())); + + // Make utf8 null terminated. + utf8.push_back(0); + utf8.pop_back(); + + return make_error_code(errc::success); + } + + error_code TempDir(SmallVectorImpl<wchar_t> &result) { + retry_temp_dir: + DWORD len = ::GetTempPathW(result.capacity(), result.begin()); + + if (len == 0) + return make_error_code(windows_error(::GetLastError())); + + if (len > result.capacity()) { + result.reserve(len); + goto retry_temp_dir; + } + + result.set_size(len); + return make_error_code(errc::success); + } + + struct AutoCryptoProvider { + HCRYPTPROV CryptoProvider; + + ~AutoCryptoProvider() { + ::CryptReleaseContext(CryptoProvider, 0); + } + + operator HCRYPTPROV() const {return CryptoProvider;} + }; } namespace llvm { @@ -123,6 +181,152 @@ error_code copy_file(const Twine &from, const Twine &to, copy_option copt) { return make_error_code(errc::success); } +error_code exists(const Twine &path, bool &result) { + SmallString<128> path_storage; + SmallVector<wchar_t, 128> path_utf16; + + if (error_code ec = UTF8ToUTF16(path.toStringRef(path_storage), + path_utf16)) + return ec; + + DWORD attributes = ::GetFileAttributesW(path_utf16.begin()); + + if (attributes == INVALID_FILE_ATTRIBUTES) { + // See if the file didn't actually exist. + error_code ec = make_error_code(windows_error(::GetLastError())); + if (ec != error_code(windows_error::file_not_found) && + ec != error_code(windows_error::path_not_found)) + return ec; + result = false; + } else + result = true; + return make_error_code(errc::success); +} + +error_code unique_file(const Twine &model, int &result_fd, + SmallVectorImpl<char> &result_path) { + // Use result_path as temp storage. + result_path.set_size(0); + StringRef m = model.toStringRef(result_path); + + SmallVector<wchar_t, 128> model_utf16; + if (error_code ec = UTF8ToUTF16(m, model_utf16)) return ec; + + // Make model absolute by prepending a temp directory if it's not already. + bool absolute; + if (error_code ec = path::is_absolute(m, absolute)) return ec; + + if (!absolute) { + SmallVector<wchar_t, 64> temp_dir; + if (error_code ec = TempDir(temp_dir)) return ec; + // Handle c: by removing it. + if (model_utf16.size() > 2 && model_utf16[1] == L':') { + model_utf16.erase(model_utf16.begin(), model_utf16.begin() + 2); + } + model_utf16.insert(model_utf16.begin(), temp_dir.begin(), temp_dir.end()); + } + + // Replace '%' with random chars. From here on, DO NOT modify model. It may be + // needed if the randomly chosen path already exists. + SmallVector<wchar_t, 128> random_path_utf16; + + // Get a Crypto Provider for CryptGenRandom. + AutoCryptoProvider CryptoProvider; + BOOL success = ::CryptAcquireContextW(&CryptoProvider.CryptoProvider, + NULL, + NULL, + PROV_RSA_FULL, + NULL); + if (!success) + return make_error_code(windows_error(::GetLastError())); + +retry_random_path: + random_path_utf16.set_size(0); + for (SmallVectorImpl<wchar_t>::const_iterator i = model_utf16.begin(), + e = model_utf16.end(); + i != e; ++i) { + if (*i == L'%') { + BYTE val = 0; + if (!::CryptGenRandom(CryptoProvider, 1, &val)) + return make_error_code(windows_error(::GetLastError())); + random_path_utf16.push_back("0123456789abcdef"[val & 15]); + } + else + random_path_utf16.push_back(*i); + } + // Make random_path_utf16 null terminated. + random_path_utf16.push_back(0); + random_path_utf16.pop_back(); + + // Try to create + open the path. +retry_create_file: + HANDLE TempFileHandle = ::CreateFileW(random_path_utf16.begin(), + GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ, + NULL, + // Return ERROR_FILE_EXISTS if the file + // already exists. + CREATE_NEW, + FILE_ATTRIBUTE_TEMPORARY, + NULL); + if (TempFileHandle == INVALID_HANDLE_VALUE) { + // If the file existed, try again, otherwise, error. + error_code ec = make_error_code(windows_error(::GetLastError())); + if (ec == error_code(windows_error::file_exists)) + goto retry_random_path; + // Check for non-existing parent directories. + if (ec == error_code(windows_error::path_not_found)) { + // Create the directories using result_path as temp storage. + if (error_code ec = UTF16ToUTF8(random_path_utf16.begin(), + random_path_utf16.size(), result_path)) + return ec; + StringRef p(result_path.begin(), result_path.size()); + SmallString<64> dir_to_create; + for (path::const_iterator i = path::begin(p), + e = --path::end(p); i != e; ++i) { + if (error_code ec = path::append(dir_to_create, *i)) return ec; + bool Exists; + if (error_code ec = exists(Twine(dir_to_create), Exists)) return ec; + if (!Exists) { + // If c: doesn't exist, bail. + if (i->endswith(":")) + return ec; + + SmallVector<wchar_t, 64> dir_to_create_utf16; + if (error_code ec = UTF8ToUTF16(dir_to_create, dir_to_create_utf16)) + return ec; + + // Create the directory. + if (!::CreateDirectoryW(dir_to_create_utf16.begin(), NULL)) + return make_error_code(windows_error(::GetLastError())); + } + } + goto retry_create_file; + } + return ec; + } + + // Set result_path to the utf-8 representation of the path. + if (error_code ec = UTF16ToUTF8(random_path_utf16.begin(), + random_path_utf16.size(), result_path)) { + ::CloseHandle(TempFileHandle); + ::DeleteFileW(random_path_utf16.begin()); + return ec; + } + + // Convert the Windows API file handle into a C-runtime handle. + int fd = ::_open_osfhandle(intptr_t(TempFileHandle), 0); + if (fd == -1) { + ::CloseHandle(TempFileHandle); + ::DeleteFileW(random_path_utf16.begin()); + // MSDN doesn't say anything about _open_osfhandle setting errno or + // GetLastError(), so just return invalid_handle. + return make_error_code(windows_error::invalid_handle); + } + + result_fd = fd; + return make_error_code(errc::success); +} } // end namespace fs } // end namespace sys } // end namespace llvm diff --git a/lib/Support/Windows/system_error.inc b/lib/Support/Windows/system_error.inc index 8dc4799e7c..73304d517f 100644 --- a/lib/Support/Windows/system_error.inc +++ b/lib/Support/Windows/system_error.inc @@ -96,6 +96,7 @@ _system_error_category::default_error_condition(int ev) const { MAP_ERR_TO_COND(ERROR_OPERATION_ABORTED, operation_canceled); MAP_ERR_TO_COND(ERROR_OUTOFMEMORY, not_enough_memory); MAP_ERR_TO_COND(ERROR_PATH_NOT_FOUND, no_such_file_or_directory); + MAP_ERR_TO_COND(ERROR_BAD_NETPATH, no_such_file_or_directory); MAP_ERR_TO_COND(ERROR_READ_FAULT, io_error); MAP_ERR_TO_COND(ERROR_RETRY, resource_unavailable_try_again); MAP_ERR_TO_COND(ERROR_SEEK, io_error); |