aboutsummaryrefslogtreecommitdiffstats
path: root/lib/Support/Windows
diff options
context:
space:
mode:
authorMichael J. Spencer <bigcheesegs@gmail.com>2010-12-03 01:21:28 +0000
committerMichael J. Spencer <bigcheesegs@gmail.com>2010-12-03 01:21:28 +0000
commit3cb84ef65dd417bc152fdaa173127966ca949318 (patch)
tree133d904ceb53dd1218f7c6857f0087c837960393 /lib/Support/Windows
parentf4e7b167a09df546b4b339ca9ebc445f747a724a (diff)
downloadexternal_llvm-3cb84ef65dd417bc152fdaa173127966ca949318.zip
external_llvm-3cb84ef65dd417bc152fdaa173127966ca949318.tar.gz
external_llvm-3cb84ef65dd417bc152fdaa173127966ca949318.tar.bz2
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.inc204
-rw-r--r--lib/Support/Windows/system_error.inc1
2 files changed, 205 insertions, 0 deletions
diff --git a/lib/Support/Windows/PathV2.inc b/lib/Support/Windows/PathV2.inc
index b1f8ae0..909deb0 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 8dc4799..73304d5 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);